summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mkosi/mkosi.arch1
-rw-r--r--.packit.yml2
-rw-r--r--TODO65
-rw-r--r--docs/COREDUMP_PACKAGE_METADATA.md106
-rw-r--r--docs/DISCOVERABLE_PARTITIONS.md6
-rw-r--r--docs/JOURNAL_NATIVE_PROTOCOL.md190
-rw-r--r--hwdb.d/60-autosuspend.hwdb8
-rw-r--r--hwdb.d/60-keyboard.hwdb46
-rw-r--r--hwdb.d/60-sensor.hwdb4
-rw-r--r--man/homectl.xml4
-rw-r--r--man/meson.build25
-rw-r--r--man/networkd.conf.xml57
-rw-r--r--man/oomd.conf.xml8
-rw-r--r--man/org.freedesktop.portable1.xml8
-rw-r--r--man/org.freedesktop.systemd1.xml42
-rw-r--r--man/os-release.xml4
-rw-r--r--man/repart.d.xml81
-rw-r--r--man/rules/meson.build7
-rw-r--r--man/sd-id128.xml80
-rw-r--r--man/sd_bus_is_open.xml7
-rw-r--r--man/sd_journal_print.xml3
-rw-r--r--man/sd_login_monitor_new.xml2
-rw-r--r--man/systemd-coredump.xml14
-rw-r--r--man/systemd-cryptenroll.xml2
-rw-r--r--man/systemd-cryptsetup@.service.xml15
-rw-r--r--man/systemd-dissect.xml4
-rw-r--r--man/systemd-journald.service.xml7
-rw-r--r--man/systemd-networkd-wait-online.service.xml28
-rw-r--r--man/systemd-oomd.service.xml7
-rw-r--r--man/systemd-repart.xml33
-rw-r--r--man/systemd-tmpfiles.xml22
-rw-r--r--man/systemd-udevd.service.xml6
-rw-r--r--man/systemd.exec.xml12
-rw-r--r--man/systemd.network.xml112
-rw-r--r--man/systemd.resource-control.xml52
-rw-r--r--man/systemd.special.xml40
-rw-r--r--man/tmpfiles.d.xml4
-rw-r--r--man/udev.conf.xml4
-rw-r--r--meson.build231
-rwxr-xr-xmkosi.build12
-rw-r--r--po/ko.po10
-rw-r--r--rules.d/81-net-dhcp.rules14
-rw-r--r--rules.d/meson.build1
-rw-r--r--shell-completion/bash/machinectl2
-rw-r--r--shell-completion/bash/networkctl2
-rw-r--r--shell-completion/bash/systemd-analyze2
-rw-r--r--shell-completion/bash/systemd-cat2
-rw-r--r--shell-completion/bash/systemd-cgls2
-rw-r--r--shell-completion/bash/systemd-cgtop2
-rw-r--r--shell-completion/bash/systemd-delta2
-rw-r--r--shell-completion/bash/systemd-detect-virt2
-rw-r--r--shell-completion/bash/systemd-id1282
-rw-r--r--shell-completion/bash/systemd-nspawn2
-rw-r--r--shell-completion/bash/systemd-path2
-rw-r--r--shell-completion/zsh/_localectl2
-rw-r--r--src/activate/activate.c6
-rw-r--r--src/analyze/analyze.c2
-rw-r--r--src/basic/build.h5
-rw-r--r--src/basic/cgroup-util.c3
-rw-r--r--src/basic/cgroup-util.h4
-rw-r--r--src/basic/copy.c2
-rw-r--r--src/basic/device-nodes.c17
-rw-r--r--src/basic/efivars.c2
-rw-r--r--src/basic/errno-util.h2
-rw-r--r--src/basic/fileio.c9
-rw-r--r--src/basic/fileio.h21
-rw-r--r--src/basic/fs-util.c21
-rw-r--r--src/basic/fs-util.h2
-rwxr-xr-xsrc/basic/generate-af-list.sh9
-rwxr-xr-xsrc/basic/generate-arphrd-list.sh9
-rwxr-xr-xsrc/basic/generate-cap-list.sh9
-rwxr-xr-xsrc/basic/generate-errno-list.sh7
-rw-r--r--src/basic/in-addr-util.c43
-rw-r--r--src/basic/in-addr-util.h8
-rwxr-xr-xsrc/basic/linux/update.sh9
-rw-r--r--src/basic/locale-util.c4
-rw-r--r--src/basic/locale-util.h3
-rw-r--r--src/basic/log.h28
-rw-r--r--src/basic/missing_capability.h2
-rw-r--r--src/basic/path-lookup.c2
-rw-r--r--src/basic/path-util.c21
-rw-r--r--src/basic/path-util.h2
-rw-r--r--src/basic/proc-cmdline.c2
-rw-r--r--src/basic/recovery-key.c7
-rw-r--r--src/basic/special.h2
-rw-r--r--src/basic/strbuf.c14
-rw-r--r--src/basic/terminal-util.c4
-rw-r--r--src/basic/time-util.c2
-rw-r--r--src/basic/tmpfile-util.c2
-rw-r--r--src/basic/unit-def.c33
-rw-r--r--src/boot/bless-boot.c2
-rw-r--r--src/boot/bootctl.c2
-rw-r--r--src/boot/efi/boot.c2
-rw-r--r--src/boot/efi/meson.build6
-rwxr-xr-xsrc/boot/efi/no-undefined-symbols.sh5
-rw-r--r--src/boot/efi/stub.c6
-rw-r--r--src/boot/efi/util.c2
-rw-r--r--src/busctl/busctl.c8
-rw-r--r--src/core/bpf-devices.c2
-rw-r--r--src/core/bpf-firewall.c9
-rw-r--r--src/core/bpf-foreign.c151
-rw-r--r--src/core/bpf-foreign.h12
-rw-r--r--src/core/cgroup.c74
-rw-r--r--src/core/cgroup.h10
-rw-r--r--src/core/dbus-cgroup.c122
-rw-r--r--src/core/dbus-execute.c41
-rw-r--r--src/core/dbus-manager.c16
-rw-r--r--src/core/dbus-manager.h1
-rw-r--r--src/core/dbus-path.c2
-rw-r--r--src/core/dbus-scope.c2
-rw-r--r--src/core/dbus-service.c14
-rw-r--r--src/core/dbus-timer.c2
-rw-r--r--src/core/dbus-unit.c36
-rw-r--r--src/core/dbus.c2
-rw-r--r--src/core/execute.c5
-rw-r--r--src/core/job.c2
-rw-r--r--src/core/load-fragment-gperf.gperf.m43
-rw-r--r--src/core/load-fragment.c80
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/main.c4
-rw-r--r--src/core/manager.c9
-rw-r--r--src/core/meson.build2
-rw-r--r--src/core/namespace.c8
-rw-r--r--src/core/path.c2
-rw-r--r--src/core/selinux-access.c9
-rw-r--r--src/core/service.c11
-rw-r--r--src/core/system.conf.in1
-rw-r--r--src/core/timer.c2
-rw-r--r--src/core/unit.c9
-rw-r--r--src/core/unit.h17
-rw-r--r--src/core/user.conf.in2
-rw-r--r--src/coredump/coredump.c36
-rw-r--r--src/coredump/coredump.conf2
-rw-r--r--src/coredump/coredumpctl.c41
-rw-r--r--src/coredump/stacktrace.c219
-rw-r--r--src/coredump/stacktrace.h4
-rw-r--r--src/cryptsetup/cryptsetup-keyfile.c2
-rw-r--r--src/cryptsetup/cryptsetup.c2
-rw-r--r--src/dissect/dissect.c4
-rw-r--r--src/environment-d-generator/environment-d-generator.c2
-rw-r--r--src/fstab-generator/fstab-generator.c99
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c6
-rw-r--r--src/home/homectl.c2
-rw-r--r--src/home/homed-bus.c2
-rw-r--r--src/home/homed-home.c60
-rw-r--r--src/home/homed-manager.c2
-rw-r--r--src/home/homed.conf2
-rw-r--r--src/home/user-record-util.c8
-rw-r--r--src/import/curl-util.c2
-rw-r--r--src/import/importd.c2
-rw-r--r--src/journal-remote/journal-gatewayd.c2
-rw-r--r--src/journal-remote/journal-remote.c2
-rw-r--r--src/journal-remote/journal-remote.conf.in2
-rw-r--r--src/journal-remote/journal-upload.c2
-rw-r--r--src/journal-remote/journal-upload.conf.in2
-rw-r--r--src/journal/journalctl.c8
-rw-r--r--src/journal/journald-kmsg.c9
-rw-r--r--src/journal/journald-stream.c39
-rw-r--r--src/journal/journald.conf2
-rw-r--r--src/libsystemd-network/dhcp-internal.h20
-rw-r--r--src/libsystemd-network/dhcp-network.c15
-rw-r--r--src/libsystemd-network/dhcp-option.c3
-rw-r--r--src/libsystemd-network/dhcp-server-internal.h24
-rw-r--r--src/libsystemd-network/dhcp6-internal.h22
-rw-r--r--src/libsystemd-network/dhcp6-option.c2
-rw-r--r--src/libsystemd-network/fuzz-dhcp6-client.c2
-rw-r--r--src/libsystemd-network/lldp-internal.h22
-rw-r--r--src/libsystemd-network/ndisc-internal.h22
-rw-r--r--src/libsystemd-network/network-internal.h2
-rw-r--r--src/libsystemd-network/radv-internal.h22
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c7
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c60
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c209
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c19
-rw-r--r--src/libsystemd-network/sd-ipv4acd.c20
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c20
-rw-r--r--src/libsystemd-network/test-dhcp-server.c11
-rw-r--r--src/libsystemd/libsystemd.sym1
-rw-r--r--src/libsystemd/sd-bus/bus-error.c28
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c10
-rw-r--r--src/libsystemd/sd-device/device-internal.h2
-rw-r--r--src/libsystemd/sd-device/device-util.h17
-rw-r--r--src/libsystemd/sd-device/sd-device.c49
-rwxr-xr-xsrc/libsystemd/sd-journal/generate-audit_type-list.sh17
-rw-r--r--src/libsystemd/sd-journal/journal-file.c6
-rw-r--r--src/libsystemd/sd-journal/journal-vacuum.c2
-rw-r--r--src/libsystemd/sd-journal/sd-journal.c2
-rw-r--r--src/libsystemd/sd-login/sd-login.c2
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c10
-rw-r--r--src/libsystemd/sd-network/network-util.c9
-rw-r--r--src/libsystemd/sd-network/network-util.h13
-rw-r--r--src/libsystemd/sd-network/sd-network.c36
-rw-r--r--src/libudev/test-libudev.c2
-rw-r--r--src/locale/keymap-util.c2
-rw-r--r--src/locale/localed.c10
-rw-r--r--src/login/logind-core.c2
-rw-r--r--src/login/logind-dbus.c32
-rw-r--r--src/login/logind-seat-dbus.c2
-rw-r--r--src/login/logind-session-dbus.c36
-rw-r--r--src/login/logind-session.c8
-rw-r--r--src/login/logind.c4
-rw-r--r--src/login/logind.conf.in2
-rw-r--r--src/machine/image-dbus.c6
-rw-r--r--src/machine/machine-dbus.c34
-rw-r--r--src/machine/machine.c6
-rw-r--r--src/machine/machined-dbus.c20
-rw-r--r--src/machine/operation.c4
-rw-r--r--src/network/netdev/netdev.c7
-rw-r--r--src/network/netdev/netdev.h37
-rw-r--r--src/network/netdev/tuntap.c2
-rw-r--r--src/network/netdev/wireguard.c7
-rw-r--r--src/network/networkctl.c63
-rw-r--r--src/network/networkd-address.c47
-rw-r--r--src/network/networkd-address.h2
-rw-r--r--src/network/networkd-conf.c153
-rw-r--r--src/network/networkd-conf.h3
-rw-r--r--src/network/networkd-dhcp-common.c473
-rw-r--r--src/network/networkd-dhcp-common.h22
-rw-r--r--src/network/networkd-dhcp-server-bus.c3
-rw-r--r--src/network/networkd-dhcp-server.c34
-rw-r--r--src/network/networkd-dhcp-server.h1
-rw-r--r--src/network/networkd-dhcp4.c710
-rw-r--r--src/network/networkd-dhcp4.h2
-rw-r--r--src/network/networkd-dhcp6.c132
-rw-r--r--src/network/networkd-dhcp6.h2
-rw-r--r--src/network/networkd-gperf.gperf19
-rw-r--r--src/network/networkd-ipv4ll.c18
-rw-r--r--src/network/networkd-link-bus.c6
-rw-r--r--src/network/networkd-link.c115
-rw-r--r--src/network/networkd-link.h3
-rw-r--r--src/network/networkd-lldp-rx.c17
-rw-r--r--src/network/networkd-lldp-tx.c2
-rw-r--r--src/network/networkd-manager-bus.c4
-rw-r--r--src/network/networkd-manager.c23
-rw-r--r--src/network/networkd-manager.h16
-rw-r--r--src/network/networkd-mdb.c12
-rw-r--r--src/network/networkd-ndisc.c36
-rw-r--r--src/network/networkd-ndisc.h1
-rw-r--r--src/network/networkd-network-gperf.gperf29
-rw-r--r--src/network/networkd-network.c73
-rw-r--r--src/network/networkd-network.h23
-rw-r--r--src/network/networkd-nexthop.c6
-rw-r--r--src/network/networkd-radv.c42
-rw-r--r--src/network/networkd-radv.h2
-rw-r--r--src/network/networkd-route.c4
-rw-r--r--src/network/networkd-routing-policy-rule.c2
-rw-r--r--src/network/networkd-state-file.c59
-rw-r--r--src/network/networkd-util.h11
-rw-r--r--src/network/networkd.conf10
-rw-r--r--src/network/wait-online/link.c44
-rw-r--r--src/network/wait-online/link.h3
-rw-r--r--src/network/wait-online/manager.c37
-rw-r--r--src/network/wait-online/manager.h2
-rw-r--r--src/network/wait-online/wait-online.c20
-rw-r--r--src/nspawn/nspawn.c2
-rw-r--r--src/oom/oomd-manager.c291
-rw-r--r--src/oom/oomd-manager.h16
-rw-r--r--src/oom/oomd-util.c67
-rw-r--r--src/oom/oomd-util.h38
-rw-r--r--src/oom/oomd.c3
-rw-r--r--src/oom/oomd.conf2
-rw-r--r--src/oom/test-oomd-util.c78
-rw-r--r--src/partition/repart.c1001
-rwxr-xr-xsrc/partition/test-repart.sh104
-rw-r--r--src/portable/portable.c4
-rw-r--r--src/portable/portablectl.c2
-rw-r--r--src/portable/portabled-bus.c4
-rw-r--r--src/portable/portabled-image-bus.c4
-rw-r--r--src/portable/portabled-operation.c4
-rw-r--r--src/pstore/pstore.conf2
-rw-r--r--src/random-seed/random-seed.c2
-rw-r--r--src/resolve/resolved-bus.c20
-rw-r--r--src/resolve/resolved-dns-packet.c29
-rw-r--r--src/resolve/resolved-dns-packet.h2
-rw-r--r--src/resolve/resolved-dns-query.c4
-rw-r--r--src/resolve/resolved-dns-question.c103
-rw-r--r--src/resolve/resolved-dns-question.h46
-rw-r--r--src/resolve/resolved-dns-rr.h8
-rw-r--r--src/resolve/resolved-dns-scope.c16
-rw-r--r--src/resolve/resolved-dns-stub.c9
-rw-r--r--src/resolve/resolved-dns-transaction.h5
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c5
-rw-r--r--src/resolve/resolved-link-bus.c2
-rw-r--r--src/resolve/resolved-mdns.c104
-rw-r--r--src/resolve/resolved.conf.in2
-rw-r--r--src/rfkill/rfkill.c8
-rw-r--r--src/shared/acl-util.c6
-rw-r--r--src/shared/base-filesystem.c9
-rw-r--r--src/shared/bpf-program.c104
-rw-r--r--src/shared/bpf-program.h8
-rw-r--r--src/shared/bus-message-util.c6
-rw-r--r--src/shared/bus-unit-util.c20
-rw-r--r--src/shared/bus-wait-for-jobs.c5
-rw-r--r--src/shared/clean-ipc.c2
-rw-r--r--src/shared/clock-util.c2
-rw-r--r--src/shared/cryptsetup-util.h8
-rw-r--r--src/shared/discover-image.c5
-rw-r--r--src/shared/dissect-image.c97
-rw-r--r--src/shared/dissect-image.h15
-rw-r--r--src/shared/firewall-util-iptables.c25
-rw-r--r--src/shared/firewall-util-private.h3
-rw-r--r--src/shared/format-table.c65
-rwxr-xr-xsrc/shared/generate-ip-protocol-list.sh9
-rw-r--r--src/shared/generator.c59
-rw-r--r--src/shared/gpt.c57
-rw-r--r--src/shared/gpt.h7
-rw-r--r--src/shared/install.c2
-rw-r--r--src/shared/log-link.h42
-rw-r--r--src/shared/logs-show.c4
-rw-r--r--src/shared/loop-util.c82
-rw-r--r--src/shared/loop-util.h4
-rw-r--r--src/shared/module-util.c9
-rw-r--r--src/shared/mount-util.h6
-rw-r--r--src/shared/net-condition.c2
-rw-r--r--src/shared/openssl-util.c2
-rw-r--r--src/shared/pkcs11-util.c2
-rw-r--r--src/shared/qrcode-util.c2
-rw-r--r--src/shared/sleep-config.c2
-rw-r--r--src/shared/tpm2-util.c4
-rw-r--r--src/shared/wifi-util.c4
-rw-r--r--src/sleep/sleep.c8
-rw-r--r--src/sleep/sleep.conf2
-rw-r--r--src/stdio-bridge/stdio-bridge.c2
-rw-r--r--src/sysext/sysext.c2
-rw-r--r--src/systemctl/systemctl-edit.c2
-rw-r--r--src/systemctl/systemctl-list-units.c2
-rw-r--r--src/systemctl/systemctl-show.c21
-rw-r--r--src/systemd/sd-device.h1
-rw-r--r--src/systemd/sd-dhcp-client.h1
-rw-r--r--src/systemd/sd-dhcp-lease.h58
-rw-r--r--src/systemd/sd-dhcp-server.h3
-rw-r--r--src/systemd/sd-id128.h27
-rw-r--r--src/systemd/sd-login.h2
-rw-r--r--src/systemd/sd-network.h5
-rw-r--r--src/sysv-generator/sysv-generator.c6
-rw-r--r--src/test/meson.build9
-rw-r--r--src/test/test-bpf-foreign-programs.c332
-rw-r--r--src/test/test-capability.c2
-rw-r--r--src/test/test-cgroup-mask.c2
-rw-r--r--src/test/test-cgroup-setup.c2
-rw-r--r--src/test/test-cgroup-util.c2
-rw-r--r--src/test/test-extract-word.c14
-rw-r--r--src/test/test-firewall-util.c7
-rw-r--r--src/test/test-id128.c7
-rw-r--r--src/test/test-loop-block.c6
-rw-r--r--src/test/test-path-util.c5
-rw-r--r--src/test/test-sigbus.c3
-rw-r--r--src/test/test-udev.c2
-rw-r--r--src/timedate/timedatectl.c25
-rw-r--r--src/timedate/timedated.c42
-rw-r--r--src/timesync/timesyncd.conf.in2
-rw-r--r--src/timesync/wait-sync.c2
-rw-r--r--src/tmpfiles/tmpfiles.c71
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c2
-rw-r--r--src/udev/cdrom_id/cdrom_id.c60
-rw-r--r--src/udev/dmi_memory_id/dmi_memory_id.c2
-rwxr-xr-xsrc/udev/generate-keyboard-keys-gperf.sh3
-rwxr-xr-xsrc/udev/generate-keyboard-keys-list.sh5
-rw-r--r--src/udev/net/link-config.c14
-rw-r--r--src/udev/scsi_id/scsi_id.c203
-rw-r--r--src/udev/udev-builtin-net_id.c108
-rw-r--r--src/udev/udev-rules.c32
-rw-r--r--src/udev/udevadm-info.c2
-rw-r--r--src/udev/udevadm.h1
-rw-r--r--src/udev/udevd.c2
-rw-r--r--src/vconsole/vconsole-setup.c4
-rwxr-xr-xtest/TEST-01-BASIC/test.sh8
-rwxr-xr-xtest/TEST-02-UNITTESTS/test.sh65
-rwxr-xr-xtest/TEST-03-JOBS/test.sh4
-rwxr-xr-xtest/TEST-04-JOURNAL/test.sh4
-rwxr-xr-xtest/TEST-05-RLIMITS/test.sh4
-rwxr-xr-xtest/TEST-06-SELINUX/test.sh36
-rwxr-xr-xtest/TEST-07-ISSUE-1981/test.sh4
-rwxr-xr-xtest/TEST-08-ISSUE-2730/test.sh5
-rwxr-xr-xtest/TEST-09-ISSUE-2691/test.sh5
-rwxr-xr-xtest/TEST-10-ISSUE-2467/test.sh4
-rwxr-xr-xtest/TEST-11-ISSUE-3166/test.sh4
-rwxr-xr-xtest/TEST-12-ISSUE-3171/test.sh4
-rwxr-xr-xtest/TEST-13-NSPAWN-SMOKE/test.sh10
-rwxr-xr-xtest/TEST-14-MACHINE-ID/test.sh6
-rwxr-xr-xtest/TEST-15-DROPIN/test.sh4
-rwxr-xr-xtest/TEST-16-EXTEND-TIMEOUT/test.sh4
-rwxr-xr-xtest/TEST-17-UDEV/test.sh5
-rwxr-xr-xtest/TEST-18-FAILUREACTION/test.sh5
-rwxr-xr-xtest/TEST-19-DELEGATE/test.sh5
-rwxr-xr-xtest/TEST-20-MAINPIDGAMES/test.sh4
-rwxr-xr-xtest/TEST-22-TMPFILES/test.sh9
-rwxr-xr-xtest/TEST-23-TYPE-EXEC/test.sh5
-rwxr-xr-xtest/TEST-24-CRYPTSETUP/test.sh61
-rwxr-xr-xtest/TEST-25-IMPORT/test.sh4
-rwxr-xr-xtest/TEST-26-SETENV/test.sh4
-rwxr-xr-xtest/TEST-27-STDOUTFILE/test.sh4
-rwxr-xr-xtest/TEST-28-PERCENTJ-WANTEDBY/test.sh4
-rwxr-xr-xtest/TEST-29-PORTABLE/test.sh4
-rwxr-xr-xtest/TEST-30-ONCLOCKCHANGE/test.sh5
-rwxr-xr-xtest/TEST-31-DEVICE-ENUMERATION/test.sh5
-rwxr-xr-xtest/TEST-32-OOMPOLICY/test.sh5
-rwxr-xr-xtest/TEST-33-CLEAN-UNIT/test.sh5
-rwxr-xr-xtest/TEST-34-DYNAMICUSERMIGRATE/test.sh5
-rwxr-xr-xtest/TEST-36-NUMAPOLICY/test.sh5
-rwxr-xr-xtest/TEST-37-RUNTIMEDIRECTORYPRESERVE/test.sh5
-rwxr-xr-xtest/TEST-38-FREEZER/test.sh5
-rwxr-xr-xtest/TEST-39-EXECRELOAD/test.sh5
-rwxr-xr-xtest/TEST-40-EXEC-COMMAND-EX/test.sh5
-rwxr-xr-xtest/TEST-41-ONESHOT-RESTART/test.sh5
-rwxr-xr-xtest/TEST-42-EXECSTOPPOST/test.sh4
-rwxr-xr-xtest/TEST-43-PRIVATEUSER-UNPRIV/test.sh5
-rwxr-xr-xtest/TEST-44-LOG-NAMESPACE/test.sh4
-rwxr-xr-xtest/TEST-46-HOMED/test.sh4
-rwxr-xr-xtest/TEST-47-ISSUE-14566/test.sh5
-rwxr-xr-xtest/TEST-48-START-STOP-NO-RELOAD/test.sh5
-rwxr-xr-xtest/TEST-49-RUNTIME-BIND-PATHS/test.sh4
-rw-r--r--test/TEST-50-DISSECT/deny-list-ubuntu-ci2
-rwxr-xr-xtest/TEST-50-DISSECT/test.sh5
-rwxr-xr-xtest/TEST-51-ISSUE-16115/test.sh5
-rwxr-xr-xtest/TEST-52-HONORFIRSTSHUTDOWN/test.sh14
-rwxr-xr-xtest/TEST-53-ISSUE-16347/test.sh7
-rwxr-xr-xtest/TEST-54-CREDS/test.sh4
-rwxr-xr-xtest/TEST-55-OOMD/test.sh55
-rwxr-xr-xtest/TEST-56-EXIT-TYPE/test.sh5
l---------test/TEST-58-REPART/Makefile1
-rwxr-xr-xtest/TEST-58-REPART/test.sh6
l---------test/TEST-59-RELOADING-RESTART/Makefile1
-rwxr-xr-xtest/TEST-59-RELOADING-RESTART/test.sh9
-rw-r--r--test/fuzz/fuzz-network-parser/directives.network10
-rw-r--r--test/fuzz/fuzz-unit-file/directives-all.service1
-rw-r--r--test/fuzz/fuzz-unit-file/directives.mount1
-rw-r--r--test/fuzz/fuzz-unit-file/directives.scope1
-rw-r--r--test/fuzz/fuzz-unit-file/directives.service1
-rw-r--r--test/fuzz/fuzz-unit-file/directives.slice1
-rw-r--r--test/fuzz/fuzz-unit-file/directives.socket1
-rw-r--r--test/fuzz/fuzz-unit-file/directives.swap1
-rw-r--r--test/fuzz/fuzz-unit-file/oss-fuzz-32991bin0 -> 35 bytes
-rw-r--r--test/test-execute/exec-standardoutput-append.service4
-rw-r--r--test/test-execute/exec-standardoutput-file.service4
-rw-r--r--test/test-execute/exec-standardoutput-truncate.service4
-rw-r--r--test/test-functions1531
-rw-r--r--test/test-network/conf/agent-client-peer.network9
-rw-r--r--test/test-network/conf/agent-client.network5
-rw-r--r--test/test-network/conf/agent-server-peer.network5
-rw-r--r--test/test-network/conf/agent-server.network10
-rw-r--r--test/test-network/conf/agent-veth-client.netdev8
-rw-r--r--test/test-network/conf/agent-veth-server.netdev8
-rw-r--r--test/test-network/conf/dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network9
-rw-r--r--test/test-network/conf/dhcp-server-with-ipv6-prefix.network11
-rw-r--r--test/test-network/conf/ipv6-prefix-with-delay.network12
-rw-r--r--test/test-network/conf/ipv6ra-prefix-client-with-static-ipv4-address.network6
-rw-r--r--test/test-network/conf/state-file-tests.network2
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py118
-rw-r--r--test/testsuite-28.units/specifier-j-wants.service2
-rwxr-xr-xtest/testsuite-52.units/testsuite-52.sh4
-rwxr-xr-xtest/units/testsuite-02.sh13
-rwxr-xr-xtest/units/testsuite-03.sh29
-rwxr-xr-xtest/units/testsuite-04.sh25
-rwxr-xr-xtest/units/testsuite-05.sh5
-rwxr-xr-xtest/units/testsuite-06.sh3
-rwxr-xr-xtest/units/testsuite-07.sh18
-rwxr-xr-xtest/units/testsuite-11.sh5
-rwxr-xr-xtest/units/testsuite-12.sh7
-rwxr-xr-xtest/units/testsuite-13.sh96
-rwxr-xr-xtest/units/testsuite-14.sh14
-rwxr-xr-xtest/units/testsuite-15.sh52
-rwxr-xr-xtest/units/testsuite-16.sh32
-rwxr-xr-xtest/units/testsuite-17.01.sh4
-rwxr-xr-xtest/units/testsuite-17.02.sh2
-rwxr-xr-xtest/units/testsuite-17.03.sh10
-rwxr-xr-xtest/units/testsuite-17.04.sh59
-rwxr-xr-xtest/units/testsuite-17.05.sh2
-rwxr-xr-xtest/units/testsuite-17.sh10
-rwxr-xr-xtest/units/testsuite-18.sh8
-rwxr-xr-xtest/units/testsuite-19.sh4
-rwxr-xr-xtest/units/testsuite-20.sh56
-rwxr-xr-xtest/units/testsuite-22.01.sh7
-rwxr-xr-xtest/units/testsuite-22.02.sh38
-rwxr-xr-xtest/units/testsuite-22.03.sh87
-rwxr-xr-xtest/units/testsuite-22.04.sh14
-rwxr-xr-xtest/units/testsuite-22.05.sh22
-rwxr-xr-xtest/units/testsuite-22.06.sh11
-rwxr-xr-xtest/units/testsuite-22.07.sh14
-rwxr-xr-xtest/units/testsuite-22.08.sh15
-rwxr-xr-xtest/units/testsuite-22.09.sh4
-rwxr-xr-xtest/units/testsuite-22.10.sh3
-rwxr-xr-xtest/units/testsuite-22.11.sh141
-rwxr-xr-xtest/units/testsuite-22.sh10
-rwxr-xr-xtest/units/testsuite-23.sh12
-rwxr-xr-xtest/units/testsuite-25.sh39
-rwxr-xr-xtest/units/testsuite-26.sh8
-rwxr-xr-xtest/units/testsuite-27.sh2
-rwxr-xr-xtest/units/testsuite-29.sh4
-rwxr-xr-xtest/units/testsuite-30.sh12
-rwxr-xr-xtest/units/testsuite-31.sh4
-rwxr-xr-xtest/units/testsuite-32.sh7
-rwxr-xr-xtest/units/testsuite-33.sh194
-rwxr-xr-xtest/units/testsuite-34.sh27
-rwxr-xr-xtest/units/testsuite-36.sh240
-rwxr-xr-xtest/units/testsuite-37.sh6
-rwxr-xr-xtest/units/testsuite-38.sh44
-rwxr-xr-xtest/units/testsuite-39.sh40
-rwxr-xr-xtest/units/testsuite-40.sh4
-rwxr-xr-xtest/units/testsuite-41.sh19
-rwxr-xr-xtest/units/testsuite-42.sh30
-rwxr-xr-xtest/units/testsuite-43.sh18
-rwxr-xr-xtest/units/testsuite-44.sh14
-rwxr-xr-xtest/units/testsuite-46.sh36
-rwxr-xr-xtest/units/testsuite-47-repro.sh2
-rwxr-xr-xtest/units/testsuite-47.sh4
-rwxr-xr-xtest/units/testsuite-48.sh14
-rwxr-xr-xtest/units/testsuite-49.sh8
-rwxr-xr-xtest/units/testsuite-50.sh167
-rwxr-xr-xtest/units/testsuite-51.sh2
-rwxr-xr-xtest/units/testsuite-53.sh4
-rwxr-xr-xtest/units/testsuite-54.sh17
-rwxr-xr-xtest/units/testsuite-55-slowgrowth.sh27
-rwxr-xr-xtest/units/testsuite-55.sh29
-rwxr-xr-xtest/units/testsuite-56.sh16
-rw-r--r--test/units/testsuite-58.service6
-rwxr-xr-xtest/units/testsuite-58.sh72
-rw-r--r--test/units/testsuite-59.service6
-rwxr-xr-xtest/units/testsuite-59.sh90
-rwxr-xr-xtools/add-git-hook.sh4
-rwxr-xr-xtools/check-api-docs.sh15
-rwxr-xr-xtools/check-directives.sh1
-rwxr-xr-xtools/check-help.sh31
-rwxr-xr-xtools/find-build-dir.sh14
-rwxr-xr-xtools/find-double-newline.sh20
-rwxr-xr-xtools/find-tabs.sh20
-rwxr-xr-xtools/meson-apply-m4.sh20
-rwxr-xr-xtools/meson-make-symlink.sh11
-rwxr-xr-xtools/meson-vcs-tag.sh4
-rwxr-xr-xtools/update-hwdb-autosuspend.sh2
-rwxr-xr-xtools/update-hwdb.sh4
-rwxr-xr-xtools/update-syscall-tables.sh4
-rw-r--r--units/initrd-fs.target2
-rw-r--r--units/initrd-usr-fs.target17
-rw-r--r--units/initrd.target4
-rw-r--r--units/meson.build4
-rw-r--r--units/system-systemd\x2dcryptsetup.slice2
-rw-r--r--units/systemd-firstboot.service16
-rw-r--r--units/systemd-networkd.socket3
-rw-r--r--units/systemd-repart.service.in7
-rw-r--r--units/systemd-sysusers.service8
-rw-r--r--units/systemd-volatile-root.service.in2
542 files changed, 9835 insertions, 4663 deletions
diff --git a/.mkosi/mkosi.arch b/.mkosi/mkosi.arch
index 6192c19104..867a869e1a 100644
--- a/.mkosi/mkosi.arch
+++ b/.mkosi/mkosi.arch
@@ -42,6 +42,7 @@ BuildPackages=
python
python-lxml
qrencode
+ rsync
xz
zstd
diff --git a/.packit.yml b/.packit.yml
index 75f9d3abc9..4545e30e08 100644
--- a/.packit.yml
+++ b/.packit.yml
@@ -18,6 +18,8 @@ actions:
post-upstream-clone:
# Use the Fedora Rawhide specfile
- "git clone https://src.fedoraproject.org/rpms/systemd .packit_rpm --depth=1"
+ # Drop the "sources" file so rebase-helper doesn't think we're a dist-git
+ - "rm -fv .packit_rpm/sources"
# Drop backported patches from the specfile, but keep the downstream-only ones
# - Patch0000-0499: backported patches from upstream
# - Patch0500-9999: downstream-only patches
diff --git a/TODO b/TODO
index f3e0ddaac0..682f296d60 100644
--- a/TODO
+++ b/TODO
@@ -22,6 +22,50 @@ Janitorial Clean-ups:
Features:
+* ability to insert trusted configuration and secrets into the boot parameters
+ of a kernel booting in a VM or on baremetal some way, via TPM
+ protection. idea:
+ 1. pass via /proc/bootconfig
+ 2. for secrets: put secrets in node of /proc/bootconfig, decrypt them via
+ TPM early on in PID 1, put them in $CREDENTIAL_PATH logic
+ 3. for config: put signed data in node /proc/booconfig, validate via TPM
+ early on in PID 1, put data into /run/bootconfig/ as individual files
+ 4. boot loader/stub should pick these up automatically from the boot loader
+ file systems
+
+* journald: support RFC3164 fully for the incoming syslog transport, see
+ https://github.com/systemd/systemd/issues/19251#issuecomment-816601955
+
+* nspawn: support uid mapping bind mounts, as defined available in kernel 5.12,
+ for all our disk image needs
+
+* homed: if kernel 5.12 uid mapping mounts exist, use that instead of recursive
+ chowns.
+
+* cryptsetup: tweak tpm2-device=auto logic, abort quickly if firmware tells us
+ there isn't any TPM2 device anyway. that way, we'll wait for the TPM2 device
+ to show up only if registered in LUKS header + the firmware suggests there is
+ a device worth waiting for.
+
+* systemd-sysext: optionally, run it in initrd already, before transitioning
+ into host, to open up possibility for services shipped like that.
+
+* add a flag to the GPT spec that says "grow my fs to partition size", and make
+ it settable via systemd-repart. Add in growfs jobs in
+ systemd-gpt-auto-generator when it is set, and issue the ioctls while
+ mounting in systemd-npsawn --image=. That way systemd-repart suffices to
+ enlarge an image.
+
+* add a new switch --auto-definitions=yes/no or so to systemd-repart. If
+ specified, synthesize a definition automatically if we can: enlarge last
+ partition on disk, but only if it is marked for growing and not read-only.
+
+* add a switch to homectl (maybe called --first-boot) where it will check if
+ any non-system users exist, and if not prompts interactively for basic user
+ info, mimicking systemd-firstboot. Then, place this in a service that runs
+ after systemd-homed, but before gdm and friends, as a simple, barebones
+ fallback logic to get a regular user created on uninitialized systems.
+
* maybe add a tool that displays most recent journal logs as QR code to scan
off screen and run it automatically on boot failures, emergency logs and
such. Use DRM APIs directly, see
@@ -36,7 +80,9 @@ Features:
* systemd-repart: read LUKS encryption key from $CREDENTIALS_PATH
* introduce /dev/disk/root/* symlinks that allow referencing partitions on the
- disk the rootfs is on in a reasonably secure way.
+ disk the rootfs is on in a reasonably secure way. (or maybe: add
+ /dev/gpt-auto-{home,srv,boot,…} similar in style to /dev/gpt-auto-root as we
+ already have it.
* systemd-repart: add a switch to factory reset the partition table without
immediately applying the new configuration again. i.e. --factory-reset=leave
@@ -179,17 +225,6 @@ Features:
* Add service setting to run a service within the specified VRF. i.e. do the
equivalent of "ip vrf exec".
-* export action of device object on sd-device, so that monitor becomes useful
-
-* add root=tmpfs that mounts a tmpfs to /sysroot (to be used in combination
- with usr=…, for a similar effect as systemd.volatile=yes but without the
- "hide-out" effect). Also, add root=gpt-auto-late support or so, that is like
- root=gpt-auto but initially mounts a tmpfs to /sysroot, and then revisits
- later after systemd-repart ran. Usecase: let's ship images with only /usr
- partition, then on first boot create the root partition. In this case we want
- to read the repart data from /usr before the root partition exists. Add
- usr=gpt-auto that automatically finds a /usr partition.
-
* change SwitchRoot() implementation in PID 1 to use pivot_root(".", "."), as
documented in the pivot_root(2) man page, so that we can drop the /oldroot
temporary dir.
@@ -369,8 +404,6 @@ Features:
* systemd-repart: allow sizing partitions as factor of available RAM, so that
we can reasonably size swap partitions for hibernation.
-* systemd-repart: allow managing the gpt read-only partition flag + auto-mount flag
-
* systemd-repart: allow boolean option that ensures that if existing partition
doesn't exist within the configured size bounds the whole command fails. This
is useful to implement ESP vs. XBOOTLDR schemes in installers: have one set
@@ -797,10 +830,6 @@ Features:
"systemd-gdb" for attaching to the start-up of any system service in its
natural habitat.
-* gpt-auto logic: related to the above, maybe support a "secondary" root
- partition, that is mounted to / and is writable, and where the actual root's
- /usr is mounted into.
-
* gpt-auto logic: support encrypted swap, add kernel cmdline option to force it, and honour a gpt bit about it, plus maybe a configuration file
* drop nss-myhostname in favour of nss-resolve?
diff --git a/docs/COREDUMP_PACKAGE_METADATA.md b/docs/COREDUMP_PACKAGE_METADATA.md
new file mode 100644
index 0000000000..ab7c4596de
--- /dev/null
+++ b/docs/COREDUMP_PACKAGE_METADATA.md
@@ -0,0 +1,106 @@
+---
+title: Package Metadata for Core Files
+category: Interfaces
+layout: default
+---
+
+# Package Metadata for Core Files
+
+*Intended audience: hackers working on userspace subsystems that create ELF binaries
+or parse ELF core files.*
+
+## Motivation
+
+ELF binaries get stamped with a unique, build-time generated hex string identifier called
+`build-id`, [which gets embedded as an ELF note called `.note.gnu.build-id`](https://fedoraproject.org/wiki/Releases/FeatureBuildId).
+In most cases, this allows to associate a stripped binary with its debugging information.
+It is used, for example, to dynamically fetch DWARF symbols from a debuginfo server, or
+to query the local package manager and find out the package metadata or, again, the DWARF
+symbols or program sources.
+
+However, this usage of the `build-id` requires either local metadata, usually set up by
+the package manager, or access to a remote server over the network. Both of those might
+be unavailable or forbidden.
+
+Thus it becomes desirable to add additional metadata to a binary at build time, so that
+`systemd-coredump` and other services analyzing core files are able to extract said
+metadata simply from the core file itself, without external dependencies.
+
+## Implementation
+
+This document will attempt to define a common metadata format specification, so that
+multiple implementers might use it when building packages, or core file analyzers, and
+so on.
+
+The metadata will be embedded in a single, new ELF header section, in a key-value JSON
+format. Implementers working on parsing core files should not assume a specific list of
+keys, but parse anything that is included in the section.
+Implementers working on build tools should strive to use the same key names, for
+consistency. The most common will be listed here. When corresponding to the content of
+os-release, the values should match, again for consistency.
+
+* Section header
+
+```
+SECTION: `.note.package`
+node-id: `0xcafe1a7e`
+Owner: `FDO` (FreeDesktop.org)
+Value: a JSON string with the structure described below
+```
+
+* JSON payload
+
+```json
+{
+ "type":"rpm", # this provides a namespace for the package+package-version fields
+ "os":"fedora",
+ "osVersion":"33",
+ "name":"coreutils",
+ "version": "4711.0815.fc13.arm32",
+ "osCpe": # A CPE name for the operating system, `CPE_NAME` from os-release is a good default
+}
+```
+
+A reference implementations of a [build-time tool is provided](https://github.com/systemd/package-notes)
+and can be used to generate a linker script, which can then be used at build time via
+```LDFLAGS="-Wl,-T,/path/to/generated/script"``` to include the note in the binary.
+
+Generator:
+```console
+$ ./generate-package-notes.py --rpm systemd-248~rc2-1.fc34
+SECTIONS
+{
+ .note.package : ALIGN(4) {
+ BYTE(0x04) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Owner including NUL */
+ BYTE(0x64) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Value including NUL */
+ BYTE(0x7e) BYTE(0x1a) BYTE(0xfe) BYTE(0xca) /* Note ID */
+ BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
+ BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","name":"systemd","version":"248~rc2-1.fc34","osCpe":"cpe:/o:fedoraproject:fedora:33"}\x00' */
+ BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
+ BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
+ BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6e)
+ BYTE(0x61) BYTE(0x6d) BYTE(0x65) BYTE(0x22)
+ BYTE(0x3a) BYTE(0x22) BYTE(0x73) BYTE(0x79)
+ BYTE(0x73) BYTE(0x74) BYTE(0x65) BYTE(0x6d)
+ BYTE(0x64) BYTE(0x22) BYTE(0x2c) BYTE(0x22)
+ BYTE(0x76) BYTE(0x65) BYTE(0x72) BYTE(0x73)
+ BYTE(0x69) BYTE(0x6f) BYTE(0x6e) BYTE(0x22)
+ BYTE(0x3a) BYTE(0x22) BYTE(0x32) BYTE(0x34)
+ BYTE(0x38) BYTE(0x7e) BYTE(0x72) BYTE(0x63)
+ BYTE(0x32) BYTE(0x2d) BYTE(0x31) BYTE(0x2e)
+ BYTE(0x66) BYTE(0x63) BYTE(0x33) BYTE(0x34)
+ BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
+ BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
+ BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x63)
+ BYTE(0x70) BYTE(0x65) BYTE(0x3a) BYTE(0x2f)
+ BYTE(0x6f) BYTE(0x3a) BYTE(0x66) BYTE(0x65)
+ BYTE(0x64) BYTE(0x6f) BYTE(0x72) BYTE(0x61)
+ BYTE(0x70) BYTE(0x72) BYTE(0x6f) BYTE(0x6a)
+ BYTE(0x65) BYTE(0x63) BYTE(0x74) BYTE(0x3a)
+ BYTE(0x66) BYTE(0x65) BYTE(0x64) BYTE(0x6f)
+ BYTE(0x72) BYTE(0x61) BYTE(0x3a) BYTE(0x33)
+ BYTE(0x33) BYTE(0x22) BYTE(0x7d) BYTE(0x00)
+ }
+}
+INSERT AFTER .note.gnu.build-id;
+```
diff --git a/docs/DISCOVERABLE_PARTITIONS.md b/docs/DISCOVERABLE_PARTITIONS.md
index 81d07f88a6..c4074702c4 100644
--- a/docs/DISCOVERABLE_PARTITIONS.md
+++ b/docs/DISCOVERABLE_PARTITIONS.md
@@ -62,7 +62,7 @@ Interface](https://systemd.io/BOOT_LOADER_INTERFACE).
| `4301d2a6-4e3b-4b2a-bb94-9e0b2c4225ea` | _`/usr/` Partition (Itanium/IA-64)_ | ditto | ditto |
| `b933fb22-5c3f-4f91-af90-e2bb0fa50702` | _`/usr/` Partition (RISC-V 32-bit)_ | ditto | ditto |
| `beaec34b-8442-439b-a40b-984381ed097d` | _`/usr/` Partition (RISC-V 64-bit)_ | ditto | ditto |
-| `8f461b0d-14ee-4e81-9aa9-049b6fb97abd` | _`/usr/` Verity Partition (x86)_ | Any native, optionally in LUKS | Similar semantics to root Verity partition, but just for the `/usr/` partition. |
+| `8f461b0d-14ee-4e81-9aa9-049b6fb97abd` | _`/usr/` Verity Partition (x86)_ | A dm-verity superblock followed by hash data | Similar semantics to root Verity partition, but just for the `/usr/` partition. |
| `77ff5f63-e7b6-4633-acf4-1565b864c0e6` | _`/usr/` Verity Partition (x86-64)_ | ditto | ditto |
| `c215d751-7bcd-4649-be90-6627490a4c05` | _`/usr/` Verity Partition (32-bit ARM)_ | ditto | ditto |
| `6e11a4e7-fbca-4ded-b9e9-e1a512bb664e` | _`/usr/` Verity Partition (64-bit ARM/AArch64)_ | ditto | ditto |
@@ -73,10 +73,10 @@ Interface](https://systemd.io/BOOT_LOADER_INTERFACE).
| `3b8f8425-20e0-4f3b-907f-1a25a76f98e8` | _Server Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/srv/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/srv`. |
| `4d21b016-b534-45c2-a9fb-5c16e091fd2d` | _Variable Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/` — under the condition that its partition UUID matches the first 128 bit of `HMAC-SHA256(machine-id, 0x4d21b016b53445c2a9fb5c16e091fd2d)` (i.e. the SHA256 HMAC hash of the binary type UUID keyed by the machine ID as read from [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html). This special requirement is made because `/var/` (unlike the other partition types listed here) is inherently private to a specific installation and cannot possibly be shared between multiple OS installations on the same disk, and thus should be bound to a specific instance of the OS, identified by its machine ID. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`. |
| `7ec6f557-3bc5-4aca-b293-16ef5df639d1` | _Temporary Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/tmp/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/tmp`. Note that the intended mount point is indeed `/var/tmp/`, not `/tmp/`. The latter is typically maintained in memory via <tt>tmpfs</tt> and does not require a partition on disk. In some cases it might be desirable to make `/tmp/` persistent too, in which case it is recommended to make it a symlink or bind mount to `/var/tmp/`, thus not requiring its own partition type UUID. |
-| `0657fd6d-a4ab-43c4-84e5-0933c84b4f4f` | _Swap_ | Swap | All swap partitions on the disk containing the root partition are automatically enabled. |
+| `0657fd6d-a4ab-43c4-84e5-0933c84b4f4f` | _Swap_ | Swap | All swap partitions on the disk containing the root partition are automatically enabled. This partition type predates the Discoverable Partitions Specification. |
+| `0fc63daf-8483-4772-8e79-3d69d8477de4` | _Generic Linux Data Partitions_ | Any native, optionally in LUKS | No automatic mounting takes place for other Linux data partitions. This partition type should be used for all partitions that carry Linux file systems. The installer needs to mount them explicitly via entries in <tt>/etc/fstab</tt>. Optionally, these partitions may be encrypted with LUKS. This partition type predates the Discoverable Partitions Specification. |
| `c12a7328-f81f-11d2-ba4b-00a0c93ec93b` | _EFI System Partition_ | VFAT | The ESP used for the current boot is automatically mounted to `/efi/` (or `/boot/` as fallback), unless a different partition is mounted there (possibly via `/etc/fstab`, or because the Extended Boot Loader Partition — see below — exists) or the directory is non-empty on the root disk. This partition type is defined by the [UEFI Specification](http://www.uefi.org/specifications). |
| `bc13c2ff-59e6-4262-a352-b275fd6f7172` | _Extended Boot Loader Partition_ | Typically VFAT | The Extended Boot Loader Partition (XBOOTLDR) used for the current boot is automatically mounted to <tt>/boot/</tt>, unless a different partition is mounted there (possibly via <tt>/etc/fstab</tt>) or the directory is non-empty on the root disk. This partition type is defined by the [Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION). |
-| `0fc63daf-8483-4772-8e79-3d69d8477de4` | _Other Data Partitions_ | Any native, optionally in LUKS | No automatic mounting takes place for other Linux data partitions. This partition type should be used for all partitions that carry Linux file systems. The installer needs to mount them explicitly via entries in <tt>/etc/fstab</tt>. Optionally, these partitions may be encrypted with LUKS. |
Other GPT type IDs might be used on Linux, for example to mark software RAID or
LVM partitions. The definitions of those GPT types is outside of the scope of
diff --git a/docs/JOURNAL_NATIVE_PROTOCOL.md b/docs/JOURNAL_NATIVE_PROTOCOL.md
new file mode 100644
index 0000000000..fced45942b
--- /dev/null
+++ b/docs/JOURNAL_NATIVE_PROTOCOL.md
@@ -0,0 +1,190 @@
+---
+title: Native Journal Protocol
+category: Interfaces
+layout: default
+---
+
+# Native Journal Protocol
+
+`systemd-journald.service` accepts log data via various protocols:
+
+* Classic RFC3164 BSD syslog via the `/dev/log` socket
+* STDOUT/STDERR of programs via `StandardOutput=journal` + `StandardError=journal` in service files (both of which are default settings)
+* Kernel log messages via the `/dev/kmsg` device node
+* Audit records via the kernel's audit subsystem
+* Structured log messages via `journald`'s native protocol
+
+The latter is what this document is about: if you are developing a program and
+want to pass structured log data to `journald`, it's the Journal's native
+protocol what you want to use. The systemd project provides the
+[`sd_journal_print(3)`](https://www.freedesktop.org/software/systemd/man/sd_journal_print.html)
+API that implements the client side of this protocol. This document explains
+what this interface does behind the scenes, in case you'd like to implement a
+client for it yourself, without linking to `libsystemd` — for example because
+you work in a programming language other than C or otherwise want to avoid the
+dependency.
+
+## Basics
+
+The native protocol of `journald` is spoken on the
+`/run/systemd/journal/socket` `AF_UNIX`/`SOCK_DGRAM` socket on which
+`systemd-journald.service` listens. Each datagram sent to this socket
+encapsulates one journal entry that shall be written. Since datagrams are
+subject to a size limit and we want to allow large journal entries, datagrams
+sent over this socket may come in one of two formats:
+
+* A datagram with the literal journal entry data as payload, without
+ any file descriptors attached.
+
+* A datagram with an empty payload, but with a single
+ [`memfd`](https://man7.org/linux/man-pages/man2/memfd_create.2.html)
+ file descriptor that contains the literal journal entry data.
+
+Other combinations are not permitted, i.e. datagrams with both payload and file
+descriptors, or datagrams with neither, or more than one file descriptor. Such
+datagrams are ignored. The `memfd` file descriptor should be fully sealed. The
+binary format in the datagram payload and in the `memfd` memory is
+identical. Typically a client would attempt to first send the data as datagram
+payload, but if this fails with an `EMSGSIZE` error it would immediately retry
+via the `memfd` logic.
+
+A client probably should bump up the `SO_SNDBUF` socket option of its `AF_UNIX`
+socket towards `journald` in order to delay blocking I/O as much as possible.
+
+## Data Format
+
+Each datagram should consist of a number of environment-like key/value
+assignments. Unlike environment variable assignments the value may contain NUL
+bytes however, as well as any other binary data. Keys may not include the `=`
+or newline characters (or any other control characters or non-ASCII characters)
+and may not be empty.
+
+Serialization into the datagram payload or `memfd` is straight-forward: each
+key/value pair is serialized via one of two methods:
+
+* The first method inserts a `=` character between key and value, and suffixes
+the result with `\n` (i.e. the newline character, ASCII code 10). Example: a
+key `FOO` with a value `BAR` is serialized `F`, `O`, `O`, `=`, `B`, `A`, `R`,
+`\n`.
+
+* The second method should be used if the value of a field contains a `\n`
+byte. In this case, the key name is serialized as is, followed by a `\n`
+character, followed by a (non-aligned) little-endian unsigned 64bit integer
+encoding the size of the value, followed by the literal value data, followed by
+`\n`. Example: a key `FOO` with a value `BAR` may be serialized using this
+second method as: `F`, `O`, `O`, `\n`, `\003`, `\000`, `\000`, `\000`, `\000`,
+`\000`, `\000`, `\000`, `B`, `A`, `R`, `\n`.
+
+If the value of a key/value pair contains a newline character (`\n`), it *must*
+be serialized using the second method. If it does not, either method is
+permitted. However, it is generally recommended to use the first method if
+possible for all key/value pairs where applicable since the generated datagrams
+are easily recognized and understood by the human eye this way, without any
+manual binary decoding — which improves the debugging experience a lot, in
+particular with tools such as `strace` that can show datagram content as text
+dump. After all, log messages are highly relevant for debugging programs, hence
+optimizing log traffic for readability without special tools is generally
+desirable.
+
+Note that keys that begin with `_` have special semantics in `journald`: they
+are *trusted* and implicitly appended by `journald` on the receiving
+side. Clients should not send them — if they do anyway, they will be ignored.
+
+The most important key/value pair to send is `MESSAGE=`, as that contains the
+actual log message text. Other relevant keys a client should send in most cases
+are `PRIORITY=`, `CODE_FILE=`, `CODE_LINE=`, `CODE_FUNC=`, `ERRNO=`. It's
+recommended to generate these fields implicitly on the client side. For further
+information see the [relevant documentation of these
+fields](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html).
+
+The order in which the fields are serialized within one datagram is undefined
+and may be freely chosen by the client. The server side might or might not
+retain or reorder it when writing it to the Journal.
+
+Some programs might generate multi-line log messages (e.g. a stack unwinder
+generating log output about a stack trace, with one line for each stack
+frame). It's highly recommended to send these as a single datagram, using a
+single `MESSAGE=` field with embedded newline characters between the lines (the
+second serialization method described above must hence be used for this
+field). If possible do not split up individual events into multiple Journal
+events that might then be processed and written into the Journal as separate
+entries. The Journal toolchain is capable of handling multi-line log entries
+just fine, and it's generally preferred to have a single set of metadata fields
+associated with each multi-line message.
+
+Note that the same keys may be used multiple times within the same datagram,
+with different values. The Journal supports this and will write such entries to
+disk without complaining. This is useful for associating a single log entry
+with multiple suitable objects of the same type at once. This should only be
+used for specific Journal fields however, where this is expected. Do not use
+this for Journal fields where this is not expected and where code reasonably
+assumes per-event uniqueness of the keys. In most cases code that consumes and
+displays log entries is likely to ignore such non-unique fields or only
+consider the first of the specified values. Specifically, if a Journal entry
+contains multiple `MESSAGE=` fields, likely only the first one is
+displayed. Note that a well-written logging client library thus will not use a
+plain dictionary for accepting structured log metadata, but rather a data
+structure that allows non-unique keys, for example an array, or a dictionary
+that optionally maps to a set of values instead of a single value.
+
+## Example Datagram
+
+Here's an encoded message, with various common fields, all encoded according to
+the first serialization method, with the exception of one, where the value
+contains a newline character, and thus the second method is needed to be used.
+
+```
+PRIORITY=3\n
+SYSLOG_FACILITY=3\n
+CODE_FILE=src/foobar.c\n
+CODE_LINE=77\n
+BINARY_BLOB\n
+\004\000\000\000\000\000\000\000xx\nx\n
+CODE_FUNC=some_func\n
+SYSLOG_IDENTIFIER=footool\n
+MESSAGE=Something happened.\n
+```
+
+(Lines are broken here after each `\n` to make things more readable. C-style
+backslash escaping is used.)
+
+## Automatic Protocol Upgrading
+
+It might be wise to automatically upgrade to logging via the Journal's native
+protocol in clients that previously used the BSD syslog protocol. Behaviour in
+this case should be pretty obvious: try connecting a socket to
+`/run/systemd/journal/socket` first (on success use the native Journal
+protocol), and if that fails fall back to `/dev/log` (and use the BSD syslog
+protocol).
+
+Programs normally logging to STDERR might also choose to upgrade to native
+Journal logging in case they are invoked via systemd's service logic, where
+STDOUT and STDERR are going to the Journal anyway. By preferring the native
+protocol over STDERR-based logging, structured metadata can be passed along,
+including priority information and more — which is not available on STDERR
+based logging. If a program wants to detect automatically whether its STDERR is
+connected to the Journal's stream transport, look for the `$JOURNAL_STREAM`
+environment variable. The systemd service logic sets this variable to a
+colon-separated pair of device and inode number (formatted in decimal ASCII) of
+the STDERR file descriptor. If the `.st_dev` and `.st_ino` fields of the
+`struct stat` data returned by `fstat(STDERR_FILENO, …)` match these values a
+program can be sure its STDERR is connected to the Journal, and may then opt to
+upgrade to the native Journal protocol via an `AF_UNIX` socket of its own, and
+cease to use STDERR.
+
+Why bother with this environment variable check? A service program invoked by
+systemd might employ shell-style I/O redirection on invoked subprograms, and
+those should likely not upgrade to the native Journal protocol, but instead
+continue to use the redirected file descriptors passed to them. Thus, by
+comparing the device and inode number of the actual STDERR file descriptor with
+the one the service manager passed, one can make sure that no I/O redirection
+took place for the current program.
+
+## Alternative Implementations
+
+If you are looking for alternative implementations of this protocol (besides
+systemd's own in `sd_journal_print()`), consider
+[GLib's](https://gitlab.gnome.org/GNOME/glib/-/blob/master/glib/gmessages.c) or
+[`dbus-broker`'s](https://github.com/bus1/dbus-broker/blob/main/src/util/log.c).
+
+And that's already all there is to it.
diff --git a/hwdb.d/60-autosuspend.hwdb b/hwdb.d/60-autosuspend.hwdb
index 55d3fcc3b0..15dcc2f1ee 100644
--- a/hwdb.d/60-autosuspend.hwdb
+++ b/hwdb.d/60-autosuspend.hwdb
@@ -56,6 +56,14 @@ usb:v0627p0001:*QEMU USB Tablet*
ID_AUTOSUSPEND=1
#########################################
+# Sierra Wireless
+#########################################
+
+# Sierra Wireless EM7345 4G LTE modem
+usb:v1199pA001*
+ ID_AUTOSUSPEND=1
+
+#########################################
# Wacom
#########################################
diff --git a/hwdb.d/60-keyboard.hwdb b/hwdb.d/60-keyboard.hwdb
index 5485f892bb..4f403387b8 100644
--- a/hwdb.d/60-keyboard.hwdb
+++ b/hwdb.d/60-keyboard.hwdb
@@ -684,6 +684,21 @@ evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*ProBook*645*G4*:*
KEYBOARD_KEY_73=slash # Slash key
KEYBOARD_KEY_f8=wlan # Wireless HW switch button
+# HP ProBook 455 G5
+evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*ProBook*455*G5*:*
+ KEYBOARD_KEY_85=unknown # lid close; also reported via special evdev
+ KEYBOARD_KEY_f8=wlan # Wireless HW switch button
+
+# HP mt44 Mobile Thin Client
+evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*mt44*Mobile*Thin*Client*:*
+ KEYBOARD_KEY_64=calendar # Calendar icon (Fn + F12)
+ KEYBOARD_KEY_6d=displaytoggle # Display icon
+ KEYBOARD_KEY_66=connect # Pickup phone button → connect → XF86Go
+ KEYBOARD_KEY_65=cancel # Hangup phone button → cancel → Cancel
+ KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute
+ KEYBOARD_KEY_85=unknown # lid close; also reported via special evdev
+ KEYBOARD_KEY_f8=wlan # Wireless HW switch button
+
# HP Stream 7
# The ACPI tables contains a gpio-keys entry for a non connected GPIO
# causing spurious events, map this to unknown to disable it
@@ -1236,7 +1251,7 @@ evdev:input:b0003v1532p0200*
KEYBOARD_KEY_c01cb=down # zoomout
###########################################################
-# Micro Star
+# MSI (aka "Micro Star")
###########################################################
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pn*:*
@@ -1275,24 +1290,19 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-Star*:pn*PR200*:*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pnU90/U100:*
KEYBOARD_KEY_e4=reserved
-# MSI Prestige15 A10SC specific keycodes. Needed for microphone and screen rotation
-evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-Star*:pn*A10SC*:*
- KEYBOARD_KEY_f1=f20
- KEYBOARD_KEY_f2=f21
-
-# MSI Modern series
-evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-StarInternational*:pnModern*:*
- KEYBOARD_KEY_f1=f20 # Fn+F5 micmute
- KEYBOARD_KEY_76=f21 # Fn+F4 touchpad, becomes meta+ctrl+toggle
+# Keymaps MSI Prestige And MSI Modern FnKeys and Special keys
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-Star*:pn*Prestige*:*
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-Star*:pn*Modern*:*
+ KEYBOARD_KEY_56=backslash # Secondary backslash key
+ KEYBOARD_KEY_f1=f20 # Fn+F5 Micmute
+ KEYBOARD_KEY_76=f21 # Fn+F4 Toggle touchpad, sends meta+ctrl+toggle
KEYBOARD_KEY_91=prog1 # Fn+F7 Creation Center, sometime F7
- KEYBOARD_KEY_f2=prog2 # Fn+F12 screen rotation
- KEYBOARD_KEY_97=unknown # lid close
- KEYBOARD_KEY_98=unknown # lid open
- #Fn+PrntScr sends meta+shif+s
-
-###########################################################
-# MSI
-###########################################################
+ KEYBOARD_KEY_f2=prog2 # Fn+F12 Screen rotation
+ KEYBOARD_KEY_8d=prog3 # Fn+A Change True Color selections
+ KEYBOARD_KEY_8c=prog4 # Fn+Z Launch True Color
+ KEYBOARD_KEY_f5=fn_esc # Fn+esc Toggle the behaviour of Fn keys
+ KEYBOARD_KEY_97=unknown # Lid close
+ KEYBOARD_KEY_98=unknown # Lid open
evdev:name:MSI Laptop hotkeys:dmi:bvn*:bvr*:bd*:svn*:pnM[iI][cC][rR][oO]-S[tT][aA][rR]*:*
KEYBOARD_KEY_0213=f22
diff --git a/hwdb.d/60-sensor.hwdb b/hwdb.d/60-sensor.hwdb
index 54f6f34b7f..233cde8541 100644
--- a/hwdb.d/60-sensor.hwdb
+++ b/hwdb.d/60-sensor.hwdb
@@ -735,6 +735,10 @@ sensor:modalias:acpi:KIOX020A*:dmi:*:svnTECLAST:pnF6Pro:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
ACCEL_LOCATION=base
+# Teclast Tbook 11 (E5A6)
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnTbooK11:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
# Teclast X80 Plus (H5C5)
sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnDefaultstring:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
diff --git a/man/homectl.xml b/man/homectl.xml
index 184bdcf6fd..f2858166f7 100644
--- a/man/homectl.xml
+++ b/man/homectl.xml
@@ -846,6 +846,10 @@
<title>Exit status</title>
<para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+
+ <para>When a command is invoked with <command>with</command>, the exit status of the child is
+ propagated. Effectively, <command>homectl</command> will exit without error if the command is
+ successfully invoked <emphasis>and</emphasis> finishes successfully.</para>
</refsect1>
<xi:include href="common-variables.xml" />
diff --git a/man/meson.build b/man/meson.build
index 3cae8446cd..f9c4b83dc8 100644
--- a/man/meson.build
+++ b/man/meson.build
@@ -184,17 +184,20 @@ html = custom_target(
depends : html_pages,
command : ['echo'])
-run_target(
- 'doc-sync',
- depends : man_pages + html_pages,
- command : ['rsync', '-rlv',
- '--delete-excluded',
- '--include=man',
- '--include=*.html',
- '--exclude=*',
- '--omit-dir-times',
- meson.current_build_dir(),
- get_option('www-target')])
+rsync = find_program('rsync', required : false)
+if rsync.found()
+ run_target(
+ 'doc-sync',
+ depends : man_pages + html_pages,
+ command : [rsync, '-rlv',
+ '--delete-excluded',
+ '--include=man',
+ '--include=*.html',
+ '--exclude=*',
+ '--omit-dir-times',
+ meson.current_build_dir(),
+ get_option('www-target')])
+endif
############################################################
diff --git a/man/networkd.conf.xml b/man/networkd.conf.xml
index dcce2095ed..85b21ee7f9 100644
--- a/man/networkd.conf.xml
+++ b/man/networkd.conf.xml
@@ -63,11 +63,23 @@
</varlistentry>
<varlistentry>
+ <term><varname>ManageForeignRoutingPolicyRules=</varname></term>
+ <listitem><para>A boolean. When true, <command>systemd-networkd</command> will remove rules
+ that are not configured in .network files (except for rules with protocol
+ <literal>kernel</literal>). When false, it will not remove any foreign rules, keeping them even
+ if they are not configured in a .network file. Defaults to yes.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>ManageForeignRoutes=</varname></term>
- <listitem><para>A boolean. When true, <command>systemd-networkd</command> will store any routes
- configured by other tools in its memory. When false, <command>systemd-networkd</command> will
- not manage the foreign routes, thus they are kept even if <varname>KeepConfiguration=</varname>
- is false. Defaults to yes.</para></listitem>
+ <listitem><para>A boolean. When true, <command>systemd-networkd</command> will remove routes
+ that are not configured in .network files (except for routes with protocol
+ <literal>kernel</literal>, <literal>dhcp</literal> when <varname>KeepConfiguration=</varname>
+ is true or <literal>dhcp</literal>, and <literal>static</literal> when
+ <varname>KeepConfiguration=</varname> is true or <literal>static</literal>). When false, it will
+ not remove any foreign routes, keeping them even if they are not configured in a .network file.
+ Defaults to yes.</para></listitem>
</varlistentry>
<varlistentry>
@@ -86,15 +98,12 @@
</refsect1>
<refsect1>
- <title>[DHCP] Section Options</title>
-
- <para>This section configures the DHCP Unique Identifier (DUID) value used by DHCP
- protocol. DHCPv6 client protocol sends the DHCP Unique Identifier and the interface
- Identity Association Identifier (IAID) to a DHCP server when acquiring a dynamic IPv6
- address. DHCPv4 client protocol sends IAID and DUID to the DHCP server when acquiring
- a dynamic IPv4 address if <option>ClientIdentifier=duid</option>. IAID and DUID allows
- a DHCP server to uniquely identify the machine and the interface requesting a DHCP IP.
- To configure IAID and ClientIdentifier, see
+ <title>[DHCPv4] Section Options</title>
+
+ <para>This section configures the DHCP Unique Identifier (DUID) value used by DHCP protocol. DHCPv4
+ client protocol sends IAID and DUID to the DHCP server when acquiring a dynamic IPv4 address if
+ <option>ClientIdentifier=duid</option>. IAID and DUID allows a DHCP server to uniquely identify the
+ machine and the interface requesting a DHCP IP address. To configure IAID and ClientIdentifier, see
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
@@ -177,6 +186,28 @@ DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00</programlisting>
</refsect1>
<refsect1>
+ <title>[DHCPv6] Section Options</title>
+
+ <para>This section configures the DHCP Unique Identifier (DUID) value used by DHCPv6 protocol.
+ DHCPv6 client protocol sends the DHCP Unique Identifier and the interface Identity Association
+ Identifier (IAID) to a DHCPv6 server when acquiring a dynamic IPv6 address. IAID and DUID allows a
+ DHCPv6 server to uniquely identify the machine and the interface requesting a DHCP IP address. To
+ configure IAID, see
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
+
+ <para>The following options are understood:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>DUIDType=</varname></term>
+ <term><varname>DUIDRawData=</varname></term>
+ <listitem><para>As in the [DHCPv4] section.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
diff --git a/man/oomd.conf.xml b/man/oomd.conf.xml
index 6156c98fbd..27914e0699 100644
--- a/man/oomd.conf.xml
+++ b/man/oomd.conf.xml
@@ -52,9 +52,9 @@
<listitem><para>Sets the limit for swap usage on the system before <command>systemd-oomd</command>
will take action. If the fraction of swap used on the system is more than what is defined here,
- <command>systemd-oomd</command> will act on eligible descendant control groups, starting from the
- ones with the highest swap usage to the lowest swap usage. Which control groups are monitored and
- what action gets taken depends on what the unit has configured for
+ <command>systemd-oomd</command> will act on eligible descendant control groups with swap usage greater
+ than 5% of total swap, starting from the ones with the highest swap usage. Which
+ control groups are monitored and what action gets taken depends on what the unit has configured for
<varname>ManagedOOMSwap=</varname>. Takes a value specified in percent (when suffixed with "%"),
permille ("‰") or permyriad ("‱"), between 0% and 100%, inclusive. Defaults to 90%.</para></listitem>
</varlistentry>
@@ -81,7 +81,7 @@
<listitem><para>Sets the amount of time a unit's control group needs to have exceeded memory pressure
limits before <command>systemd-oomd</command> will take action. Memory pressure limits are defined by
<varname>DefaultMemoryPressureLimit=</varname> and <varname>ManagedOOMMemoryPressureLimit=</varname>.
- Defaults to 30 seconds when this property is unset or set to 0.</para></listitem>
+ Must be set to 0, or at least 1 second. Defaults to 30 seconds when unset or 0.</para></listitem>
</varlistentry>
</variablelist>
diff --git a/man/org.freedesktop.portable1.xml b/man/org.freedesktop.portable1.xml
index 46dca55745..93b65accca 100644
--- a/man/org.freedesktop.portable1.xml
+++ b/man/org.freedesktop.portable1.xml
@@ -185,7 +185,7 @@ node /org/freedesktop/portable1 {
<para><function>GetImageMetadataWithExtensions()</function> retrieves metadata associated with an image.
This method is a superset of <function>GetImageMetadata()</function> with the addition of
- a list of extensions as input parameter, which were overlayed on top of the main
+ a list of extensions as input parameter, which were overlaid on top of the main
image via <function>AttachImageWithExtensions()</function>.
The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
@@ -239,7 +239,7 @@ node /org/freedesktop/portable1 {
<para><function>AttachImageWithExtensions()</function> attaches a portable image to the system.
This method is a superset of <function>AttachImage()</function> with the addition of
- a list of extensions as input parameter, which will be overlayed on top of the main
+ a list of extensions as input parameter, which will be overlaid on top of the main
image. When this method is used, detaching must be done by passing the same arguments via the
<function>DetachImageWithExtensions()</function> method. For more details on this functionality,
see the <varname>MountImages=</varname> entry on
@@ -261,7 +261,7 @@ node /org/freedesktop/portable1 {
<para><function>DetachImageWithExtensions()</function> detaches a portable image from the system.
This method is a superset of <function>DetachImage()</function> with the addition of
- a list of extensions as input parameter, which were overlayed on top of the main
+ a list of extensions as input parameter, which were overlaid on top of the main
image via <function>AttachImageWithExtensions()</function>.
The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
@@ -276,7 +276,7 @@ node /org/freedesktop/portable1 {
<para><function>ReattachImageWithExtensions()</function> reattaches a portable image to the system.
This method is a superset of <function>ReattachImage()</function> with the addition of
- a list of extensions as input parameter, which will be overlayed on top of the main
+ a list of extensions as input parameter, which will be overlaid on top of the main
image. For more details on this functionality, see the <varname>MountImages=</varname> entry on
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
and <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 614871bce2..7a44c9be9b 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -492,6 +492,8 @@ node /org/freedesktop/systemd1 {
readonly t TimerSlackNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s DefaultOOMPolicy = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s CtrlAltDelBurstAction = '...';
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
@@ -733,6 +735,8 @@ node /org/freedesktop/systemd1 {
<!--property DefaultOOMPolicy is not documented!-->
+ <!--property CtrlAltDelBurstAction is not documented!-->
+
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Manager"/>
@@ -1127,6 +1131,8 @@ node /org/freedesktop/systemd1 {
<variablelist class="dbus-property" generated="True" extra-ref="DefaultOOMPolicy"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="CtrlAltDelBurstAction"/>
+
<!--End of Autogenerated section-->
<refsect2>
@@ -2474,6 +2480,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -3008,6 +3016,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
@@ -3566,6 +3576,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@@ -4251,6 +4263,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -4811,6 +4825,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
@@ -5365,6 +5381,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@@ -5952,6 +5970,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -6440,6 +6460,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
@@ -6912,6 +6934,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@@ -7620,6 +7644,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -8094,6 +8120,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
@@ -8552,6 +8580,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@@ -9113,6 +9143,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
@@ -9251,6 +9283,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
@@ -9393,6 +9427,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<!--End of Autogenerated section-->
<refsect2>
@@ -9554,6 +9590,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -9708,6 +9746,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property KillMode is not documented!-->
<!--property KillSignal is not documented!-->
@@ -9876,6 +9916,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
diff --git a/man/os-release.xml b/man/os-release.xml
index e8462500f2..cda0779303 100644
--- a/man/os-release.xml
+++ b/man/os-release.xml
@@ -352,9 +352,9 @@
<listitem><para> A lower-case string (no spaces or other characters outside of 0–9, a–z, ".", "_" and
"-"), identifying a specific image of the operating system. This is supposed to be used for
environments where OS images are prepared, built, shipped and updated as comprehensive, consistent OS
- images. This field is optional and may not be implemented on all systems, in particulary not on those
+ images. This field is optional and may not be implemented on all systems, in particularly not on those
that are not managed via images but put together and updated from individual packages and on the
- local system. Examples: <literal>IMAGE_ID=vendorx-cashier-system</literal>,
+ local system. Examples: <literal>IMAGE_ID=vendorx-cashier-system</literal>,
<literal>IMAGE_ID=netbook-image</literal> </para></listitem>
</varlistentry>
diff --git a/man/repart.d.xml b/man/repart.d.xml
index b6346b3f85..d45a4c9b18 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -422,12 +422,25 @@
<varlistentry>
<term><varname>CopyBlocks=</varname></term>
- <listitem><para>Takes a path to a regular file, block device node or directory. If specified and the
- partition is newly created the data from the specified path is written to the newly created
- partition, on the block level. If a directory is specified the backing block device of the file
- system the directory is on is determined and the data read directly from that. This option is useful
- to efficiently replicate existing file systems on the block level on a new partition, for example to
- build a simple OS installer or OS image builder.</para>
+ <listitem><para>Takes a path to a regular file, block device node or directory, or the special value
+ <literal>auto</literal>. If specified and the partition is newly created, the data from the specified
+ path is written to the newly created partition, on the block level. If a directory is specified, the
+ backing block device of the file system the directory is on is determined, and the data read directly
+ from that. This option is useful to efficiently replicate existing file systems onto new partitions
+ on the block level — for example to build a simple OS installer or an OS image builder.</para>
+
+ <para>If the special value <literal>auto</literal> is specified, the source to copy from is
+ automatically picked up from the running system (or the image specified with
+ <option>--image=</option> — if used). A partition that matches both the configured partition type (as
+ declared with <varname>Type=</varname> above), and the currently mounted directory appropriate for
+ that partition type is determined. For example, if the partition type is set to
+ <literal>root</literal> the partition backing the root directory (<filename>/</filename>) is used as
+ source to copy from — if its partition type is set to <literal>root</literal> as well. If the
+ declared type is <literal>usr</literal> the partition backing <filename>/usr/</filename> is used as
+ source to copy blocks from — if its partition type is set to <literal>usr</literal> too. The logic is
+ capable of automatically tracking down the the backing partitions for encrypted and Verity-enabled
+ volumes. <literal>CopyBlocks=auto</literal> is useful for implementing "self-replicating" systems,
+ i.e. systems that are their own installer.</para>
<para>The file specified here must have a size that is a multiple of the basic block size 512 and not
be empty. If this option is used, the size allocation algorithm is slightly altered: the partition is
@@ -486,7 +499,38 @@
<para>The copy operation is executed before the file system is registered in the partition table,
thus ensuring that a file system populated this way only ever exists fully initialized.</para>
- <para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para></listitem>
+ <para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para>
+
+ <para>When <command>systemd-repart</command> is invoked with the <option>--image=</option> or
+ <option>--root=</option> command line switches the source paths specified are taken relative to the
+ specified root directory or disk image root.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MakeDirectories=</varname></term>
+
+ <listitem><para>akes one or more absolute paths, separated by whitespace, each declaring a directory
+ to create within the new file system. Behaviour is similar to <varname>CopyFiles=</varname>, but
+ instead of copying in a set of files this just creates the specified directories with the default
+ mode of 0755 owned by the root user and group, plus all their parent directories (with the same
+ ownership and access mode). To configure directories with different ownership or access mode, use
+ <varname>CopyFiles=</varname> and specify a source tree to copy containing appropriately
+ owned/configured directories. This option may be used more than once to create multiple
+ directories. When <varname>CopyFiles=</varname> and <varname>MakeDirectories=</varname> are used
+ together the former is applied first. If a directory listed already exists no operation is executed
+ (in particular, the ownership/access mode of the directories is left as is).</para>
+
+ <para>The primary usecase for this option is to create a minimal set of directories that may be
+ mounted over by other partitions contained in the same disk image. For example, a disk image where
+ the root file system is formatted at first boot might want to automatically pre-create
+ <filename>/usr/</filename> in it this way, so that the <literal>usr</literal> partition may
+ over-mount it.</para>
+
+ <para>Consider using
+ <citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ with its <option>--image=</option> option to pre-create other, more complex directory hierarchies (as
+ well as other inodes) with fine-grained control of ownership, access modes and other file
+ attributes.</para></listitem>
</varlistentry>
<varlistentry>
@@ -521,6 +565,29 @@
factory reset operation. This functionality is useful to implement schemes where images can be reset
into their original state by removing partitions and creating them anew. Defaults to off.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>Flags=</varname></term>
+
+ <listitem><para>Configures the 64bit GPT partition flags to set for the partition when creating
+ it. This option has no effect if the partition already exists. If not specified the flags values is
+ set to all zeroes, except if the partition type (as configured with <varname>Type=</varname> above)
+ refers to a Verity partition, in which case bit 60 is set (i.e. the read-only bit). This bit may also
+ be configured separately via <varname>ReadOnly=</varname>, see below. Specify the flags value in
+ hexadecimal (by prefixing it with <literal>0x</literal>), binary (prefix <literal>0b</literal>) or
+ decimal (no prefix).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ReadOnly=</varname></term>
+
+ <listitem><para>Configures the Read-Only partition flags (bit 60) of the partition table entry. This
+ option is a friendly way to set bit 60 of the partition flags value without setting any of the other
+ bits, and may be set via <varname>Flags=</varname> too, see above.</para>
+
+ <para>If both <varname>Flags=</varname> and <varname>ReadOnly=</varname> are set the latter controls
+ the value of the flag.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/rules/meson.build b/man/rules/meson.build
index 7ef26cb2c6..05b087db3e 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -119,7 +119,8 @@ manpages = [
['sd-hwdb', '3', [], ''],
['sd-id128',
'3',
- ['SD_ID128_CONST_STR',
+ ['SD_ID128_ALLF',
+ 'SD_ID128_CONST_STR',
'SD_ID128_FORMAT_STR',
'SD_ID128_FORMAT_VAL',
'SD_ID128_MAKE',
@@ -127,6 +128,10 @@ manpages = [
'SD_ID128_NULL',
'SD_ID128_UUID_FORMAT_STR',
'sd_id128_equal',
+ 'sd_id128_in_set',
+ 'sd_id128_in_set_sentinel',
+ 'sd_id128_in_setv',
+ 'sd_id128_is_allf',
'sd_id128_is_null',
'sd_id128_t'],
''],
diff --git a/man/sd-id128.xml b/man/sd-id128.xml
index 40a3cc59ae..1890f6d6a5 100644
--- a/man/sd-id128.xml
+++ b/man/sd-id128.xml
@@ -18,16 +18,21 @@
<refnamediv>
<refname>sd-id128</refname>
- <refname>sd_id128_t</refname>
+ <refname>SD_ID128_ALLF</refname>
+ <refname>SD_ID128_CONST_STR</refname>
+ <refname>SD_ID128_FORMAT_STR</refname>
+ <refname>SD_ID128_FORMAT_VAL</refname>
<refname>SD_ID128_MAKE</refname>
<refname>SD_ID128_MAKE_STR</refname>
<refname>SD_ID128_NULL</refname>
- <refname>SD_ID128_CONST_STR</refname>
- <refname>SD_ID128_FORMAT_STR</refname>
<refname>SD_ID128_UUID_FORMAT_STR</refname>
- <refname>SD_ID128_FORMAT_VAL</refname>
<refname>sd_id128_equal</refname>
+ <refname>sd_id128_in_set</refname>
+ <refname>sd_id128_in_set_sentinel</refname>
+ <refname>sd_id128_in_setv</refname>
+ <refname>sd_id128_is_allf</refname>
<refname>sd_id128_is_null</refname>
+ <refname>sd_id128_t</refname>
<refpurpose>APIs for processing 128-bit IDs</refpurpose>
</refnamediv>
@@ -65,8 +70,8 @@
union type:</para>
<programlisting>typedef union sd_id128 {
- uint8_t bytes[16];
- uint64_t qwords[2];
+ uint8_t bytes[16];
+ uint64_t qwords[2];
} sd_id128_t;</programlisting>
<para>This union type allows accessing the 128-bit ID as 16
@@ -85,7 +90,7 @@
<programlisting>#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)</programlisting>
- <para><constant>SD_ID128_NULL</constant> may be used to refer to the 128bit ID consisting of only
+ <para><constant>SD_ID128_NULL</constant> may be used to refer to the 128-bit ID consisting of only
<constant>NUL</constant> bytes.</para>
<para><function>SD_ID128_MAKE_STR()</function> is similar to <function>SD_ID128_MAKE()</function>, but creates a
@@ -95,7 +100,7 @@
#define SD_MESSAGE_COREDUMP_STR SD_ID128_MAKE_STR(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
int main(int argc, char **argv) {
- puts("Match for coredumps: MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
+ puts("Match for coredumps: MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
}
</programlisting>
@@ -104,7 +109,7 @@ int main(int argc, char **argv) {
following example code will output the string
"fc2e22bc6ee647b6b90729ab34a250b1":</para>
<programlisting>int main(int argc, char *argv[]) {
- puts("Match for coredumps: %s", SD_ID128_CONST_STR(SD_MESSAGE_COREDUMP));
+ puts("Match for coredumps: %s", SD_ID128_CONST_STR(SD_MESSAGE_COREDUMP));
}</programlisting>
<para><constant>SD_ID128_FORMAT_STR</constant> and <function>SD_ID128_FORMAT_VAL()</function> may
@@ -113,10 +118,10 @@ int main(int argc, char **argv) {
format string, as shown in the following example:</para>
<programlisting>int main(int argc, char *argv[]) {
- sd_id128_t id;
- id = SD_ID128_MAKE(ee,89,be,71,bd,6e,43,d6,91,e6,c5,5d,eb,03,02,07);
- printf("The ID encoded in this C file is " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(id));
- return 0;
+ sd_id128_t id;
+ id = SD_ID128_MAKE(ee,89,be,71,bd,6e,43,d6,91,e6,c5,5d,eb,03,02,07);
+ printf("The ID encoded in this C file is " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(id));
+ return 0;
}</programlisting>
<para><constant>SD_ID128_UUID_FORMAT_STR</constant> is similar to
@@ -127,21 +132,50 @@ int main(int argc, char **argv) {
<para>Use <function>sd_id128_equal()</function> to compare two 128-bit IDs:</para>
<programlisting>int main(int argc, char *argv[]) {
- sd_id128_t a, b, c;
- a = SD_ID128_MAKE(ee,89,be,71,bd,6e,43,d6,91,e6,c5,5d,eb,03,02,07);
- b = SD_ID128_MAKE(f2,28,88,9c,5f,09,44,15,9d,d7,04,77,58,cb,e7,3e);
- c = a;
- assert(sd_id128_equal(a, c));
- assert(!sd_id128_equal(a, b));
- return 0;
+ sd_id128_t a, b, c;
+ a = SD_ID128_MAKE(ee,89,be,71,bd,6e,43,d6,91,e6,c5,5d,eb,03,02,07);
+ b = SD_ID128_MAKE(f2,28,88,9c,5f,09,44,15,9d,d7,04,77,58,cb,e7,3e);
+ c = a;
+ assert(sd_id128_equal(a, c));
+ assert(!sd_id128_equal(a, b));
+ return 0;
}</programlisting>
- <para>Use <function>sd_id128_is_null()</function> to check if an 128bit ID consists of only
+ <para>Use <function>sd_id128_is_null()</function> to check if an 128-bit ID consists of only
<constant>NUL</constant> bytes:</para>
+ <programlisting>assert(sd_id128_is_null(SD_ID128_NULL));</programlisting>
+
+ <para>Similarly, use <function>sd_id128_is_allf()</function> to check if an 128-bit ID consists of only
+ <constant>0xFF</constant> bytes (all bits on):</para>
+
+ <programlisting>assert(sd_id128_is_allf(SD_ID128_ALLF));</programlisting>
+
+ <para>For convenience, <function>sd_id128_in_set()</function> takes a list of IDs and
+ returns true if any are equal to the first argument:</para>
+
<programlisting>int main(int argc, char *argv[]) {
- assert(sd_id128_is_null(SD_ID128_NULL));
-}</programlisting>
+ sd_id12_t a = SD_ID128_MAKE(ee,89,be,71,bd,6e,43,d6,91,e6,c5,5d,eb,03,02,07);
+ assert(sd_id128_in_set(a, a));
+ assert(sd_id128_in_set(a, a, a));
+ assert(!sd_id128_in_set(a));
+ assert(!sd_id128_in_set(a,
+ SD_ID128_MAKE(f2,28,88,9c,5f,09,44,15,9d,d7,04,77,58,cb,e7,3e)
+ SD_ID128_MAKE(2f,88,28,5f,9c,44,09,9d,d7,15,77,04,bc,85,7e,e3)
+ SD_ID128_ALLF));
+ return 0;
+}
+</programlisting>
+
+ <para><function>sd_id128_in_set()</function> is defined as a macro over
+ <function>sd_id128_in_set_sentinel()</function>, adding the <constant>SD_ID128_NULL</constant>
+ sentinel. Since <function>sd_id128_in_set_sentinel()</function> uses <constant>SD_ID128_NULL</constant>
+ as the sentinel, <constant>SD_ID128_NULL</constant> cannot be otherwise placed in the argument list.
+ </para>
+
+ <para><function>sd_id128_in_setv()</function> is similar to
+ <function>sd_id128_in_set_sentinel()</function>, but takes a <structname>struct varargs</structname>
+ argument.</para>
<para>Note that new, randomized IDs may be generated with
<citerefentry><refentrytitle>systemd-id128</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
diff --git a/man/sd_bus_is_open.xml b/man/sd_bus_is_open.xml
index 8e0aed29b3..621ed272bb 100644
--- a/man/sd_bus_is_open.xml
+++ b/man/sd_bus_is_open.xml
@@ -57,6 +57,9 @@
zero outside of this state, and positive otherwise. Effectively, this function returns positive while regular
messages can be sent or received on the connection.</para>
+ <para>The <parameter>bus</parameter> argument may be <constant>NULL</constant>, zero is also returned in
+ that case.</para>
+
<para>To be notified when the connection is fully established, use
<citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
install a match for the <function>Connected()</function> signal on the
@@ -68,8 +71,8 @@
<refsect1>
<title>Return Value</title>
- <para>On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style
- error code.</para>
+ <para>Those functions return 0 if the bus is <emphasis>not</emphasis> in the given state, and a positive
+ integer when it is. On failure, a negative errno-style error code is returned.</para>
<refsect2>
<title>Errors</title>
diff --git a/man/sd_journal_print.xml b/man/sd_journal_print.xml
index 68a4a0a5c0..f7a70e9b0f 100644
--- a/man/sd_journal_print.xml
+++ b/man/sd_journal_print.xml
@@ -218,6 +218,9 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid(
<function>sd_journal_send()</function>. Using
<function>syslog()</function> has the benefit of being
more portable.</para>
+
+ <para>These functions implement a client to the <ulink
+ url="https://systemd.io/JOURNAL_NATIVE_PROTOCOL">Native Journal Protocol</ulink>.</para>
</refsect1>
<refsect1>
diff --git a/man/sd_login_monitor_new.xml b/man/sd_login_monitor_new.xml
index 57d22f9f65..081796249d 100644
--- a/man/sd_login_monitor_new.xml
+++ b/man/sd_login_monitor_new.xml
@@ -115,7 +115,7 @@
__attribute__((cleanup(sd_login_monitor_unrefp))) sd_login_monitor *m = NULL;
int r;
- r = sd_login_monitor_default(&amp;m);
+ r = sd_login_monitor_new(NULL, &amp;m);
if (r &lt; 0)
fprintf(stderr, "Failed to allocate login monitor object: %s\n", strerror(-r));
diff --git a/man/systemd-coredump.xml b/man/systemd-coredump.xml
index 117b9eb6d0..8719ddaae9 100644
--- a/man/systemd-coredump.xml
+++ b/man/systemd-coredump.xml
@@ -353,6 +353,20 @@ flags: ...
</varlistentry>
<varlistentry>
+ <term><varname>COREDUMP_PACKAGE_NAME=</varname></term>
+ <term><varname>COREDUMP_PACKAGE_VERSION=</varname></term>
+ <term><varname>COREDUMP_PACKAGE_JSON=</varname></term>
+
+ <listitem><para>If the executable contained .package metadata ELF notes, they will be
+ parsed and attached. The <varname>package</varname> and <varname>packageVersion</varname>
+ of the 'main' ELF module (ie: the executable) will be appended individually. The
+ JSON-formatted content of all modules will be appended as a single JSON object, each with
+ the module name as the key. For more information about this metadata format and content, see
+ <ulink url="https://systemd.io/COREDUMP_PACKAGE_METADATA/">the coredump metadata spec</ulink>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>MESSAGE=</varname></term>
<listitem><para>The message generated by <command>systemd-coredump</command> that includes the
diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml
index 93acdd02a5..9751444e50 100644
--- a/man/systemd-cryptenroll.xml
+++ b/man/systemd-cryptenroll.xml
@@ -215,7 +215,7 @@
<row>
<entry>8</entry>
- <entry><citerefentry><refentrytitle>sd-boot</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures the kernel command line in this PCR.</entry>
+ <entry><citerefentry><refentrytitle>sd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the kernel command line in this PCR.</entry>
</row>
</tbody>
</tgroup>
diff --git a/man/systemd-cryptsetup@.service.xml b/man/systemd-cryptsetup@.service.xml
index c70d6a9d3e..668208a01d 100644
--- a/man/systemd-cryptsetup@.service.xml
+++ b/man/systemd-cryptsetup@.service.xml
@@ -17,22 +17,29 @@
<refnamediv>
<refname>systemd-cryptsetup@.service</refname>
+ <!-- <refname>system-systemd\x2dcryptsetup.slice</refname> — this causes meson to go haywire because it
+ thinks this is a (windows) path. Let's just not create the alias for this name, and only include it
+ in the synopsis. -->
<refname>systemd-cryptsetup</refname>
<refpurpose>Full disk decryption logic</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-cryptsetup@.service</filename></para>
+ <para><filename>system-systemd\x2dcryptsetup.slice</filename></para>
<para><filename>/usr/lib/systemd/systemd-cryptsetup</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><filename>systemd-cryptsetup@.service</filename> is a
- service responsible for setting up encrypted block devices. It is
- instantiated for each device that requires decryption for
- access.</para>
+ <para><filename>systemd-cryptsetup@.service</filename> is a service responsible for setting up encrypted
+ block devices. It is instantiated for each device that requires decryption for access.</para>
+
+ <para><filename>systemd-cryptsetup@.service</filename> instances are part of the
+ <filename>system-systemd\x2dcryptsetup.slice</filename> slice, which is destroyed only very late in the
+ shutdown procedure. This allows the encrypted devices to remain up until filesystems have been unmounted.
+ </para>
<para><filename>systemd-cryptsetup@.service</filename> will ask
for hard disk passwords via the <ulink
diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml
index caaf68dcae..0de772cf5e 100644
--- a/man/systemd-dissect.xml
+++ b/man/systemd-dissect.xml
@@ -187,8 +187,8 @@
<term><option>--fsck=no</option></term>
<listitem><para>Turn off automatic file system checking. By default when an image is accessed for
- writing (by <option>--mount</option> or <option>--add</option>) the file systems contained in the OS
- image are automatically checked using the appropriate <citerefentry
+ writing (by <option>--mount</option> or <option>--copy-to</option>) the file systems contained in the
+ OS image are automatically checked using the appropriate <citerefentry
project='man-pages'><refentrytitle>fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>
command, in automatic fixing mode. This behavior may be switched off using
<option>--fsck=no</option>.</para></listitem>
diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml
index 35cfbde86c..875393b408 100644
--- a/man/systemd-journald.service.xml
+++ b/man/systemd-journald.service.xml
@@ -53,9 +53,10 @@
project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call</para></listitem>
- <listitem><para>Structured system log messages via the native
- Journal API, see
- <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry></para></listitem>
+ <listitem><para>Structured system log messages via the native Journal API, see
+ <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ and <ulink url="https://systemd.io/JOURNAL_NATIVE_PROTOCOL">Native Journal
+ Protocol</ulink></para></listitem>
<listitem><para>Standard output and standard error of service units. For further details see
below.</para></listitem>
diff --git a/man/systemd-networkd-wait-online.service.xml b/man/systemd-networkd-wait-online.service.xml
index 6d2c71d8c7..31aa02218a 100644
--- a/man/systemd-networkd-wait-online.service.xml
+++ b/man/systemd-networkd-wait-online.service.xml
@@ -86,6 +86,34 @@
</varlistentry>
<varlistentry>
+ <term><option>-4</option></term>
+ <term><option>--ipv4</option></term>
+
+ <listitem><para>Waiting for an IPv4 address of each network interface to be configured. If this
+ option is specified with <option>--any</option>, then
+ <command>systemd-networkd-wait-online</command> exits with success when at least one interface
+ becomes online and has an IPv4 address. The option is applied only for the operational state
+ <literal>degraded</literal> or above. If neither <option>--ipv4</option> nor
+ <option>--ipv6</option> is specified, then the value from
+ <varname>RequiredFamilyForOnline=</varname> in the corresponding <filename>.network</filename>
+ file is used if present.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-6</option></term>
+ <term><option>--ipv6</option></term>
+
+ <listitem><para>Waiting for an IPv6 address of each network interface to be configured. If this
+ option is specified with <option>--any</option>, then
+ <command>systemd-networkd-wait-online</command> exits with success when at least one interface
+ becomes online and has an IPv6 address. The option is applied only for the operational state
+ <literal>degraded</literal> or above. If neither <option>--ipv4</option> nor
+ <option>--ipv6</option> is specified, then the value from
+ <varname>RequiredFamilyForOnline=</varname> in the corresponding <filename>.network</filename>
+ file is used if present.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--any</option></term>
<listitem><para>Even if several interfaces are in configuring state,
diff --git a/man/systemd-oomd.service.xml b/man/systemd-oomd.service.xml
index ebd2467ee2..86c47e5b57 100644
--- a/man/systemd-oomd.service.xml
+++ b/man/systemd-oomd.service.xml
@@ -48,10 +48,9 @@
<title>Setup Information</title>
<para>The system must be running systemd with a full unified cgroup hierarchy for the expected cgroups-v2 features.
- Furthermore, resource accounting must be turned on for all units monitored by <command>systemd-oomd</command>.
- The easiest way to turn on resource accounting is by ensuring the values for <varname>DefaultCPUAccounting</varname>,
- <varname>DefaultIOAccounting</varname>, <varname>DefaultMemoryAccounting</varname>, and
- <varname>DefaultTasksAccounting</varname> are set to <constant>true</constant> in
+ Furthermore, memory accounting must be turned on for all units monitored by <command>systemd-oomd</command>.
+ The easiest way to turn on memory accounting is by ensuring the value for <varname>DefaultMemoryAccounting=</varname>
+ is set to <constant>true</constant> in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<para>You will need a kernel compiled with PSI support. This is available in Linux 4.20 and above.</para>
diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml
index a5a0890c52..380ba57884 100644
--- a/man/systemd-repart.xml
+++ b/man/systemd-repart.xml
@@ -40,16 +40,17 @@
<citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
- <para>If invoked with no arguments, it operates on the block device backing the root file system partition
- of the OS, thus growing and adding partitions of the booted OS image itself. When called in the initial
- RAM disk it operates on the block device backing <filename>/sysroot/</filename> instead, i.e. on the
- block device the system will soon transition into. The <filename>systemd-repart.service</filename>
- service is generally run at boot in the initial RAM disk, in order to augment the partition table of the
- OS before its partitions are mounted. <command>systemd-repart</command> (mostly) operates in a purely
- incremental mode: it only grows existing and adds new partitions; it does not shrink, delete or move
- existing partitions. The service is intended to be run on every boot, but when it detects that the
- partition table already matches the installed <filename>repart.d/*.conf</filename> configuration
- files, it executes no operation.</para>
+ <para>If invoked with no arguments, it operates on the block device backing the root file system
+ partition of the running OS, thus growing and adding partitions of the booted OS image itself. If
+ <varname>--image=</varname> is used it will operate on the specified image file. When called in the
+ <literal>initrd</literal> it operates on the block device backing <filename>/sysroot/</filename> instead,
+ i.e. on the block device the system will soon transition into. The
+ <filename>systemd-repart.service</filename> service is generally run at boot in the initial RAM disk, in
+ order to augment the partition table of the OS before its partitions are
+ mounted. <command>systemd-repart</command> (mostly) operates in a purely incremental mode: it only grows
+ existing and adds new partitions; it does not shrink, delete or move existing partitions. The service is
+ intended to be run on every boot, but when it detects that the partition table already matches the
+ installed <filename>repart.d/*.conf</filename> configuration files, it executes no operation.</para>
<para><command>systemd-repart</command> is intended to be used when deploying OS images, to automatically
adjust them to the system they are running on, during first boot. This way the deployed image can be
@@ -251,14 +252,22 @@
<term><option>--root=</option></term>
<listitem><para>Takes a path to a directory to use as root file system when searching for
- <filename>repart.d/*.conf</filename> files and for the machine ID file to use as seed. By default
- when invoked on the regular system this defaults to the host's root file system
+ <filename>repart.d/*.conf</filename> files, for the machine ID file to use as seed and for the
+ <varname>CopyFiles=</varname> and <varname>CopyBlocks=</varname> source files and directories. By
+ default when invoked on the regular system this defaults to the host's root file system
<filename>/</filename>. If invoked from the initial RAM disk this defaults to
<filename>/sysroot/</filename>, so that the tool operates on the configuration and machine ID stored
in the root file system later transitioned into itself.</para></listitem>
</varlistentry>
<varlistentry>
+ <term><option>--image=</option></term>
+
+ <listitem><para>Takes a path to a disk image file or device to mount and use in a similar fashion to
+ <option>--root=</option>, see above.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--seed=</option></term>
<listitem><para>Takes a UUID as argument or the special value <constant>random</constant>. If a UUID
diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml
index 5f97e37025..15bc1ea889 100644
--- a/man/systemd-tmpfiles.xml
+++ b/man/systemd-tmpfiles.xml
@@ -263,16 +263,18 @@
<refsect1>
<title>Exit status</title>
- <para>On success, 0 is returned. If the configuration was syntactically invalid (syntax errors,
- missing arguments, …), so some lines had to be ignored, but no other errors occurred,
- <constant>65</constant> is returned (<constant>EX_DATAERR</constant> from
- <filename>/usr/include/sysexits.h</filename>). If the configuration was syntactically valid, but
- could not be executed (lack of permissions, creation of files in missing directories, invalid
- contents when writing to <filename>/sys/</filename> values, …), <constant>73</constant> is
- returned (<constant>EX_CANTCREAT</constant> from <filename>/usr/include/sysexits.h</filename>).
- Otherwise, <constant>1</constant> is returned (<constant>EXIT_FAILURE</constant> from
- <filename>/usr/include/stdlib.h</filename>).
- </para>
+ <para>On success, 0 is returned. If the configuration was syntactically invalid (syntax errors, missing
+ arguments, …), so some lines had to be ignored, but no other errors occurred, <constant>65</constant> is
+ returned (<constant>EX_DATAERR</constant> from <filename>/usr/include/sysexits.h</filename>). If the
+ configuration was syntactically valid, but could not be executed (lack of permissions, creation of files
+ in missing directories, invalid contents when writing to <filename>/sys/</filename> values, …),
+ <constant>73</constant> is returned (<constant>EX_CANTCREAT</constant> from
+ <filename>/usr/include/sysexits.h</filename>). Otherwise, <constant>1</constant> is returned
+ (<constant>EXIT_FAILURE</constant> from <filename>/usr/include/stdlib.h</filename>).</para>
+
+ <para>Note: when creating items, if the target already exists, but is of the wrong type or otherwise does
+ not match the requested state, and forced operation has not been requested with <literal>+</literal>,
+ a message is emitted, but the failure is otherwise ignored.</para>
</refsect1>
<refsect1>
diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml
index 5df4cd6a22..32cb82bac3 100644
--- a/man/systemd-udevd.service.xml
+++ b/man/systemd-udevd.service.xml
@@ -88,8 +88,8 @@
<term><option>-e</option></term>
<term><option>--exec-delay=</option></term>
<listitem>
- <para>Delay the execution of <varname>RUN</varname>
- instructions by the given number of seconds. This option
+ <para>Delay the execution of each <varname>RUN{<replaceable>program</replaceable>}</varname>
+ parameter by the given number of seconds. This option
might be useful when debugging system crashes during
coldplug caused by loading non-working kernel
modules.</para>
@@ -160,7 +160,7 @@
<term><varname>udev.exec_delay=</varname></term>
<term><varname>rd.udev.exec_delay=</varname></term>
<listitem>
- <para>Delay the execution of <varname>RUN</varname> instructions by the given
+ <para>Delay the execution of each <varname>RUN{<replaceable>program</replaceable>}</varname> parameter by the given
number of seconds. This option might be useful when
debugging system crashes during coldplug caused by loading
non-working kernel modules.</para>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 6ae630f615..2aefb4eb25 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1777,11 +1777,13 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
<term><varname>RestrictAddressFamilies=</varname></term>
<listitem><para>Restricts the set of socket address families accessible to the processes of this
- unit. Takes a space-separated list of address family names to allow-list, such as
- <constant>AF_UNIX</constant>, <constant>AF_INET</constant> or <constant>AF_INET6</constant>. When
- prefixed with <constant>~</constant> the listed address families will be applied as deny list,
- otherwise as allow list. Note that this restricts access to the <citerefentry
- project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+ unit. Takes <literal>none</literal>, or a space-separated list of address family names to
+ allow-list, such as <constant>AF_UNIX</constant>, <constant>AF_INET</constant> or
+ <constant>AF_INET6</constant>. When <literal>none</literal> is specified, then all address
+ families will be denied. When prefixed with <literal>~</literal> the listed address
+ families will be applied as deny list, otherwise as allow list. Note that this restricts access
+ to the
+ <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>2</manvolnum></citerefentry>
system call only. Sockets passed into the process by other means (for example, by using socket
activation with socket units, see
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 66759607d3..324d1266b4 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -230,6 +230,18 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>RequiredFamilyForOnline=</varname></term>
+ <listitem>
+ <para>Specifies an address family. When specified,
+ <command>systemd-networkd-wait-online</command> waits for at least one routable or link-local
+ IP address in the family should be configured on the link. Takes one of
+ <literal>ipv4</literal>, <literal>ipv6</literal>, <literal>both</literal>, or
+ <literal>any</literal>. Defaults to <literal>any</literal>. Note that this will be used only
+ when <varname>RequiredForOnline=</varname> is true, or its minimum operational state is
+ <literal>degraded</literal> or above. Otherwise, it will be ignored.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>ActivationPolicy=</varname></term>
<listitem>
<para>Specifies the policy for <command>systemd-networkd</command> managing the link
@@ -1088,6 +1100,15 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>RouteMetric=</varname></term>
+ <listitem>
+ <para>The metric of the prefix route, which is pointing to the subnet of the configured IP
+ address, taking the configured prefix length into account. Takes an unsigned integer in the
+ range 0…4294967295. When unset or set to 0, the kernel's default value is used. This
+ setting will be ignored when <varname>AddPrefixRoute=</varname> is false.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>HomeAddress=</varname></term>
<listitem>
<para>Takes a boolean. Designates this address the "home address" as defined in
@@ -1409,7 +1430,8 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<varlistentry>
<term><varname>Metric=</varname></term>
<listitem>
- <para>The metric of the route (an unsigned integer).</para>
+ <para>The metric of the route. Takes an unsigned integer in the range 0…4294967295.
+ Defaluts to unset, and the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -1639,26 +1661,24 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<varlistentry>
<term><varname>Anonymize=</varname></term>
<listitem>
- <para>Takes a boolean. When true, the options sent to the DHCP server will
- follow the <ulink url="https://tools.ietf.org/html/rfc7844">RFC 7844</ulink>
- (Anonymity Profiles for DHCP Clients) to minimize disclosure of identifying information.
- Defaults to false.</para>
+ <para>Takes a boolean. When true, the options sent to the DHCP server will follow the
+ <ulink url="https://tools.ietf.org/html/rfc7844">RFC 7844</ulink> (Anonymity Profiles for
+ DHCP Clients) to minimize disclosure of identifying information. Defaults to false.</para>
- <para>This option should only be set to true when
- <varname>MACAddressPolicy=</varname> is set to <literal>random</literal>
- (see <citerefentry
- project='man-pages'><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para>
+ <para>This option should only be set to true when <varname>MACAddressPolicy=</varname> is
+ set to <literal>random</literal> (see
+ <citerefentry project='man-pages'><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+ </para>
- <para>Note that this configuration will overwrite others.
- In concrete, the following variables will be ignored:
- <varname>SendHostname=</varname>, <varname>ClientIdentifier=</varname>,
- <varname>UseRoutes=</varname>, <varname>UseMTU=</varname>,
- <varname>VendorClassIdentifier=</varname>, <varname>UseTimezone=</varname>.</para>
+ <para>When true, <varname>SendHostname=</varname>, <varname>ClientIdentifier=</varname>,
+ <varname>VendorClassIdentifier=</varname>, <varname>UserClass=</varname>,
+ <varname>RequestOptions=</varname>, <varname>SendOption=</varname>,
+ <varname>SendVendorOption=</varname>, and <varname>MUDURL=</varname> are ignored.</para>
- <para>With this option enabled DHCP requests will mimic those generated by Microsoft Windows, in
- order to reduce the ability to fingerprint and recognize installations. This means DHCP request
- sizes will grow and lease data will be more comprehensive than normally, though most of the
- requested data is not actually used.</para>
+ <para>With this option enabled DHCP requests will mimic those generated by Microsoft
+ Windows, in order to reduce the ability to fingerprint and recognize installations. This
+ means DHCP request sizes will grow and lease data will be more comprehensive than normally,
+ though most of the requested data is not actually used.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -1827,7 +1847,8 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<varlistentry>
<term><varname>RouteMetric=</varname></term>
<listitem>
- <para>Set the routing metric for routes specified by the DHCP server. Defaults to 1024.</para>
+ <para>Set the routing metric for routes specified by the DHCP server. Takes an unsigned
+ integer in the range 0…4294967295. Defaults to 1024.</para>
</listitem>
</varlistentry>
@@ -1962,19 +1983,15 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<term><varname>UseNTP=</varname></term>
<term><varname>UseHostname=</varname></term>
<term><varname>UseDomains=</varname></term>
+ <term><varname>IAID=</varname></term>
+ <term><varname>DUIDType=</varname></term>
+ <term><varname>DUIDRawData=</varname></term>
<listitem>
<para>As in the [DHCPv4] section.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>RouteMetric=</varname></term>
- <listitem>
- <para>Set the routing metric for routes specified by the DHCP server. Defaults to 1024.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
<term><varname>RapidCommit=</varname></term>
<listitem>
<para>Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server through
@@ -2148,6 +2165,14 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<para>As in the [Address] section, but defaults to true.</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>RouteMetric=</varname></term>
+ <listitem>
+ <para>The metric of the route to the delegated prefix subnet. Takes an unsigned integer in
+ the range 0…4294967295. When unset or set to 0, the kernel's default value is used.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
@@ -2198,6 +2223,14 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
</varlistentry>
<varlistentry>
+ <term><varname>RouteMetric=</varname></term>
+ <listitem>
+ <para>Set the routing metric for the routes received in the Router Advertisement. Takes an
+ unsigned integer in the range 0…4294967295. Defaults to 1024.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>UseAutonomousPrefix=</varname></term>
<listitem>
<para>When true (the default), the autonomous prefix received in the Router Advertisement will be used and take
@@ -2285,6 +2318,17 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<variablelist class='network-directives'>
<varlistentry>
+ <term><varname>RelayTarget=</varname></term>
+ <listitem>
+ <para>Takes an IPv4 address, which must be in the format
+ described in
+ <citerefentry project='man-pages'><refentrytitle>inet_pton</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ Turns this DHCP server into a DHCP relay agent. See <ulink url="https://tools.ietf.org/html/rfc1542">RFC 1542</ulink>.
+ The address is the address of DHCP server or another relay agent to forward DHCP messages to and from.</para>
+ Check also BindToInterface= option. Turning it off is required for relaying messages outside.
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>PoolOffset=</varname></term>
<term><varname>PoolSize=</varname></term>
@@ -2407,6 +2451,13 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
then all options specified earlier are cleared. Defaults to unset.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>BindToInterface=</varname></term>
+ <listitem>Takes a boolean value. When <literal>yes</literal>, DHCP server socket will be bound
+ to its network interface and all socket communication will be restricted to this interface.
+ Defaults to <literal>yes</literal>.
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
@@ -2536,6 +2587,15 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<listitem><para>Takes a boolean. When true, adds an address from the prefix. Default to false.
</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>RouteMetric=</varname></term>
+ <listitem>
+ <para>The metric of the prefix route. Takes an unsigned integer in the range 0…4294967295.
+ When unset or set to 0, the kernel's default value is used. This setting is ignored when
+ <varname>Assign=</varname> is false.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml
index 1bc45a9f00..a2d01f7afb 100644
--- a/man/systemd.resource-control.xml
+++ b/man/systemd.resource-control.xml
@@ -696,6 +696,12 @@
<para>If these settings are used multiple times in the same unit all the specified programs are attached. If an
empty string is assigned to these settings the program list is reset and all previous specified programs ignored.</para>
+ <para>If the path <replaceable>BPF_FS_PROGRAM_PATH</replaceable> in <varname>IPIngressFilterPath=</varname> assignment
+ is already being handled by <varname>BPFProgram=</varname> ingress hook, e.g.
+ <varname>BPFProgram=</varname><constant>ingress</constant>:<replaceable>BPF_FS_PROGRAM_PATH</replaceable>,
+ the assignment will be still considered valid and the program will be attached to a cgroup. Same for
+ <varname>IPEgressFilterPath=</varname> path and <constant>egress</constant> hook.</para>
+
<para>Note that for socket-activated services, the IP filter programs configured on the socket unit apply to
all sockets associated with it directly, but not to any sockets created by the ultimately activated services
for it. Conversely, the IP filter programs configured for the service are not applied to any sockets passed into
@@ -711,6 +717,52 @@
</varlistentry>
<varlistentry>
+ <term><varname>BPFProgram=<replaceable>type</replaceable><constant>:</constant><replaceable>program-path</replaceable></varname></term>
+ <listitem>
+ <para>Add a custom cgroup BPF program.</para>
+
+ <para><varname>BPFProgram=</varname> allows attaching BPF hooks to the cgroup of a systemd unit.
+ (This generalizes the functionality exposed via <varname>IPEgressFilterPath=</varname> for egress and
+ <varname>IPIngressFilterPath=</varname> for ingress.)
+ Cgroup-bpf hooks in the form of BPF programs loaded to the BPF filesystem are attached with cgroup-bpf attach
+ flags determined by the unit. For details about attachment types and flags see <ulink
+ url="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/bpf.h"/>.
+ For general BPF documentation please refer to <ulink url="https://www.kernel.org/doc/html/latest/bpf/index.html"/>.</para>
+
+ <para>The specification of BPF program consists of a <replaceable>type</replaceable> followed by a
+ <replaceable>program-path</replaceable> with <literal>:</literal> as the separator:
+ <replaceable>type</replaceable><constant>:</constant><replaceable>program-path</replaceable>.</para>
+
+ <para><replaceable>type</replaceable> is the string name of BPF attach type also used in
+ <command>bpftool</command>. <replaceable>type</replaceable> can be one of <constant>egress</constant>,
+ <constant>ingress</constant>, <constant>sock_create</constant>, <constant>sock_ops</constant>,
+ <constant>device</constant>, <constant>bind4</constant>, <constant>bind6</constant>,
+ <constant>connect4</constant>, <constant>connect6</constant>, <constant>post_bind4</constant>,
+ <constant>post_bind6</constant>, <constant>sendmsg4</constant>, <constant>sendmsg6</constant>,
+ <constant>sysctl</constant>, <constant>recvmsg4</constant>, <constant>recvmsg6</constant>,
+ <constant>getsockopt</constant>, <constant>setsockopt</constant>.</para>
+
+ <para>Setting <varname>BPFProgram=</varname> to an empty value makes previous assignments ineffective.</para>
+ <para>Multiple assignments of the same <replaceable>type</replaceable>:<replaceable>program-path</replaceable>
+ value have the same effect as a single assignment: the program with the path <replaceable>program-path</replaceable>
+ will be attached to cgroup hook <replaceable>type</replaceable> just once.</para>
+ <para>If BPF <constant>egress</constant> pinned to <replaceable>program-path</replaceable> path is already being
+ handled by <varname>IPEgressFilterPath=</varname>, <varname>BPFProgram=</varname>
+ assignment will be considered valid and <varname>BPFProgram=</varname> will be attached to a cgroup.
+ Similarly for <constant>ingress</constant> hook and <varname>IPIngressFilterPath=</varname> assignment.</para>
+
+ <para>BPF programs passed with <varname>BPFProgram=</varname> are attached to the cgroup of a unit with BPF
+ attach flag <constant>multi</constant>, that allows further attachments of the same
+ <replaceable>type</replaceable> within cgroup hierarchy topped by the unit cgroup.</para>
+
+ <para>Examples:<programlisting>
+BPFProgram=egress:/sys/fs/bpf/egress-hook
+BPFProgram=bind6:/sys/fs/bpf/sock-addr-hook
+</programlisting></para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>DeviceAllow=</varname></term>
<listitem>
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index c9f320935d..b09c4e9fa2 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -46,6 +46,7 @@
<filename>initrd-fs.target</filename>,
<filename>initrd-root-device.target</filename>,
<filename>initrd-root-fs.target</filename>,
+ <filename>initrd-usr-fs.target</filename>,
<filename>kbrequest.target</filename>,
<filename>kexec.target</filename>,
<filename>local-fs-pre.target</filename>,
@@ -372,12 +373,13 @@
<term><filename>initrd-fs.target</filename></term>
<listitem>
<para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- automatically adds dependencies of type
- <varname>Before=</varname> to
- <filename>sysroot-usr.mount</filename> and all mount points
- found in <filename>/etc/fstab</filename> that have
- <option>x-initrd.mount</option> and not have
- <option>noauto</option> mount options set.</para>
+ automatically adds dependencies of type <varname>Before=</varname> to
+ <filename>sysroot-usr.mount</filename> and all mount points found in
+ <filename>/etc/fstab</filename> that have the <option>x-initrd.mount</option> mount option set
+ and do not have the <option>noauto</option> mount option set. It is also indirectly ordered after
+ <filename>sysroot.mount</filename>. Thus, once this target is reached the
+ <filename>/sysroot/</filename> hierarchy is fully set up, in preparation for the transition to
+ the host OS.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -396,11 +398,27 @@
<term><filename>initrd-root-fs.target</filename></term>
<listitem>
<para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- automatically adds dependencies of type
- <varname>Before=</varname> to the
- <filename>sysroot.mount</filename> unit, which is generated
- from the kernel command line.
- </para>
+ automatically adds dependencies of type <varname>Before=</varname> to the
+ <filename>sysroot.mount</filename> unit, which is generated from the kernel command line's
+ <varname>root=</varname> setting (or equivalent).</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>initrd-usr-fs.target</filename></term>
+ <listitem>
+ <para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ automatically adds dependencies of type <varname>Before=</varname> to the
+ <filename>sysusr-usr.mount</filename> unit, which is generated from the kernel command line's
+ <varname>usr=</varname> switch. Services may order themselves after this target unit in order to
+ run once the <filename>/sysusr/</filename> hierarchy becomes available, on systems that come up
+ initially without a root file system, but with an initialized <filename>/usr/</filename> and need
+ to access that before setting up the root file system to ultimately switch to. On systems where
+ <varname>usr=</varname> is not used this target is ordered after
+ <filename>sysroot.mount</filename> and thus mostly equivalent to
+ <filename>initrd-root-fs.target</filename>. In effect on any system once this target is reached
+ the file system backing <filename>/usr/</filename> is mounted, though possibly at two different
+ locations, either below the <filename>/sysusr/</filename> or the <filename>/sysroot/</filename>
+ hierarchies.</para>
</listitem>
</varlistentry>
<varlistentry>
diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index 55b763e26d..35b762ca3f 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -58,8 +58,8 @@ c+ /dev/char-device-to-[re]create mode user group - major
b /dev/block-device-to-create mode user group - major:minor
b+ /dev/block-device-to-[re]create mode user group - major:minor
C /target/to/create - - - - /source/to/copy
-x /path-or-glob/to/ignore - - - - -
-X /path-or-glob/to/ignore/recursively - - - - -
+x /path-or-glob/to/ignore/recursively - - - - -
+X /path-or-glob/to/ignore - - - - -
r /empty/dir/to/remove - - - - -
R /dir/to/remove/recursively - - - - -
z /path-or-glob/to/adjust/mode mode user group - -
diff --git a/man/udev.conf.xml b/man/udev.conf.xml
index df0a70c9fb..0f51a1aaef 100644
--- a/man/udev.conf.xml
+++ b/man/udev.conf.xml
@@ -63,8 +63,8 @@
<term><varname>exec_delay=</varname></term>
<listitem>
- <para>An integer. Delay the execution of <varname>RUN</varname>
- instructions by the given number of seconds. This option
+ <para>An integer. Delay the execution of each <varname>RUN{<replaceable>program</replaceable>}</varname>
+ parameter by the given number of seconds. This option
might be useful when debugging system crashes during
coldplug caused by loading non-working kernel
modules.</para>
diff --git a/meson.build b/meson.build
index 2d44e7948d..81db2d27ab 100644
--- a/meson.build
+++ b/meson.build
@@ -38,8 +38,8 @@ relative_source_path = run_command('realpath',
project_source_root).stdout().strip()
conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path)
-conf.set('BUILD_MODE', 'BUILD_MODE_' + get_option('mode').to_upper(),
- description : 'tailor build to development or release builds')
+conf.set10('BUILD_MODE_DEVELOPER', get_option('mode') == 'developer',
+ description : 'tailor build to development or release builds')
want_ossfuzz = get_option('oss-fuzz')
want_libfuzzer = get_option('llvm-fuzz')
@@ -395,10 +395,6 @@ possible_cc_flags = [
'-Wno-error=#warnings', # clang
'-Wno-string-plus-int', # clang
- # Disable -Wmaybe-uninitialized, since it's noisy on gcc 8 with
- # optimizations enabled, producing essentially false positives.
- '-Wno-maybe-uninitialized',
-
'-ffast-math',
'-fno-common',
'-fdiagnostics-show-option',
@@ -409,6 +405,15 @@ possible_cc_flags = [
'--param=ssp-buffer-size=4',
]
+# Disable -Wmaybe-unitialized when compiling with -Os/-O1/-O3/etc. There are
+# too many false positives with gcc >= 8. Effectively, we only test with -O0
+# and -O2; this should be enough to catch most important cases without too much
+# busywork. See https://github.com/systemd/systemd/pull/19226.
+if cc.get_id() == 'gcc' and (not '02'.contains(get_option('optimization')) or
+ cc.version().version_compare('<10'))
+ possible_cc_flags += '-Wno-maybe-uninitialized'
+endif
+
# --as-needed and --no-undefined are provided by meson by default,
# run mesonconf to see what is enabled
possible_link_flags = [
@@ -797,7 +802,7 @@ if not meson.is_cross_build()
id = id_result.stdout().to_int()
if id != 65534
warning('\n' +
- 'The local group with the configured group name "@0@" of the nobody group does not have UID 65534 (it has @1@).\n'.format(nobody_group, id) +
+ 'The local group with the configured group name "@0@" of the nobody group does not have GID 65534 (it has @1@).\n'.format(nobody_group, id) +
'Your build will result in an group table setup that is incompatible with the local system.')
endif
endif
@@ -1112,7 +1117,7 @@ else
libcurl = []
endif
conf.set10('HAVE_LIBCURL', have)
-conf.set10('CURL_NO_OLDIES', get_option('mode') == 'developer')
+conf.set10('CURL_NO_OLDIES', conf.get('BUILD_MODE_DEVELOPER') == 1)
want_libidn = get_option('libidn')
want_libidn2 = get_option('libidn2')
@@ -1227,7 +1232,7 @@ conf.set10('HAVE_LIBFIDO2', have)
want_tpm2 = get_option('tpm2')
if want_tpm2 != 'false' and not skip_deps
tpm2 = dependency('tss2-esys tss2-rc tss2-mu',
- required : want_tpm2 == 'true')
+ required : want_tpm2 == 'true')
have = tpm2.found()
else
have = false
@@ -1580,6 +1585,9 @@ if get_option('efi')
elif efi_arch == 'aarch64'
EFI_MACHINE_TYPE_NAME = 'aa64'
gnu_efi_arch = 'aarch64'
+ elif efi_arch == 'riscv64'
+ EFI_MACHINE_TYPE_NAME = 'riscv64'
+ gnu_efi_arch = 'riscv64'
else
EFI_MACHINE_TYPE_NAME = ''
gnu_efi_arch = ''
@@ -2324,7 +2332,7 @@ if conf.get('ENABLE_HOMED') == 1
endif
foreach alias : (['halt', 'poweroff', 'reboot', 'shutdown'] +
- (conf.get('HAVE_SYSV_COMPAT') == 1 ? ['runlevel', 'telinit'] : []))
+ (conf.get('HAVE_SYSV_COMPAT') == 1 ? ['runlevel', 'telinit'] : []))
meson.add_install_script(meson_make_symlink,
join_paths(rootbindir, 'systemctl'),
join_paths(rootsbindir, alias))
@@ -2745,14 +2753,14 @@ if conf.get('ENABLE_OOMD') == 1
install_dir : rootlibexecdir)
public_programs += executable(
- 'oomctl',
- oomctl_sources,
- include_directories : includes,
- link_with : [libshared],
- dependencies : [],
- install_rpath : rootlibexecdir,
- install : true,
- install_dir : rootbindir)
+ 'oomctl',
+ oomctl_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootbindir)
endif
if conf.get('ENABLE_BINFMT') == 1
@@ -3308,11 +3316,20 @@ custom_target(
'systemd-runtest.env',
output : 'systemd-runtest.env',
command : ['sh', '-c', '{ ' +
- 'echo SYSTEMD_TEST_DATA=@0@; '.format(join_paths(project_source_root, 'test')) +
- 'echo SYSTEMD_CATALOG_DIR=@0@; '.format(join_paths(project_build_root, 'catalog')) +
- '} >@OUTPUT@'],
+ 'echo SYSTEMD_TEST_DATA=@0@; '.format(join_paths(project_source_root, 'test')) +
+ 'echo SYSTEMD_CATALOG_DIR=@0@; '.format(join_paths(project_build_root, 'catalog')) +
+ '} >@OUTPUT@'],
build_by_default : true)
+test_cflags = ['-DTEST_CODE=1']
+# We intentionally do not do inline initializations with definitions for a
+# bunch of _cleanup_ variables in tests, to ensure valgrind is triggered if we
+# use the variable unexpectedly. This triggers a lot of maybe-uninitialized
+# false positives when the combination of -O2 and -flto is used. Suppress them.
+if '-O2' in get_option('c_args') and '-flto=auto' in get_option('c_args')
+ test_cflags += cc.first_supported_argument('-Wno-maybe-uninitialized')
+endif
+
foreach tuple : tests
sources = tuple[0]
link_with = tuple.length() > 1 and tuple[1].length() > 0 ? tuple[1] : [libshared]
@@ -3321,6 +3338,7 @@ foreach tuple : tests
condition = tuple.length() > 4 ? tuple[4] : ''
type = tuple.length() > 5 ? tuple[5] : ''
defs = tuple.length() > 6 ? tuple[6] : []
+ defs += test_cflags
parallel = tuple.length() > 7 ? tuple[7] : true
timeout = 30
@@ -3388,7 +3406,7 @@ exe = executable(
'test-libudev-sym',
test_libudev_sym_c,
include_directories : libudev_includes,
- c_args : '-Wno-deprecated-declarations',
+ c_args : ['-Wno-deprecated-declarations'] + test_cflags,
link_with : [libudev],
build_by_default : want_tests != 'false',
install : install_tests,
@@ -3401,7 +3419,7 @@ exe = executable(
'test-libudev-static-sym',
test_libudev_sym_c,
include_directories : libudev_includes,
- c_args : '-Wno-deprecated-declarations',
+ c_args : ['-Wno-deprecated-declarations'] + test_cflags,
link_with : [install_libudev_static],
build_by_default : want_tests != 'false' and static_libudev_pic,
install : install_tests and static_libudev_pic,
@@ -3442,7 +3460,7 @@ foreach tuple : fuzzers
include_directories : [incs, include_directories('src/fuzz')],
link_with : link_with,
dependencies : dependencies,
- c_args : defs,
+ c_args : defs + test_cflags,
link_args: link_args,
install : false,
build_by_default : fuzz_tests or fuzzer_build)
@@ -3629,7 +3647,7 @@ if dbus_docs.length() > 0
'@INPUT@'],
input : dbus_docs)
- if conf.get('BUILD_MODE') == 'BUILD_MODE_DEVELOPER'
+ if conf.get('BUILD_MODE_DEVELOPER') == 1
test('dbus-docs-fresh',
update_dbus_docs_py,
args : ['--build-dir=@0@'.format(project_build_root),
@@ -3713,10 +3731,6 @@ alt_time_epoch = run_command('date', '-Is', '-u', '-d',
status += [
'time epoch: @0@ (@1@)'.format(time_epoch, alt_time_epoch)]
-status += [
- 'static libsystemd: @0@'.format(static_libsystemd),
- 'static libudev: @0@'.format(static_libudev)]
-
# TODO:
# CFLAGS: ${OUR_CFLAGS} ${CFLAGS}
# CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS}
@@ -3739,107 +3753,112 @@ found = []
missing = []
foreach tuple : [
- ['libcryptsetup'],
- ['PAM'],
- ['pwquality'],
- ['libfdisk'],
- ['p11kit'],
- ['libfido2'],
- ['tpm2'],
+ # dependencies
+ ['ACL'],
['AUDIT'],
- ['IMA'],
['AppArmor'],
- ['SELinux'],
+ ['IMA'],
+ ['PAM'],
['SECCOMP'],
+ ['SELinux'],
['SMACK'],
- ['zlib'],
- ['xz'],
- ['zstd'],
- ['lz4'],
- ['bzip2'],
- ['ACL'],
+ ['blkid'],
+ ['elfutils'],
['gcrypt'],
- ['qrencode'],
- ['microhttpd'],
['gnutls'],
- ['openssl'],
+ ['libcryptsetup'],
['libcurl'],
- ['idn'],
- ['initrd'],
- ['compat-mutable-uid-boundaries'],
- ['nscd'],
- ['libidn2'],
+ ['libfdisk'],
+ ['libfido2'],
['libidn'],
+ ['libidn2'],
['libiptc'],
- ['elfutils'],
+ ['microhttpd'],
+ ['openssl'],
+ ['p11kit'],
+ ['pcre2'],
+ ['pwquality'],
+ ['qrencode'],
+ ['tpm2'],
+ ['xkbcommon'],
+
+ # compression libs
+ ['zstd'],
+ ['lz4'],
+ ['xz'],
+ ['zlib'],
+ ['bzip2'],
+
+ # components
+ ['backlight'],
['binfmt'],
- ['repart'],
- ['vconsole'],
- ['quotacheck'],
- ['tmpfiles'],
+ ['coredump'],
['environment.d'],
- ['sysusers'],
+ ['efi'],
+ ['gnu-efi', have_gnu_efi],
['firstboot'],
- ['randomseed'],
- ['backlight'],
- ['rfkill'],
- ['xdg-autostart'],
+ ['hibernate'],
+ ['homed'],
+ ['hostnamed'],
+ ['hwdb'],
+ ['importd'],
+ ['initrd'],
+ ['kernel-install', get_option('kernel-install')],
+ ['localed'],
['logind'],
['machined'],
+ ['networkd'],
+ ['nss-myhostname'],
+ ['nss-mymachines'],
+ ['nss-resolve'],
+ ['nss-systemd'],
+ ['oomd'],
['portabled'],
+ ['pstore'],
+ ['quotacheck'],
+ ['randomseed'],
+ ['repart'],
+ ['resolve'],
+ ['rfkill'],
['sysext'],
- ['userdb'],
- ['homed'],
- ['importd'],
- ['hostnamed'],
+ ['systemd-analyze', conf.get('ENABLE_ANALYZE') == 1],
+ ['sysusers'],
['timedated'],
['timesyncd'],
- ['localed'],
- ['networkd'],
- ['resolve'],
- ['DNS-over-TLS(gnutls)', conf.get('DNS_OVER_TLS_USE_GNUTLS') == 1],
- ['DNS-over-TLS(openssl)', conf.get('DNS_OVER_TLS_USE_OPENSSL') == 1],
- ['coredump'],
- ['pstore'],
- ['oomd'],
+ ['tmpfiles'],
+ ['userdb'],
+ ['vconsole'],
+ ['xdg-autostart'],
+
+ # optional features
+ ['idn'],
['polkit'],
- ['legacy pkla', install_polkit_pkla],
- ['efi'],
- ['gnu-efi', have_gnu_efi],
+ ['nscd'],
+ ['legacy-pkla', install_polkit_pkla],
['kmod'],
- ['xkbcommon'],
- ['pcre2'],
- ['blkid'],
['dbus'],
['glib'],
- ['nss-myhostname'],
- ['nss-mymachines'],
- ['nss-resolve'],
- ['nss-systemd'],
- ['hwdb'],
['tpm'],
- ['man pages', want_man],
- ['html pages', want_html],
- ['man page indices', want_man and have_lxml],
+ ['man pages', want_man],
+ ['html pages', want_html],
+ ['man page indices', want_man and have_lxml],
['SysV compat'],
+ ['compat-mutable-uid-boundaries'],
['utmp'],
['ldconfig'],
- ['hibernate'],
- ['adm group', get_option('adm-group')],
- ['wheel group', get_option('wheel-group')],
+ ['adm group', get_option('adm-group')],
+ ['wheel group', get_option('wheel-group')],
['gshadow'],
['debug hashmap'],
['debug mmap cache'],
['debug siphash'],
- ['valgrind', conf.get('VALGRIND') == 1],
- ['trace logging', conf.get('LOG_TRACE') == 1],
- ['install tests', install_tests],
+ ['valgrind', conf.get('VALGRIND') == 1],
+ ['trace logging', conf.get('LOG_TRACE') == 1],
+ ['install tests', install_tests],
['link-udev-shared', get_option('link-udev-shared')],
['link-systemctl-shared', get_option('link-systemctl-shared')],
['link-networkd-shared', get_option('link-networkd-shared')],
['link-timesyncd-shared', get_option('link-timesyncd-shared')],
- ['kernel-install', get_option('kernel-install')],
- ['systemd-analyze', conf.get('ENABLE_ANALYZE') == 1],
['fexecve'],
['standalone-binaries', get_option('standalone-binaries')],
]
@@ -3858,6 +3877,26 @@ foreach tuple : [
endif
endforeach
+if static_libsystemd == 'false'
+ missing += 'static-libsystemd'
+else
+ found += 'static-libsystemd(@0@)'.format(static_libsystemd)
+endif
+
+if static_libudev == 'false'
+ missing += 'static-libudev'
+else
+ found += 'static-libudev(@0@)'.format(static_libudev)
+endif
+
+if conf.get('DNS_OVER_TLS_USE_GNUTLS') == 1
+ found += 'DNS-over-TLS(gnutls)'
+elif conf.get('DNS_OVER_TLS_USE_OPENSSL') == 1
+ found += 'DNS-over-TLS(openssl)'
+else
+ missing += 'DNS-over-TLS'
+endif
+
status += [
'',
'enabled features: @0@'.format(', '.join(found)),
diff --git a/mkosi.build b/mkosi.build
index a74fc196be..ff339b440c 100755
--- a/mkosi.build
+++ b/mkosi.build
@@ -14,6 +14,18 @@ if ! mountpoint -q "$SRCDIR"; then
umask 022
fi
+# On Fedora "ld" is (unfortunately — if you ask me) managed via
+# "alternatives". Since we'd like to support building images in environments
+# with only /usr/ around (e.g. mkosi's UsrOnly=1 option), we have the problem
+# that /usr/bin/ld is a symlink that points to a non-existing file in
+# /etc/alternative/ in this mode. Let's work around this for now by manually
+# redirect "ld" to "ld.bfd", i.e. circumventing the /usr/bin/ld symlink.
+if [ ! -x /usr/bin/ld -a -x /usr/bin/ld.bfd ] ; then
+ mkdir -p "$HOME"/bin
+ ln -s /usr/bin/ld.bfd "$HOME"/bin/ld
+ PATH="$HOME/bin:$PATH"
+fi
+
# If mkosi.builddir/ exists mkosi will set $BUILDDIR to it, let's then use it
# as out-of-tree build dir. Otherwise, let's make up our own builddir.
[ -z "$BUILDDIR" ] && BUILDDIR=build
diff --git a/po/ko.po b/po/ko.po
index 3713b161ec..a7177e99e3 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2021-03-25 03:01+0000\n"
+"PO-Revision-Date: 2021-04-15 10:01+0000\n"
"Last-Translator: simmon <simmon@nplob.com>\n"
"Language-Team: Korean <https://translate.fedoraproject.org/projects/systemd/"
"master/ko/>\n"
@@ -18,7 +18,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.5.1\n"
+"X-Generator: Weblate 4.5.3\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
@@ -54,9 +54,7 @@ msgstr "시스템 및 서비스 관리자 환경 변수 설정 또는 설정 해
msgid ""
"Authentication is required to set or unset system and service manager "
"environment variables."
-msgstr ""
-"시스템 및 서비스 관리자 환경 변수를 설정하거나 설정 해제하려면 인증이 필요합"
-"니다."
+msgstr "시스템 및 서비스 관리자 환경 변수를 설정하거나 설정 또는 해제하려면 인증이 필요합니다."
#: src/core/org.freedesktop.systemd1.policy.in:64
msgid "Reload the systemd state"
@@ -132,7 +130,7 @@ msgstr "정적 호스트 이름 설정"
msgid ""
"Authentication is required to set the statically configured local hostname, "
"as well as the pretty hostname."
-msgstr "로컬호스트 이름을 모양새를 갖춘 호스트이름 처럼 정적으로 설정하려면 인증이 필요합니다."
+msgstr "로컬호스트 이름을 지정 호스트이름 처럼 정적으로 설정하려면 인증이 필요합니다."
#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
diff --git a/rules.d/81-net-dhcp.rules b/rules.d/81-net-dhcp.rules
new file mode 100644
index 0000000000..2ef25ba060
--- /dev/null
+++ b/rules.d/81-net-dhcp.rules
@@ -0,0 +1,14 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="net_dhcp_end"
+SUBSYSTEM!="net", GOTO="net_dhcp_end"
+
+# Network interfaces requiring DHCPOFFER messages to be broadcast
+# must set ID_NET_DHCP_BROADCAST to "1". This property will be
+# checked by the networkd DHCP4 client to set the DHCP option
+
+# s390 ccwgroup interfaces in layer3 mode need broadcast DHCPOFFER
+# using the link driver to detect this condition
+ENV{ID_NET_DRIVER}=="qeth_l3", ENV{ID_NET_DHCP_BROADCAST}="1"
+
+LABEL="net_dhcp_end"
diff --git a/rules.d/meson.build b/rules.d/meson.build
index 42fa451c6b..4bbba09fd5 100644
--- a/rules.d/meson.build
+++ b/rules.d/meson.build
@@ -26,6 +26,7 @@ rules = files('''
75-probe_mtd.rules
78-sound-card.rules
80-net-setup-link.rules
+ 81-net-dhcp.rules
'''.split())
if conf.get('HAVE_KMOD') == 1
diff --git a/shell-completion/bash/machinectl b/shell-completion/bash/machinectl
index cd16e47721..bc3a7889b3 100644
--- a/shell-completion/bash/machinectl
+++ b/shell-completion/bash/machinectl
@@ -30,7 +30,7 @@ __get_machines() {
}
_machinectl() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
diff --git a/shell-completion/bash/networkctl b/shell-completion/bash/networkctl
index 9282ee8737..ab2a5f7015 100644
--- a/shell-completion/bash/networkctl
+++ b/shell-completion/bash/networkctl
@@ -30,7 +30,7 @@ __get_links() {
_networkctl() {
local i verb comps
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-a --all -h --help --version --no-pager --no-legend -s --stats -l --full'
[ARG]='-n --lines'
diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze
index 92f81a60fc..eded49b50e 100644
--- a/shell-completion/bash/systemd-analyze
+++ b/shell-completion/bash/systemd-analyze
@@ -46,7 +46,7 @@ __get_syscall_sets() {
_systemd_analyze() {
local i verb comps mode
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version --system --user --global --order --require --no-pager
diff --git a/shell-completion/bash/systemd-cat b/shell-completion/bash/systemd-cat
index 9413b6fa70..737f33981b 100644
--- a/shell-completion/bash/systemd-cat
+++ b/shell-completion/bash/systemd-cat
@@ -25,7 +25,7 @@ __contains_word() {
}
_systemd_cat() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
diff --git a/shell-completion/bash/systemd-cgls b/shell-completion/bash/systemd-cgls
index 7caaf89ddb..0b6a8fda0d 100644
--- a/shell-completion/bash/systemd-cgls
+++ b/shell-completion/bash/systemd-cgls
@@ -37,7 +37,7 @@ __get_units_have_cgroup() {
}
_systemd_cgls() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
diff --git a/shell-completion/bash/systemd-cgtop b/shell-completion/bash/systemd-cgtop
index b186f1bd53..2c59b6c9f6 100644
--- a/shell-completion/bash/systemd-cgtop
+++ b/shell-completion/bash/systemd-cgtop
@@ -29,7 +29,7 @@ __get_machines() {
}
_systemd_cgtop() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local comps
local -A OPTS=(
diff --git a/shell-completion/bash/systemd-delta b/shell-completion/bash/systemd-delta
index baf86b0813..f97b6dd5b2 100644
--- a/shell-completion/bash/systemd-delta
+++ b/shell-completion/bash/systemd-delta
@@ -24,7 +24,7 @@ __contains_word() {
}
_systemd-delta() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local comps
local -A OPTS=(
diff --git a/shell-completion/bash/systemd-detect-virt b/shell-completion/bash/systemd-detect-virt
index fb35efaadb..0c9da3a22e 100644
--- a/shell-completion/bash/systemd-detect-virt
+++ b/shell-completion/bash/systemd-detect-virt
@@ -24,7 +24,7 @@ __contains_word() {
}
_systemd_detect_virt() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
diff --git a/shell-completion/bash/systemd-id128 b/shell-completion/bash/systemd-id128
index 937556154a..25110d1309 100644
--- a/shell-completion/bash/systemd-id128
+++ b/shell-completion/bash/systemd-id128
@@ -26,7 +26,7 @@ __contains_word () {
_systemd_id128() {
local i verb comps
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version -p --pretty'
[ARG]='-a --app-specific'
diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn
index ebd97a9de1..ed1296a878 100644
--- a/shell-completion/bash/systemd-nspawn
+++ b/shell-completion/bash/systemd-nspawn
@@ -58,7 +58,7 @@ __get_rlimit() {
}
_systemd_nspawn() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
diff --git a/shell-completion/bash/systemd-path b/shell-completion/bash/systemd-path
index cae4ac1b30..4330c3e05b 100644
--- a/shell-completion/bash/systemd-path
+++ b/shell-completion/bash/systemd-path
@@ -30,7 +30,7 @@ __get_names() {
_systemd_path() {
local comps
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version'
[ARG]='--suffix'
diff --git a/shell-completion/zsh/_localectl b/shell-completion/zsh/_localectl
index 1c7ac82b65..851ac8253b 100644
--- a/shell-completion/zsh/_localectl
+++ b/shell-completion/zsh/_localectl
@@ -39,7 +39,7 @@ _localectl_set-x11-keymap() {
local -a _file _layout _model _variant _options
local _xorg_lst
_xorg_lst=${"$($commands[pkg-config] xkeyboard-config --variable=xkb_base)"}
- _file=( ${(ps:\n\!:)"$(<$_xorg_lst/rules/xorg.lst)"} )
+ _file=( ${(ps:\n\!:)"$(<$_xorg_lst/rules/base.lst)"} )
_layout=( ${${${(M)${(f)_file[2]}:# *}# }%% *} )
_model=( ${${${(M)${(f)_file[1]}:# *}# }%% *} )
_variant=( ${${${(M)${(f)_file[3]}:# *}# }%% *} )
diff --git a/src/activate/activate.c b/src/activate/activate.c
index f298b1d491..8c61c3ca7f 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -151,7 +151,7 @@ static int exec_process(const char *name, char **argv, char **env, int start_fd,
envp[n_env++] = k;
} else {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
const char *n;
p = strjoin(*s, "=");
@@ -421,7 +421,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_FDNAME: {
- _cleanup_strv_free_ char **names;
+ _cleanup_strv_free_ char **names = NULL;
char **s;
names = strv_split(optarg, ":");
@@ -430,7 +430,7 @@ static int parse_argv(int argc, char *argv[]) {
STRV_FOREACH(s, names)
if (!fdname_is_valid(*s)) {
- _cleanup_free_ char *esc;
+ _cleanup_free_ char *esc = NULL;
esc = cescape(*s);
log_warning("File descriptor name \"%s\" is not valid.", esc);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 1a38d878a3..1ad3731852 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -15,7 +15,6 @@
#include "analyze-condition.h"
#include "analyze-security.h"
#include "analyze-verify.h"
-#include "build.h"
#include "bus-error.h"
#include "bus-locator.h"
#include "bus-map-properties.h"
@@ -53,6 +52,7 @@
#include "unit-name.h"
#include "util.h"
#include "verbs.h"
+#include "version.h"
#define SCALE_X (0.1 / 1000.0) /* pixels per us */
#define SCALE_Y (20.0)
diff --git a/src/basic/build.h b/src/basic/build.h
index 3de0d36cc9..87276bf686 100644
--- a/src/basic/build.h
+++ b/src/basic/build.h
@@ -4,8 +4,3 @@
#include "version.h"
extern const char* const systemd_features;
-
-enum {
- BUILD_MODE_DEVELOPER,
- BUILD_MODE_RELEASE,
-};
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 8d3e5237a8..1ac1f6dff0 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -660,7 +660,7 @@ int cg_remove_xattr(const char *controller, const char *path, const char *name)
int cg_pid_get_path(const char *controller, pid_t pid, char **ret_path) {
_cleanup_fclose_ FILE *f = NULL;
- const char *fs, *controller_str = NULL; /* silence gcc warning about unitialized variable */
+ const char *fs, *controller_str = NULL; /* avoid false maybe-uninitialized warning */
int unified, r;
assert(pid >= 0);
@@ -2163,6 +2163,7 @@ static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
[CGROUP_CONTROLLER_PIDS] = "pids",
[CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall",
[CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
+ [CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index f79e384147..8894fd9b0a 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -30,6 +30,7 @@ typedef enum CGroupController {
/* BPF-based pseudo-controllers, v2 only */
CGROUP_CONTROLLER_BPF_FIREWALL,
CGROUP_CONTROLLER_BPF_DEVICES,
+ CGROUP_CONTROLLER_BPF_FOREIGN,
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -EINVAL,
@@ -49,6 +50,7 @@ typedef enum CGroupMask {
CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS),
CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL),
CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
+ CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
/* All real cgroup v1 controllers */
CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
@@ -57,7 +59,7 @@ typedef enum CGroupMask {
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
/* All cgroup v2 BPF pseudo-controllers */
- CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES,
+ CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN,
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
} CGroupMask;
diff --git a/src/basic/copy.c b/src/basic/copy.c
index d25777cc4b..af9ac842a1 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -1072,7 +1072,7 @@ int copy_file_full(
_cleanup_close_ int fdf = -1;
struct stat st;
- int fdt = -1, r;
+ int r, fdt = -1; /* avoid false maybe-uninitialized warning */
assert(from);
assert(to);
diff --git a/src/basic/device-nodes.c b/src/basic/device-nodes.c
index 158ab738e8..ec5613ac58 100644
--- a/src/basic/device-nodes.c
+++ b/src/basic/device-nodes.c
@@ -8,15 +8,12 @@
#include "utf8.h"
int allow_listed_char_for_devnode(char c, const char *white) {
-
- if ((c >= '0' && c <= '9') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= 'a' && c <= 'z') ||
- strchr("#+-.:=@_", c) != NULL ||
- (white != NULL && strchr(white, c) != NULL))
- return 1;
-
- return 0;
+ return
+ (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ strchr("#+-.:=@_", c) ||
+ (white && strchr(white, c));
}
int encode_devnode_name(const char *str, char *str_enc, size_t len) {
@@ -31,7 +28,7 @@ int encode_devnode_name(const char *str, char *str_enc, size_t len) {
seqlen = utf8_encoded_valid_unichar(str + i, SIZE_MAX);
if (seqlen > 1) {
- if (len-j < (size_t)seqlen)
+ if (len-j < (size_t) seqlen)
return -EINVAL;
memcpy(&str_enc[j], &str[i], seqlen);
diff --git a/src/basic/efivars.c b/src/basic/efivars.c
index 2139cf3a69..7e1e9e6047 100644
--- a/src/basic/efivars.c
+++ b/src/basic/efivars.c
@@ -350,7 +350,7 @@ int cache_efi_options_variable(void) {
* (NB: For testing purposes, we still check the $SYSTEMD_EFI_OPTIONS env var before accessing this
* cache, even when in SecureBoot mode.) */
if (is_efi_secure_boot()) {
- _cleanup_free_ char *k;
+ _cleanup_free_ char *k = NULL;
k = efi_variable_path(EFI_VENDOR_SYSTEMD, "SystemdOptions");
if (!k)
diff --git a/src/basic/errno-util.h b/src/basic/errno-util.h
index 5609820b88..3f2d0af56d 100644
--- a/src/basic/errno-util.h
+++ b/src/basic/errno-util.h
@@ -33,7 +33,7 @@ static inline int negative_errno(void) {
static inline const char *strerror_safe(int error) {
/* 'safe' here does NOT mean thread safety. */
- return strerror(abs(error));
+ return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */
}
static inline int errno_or_else(int fallback) {
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index abd822796e..414fd15d56 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -57,7 +57,7 @@ int fdopen_unlocked(int fd, const char *options, FILE **ret) {
}
int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) {
- int r;
+ int r;
assert(fd);
@@ -286,7 +286,7 @@ fail:
/* OK, the operation failed, but let's see if the right
* contents in place already. If so, eat up the error. */
- q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+ q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE));
if (q <= 0)
return r;
@@ -1004,7 +1004,6 @@ int chase_symlinks_and_fopen_unlocked(
_cleanup_close_ int fd = -1;
_cleanup_free_ char *final_path = NULL;
int mode_flags, r;
- FILE *f;
assert(path);
assert(open_flags);
@@ -1018,12 +1017,10 @@ int chase_symlinks_and_fopen_unlocked(
if (fd < 0)
return fd;
- r = fdopen_unlocked(fd, open_flags, &f);
+ r = take_fdopen_unlocked(&fd, open_flags, ret_file);
if (r < 0)
return r;
- TAKE_FD(fd);
- *ret_file = f;
if (ret_path)
*ret_path = TAKE_PTR(final_path);
return 0;
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 64a30ab7bb..752995c2a3 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -15,16 +15,17 @@
#define LONG_LINE_MAX (1U*1024U*1024U)
typedef enum {
- WRITE_STRING_FILE_CREATE = 1 << 0,
- WRITE_STRING_FILE_TRUNCATE = 1 << 1,
- WRITE_STRING_FILE_ATOMIC = 1 << 2,
- WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
- WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
- WRITE_STRING_FILE_SYNC = 1 << 5,
- WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 6,
- WRITE_STRING_FILE_NOFOLLOW = 1 << 7,
- WRITE_STRING_FILE_MKDIR_0755 = 1 << 8,
- WRITE_STRING_FILE_MODE_0600 = 1 << 9,
+ WRITE_STRING_FILE_CREATE = 1 << 0,
+ WRITE_STRING_FILE_TRUNCATE = 1 << 1,
+ WRITE_STRING_FILE_ATOMIC = 1 << 2,
+ WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
+ WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
+ WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5,
+ WRITE_STRING_FILE_SYNC = 1 << 6,
+ WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 7,
+ WRITE_STRING_FILE_NOFOLLOW = 1 << 8,
+ WRITE_STRING_FILE_MKDIR_0755 = 1 << 9,
+ WRITE_STRING_FILE_MODE_0600 = 1 << 10,
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index b2a4e8036f..46d6f7780e 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -24,6 +24,7 @@
#include "path-util.h"
#include "process-util.h"
#include "random-util.h"
+#include "ratelimit.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -1715,3 +1716,23 @@ do_rename:
return 1;
}
+
+int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) {
+ RateLimit rl;
+ int r;
+
+ r = posix_fallocate(fd, offset, size); /* returns positive errnos on error */
+ if (r != EINTR)
+ return -r; /* Let's return negative errnos, like common in our codebase */
+
+ /* On EINTR try a couple of times more, but protect against busy looping
+ * (not more than 16 times per 10s) */
+ rl = (RateLimit) { 10 * USEC_PER_SEC, 16 };
+ while (ratelimit_below(&rl)) {
+ r = posix_fallocate(fd, offset, size);
+ if (r != EINTR)
+ return -r;
+ }
+
+ return -EINTR;
+}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 45115fd3db..027037f7a7 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -146,3 +146,5 @@ int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const
static inline int conservative_rename(const char *oldpath, const char *newpath) {
return conservative_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath);
}
+
+int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
diff --git a/src/basic/generate-af-list.sh b/src/basic/generate-af-list.sh
index 0a5c5c4cd7..f747e0d065 100755
--- a/src/basic/generate-af-list.sh
+++ b/src/basic/generate-af-list.sh
@@ -1,7 +1,8 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
-$1 -E -dM -include sys/socket.h -include "$2" -include "$3" - </dev/null | \
- grep -Ev 'AF_UNSPEC|AF_MAX' | \
- awk '/^#define[ \t]+AF_[^ \t]+[ \t]+[AP]F_[^ \t]/ { print $2; }'
+${1:?} -E -dM -include sys/socket.h -include "${2:?}" -include "${3:?}" - </dev/null | \
+ grep -Ev 'AF_UNSPEC|AF_MAX' | \
+ awk '/^#define[ \t]+AF_[^ \t]+[ \t]+[AP]F_[^ \t]/ { print $2; }'
diff --git a/src/basic/generate-arphrd-list.sh b/src/basic/generate-arphrd-list.sh
index 9a2661b4d8..8a8c276d5f 100755
--- a/src/basic/generate-arphrd-list.sh
+++ b/src/basic/generate-arphrd-list.sh
@@ -1,7 +1,8 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
-$1 -dM -include linux/if_arp.h -include "$2" - </dev/null | \
- awk '/^#define[ \t]+ARPHRD_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \
- sed -e 's/ARPHRD_//'
+${1:?} -dM -include linux/if_arp.h -include "${2:?}" - </dev/null | \
+ awk '/^#define[ \t]+ARPHRD_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \
+ sed -e 's/ARPHRD_//'
diff --git a/src/basic/generate-cap-list.sh b/src/basic/generate-cap-list.sh
index 89d5bb03d9..eb1357efa2 100755
--- a/src/basic/generate-cap-list.sh
+++ b/src/basic/generate-cap-list.sh
@@ -1,7 +1,8 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
-$1 -dM -include linux/capability.h -include "$2" -include "$3" - </dev/null | \
- awk '/^#define[ \t]+CAP_[A-Z_]+[ \t]+/ { print $2; }' | \
- grep -v CAP_LAST_CAP
+${1:?} -dM -include linux/capability.h -include "${2:?}" -include "${3:?}" - </dev/null | \
+ awk '/^#define[ \t]+CAP_[A-Z_]+[ \t]+/ { print $2; }' | \
+ grep -v CAP_LAST_CAP
diff --git a/src/basic/generate-errno-list.sh b/src/basic/generate-errno-list.sh
index 4c13b3157d..dceeba415c 100755
--- a/src/basic/generate-errno-list.sh
+++ b/src/basic/generate-errno-list.sh
@@ -1,6 +1,7 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
-$1 -dM -include errno.h - </dev/null | \
- awk '/^#define[ \t]+E[^ _]+[ \t]+/ { print $2; }'
+${1:?} -dM -include errno.h - </dev/null | \
+ awk '/^#define[ \t]+E[^ _]+[ \t]+/ { print $2; }'
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
index f7a7252f1a..9d7e51f8fd 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -51,7 +51,7 @@ bool in4_addr_is_link_local(const struct in_addr *a) {
bool in6_addr_is_link_local(const struct in6_addr *a) {
assert(a);
- return IN6_IS_ADDR_LINKLOCAL(a);
+ return IN6_IS_ADDR_LINKLOCAL(a); /* lgtm [cpp/potentially-dangerous-function] */
}
int in_addr_is_link_local(int family, const union in_addr_union *u) {
@@ -116,7 +116,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) {
return in4_addr_is_localhost(&u->in);
if (family == AF_INET6)
- return IN6_IS_ADDR_LOOPBACK(&u->in6);
+ return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */
return -EAFNOSUPPORT;
}
@@ -803,6 +803,9 @@ int in_addr_prefix_from_string_auto_internal(
}
static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
+ assert(a);
+ assert(state);
+
siphash24_compress(&a->family, sizeof(a->family), state);
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
}
@@ -810,6 +813,9 @@ static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash
static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
int r;
+ assert(x);
+ assert(y);
+
r = CMP(x->family, y->family);
if (r != 0)
return r;
@@ -819,13 +825,46 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
+static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) {
+ assert(a);
+ assert(state);
+
+ siphash24_compress(&a->family, sizeof(a->family), state);
+ siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
+ siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
+}
+
+static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) {
+ int r;
+
+ assert(x);
+ assert(y);
+
+ r = CMP(x->family, y->family);
+ if (r != 0)
+ return r;
+
+ r = CMP(x->prefixlen, y->prefixlen);
+ if (r != 0)
+ return r;
+
+ return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
+}
+
+DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(in_addr_prefix_hash_ops_free, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func, free);
+
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
assert(addr);
+ assert(state);
siphash24_compress(addr, sizeof(*addr), state);
}
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
+ assert(a);
+ assert(b);
+
return memcmp(a, b, sizeof(*a));
}
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index 519ee53b3a..d5caf662ab 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -20,6 +20,12 @@ struct in_addr_data {
union in_addr_union address;
};
+struct in_addr_prefix {
+ int family;
+ uint8_t prefixlen;
+ union in_addr_union address;
+};
+
bool in4_addr_is_null(const struct in_addr *a);
static inline bool in4_addr_is_set(const struct in_addr *a) {
return !in4_addr_is_null(a);
@@ -111,4 +117,6 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
extern const struct hash_ops in_addr_data_hash_ops;
+extern const struct hash_ops in_addr_prefix_hash_ops;
+extern const struct hash_ops in_addr_prefix_hash_ops_free;
extern const struct hash_ops in6_addr_hash_ops;
diff --git a/src/basic/linux/update.sh b/src/basic/linux/update.sh
index ca0b9ec077..1ada894f09 100755
--- a/src/basic/linux/update.sh
+++ b/src/basic/linux/update.sh
@@ -1,13 +1,14 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
for i in *.h */*.h; do
- if [[ $i == 'loadavg.h' ]]; then
- curl --fail https://raw.githubusercontent.com/torvalds/linux/master/include/linux/sched/$i -o $i
+ if [[ "$i" == "loadavg.h" ]]; then
+ curl --fail "https://raw.githubusercontent.com/torvalds/linux/master/include/linux/sched/$i" -o "$i"
else
- curl --fail https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/$i -o $i
+ curl --fail "https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/$i" -o "$i"
fi
- sed -i -e 's/__user //g' -e '/^#include <linux\/compiler.h>/ d' $i
+ sed -i -e 's/__user //g' -e '/^#include <linux\/compiler.h>/ d' "$i"
done
diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c
index 93261e675e..b818078158 100644
--- a/src/basic/locale-util.c
+++ b/src/basic/locale-util.c
@@ -427,8 +427,10 @@ const char *special_glyph(SpecialGlyph code) {
},
};
- assert(code < _SPECIAL_GLYPH_MAX);
+ if (code < 0)
+ return NULL;
+ assert(code < _SPECIAL_GLYPH_MAX);
return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code];
}
diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h
index 558c5a898b..df259d1bbd 100644
--- a/src/basic/locale-util.h
+++ b/src/basic/locale-util.h
@@ -39,7 +39,7 @@ void init_gettext(void);
bool is_locale_utf8(void);
-typedef enum {
+typedef enum SpecialGlyph {
SPECIAL_GLYPH_TREE_VERTICAL,
SPECIAL_GLYPH_TREE_BRANCH,
SPECIAL_GLYPH_TREE_RIGHT,
@@ -70,6 +70,7 @@ typedef enum {
SPECIAL_GLYPH_LOCK_AND_KEY,
SPECIAL_GLYPH_TOUCH,
_SPECIAL_GLYPH_MAX,
+ _SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
const char *special_glyph(SpecialGlyph code) _const_;
diff --git a/src/basic/log.h b/src/basic/log.h
index b3d32abfc8..51ba3d8fde 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -4,6 +4,7 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include <syslog.h>
#include "macro.h"
@@ -188,20 +189,39 @@ void log_assert_failed_return(
log_dispatch_internal(level, error, PROJECT_FILE, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer)
/* Logging with level */
-#define log_full_errno(level, error, ...) \
+#define log_full_errno_zerook(level, error, ...) \
({ \
int _level = (level), _e = (error); \
- (log_get_max_level() >= LOG_PRI(_level)) \
+ _e = (log_get_max_level() >= LOG_PRI(_level)) \
? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
: -ERRNO_VALUE(_e); \
+ _e < 0 ? _e : -ESTRPIPE; \
+ })
+
+#if BUILD_MODE_DEVELOPER && !defined(TEST_CODE)
+# define ASSERT_NON_ZERO(x) assert((x) != 0)
+#else
+# define ASSERT_NON_ZERO(x)
+#endif
+
+#define log_full_errno(level, error, ...) \
+ ({ \
+ int _error = (error); \
+ ASSERT_NON_ZERO(_error); \
+ log_full_errno_zerook(level, _error, __VA_ARGS__); \
})
-#define log_full(level, ...) (void) log_full_errno((level), 0, __VA_ARGS__)
+#define log_full(level, fmt, ...) \
+ ({ \
+ if (BUILD_MODE_DEVELOPER) \
+ assert(!strstr(fmt, "%m")); \
+ (void) log_full_errno_zerook(level, 0, fmt, ##__VA_ARGS__); \
+ })
int log_emergency_level(void);
/* Normal logging */
-#define log_debug(...) log_full_errno(LOG_DEBUG, 0, __VA_ARGS__)
+#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
diff --git a/src/basic/missing_capability.h b/src/basic/missing_capability.h
index 4cf31cb839..5adda554e5 100644
--- a/src/basic/missing_capability.h
+++ b/src/basic/missing_capability.h
@@ -27,7 +27,7 @@
#ifdef CAP_LAST_CAP
# if CAP_LAST_CAP > SYSTEMD_CAP_LAST_CAP
-# if BUILD_MODE == BUILD_MODE_DEVELOPER && defined(TEST_CAPABILITY_C)
+# if BUILD_MODE_DEVELOPER && defined(TEST_CAPABILITY_C)
# warning "The capability list here is outdated"
# endif
# else
diff --git a/src/basic/path-lookup.c b/src/basic/path-lookup.c
index 96b82170d0..e53c2302b1 100644
--- a/src/basic/path-lookup.c
+++ b/src/basic/path-lookup.c
@@ -772,7 +772,7 @@ void lookup_paths_log(LookupPaths *p) {
log_debug("Ignoring unit files.");
p->search_path = strv_free(p->search_path);
} else {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
t = strv_join(p->search_path, "\n\t");
log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t));
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index f40f3f27e9..f98859939c 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -527,6 +527,27 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) {
return path_equal(a, b) || files_same(a, b, flags) > 0;
}
+bool path_equal_filename(const char *a, const char *b) {
+ _cleanup_free_ char *a_basename = NULL, *b_basename = NULL;
+ int r;
+
+ assert(a);
+ assert(b);
+
+ r = path_extract_filename(a, &a_basename);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse basename of %s: %m", a);
+ return false;
+ }
+ r = path_extract_filename(b, &b_basename);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse basename of %s: %m", b);
+ return false;
+ }
+
+ return path_equal(a_basename, b_basename);
+}
+
char* path_join_internal(const char *first, ...) {
char *joined, *q;
const char *p;
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index c0746f68d7..f82d935dc5 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -61,6 +61,8 @@ char* path_startswith(const char *path, const char *prefix) _pure_;
int path_compare(const char *a, const char *b) _pure_;
bool path_equal(const char *a, const char *b) _pure_;
bool path_equal_or_files_same(const char *a, const char *b, int flags);
+/* Compares only the last portion of the input paths, ie: the filenames */
+bool path_equal_filename(const char *a, const char *b);
char* path_join_internal(const char *first, ...);
#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, POINTER_MAX)
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index 0b6fb137bd..410b8a3eb5 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -47,7 +47,7 @@ static int proc_cmdline_extract_first(const char **p, char **ret_word, ProcCmdli
_cleanup_free_ char *word = NULL;
const char *c;
- r = extract_first_word(&q, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
+ r = extract_first_word(&q, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
if (r == 0)
diff --git a/src/basic/recovery-key.c b/src/basic/recovery-key.c
index a3c4500dff..cad639a023 100644
--- a/src/basic/recovery-key.c
+++ b/src/basic/recovery-key.c
@@ -74,6 +74,7 @@ int normalize_recovery_key(const char *password, char **ret) {
int make_recovery_key(char **ret) {
_cleanup_(erase_and_freep) char *formatted = NULL;
_cleanup_(erase_and_freep) uint8_t *key = NULL;
+ size_t j = 0;
int r;
assert(ret);
@@ -91,7 +92,7 @@ int make_recovery_key(char **ret) {
if (!formatted)
return -ENOMEM;
- for (size_t i = 0, j = 0; i < RECOVERY_KEY_MODHEX_RAW_LENGTH; i++) {
+ for (size_t i = 0; i < RECOVERY_KEY_MODHEX_RAW_LENGTH; i++) {
formatted[j++] = modhex_alphabet[key[i] >> 4];
formatted[j++] = modhex_alphabet[key[i] & 0xF];
@@ -99,7 +100,9 @@ int make_recovery_key(char **ret) {
formatted[j++] = '-';
}
- formatted[RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1] = 0;
+ assert(j == RECOVERY_KEY_MODHEX_FORMATTED_LENGTH);
+ assert(formatted[RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1] == '-');
+ formatted[RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1] = 0; /* replace final dash with a NUL */
*ret = TAKE_PTR(formatted);
return 0;
diff --git a/src/basic/special.h b/src/basic/special.h
index b9b7be7a7d..78f22f1ac9 100644
--- a/src/basic/special.h
+++ b/src/basic/special.h
@@ -37,6 +37,7 @@
#define SPECIAL_INITRD_FS_TARGET "initrd-fs.target"
#define SPECIAL_INITRD_ROOT_DEVICE_TARGET "initrd-root-device.target"
#define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target"
+#define SPECIAL_INITRD_USR_FS_TARGET "initrd-usr-fs.target"
#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */
#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target"
#define SPECIAL_SWAP_TARGET "swap.target"
@@ -79,6 +80,7 @@
/* Magic early boot services */
#define SPECIAL_FSCK_SERVICE "systemd-fsck@.service"
#define SPECIAL_FSCK_ROOT_SERVICE "systemd-fsck-root.service"
+#define SPECIAL_FSCK_USR_SERVICE "systemd-fsck-usr.service"
#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service"
#define SPECIAL_QUOTAON_SERVICE "quotaon.service"
#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service"
diff --git a/src/basic/strbuf.c b/src/basic/strbuf.c
index 212b561fa6..0617acc8d2 100644
--- a/src/basic/strbuf.c
+++ b/src/basic/strbuf.c
@@ -107,11 +107,9 @@ static void bubbleinsert(struct strbuf_node *node,
/* add string, return the index/offset into the buffer */
ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
uint8_t c;
- struct strbuf_node *node;
- size_t depth;
char *buf_new;
struct strbuf_child_entry *child;
- struct strbuf_node *node_child;
+ struct strbuf_node *node;
ssize_t off;
if (!str->root)
@@ -127,7 +125,7 @@ ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
str->in_len += len;
node = str->root;
- for (depth = 0; depth <= len; depth++) {
+ for (size_t depth = 0; depth <= len; depth++) {
struct strbuf_child_entry search;
/* match against current node */
@@ -159,6 +157,8 @@ ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
str->buf[str->len++] = '\0';
/* new node */
+ _cleanup_free_ struct strbuf_node *node_child = NULL;
+
node_child = new(struct strbuf_node, 1);
if (!node_child)
return -ENOMEM;
@@ -169,15 +169,13 @@ ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
/* extend array, add new entry, sort for bisection */
child = reallocarray(node->children, node->children_count + 1, sizeof(struct strbuf_child_entry));
- if (!child) {
- free(node_child);
+ if (!child)
return -ENOMEM;
- }
str->nodes_count++;
node->children = child;
- bubbleinsert(node, c, node_child);
+ bubbleinsert(node, c, TAKE_PTR(node_child));
return off;
}
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 1a3f9ccb33..fafdaaa090 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -52,7 +52,7 @@ static volatile int cached_color_mode = _COLOR_INVALID;
static volatile int cached_underline_enabled = -1;
int chvt(int vt) {
- _cleanup_close_ int fd;
+ _cleanup_close_ int fd = -1;
/* Switch to the specified vt number. If the VT is specified <= 0 switch to the VT the kernel log messages go,
* if that's configured. */
@@ -514,7 +514,7 @@ int terminal_vhangup_fd(int fd) {
}
int terminal_vhangup(const char *name) {
- _cleanup_close_ int fd;
+ _cleanup_close_ int fd = -1;
fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 3c2b25bd2a..78d0390a00 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -1547,7 +1547,7 @@ int time_change_fd(void) {
.it_value.tv_sec = TIME_T_MAX,
};
- _cleanup_close_ int fd;
+ _cleanup_close_ int fd = -1;
assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c
index 5ee71d0158..c5ae92f985 100644
--- a/src/basic/tmpfile-util.c
+++ b/src/basic/tmpfile-util.c
@@ -65,7 +65,7 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
/* This is much like mkostemp() but is subject to umask(). */
int mkostemp_safe(char *pattern) {
- int fd = -1; /* avoid false maybe-uninitialized warning */
+ int fd = -1; /* avoid false maybe-uninitialized warning */
assert(pattern);
diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c
index 6fbb947f09..5cfabca83f 100644
--- a/src/basic/unit-def.c
+++ b/src/basic/unit-def.c
@@ -296,22 +296,19 @@ static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
SpecialGlyph unit_active_state_to_glyph(UnitActiveState state) {
- switch (state) {
- case UNIT_ACTIVE:
- return SPECIAL_GLYPH_BLACK_CIRCLE;
- case UNIT_RELOADING:
- return SPECIAL_GLYPH_CIRCLE_ARROW;
- case UNIT_INACTIVE:
- return SPECIAL_GLYPH_WHITE_CIRCLE;
- case UNIT_FAILED:
- return SPECIAL_GLYPH_MULTIPLICATION_SIGN;
- case UNIT_ACTIVATING:
- case UNIT_DEACTIVATING:
- return SPECIAL_GLYPH_BLACK_CIRCLE;
- case UNIT_MAINTENANCE:
- return SPECIAL_GLYPH_WHITE_CIRCLE;
-
- default:
- return SPECIAL_GLYPH_BLACK_CIRCLE;
- }
+ static const SpecialGlyph map[_UNIT_ACTIVE_STATE_MAX] = {
+ [UNIT_ACTIVE] = SPECIAL_GLYPH_BLACK_CIRCLE,
+ [UNIT_RELOADING] = SPECIAL_GLYPH_CIRCLE_ARROW,
+ [UNIT_INACTIVE] = SPECIAL_GLYPH_WHITE_CIRCLE,
+ [UNIT_FAILED] = SPECIAL_GLYPH_MULTIPLICATION_SIGN,
+ [UNIT_ACTIVATING] = SPECIAL_GLYPH_BLACK_CIRCLE,
+ [UNIT_DEACTIVATING] = SPECIAL_GLYPH_BLACK_CIRCLE,
+ [UNIT_MAINTENANCE] = SPECIAL_GLYPH_WHITE_CIRCLE,
+ };
+
+ if (state < 0)
+ return _SPECIAL_GLYPH_INVALID;
+
+ assert(state < _UNIT_ACTIVE_STATE_MAX);
+ return map[state];
}
diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c
index bd6f64915d..97ad1e0cb5 100644
--- a/src/boot/bless-boot.c
+++ b/src/boot/bless-boot.c
@@ -126,7 +126,7 @@ static int acquire_path(void) {
strv_free_and_replace(arg_path, a);
if (DEBUG_LOGGING) {
- _cleanup_free_ char *j;
+ _cleanup_free_ char *j = NULL;
j = strv_join(arg_path, ":");
log_debug("Using %s as boot loader drop-in search path.", j);
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 04cc7664e5..a684717bb0 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -312,7 +312,7 @@ static int status_variables(void) {
}
static int boot_entry_file_check(const char *root, const char *p) {
- _cleanup_free_ char *path;
+ _cleanup_free_ char *path = NULL;
path = path_join(root, p);
if (!path)
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 35248db009..24efe5de1d 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -456,7 +456,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
device_path = DevicePathFromHandle(entry->device);
if (device_path) {
- _cleanup_freepool_ CHAR16 *str;
+ _cleanup_freepool_ CHAR16 *str = NULL;
str = DevicePathToStr(device_path);
Print(L"device handle '%s'\n", str);
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index 2a37b0a9ac..574feedb98 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -190,9 +190,9 @@ if have_gnu_efi
'-znocombreloc',
'-L', efi_libdir,
efi_crt0]
- if efi_arch == 'aarch64' or efi_arch == 'arm'
- # Aarch64 and ARM32 don't have an EFI capable objcopy. Use 'binary'
- # instead, and add required symbols manually.
+ if efi_arch == 'aarch64' or efi_arch == 'arm' or efi_arch == 'riscv64'
+ # Aarch64, ARM32 and 64bit RISC-V don't have an EFI capable objcopy.
+ # Use 'binary' instead, and add required symbols manually.
efi_ldflags += ['--defsym=EFI_SUBSYSTEM=0xa']
efi_format = ['-O', 'binary']
else
diff --git a/src/boot/efi/no-undefined-symbols.sh b/src/boot/efi/no-undefined-symbols.sh
index b9541c3126..8bdb16accf 100755
--- a/src/boot/efi/no-undefined-symbols.sh
+++ b/src/boot/efi/no-undefined-symbols.sh
@@ -1,8 +1,9 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
-if nm -D -u "$1" | grep ' U '; then
+if nm -D -u "${1:?}" | grep ' U '; then
echo "Undefined symbols detected!"
exit 1
fi
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 1d9a5f07ab..082fe91c9e 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -92,7 +92,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
* is non-NULL explicitly.) */
if (efivar_get_raw(LOADER_GUID, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS &&
loaded_image->FilePath) {
- _cleanup_freepool_ CHAR16 *s;
+ _cleanup_freepool_ CHAR16 *s = NULL;
s = DevicePathToStr(loaded_image->FilePath);
efivar_set(LOADER_GUID, L"LoaderImageIdentifier", s, 0);
@@ -100,7 +100,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
/* if LoaderFirmwareInfo is not set, let's set it */
if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
- _cleanup_freepool_ CHAR16 *s;
+ _cleanup_freepool_ CHAR16 *s = NULL;
s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", s, 0);
@@ -108,7 +108,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
/* ditto for LoaderFirmwareType */
if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
- _cleanup_freepool_ CHAR16 *s;
+ _cleanup_freepool_ CHAR16 *s = NULL;
s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index 06fbd500e5..0061e03eba 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -379,7 +379,7 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s
return err;
if (size == 0) {
- _cleanup_freepool_ EFI_FILE_INFO *info;
+ _cleanup_freepool_ EFI_FILE_INFO *info = NULL;
info = LibFileInfo(handle);
if (!info)
diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c
index cbc24bc251..f081e98ae0 100644
--- a/src/busctl/busctl.c
+++ b/src/busctl/busctl.c
@@ -797,7 +797,7 @@ static Set* member_set_free(Set *s) {
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
static int on_interface(const char *interface, uint64_t flags, void *userdata) {
- _cleanup_(member_freep) Member *m;
+ _cleanup_(member_freep) Member *m = NULL;
Set *members = userdata;
int r;
@@ -828,7 +828,7 @@ static int on_interface(const char *interface, uint64_t flags, void *userdata) {
}
static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
- _cleanup_(member_freep) Member *m;
+ _cleanup_(member_freep) Member *m = NULL;
Set *members = userdata;
int r;
@@ -871,7 +871,7 @@ static int on_method(const char *interface, const char *name, const char *signat
}
static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
- _cleanup_(member_freep) Member *m;
+ _cleanup_(member_freep) Member *m = NULL;
Set *members = userdata;
int r;
@@ -910,7 +910,7 @@ static int on_signal(const char *interface, const char *name, const char *signat
}
static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
- _cleanup_(member_freep) Member *m;
+ _cleanup_(member_freep) Member *m = NULL;
Set *members = userdata;
int r;
diff --git a/src/core/bpf-devices.c b/src/core/bpf-devices.c
index 1ad7ade306..8a345a4498 100644
--- a/src/core/bpf-devices.c
+++ b/src/core/bpf-devices.c
@@ -216,7 +216,7 @@ int bpf_devices_apply_policy(
_cleanup_free_ char *controller_path = NULL;
int r;
- /* This will assign *keep_program if everything goes well. */
+ /* This will assign *prog_installed if everything goes well. */
if (!prog)
goto finish;
diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c
index 0f588b6ca5..02e33399c3 100644
--- a/src/core/bpf-firewall.c
+++ b/src/core/bpf-firewall.c
@@ -698,8 +698,7 @@ int bpf_firewall_install(Unit *u) {
if (r < 0)
return log_unit_error_errno(u, r, "Failed to determine cgroup path: %m");
- flags = (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI &&
- (u->type == UNIT_SLICE || unit_cgroup_delegate(u))) ? BPF_F_ALLOW_MULTI : 0;
+ flags = supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI ? BPF_F_ALLOW_MULTI : 0;
/* Unref the old BPF program (which will implicitly detach it) right before attaching the new program, to
* minimize the time window when we don't account for IP traffic. */
@@ -707,8 +706,7 @@ int bpf_firewall_install(Unit *u) {
u->ip_bpf_ingress_installed = bpf_program_unref(u->ip_bpf_ingress_installed);
if (u->ip_bpf_egress) {
- r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path,
- flags | (set_isempty(u->ip_bpf_custom_egress) ? 0 : BPF_F_ALLOW_MULTI));
+ r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, flags);
if (r < 0)
return log_unit_error_errno(u, r, "Attaching egress BPF program to cgroup %s failed: %m", path);
@@ -717,8 +715,7 @@ int bpf_firewall_install(Unit *u) {
}
if (u->ip_bpf_ingress) {
- r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path,
- flags | (set_isempty(u->ip_bpf_custom_ingress) ? 0 : BPF_F_ALLOW_MULTI));
+ r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, flags);
if (r < 0)
return log_unit_error_errno(u, r, "Attaching ingress BPF program to cgroup %s failed: %m", path);
diff --git a/src/core/bpf-foreign.c b/src/core/bpf-foreign.c
new file mode 100644
index 0000000000..98655bda3c
--- /dev/null
+++ b/src/core/bpf-foreign.c
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bpf-foreign.h"
+#include "bpf-program.h"
+#include "cgroup.h"
+#include "memory-util.h"
+#include "mountpoint-util.h"
+#include "set.h"
+
+typedef struct BPFForeignKey BPFForeignKey;
+struct BPFForeignKey {
+ uint32_t prog_id;
+ uint32_t attach_type;
+};
+
+static int bpf_foreign_key_new(uint32_t prog_id,
+ enum bpf_attach_type attach_type,
+ BPFForeignKey **ret) {
+ _cleanup_free_ BPFForeignKey *p = NULL;
+
+ assert(ret);
+
+ p = new(BPFForeignKey, 1);
+ if (!p)
+ return log_oom();
+
+ *p = (BPFForeignKey) {
+ .prog_id = prog_id,
+ .attach_type = attach_type,
+ };
+
+ *ret = TAKE_PTR(p);
+
+ return 0;
+}
+
+static int bpf_foreign_key_compare_func(const BPFForeignKey *a, const BPFForeignKey *b) {
+ int r = CMP(a->prog_id, b->prog_id);
+ if (r != 0)
+ return r;
+
+ return CMP(a->attach_type, b->attach_type);
+}
+
+static void bpf_foreign_key_hash_func(const BPFForeignKey *p, struct siphash *h) {
+ siphash24_compress(&p->prog_id, sizeof(p->prog_id), h);
+ siphash24_compress(&p->attach_type, sizeof(p->attach_type), h);
+}
+
+DEFINE_PRIVATE_HASH_OPS_FULL(bpf_foreign_by_key_hash_ops,
+ BPFForeignKey, bpf_foreign_key_hash_func, bpf_foreign_key_compare_func, free,
+ BPFProgram, bpf_program_unref);
+
+static int attach_programs(Unit *u, const char *path, Hashmap* foreign_by_key, uint32_t attach_flags) {
+ const BPFForeignKey *key;
+ BPFProgram *prog;
+ int r;
+
+ assert(u);
+
+ HASHMAP_FOREACH_KEY(prog, key, foreign_by_key) {
+ r = bpf_program_cgroup_attach(prog, key->attach_type, path, attach_flags);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Attaching foreign BPF program to cgroup %s failed: %m", path);
+ }
+
+ return 0;
+}
+
+/*
+ * Prepare foreign BPF program for installation:
+ * - Load the program from BPF filesystem to the kernel;
+ * - Store program FD identified by program ID and attach type in the unit.
+ */
+static int bpf_foreign_prepare(
+ Unit *u,
+ enum bpf_attach_type attach_type,
+ const char *bpffs_path) {
+ _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_free_ BPFForeignKey *key = NULL;
+ uint32_t prog_id;
+ int r;
+
+ assert(u);
+ assert(bpffs_path);
+
+ r = bpf_program_new_from_bpffs_path(bpffs_path, &prog);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to create foreign BPFProgram: %m");
+
+ r = bpf_program_get_id_by_fd(prog->kernel_fd, &prog_id);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to get BPF program id by fd: %m");
+
+ r = bpf_foreign_key_new(prog_id, attach_type, &key);
+ if (r < 0)
+ return log_unit_error_errno(u, r,
+ "Failed to create foreign BPF program key from path '%s': %m", bpffs_path);
+
+ r = hashmap_ensure_put(&u->bpf_foreign_by_key, &bpf_foreign_by_key_hash_ops, key, prog);
+ if (r == -EEXIST) {
+ log_unit_warning_errno(u, r, "Foreign BPF program already exists, ignoring: %m");
+ return 0;
+ }
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to put foreign BPFProgram into map: %m");
+
+ TAKE_PTR(key);
+ TAKE_PTR(prog);
+
+ return 0;
+}
+
+int bpf_foreign_supported(void) {
+ int r;
+
+ r = cg_all_unified();
+ if (r <= 0)
+ return r;
+
+ return path_is_mount_point("/sys/fs/bpf", NULL, 0);
+}
+
+int bpf_foreign_install(Unit *u) {
+ _cleanup_free_ char *cgroup_path = NULL;
+ CGroupBPFForeignProgram *p;
+ CGroupContext *cc;
+ int r;
+
+ assert(u);
+
+ cc = unit_get_cgroup_context(u);
+ if (!cc)
+ return 0;
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to get cgroup path: %m");
+
+ LIST_FOREACH(programs, p, cc->bpf_foreign_programs) {
+ r = bpf_foreign_prepare(u, p->attach_type, p->bpffs_path);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to prepare foreign BPF hashmap: %m");
+ }
+
+ r = attach_programs(u, cgroup_path, u->bpf_foreign_by_key, BPF_F_ALLOW_MULTI);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to install foreign BPF programs: %m");
+
+ return 0;
+}
diff --git a/src/core/bpf-foreign.h b/src/core/bpf-foreign.h
new file mode 100644
index 0000000000..7704986e3e
--- /dev/null
+++ b/src/core/bpf-foreign.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#pragma once
+
+#include "unit.h"
+
+int bpf_foreign_supported(void);
+/*
+ * Attach cgroup-bpf programs foreign to systemd, i.e. loaded to the kernel by an entity
+ * external to systemd.
+ */
+int bpf_foreign_install(Unit *u);
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 96073b108b..8b5df7610c 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -8,6 +8,7 @@
#include "blockdev-util.h"
#include "bpf-devices.h"
#include "bpf-firewall.h"
+#include "bpf-foreign.h"
#include "btrfs-util.h"
#include "bus-error.h"
#include "cgroup-setup.h"
@@ -190,6 +191,15 @@ void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockI
free(b);
}
+void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeignProgram *p) {
+ assert(c);
+ assert(p);
+
+ LIST_REMOVE(programs, c->bpf_foreign_programs, p);
+ free(p->bpffs_path);
+ free(p);
+}
+
void cgroup_context_done(CGroupContext *c) {
assert(c);
@@ -217,6 +227,9 @@ void cgroup_context_done(CGroupContext *c) {
c->ip_filters_ingress = strv_free(c->ip_filters_ingress);
c->ip_filters_egress = strv_free(c->ip_filters_egress);
+ while (c->bpf_foreign_programs)
+ cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
+
cpu_set_reset(&c->cpuset_cpus);
cpu_set_reset(&c->cpuset_mems);
}
@@ -360,6 +373,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
CGroupIODeviceLatency *l;
CGroupBlockIODeviceBandwidth *b;
CGroupBlockIODeviceWeight *w;
+ CGroupBPFForeignProgram *p;
CGroupDeviceAllow *a;
CGroupContext *c;
IPAddressAccessItem *iaai;
@@ -544,6 +558,10 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
STRV_FOREACH(path, c->ip_filters_egress)
fprintf(f, "%sIPEgressFilterPath: %s\n", prefix, *path);
+
+ LIST_FOREACH(programs, p, c->bpf_foreign_programs)
+ fprintf(f, "%sBPFProgram: %s:%s",
+ prefix, bpf_cgroup_attach_type_to_string(p->attach_type), p->bpffs_path);
}
int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) {
@@ -575,6 +593,34 @@ int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode)
return 0;
}
+int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *bpffs_path) {
+ CGroupBPFForeignProgram *p;
+ _cleanup_free_ char *d = NULL;
+
+ assert(c);
+ assert(bpffs_path);
+
+ if (!path_is_normalized(bpffs_path) || !path_is_absolute(bpffs_path))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not normalized: %m");
+
+ d = strdup(bpffs_path);
+ if (!d)
+ return log_oom();
+
+ p = new(CGroupBPFForeignProgram, 1);
+ if (!p)
+ return log_oom();
+
+ *p = (CGroupBPFForeignProgram) {
+ .attach_type = attach_type,
+ .bpffs_path = TAKE_PTR(d),
+ };
+
+ LIST_PREPEND(programs, c->bpf_foreign_programs, TAKE_PTR(p));
+
+ return 0;
+}
+
#define UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(entry) \
uint64_t unit_get_ancestor_##entry(Unit *u) { \
CGroupContext *c; \
@@ -1115,6 +1161,12 @@ static void set_io_weight(Unit *u, const char *controller, uint64_t weight) {
(void) set_attribute_and_warn(u, controller, p, buf);
}
+static void cgroup_apply_bpf_foreign_program(Unit *u) {
+ assert(u);
+
+ (void) bpf_foreign_install(u);
+}
+
static void cgroup_context_apply(
Unit *u,
CGroupMask apply_mask,
@@ -1428,6 +1480,9 @@ static void cgroup_context_apply(
if (apply_mask & CGROUP_MASK_BPF_FIREWALL)
cgroup_apply_firewall(u);
+
+ if (apply_mask & CGROUP_MASK_BPF_FOREIGN)
+ cgroup_apply_bpf_foreign_program(u);
}
static bool unit_get_needs_bpf_firewall(Unit *u) {
@@ -1460,6 +1515,17 @@ static bool unit_get_needs_bpf_firewall(Unit *u) {
return false;
}
+static bool unit_get_needs_bpf_foreign_program(Unit *u) {
+ CGroupContext *c;
+ assert(u);
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return false;
+
+ return !LIST_IS_EMPTY(c->bpf_foreign_programs);
+}
+
static CGroupMask unit_get_cgroup_mask(Unit *u) {
CGroupMask mask = 0;
CGroupContext *c;
@@ -1511,6 +1577,9 @@ static CGroupMask unit_get_bpf_mask(Unit *u) {
if (unit_get_needs_bpf_firewall(u))
mask |= CGROUP_MASK_BPF_FIREWALL;
+ if (unit_get_needs_bpf_foreign_program(u))
+ mask |= CGROUP_MASK_BPF_FOREIGN;
+
return mask;
}
@@ -2989,6 +3058,11 @@ static int cg_bpf_mask_supported(CGroupMask *ret) {
if (r > 0)
mask |= CGROUP_MASK_BPF_DEVICES;
+ /* BPF pinned prog */
+ r = bpf_foreign_supported();
+ if (r > 0)
+ mask |= CGROUP_MASK_BPF_FOREIGN;
+
*ret = mask;
return 0;
}
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index fa79ba1523..be3060eba7 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -31,6 +31,7 @@ typedef struct CGroupIODeviceLimit CGroupIODeviceLimit;
typedef struct CGroupIODeviceLatency CGroupIODeviceLatency;
typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth;
+typedef struct CGroupBPFForeignProgram CGroupBPFForeignProgram;
typedef enum CGroupDevicePolicy {
/* When devices listed, will allow those, plus built-in ones, if none are listed will allow
@@ -94,6 +95,12 @@ struct CGroupBlockIODeviceBandwidth {
uint64_t wbps;
};
+struct CGroupBPFForeignProgram {
+ LIST_FIELDS(CGroupBPFForeignProgram, programs);
+ uint32_t attach_type;
+ char *bpffs_path;
+};
+
struct CGroupContext {
bool cpu_accounting;
bool io_accounting;
@@ -142,6 +149,7 @@ struct CGroupContext {
char **ip_filters_ingress;
char **ip_filters_egress;
+ LIST_HEAD(CGroupBPFForeignProgram, bpf_foreign_programs);
/* For legacy hierarchies */
uint64_t cpu_shares;
@@ -202,8 +210,10 @@ void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *
void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLatency *l);
void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w);
void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
+void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeignProgram *p);
int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode);
+int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path);
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 04d2ba34f3..60a2ad7816 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -5,6 +5,7 @@
#include "af-list.h"
#include "alloc-util.h"
#include "bpf-firewall.h"
+#include "bpf-foreign.h"
#include "bus-get-properties.h"
#include "cgroup-util.h"
#include "cgroup.h"
@@ -347,6 +348,33 @@ static int property_get_ip_address_access(
return sd_bus_message_close_container(reply);
}
+static int property_get_bpf_foreign_program(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ CGroupContext *c = userdata;
+ CGroupBPFForeignProgram *p;
+ int r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(programs, p, c->bpf_foreign_programs) {
+ const char *attach_type = bpf_cgroup_attach_type_to_string(p->attach_type);
+
+ r = sd_bus_message_append(reply, "(ss)", attach_type, p->bpffs_path);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
@@ -398,6 +426,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("ManagedOOMMemoryPressure", "s", property_get_managed_oom_mode, offsetof(CGroupContext, moom_mem_pressure), 0),
SD_BUS_PROPERTY("ManagedOOMMemoryPressureLimit", "u", NULL, offsetof(CGroupContext, moom_mem_pressure_limit), 0),
SD_BUS_PROPERTY("ManagedOOMPreference", "s", property_get_managed_oom_preference, offsetof(CGroupContext, moom_preference), 0),
+ SD_BUS_PROPERTY("BPFProgram", "a(ss)", property_get_bpf_foreign_program, 0, 0),
SD_BUS_VTABLE_END
};
@@ -422,7 +451,7 @@ static int bus_cgroup_set_transient_property(
int b;
if (!UNIT_VTABLE(u)->can_delegate)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
r = sd_bus_message_read(message, "b", &b);
if (r < 0)
@@ -441,7 +470,7 @@ static int bus_cgroup_set_transient_property(
CGroupMask mask = 0;
if (streq(name, "DelegateControllers") && !UNIT_VTABLE(u)->can_delegate)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
r = sd_bus_message_enter_container(message, 'a', "s");
if (r < 0)
@@ -571,6 +600,85 @@ static int bus_cgroup_set_transient_property(
}
return 1;
+ } else if (streq(name, "BPFProgram")) {
+ const char *a, *p;
+ size_t n = 0;
+
+ r = sd_bus_message_enter_container(message, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(ss)", &a, &p)) > 0) {
+ int attach_type = bpf_cgroup_attach_type_from_string(a);
+ if (attach_type < 0)
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "%s expects a valid BPF attach type, got '%s'.",
+ name, a);
+
+ if (!path_is_normalized(p) || !path_is_absolute(p))
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "%s= expects a normalized absolute path.",
+ name);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ r = cgroup_add_bpf_foreign_program(c, attach_type, p);
+ if (r < 0)
+ return r;
+ }
+ n++;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ CGroupBPFForeignProgram *fp;
+ size_t size = 0;
+
+ if (n == 0)
+ while (c->bpf_foreign_programs)
+ cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
+
+ f = open_memstream_unlocked(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fputs(name, f);
+ fputs("=\n", f);
+
+ LIST_FOREACH(programs, fp, c->bpf_foreign_programs)
+ fprintf(f, "%s=%s:%s\n", name,
+ bpf_cgroup_attach_type_to_string(fp->attach_type),
+ fp->bpffs_path);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
+
+ unit_write_setting(u, flags, name, buf);
+
+ if (!LIST_IS_EMPTY(c->bpf_foreign_programs)) {
+ r = bpf_foreign_supported();
+ if (r < 0)
+ return r;
+ if (r == 0)
+ log_full(LOG_DEBUG,
+ "Transient unit %s configures a BPF program pinned to BPF "
+ "filesystem, but the local system does not support that.\n"
+ "Starting this unit will fail!", u->id);
+ }
+ }
+
+ return 1;
}
return 0;
@@ -944,7 +1052,7 @@ int bus_cgroup_set_property(
return r;
if (u64 <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "CPUQuotaPerSecUSec= value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "CPUQuotaPerSecUSec= value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->cpu_quota_per_sec_usec = u64;
@@ -1122,7 +1230,7 @@ int bus_cgroup_set_property(
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IODeviceWeight= value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "IODeviceWeight= value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
CGroupIODeviceWeight *a = NULL, *b;
@@ -1380,7 +1488,7 @@ int bus_cgroup_set_property(
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIODeviceWeight= out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIODeviceWeight= out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
CGroupBlockIODeviceWeight *a = NULL, *b;
@@ -1476,12 +1584,12 @@ int bus_cgroup_set_property(
while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
if (!valid_device_allow_pattern(path) || strpbrk(path, WHITESPACE))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node or pattern");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node or pattern");
if (isempty(rwm))
rwm = "rwm";
else if (!in_charset(rwm, "rwm"))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
CGroupDeviceAllow *a = NULL, *b;
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index eda21f4734..9c141d73b1 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -1627,7 +1627,7 @@ int bus_exec_context_set_transient_property(
unit_write_settingf(u, flags, name, "RootHash=");
} else {
- _cleanup_free_ void *p;
+ _cleanup_free_ void *p = NULL;
encoded = hexmem(roothash_decoded, roothash_decoded_size);
if (!encoded)
@@ -1673,7 +1673,7 @@ int bus_exec_context_set_transient_property(
unit_write_settingf(u, flags, name, "RootHashSignature=");
} else {
- _cleanup_free_ void *p;
+ _cleanup_free_ void *p = NULL;
ssize_t len;
len = base64mem(roothash_sig_decoded, roothash_sig_decoded_size, &encoded);
@@ -2060,7 +2060,7 @@ int bus_exec_context_set_transient_property(
return r;
if (!log_level_is_valid(level))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log level value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Log level value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->syslog_priority = (c->syslog_priority & LOG_FACMASK) | level;
@@ -2077,7 +2077,7 @@ int bus_exec_context_set_transient_property(
return r;
if (!log_facility_unshifted_is_valid(facility))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log facility value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Log facility value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->syslog_priority = (facility << 3) | LOG_PRI(c->syslog_priority);
@@ -2094,7 +2094,7 @@ int bus_exec_context_set_transient_property(
return r;
if (!isempty(n) && !log_namespace_name_valid(n))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log namespace name not valid");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Log namespace name not valid");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
@@ -2140,13 +2140,13 @@ int bus_exec_context_set_transient_property(
break;
if (memchr(p, 0, sz))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains zero byte");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains zero byte");
eq = memchr(p, '=', sz);
if (!eq)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains no '=' character");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains no '=' character");
if (!journal_field_valid(p, eq - (const char*) p, false))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field invalid");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field invalid");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
t = reallocarray(c->log_extra_fields, c->n_log_extra_fields+1, sizeof(struct iovec));
@@ -2163,7 +2163,7 @@ int bus_exec_context_set_transient_property(
((uint8_t*) copy)[sz] = 0;
if (!utf8_is_valid(copy))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field is not valid UTF-8");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field is not valid UTF-8");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE(copy, sz);
@@ -2379,8 +2379,8 @@ int bus_exec_context_set_transient_property(
return 1;
} else if (streq(name, "RestrictAddressFamilies")) {
- int allow_list;
_cleanup_strv_free_ char **l = NULL;
+ int allow_list;
r = sd_bus_message_enter_container(message, 'r', "bas");
if (r < 0)
@@ -2403,10 +2403,11 @@ int bus_exec_context_set_transient_property(
char **s;
if (strv_isempty(l)) {
- c->address_families_allow_list = false;
+ c->address_families_allow_list = allow_list;
c->address_families = set_free(c->address_families);
- unit_write_settingf(u, flags, name, "RestrictAddressFamilies=");
+ unit_write_settingf(u, flags, name, "RestrictAddressFamilies=%s",
+ allow_list ? "none" : "");
return 1;
}
@@ -2430,7 +2431,7 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
} else
- (void) set_remove(c->address_families, INT_TO_PTR(af));
+ set_remove(c->address_families, INT_TO_PTR(af));
}
joined = strv_join(l, " ");
@@ -2649,7 +2650,7 @@ int bus_exec_context_set_transient_property(
missing_ok = false;
if (!isempty(s) && !streq(s, "~") && !path_is_absolute(s))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (streq(s, "~")) {
@@ -2678,7 +2679,7 @@ int bus_exec_context_set_transient_property(
return r;
if (!isempty(s) && !fdname_is_valid(s))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
@@ -2830,7 +2831,7 @@ int bus_exec_context_set_transient_property(
return r;
if (!strv_env_is_valid(l))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (strv_isempty(l)) {
@@ -2864,7 +2865,7 @@ int bus_exec_context_set_transient_property(
return r;
if (!strv_env_name_or_assignment_is_valid(l))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UnsetEnvironment= list.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UnsetEnvironment= list.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (strv_isempty(l)) {
@@ -2897,7 +2898,7 @@ int bus_exec_context_set_transient_property(
return r;
if (!oom_score_adjust_is_valid(oa))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "OOM score adjust value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "OOM score adjust value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->oom_score_adjust = oa;
@@ -3018,7 +3019,7 @@ int bus_exec_context_set_transient_property(
return r;
if (!strv_env_name_is_valid(l))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment= block.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment= block.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (strv_isempty(l)) {
@@ -3193,7 +3194,7 @@ int bus_exec_context_set_transient_property(
if (!path_is_absolute(destination))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not absolute.", destination);
if (!IN_SET(mount_flags, 0, MS_REC))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mount flags.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mount flags.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index e33896f976..128d7778f5 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -48,6 +48,7 @@ static UnitFileFlags unit_file_bools_to_flags(bool runtime, bool force) {
}
BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_oom_policy, oom_policy, OOMPolicy);
+BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_emergency_action, emergency_action, EmergencyAction);
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_version, "s", GIT_VERSION);
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_features, "s", systemd_features);
@@ -380,7 +381,7 @@ static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char
u = manager_get_unit_by_pid(m, pid);
if (!u)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
+ return sd_bus_error_set(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
} else {
u = manager_get_unit(m, name);
if (!u)
@@ -504,7 +505,7 @@ static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userd
else if (sz == 16)
memcpy(&id, a, sz);
else
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid invocation ID");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid invocation ID");
if (sd_id128_is_null(id)) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
@@ -1235,7 +1236,7 @@ static int method_subscribe(sd_bus_message *message, void *userdata, sd_bus_erro
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_ALREADY_SUBSCRIBED, "Client is already subscribed.");
+ return sd_bus_error_set(error, BUS_ERROR_ALREADY_SUBSCRIBED, "Client is already subscribed.");
}
return sd_bus_reply_method_return(message, NULL);
@@ -1259,7 +1260,7 @@ static int method_unsubscribe(sd_bus_message *message, void *userdata, sd_bus_er
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
}
return sd_bus_reply_method_return(message, NULL);
@@ -1309,7 +1310,7 @@ static int method_dump_by_fd(sd_bus_message *message, void *userdata, sd_bus_err
}
static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed.");
}
static int verify_run_space(const char *message, sd_bus_error *error) {
@@ -1624,7 +1625,7 @@ static int method_set_environment(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
if (!strv_env_is_valid(plus))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
r = bus_verify_set_environment_async(m, message, error);
if (r < 0)
@@ -1729,7 +1730,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_
return r;
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers.");
m->return_value = code;
@@ -2723,6 +2724,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("DefaultTasksMax", "t", bus_property_get_tasks_max, offsetof(Manager, default_tasks_max), 0),
SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultOOMPolicy", "s", bus_property_get_oom_policy, offsetof(Manager, default_oom_policy), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("CtrlAltDelBurstAction", "s", bus_property_get_emergency_action, offsetof(Manager, cad_burst_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD_WITH_NAMES("GetUnit",
"s",
diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h
index f3862fca83..9b050801a7 100644
--- a/src/core/dbus-manager.h
+++ b/src/core/dbus-manager.h
@@ -15,3 +15,4 @@ void bus_manager_send_change_signal(Manager *m);
int verify_run_space_and_log(const char *message);
int bus_property_get_oom_policy(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
+int bus_property_get_emergency_action(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c
index 14e77d783d..e132cd2b3c 100644
--- a/src/core/dbus-path.c
+++ b/src/core/dbus-path.c
@@ -96,7 +96,7 @@ static int bus_path_set_transient_property(
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in %s is not absolute: %s", type_name, path);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- _cleanup_free_ char *k;
+ _cleanup_free_ char *k = NULL;
PathSpec *s;
k = strdup(path);
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
index 1bcb4836f6..90ec6a686c 100644
--- a/src/core/dbus-scope.c
+++ b/src/core/dbus-scope.c
@@ -133,7 +133,7 @@ static int bus_scope_set_transient_property(
/* We can't support direct connections with this, as direct connections know no service or unique name
* concept, but the Controller field stores exactly that. */
if (sd_bus_message_get_bus(message) != u->manager->api_bus)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Sorry, Controller= logic only supported via the bus.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Sorry, Controller= logic only supported via the bus.");
r = sd_bus_message_read(message, "s", &controller);
if (r < 0)
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 73906ebab7..4dea8d5aec 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -109,7 +109,7 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
assert(u);
if (!MANAGER_IS_SYSTEM(u->manager))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Adding bind mounts at runtime is only supported for system managers.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Adding bind mounts at runtime is only supported for system managers.");
r = mac_selinux_unit_access_check(u, message, "start", error);
if (r < 0)
@@ -120,12 +120,12 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
return r;
if (!path_is_absolute(src) || !path_is_normalized(src))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
if (!is_image && isempty(dest))
dest = src;
else if (!path_is_absolute(dest) || !path_is_normalized(dest))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
if (is_image) {
r = bus_read_mount_options(message, error, &options, NULL, "");
@@ -147,23 +147,23 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
if (u->type != UNIT_SERVICE)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not of type .service");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not of type .service");
/* If it would be dropped at startup time, return an error. The context should always be available, but
* there's an assert in exec_needs_mount_namespace, so double-check just in case. */
c = unit_get_exec_context(u);
if (!c)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot access unit execution context");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot access unit execution context");
if (path_startswith_strv(dest, c->inaccessible_paths))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s is not accessible to this unit", dest);
/* Ensure that the unit was started in a private mount namespace */
if (!exec_needs_mount_namespace(c, NULL, unit_get_exec_runtime(u)))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit not running in private mount namespace, cannot activate bind mount");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit not running in private mount namespace, cannot activate bind mount");
unit_pid = unit_main_pid(u);
if (unit_pid == 0 || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not running");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not running");
propagate_directory = strjoina("/run/systemd/propagate/", u->id);
if (is_image)
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index 88b2f2cacf..e54c473d11 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -183,7 +183,7 @@ static int timer_add_one_calendar_spec(
r = calendar_spec_from_string(str, &c);
if (r == -EINVAL)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec");
if (r < 0)
return r;
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 39d6799b59..2af7761c0f 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -10,6 +10,7 @@
#include "cgroup-util.h"
#include "condition.h"
#include "dbus-job.h"
+#include "dbus-manager.h"
#include "dbus-unit.h"
#include "dbus-util.h"
#include "dbus.h"
@@ -43,7 +44,6 @@ static bool unit_can_isolate_refuse_manual(Unit *u) {
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_collect_mode, collect_mode, CollectMode);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
-static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
static BUS_DEFINE_PROPERTY_GET(property_get_description, "s", Unit, unit_description);
static BUS_DEFINE_PROPERTY_GET2(property_get_active_state, "s", Unit, unit_active_state, unit_active_state_to_string);
static BUS_DEFINE_PROPERTY_GET2(property_get_freezer_state, "s", Unit, unit_freezer_state, freezer_state_to_string);
@@ -527,7 +527,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
}
if (!SIGNAL_VALID(signo))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
r = bus_verify_manage_units_async_full(
u,
@@ -653,7 +653,7 @@ int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error
r = bus_unit_track_remove_sender(u, message);
if (r == -EUNATCH)
- return sd_bus_error_setf(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
if (r < 0)
return r;
@@ -719,9 +719,9 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error
if (r == -EOPNOTSUPP)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not supporting cleaning.", u->id);
if (r == -EUNATCH)
- return sd_bus_error_setf(error, BUS_ERROR_NOTHING_TO_CLEAN, "No matching resources found.");
+ return sd_bus_error_set(error, BUS_ERROR_NOTHING_TO_CLEAN, "No matching resources found.");
if (r == -EBUSY)
- return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit is not inactive or has pending job.");
+ return sd_bus_error_set(error, BUS_ERROR_UNIT_BUSY, "Unit is not inactive or has pending job.");
if (r < 0)
return r;
@@ -768,9 +768,9 @@ static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userda
if (r == -EOPNOTSUPP)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not support freezing.", u->id);
if (r == -EBUSY)
- return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job.");
+ return sd_bus_error_set(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job.");
if (r == -EHOSTDOWN)
- return sd_bus_error_setf(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive.");
+ return sd_bus_error_set(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive.");
if (r == -EALREADY)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Previously requested freezer operation for unit '%s' is still in progress.", u->id);
if (r < 0)
@@ -899,7 +899,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Markers", "as", property_get_markers, offsetof(Unit, markers), 0),
SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobRunningTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_running_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_emergency_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("JobTimeoutAction", "s", bus_property_get_emergency_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobTimeoutRebootArgument", "s", NULL, offsetof(Unit, job_timeout_reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AssertResult", "b", bus_property_get_bool, offsetof(Unit, assert_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -912,10 +912,10 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Perpetual", "b", bus_property_get_bool, offsetof(Unit, perpetual), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Unit, start_ratelimit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_ratelimit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("FailureAction", "s", property_get_emergency_action, offsetof(Unit, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StartLimitAction", "s", bus_property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FailureAction", "s", bus_property_get_emergency_action, offsetof(Unit, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FailureActionExitStatus", "i", bus_property_get_int, offsetof(Unit, failure_action_exit_status), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("SuccessAction", "s", property_get_emergency_action, offsetof(Unit, success_action), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SuccessAction", "s", bus_property_get_emergency_action, offsetof(Unit, success_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SuccessActionExitStatus", "i", bus_property_get_int, offsetof(Unit, success_action_exit_status), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -1442,10 +1442,10 @@ int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd
}
if (!unit_cgroup_delegate(u))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process migration not available on non-delegated units.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Process migration not available on non-delegated units.");
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not active, refusing.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not active, refusing.");
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID, &creds);
if (r < 0)
@@ -1930,7 +1930,7 @@ static int bus_unit_set_live_property(
return r;
if (some_plus_minus && some_absolute)
- return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, "Bad marker syntax.");
+ return sd_bus_error_set(error, BUS_ERROR_BAD_UNIT_SETTING, "Bad marker syntax.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (some_absolute)
@@ -1999,7 +1999,7 @@ static int bus_set_transient_exit_status(
return r;
if (k > 255)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Exit status must be in range 0…255 or negative.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Exit status must be in range 0…255 or negative.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
*p = k < 0 ? -1 : k;
@@ -2205,11 +2205,11 @@ static int bus_unit_set_transient_property(
const char *s;
if (!UNIT_HAS_CGROUP_CONTEXT(u))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
if (u->type == UNIT_SLICE)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
if (unit_has_name(u, SPECIAL_INIT_SCOPE))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
r = sd_bus_message_read(message, "s", &s);
if (r < 0)
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 5db484b8de..26e34ac4d0 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -164,7 +164,7 @@ static int signal_activation_request(sd_bus_message *message, void *userdata, sd
if (manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SERVICE) ||
manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SOCKET)) {
- r = sd_bus_error_setf(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down.");
+ r = sd_bus_error_set(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down.");
goto failed;
}
diff --git a/src/core/execute.c b/src/core/execute.c
index 2152fa8500..9f54ad424b 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -4639,7 +4639,7 @@ static int exec_child(
final_argv = command->argv;
if (DEBUG_LOGGING) {
- _cleanup_free_ char *line;
+ _cleanup_free_ char *line = NULL;
line = exec_command_line(final_argv);
if (line)
@@ -4916,6 +4916,7 @@ void exec_context_done(ExecContext *c) {
c->stdin_data_size = 0;
c->network_namespace_path = mfree(c->network_namespace_path);
+ c->ipc_namespace_path = mfree(c->ipc_namespace_path);
c->log_namespace = mfree(c->log_namespace);
@@ -4932,7 +4933,7 @@ int exec_context_destroy_runtime_directory(const ExecContext *c, const char *run
return 0;
STRV_FOREACH(i, c->directories[EXEC_DIRECTORY_RUNTIME].paths) {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
p = path_join(runtime_prefix, "private", *i);
diff --git a/src/core/job.c b/src/core/job.c
index 56c99f93eb..d313ebdb8e 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -844,7 +844,7 @@ static void job_print_done_status_message(Unit *u, JobType t, JobResult result)
REENABLE_WARNING;
if (t == JOB_START && result == JOB_FAILED) {
- _cleanup_free_ char *quoted;
+ _cleanup_free_ char *quoted = NULL;
quoted = shell_maybe_quote(u->id, ESCAPE_BACKSLASH);
manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 5ef785c0de..4bd1207e2c 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -234,7 +234,8 @@ $1.ManagedOOMSwap, config_parse_managed_oom_mode,
$1.ManagedOOMMemoryPressure, config_parse_managed_oom_mode, 0, offsetof($1, cgroup_context.moom_mem_pressure)
$1.ManagedOOMMemoryPressureLimit, config_parse_managed_oom_mem_pressure_limit, 0, offsetof($1, cgroup_context.moom_mem_pressure_limit)
$1.ManagedOOMPreference, config_parse_managed_oom_preference, 0, offsetof($1, cgroup_context.moom_preference)
-$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0'
+$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0
+$1.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof($1, cgroup_context)'
)m4_dnl
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 1a1e58976a..9be495e1ef 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -19,6 +19,7 @@
#include "all-units.h"
#include "alloc-util.h"
#include "bpf-firewall.h"
+#include "bpf-program.h"
#include "bus-error.h"
#include "bus-internal.h"
#include "bus-util.h"
@@ -1697,6 +1698,8 @@ int config_parse_exec_cpu_affinity(
void *userdata) {
ExecContext *c = data;
+ const Unit *u = userdata;
+ _cleanup_free_ char *k = NULL;
int r;
assert(filename);
@@ -1711,7 +1714,15 @@ int config_parse_exec_cpu_affinity(
return 0;
}
- r = parse_cpu_set_extend(rvalue, &c->cpu_set, true, unit, filename, line, lvalue);
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to resolve unit specifiers in '%s', ignoring: %m",
+ rvalue);
+ return 0;
+ }
+
+ r = parse_cpu_set_extend(k, &c->cpu_set, true, unit, filename, line, lvalue);
if (r >= 0)
c->cpu_affinity_from_numa = false;
@@ -2004,7 +2015,7 @@ int config_parse_trigger_unit(
assert(rvalue);
assert(data);
- if (!hashmap_isempty(u->dependencies[UNIT_TRIGGERS])) {
+ if (UNIT_TRIGGER(u)) {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
return 0;
}
@@ -3426,6 +3437,13 @@ int config_parse_address_families(
return 0;
}
+ if (streq(rvalue, "none")) {
+ /* Forbid all address families. */
+ c->address_families = set_free(c->address_families);
+ c->address_families_allow_list = true;
+ return 0;
+ }
+
if (rvalue[0] == '~') {
invert = true;
rvalue++;
@@ -5581,6 +5599,64 @@ int config_parse_ip_filter_bpf_progs(
return 0;
}
+int config_parse_bpf_foreign_program(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ _cleanup_free_ char *resolved = NULL, *word = NULL;
+ CGroupContext *c = data;
+ Unit *u = userdata;
+ int attach_type, r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ while (c->bpf_foreign_programs)
+ cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
+
+ return 0;
+ }
+
+ r = extract_first_word(&rvalue, &word, ":", 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse foreign BPF program, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ attach_type = bpf_cgroup_attach_type_from_string(word);
+ if (attach_type < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown BPF attach type=%s, ignoring: %s", word, rvalue);
+ return 0;
+ }
+
+ r = unit_full_printf(u, rvalue, &resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ return 0;
+
+ r = cgroup_add_bpf_foreign_program(c, attach_type, resolved);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add foreign BPF program to cgroup context: %m");
+
+ return 0;
+}
+
static int merge_by_names(Unit **u, Set *names, const char *id) {
char *k;
int r;
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 4746a8a792..e99c9a4055 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -140,6 +140,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_swap_priority);
CONFIG_PARSER_PROTOTYPE(config_parse_mount_images);
CONFIG_PARSER_PROTOTYPE(config_parse_socket_timestamping);
CONFIG_PARSER_PROTOTYPE(config_parse_extension_images);
+CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
diff --git a/src/core/main.c b/src/core/main.c
index 74dd895c58..54ef7d182a 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1367,7 +1367,7 @@ static int status_welcome(void) {
static int write_container_id(void) {
const char *c;
- int r = 0; /* silence gcc warning about r being unitialized below */
+ int r = 0; /* avoid false maybe-uninitialized warning */
c = getenv("container");
if (isempty(c))
@@ -2036,7 +2036,7 @@ static void log_execution_mode(bool *ret_first_boot) {
}
} else {
if (DEBUG_LOGGING) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
t = uid_to_name(getuid());
log_debug("systemd " GIT_VERSION " running in %suser mode for user " UID_FMT "/%s. (%s)",
diff --git a/src/core/manager.c b/src/core/manager.c
index 57bb25ca25..4abe6606f6 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -248,7 +248,7 @@ static void manager_print_jobs_in_progress(Manager *m) {
}
static int have_ask_password(void) {
- _cleanup_closedir_ DIR *dir;
+ _cleanup_closedir_ DIR *dir = NULL;
struct dirent *de;
dir = opendir("/run/systemd/ask-password");
@@ -1281,7 +1281,6 @@ static unsigned manager_dispatch_stop_when_unneeded_queue(Manager *m) {
while ((u = m->stop_when_unneeded_queue)) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- assert(m->stop_when_unneeded_queue);
assert(u->in_stop_when_unneeded_queue);
LIST_REMOVE(stop_when_unneeded_queue, m->stop_when_unneeded_queue, u);
@@ -1732,13 +1731,13 @@ int manager_add_job(
assert(mode < _JOB_MODE_MAX);
if (mode == JOB_ISOLATE && type != JOB_START)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start.");
if (mode == JOB_ISOLATE && !unit->allow_isolate)
- return sd_bus_error_setf(error, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated.");
+ return sd_bus_error_set(error, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated.");
if (mode == JOB_TRIGGERING && type != JOB_STOP)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "--job-mode=triggering is only valid for stop.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "--job-mode=triggering is only valid for stop.");
log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
diff --git a/src/core/meson.build b/src/core/meson.build
index a389c906b3..a1294f3a72 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -11,6 +11,8 @@ libcore_sources = '''
bpf-devices.h
bpf-firewall.c
bpf-firewall.h
+ bpf-foreign.c
+ bpf-foreign.h
cgroup.c
cgroup.h
core-varlink.c
diff --git a/src/core/namespace.c b/src/core/namespace.c
index ccea336fee..77fb0d4394 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -1853,7 +1853,7 @@ int setup_namespace(
r = loop_device_make_by_path(
root_image,
- FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
+ FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
&loop_device);
if (r < 0)
@@ -1863,6 +1863,8 @@ int setup_namespace(
loop_device->fd,
&verity,
root_image_options,
+ loop_device->uevent_seqnum_not_before,
+ loop_device->timestamp_not_before,
dissect_image_flags,
&dissected_image);
if (r < 0)
@@ -2092,7 +2094,7 @@ int setup_namespace(
}
if (log_namespace) {
- _cleanup_free_ char *q;
+ _cleanup_free_ char *q = NULL;
q = strjoin("/run/systemd/journal.", log_namespace);
if (!q) {
@@ -2331,7 +2333,7 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
}
LIST_FOREACH(mount_options, i, item->mount_options) {
- _cleanup_(mount_options_free_allp) MountOptions *o;
+ _cleanup_(mount_options_free_allp) MountOptions *o = NULL;
o = new(MountOptions, 1);
if (!o)
diff --git a/src/core/path.c b/src/core/path.c
index b954ee1f47..04084bf63e 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -316,7 +316,7 @@ static int path_add_trigger_dependencies(Path *p) {
assert(p);
- if (!hashmap_isempty(UNIT(p)->dependencies[UNIT_TRIGGERS]))
+ if (UNIT_TRIGGER(UNIT(p)))
return 0;
r = unit_load_related_unit(UNIT(p), ".service", &x);
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index 18f6fb59bc..cdb82dd894 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -250,7 +250,7 @@ int mac_selinux_generic_access_check(
if (!enforce)
return 0;
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
}
tclass = "system";
@@ -270,11 +270,12 @@ int mac_selinux_generic_access_check(
r = errno_or_else(EPERM);
if (enforce)
- sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
+ sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
}
- log_debug_errno(r, "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s path=%s cmdline=%s: %m",
- scon, fcon, tclass, permission, enforce ? "enforcing" : "permissive", path, cl);
+ log_full_errno_zerook(LOG_DEBUG, r,
+ "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s path=%s cmdline=%s: %m",
+ scon, fcon, tclass, permission, enforce ? "enforcing" : "permissive", path, cl);
return enforce ? r : 0;
}
diff --git a/src/core/service.c b/src/core/service.c
index 550db40631..d5f79d274e 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -3474,6 +3474,15 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case SERVICE_START_POST:
case SERVICE_RELOAD:
+ /* If neither main nor control processes are running then
+ * the current state can never exit cleanly, hence immediately
+ * terminate the service. */
+ if (control_pid_good(s) <= 0)
+ service_enter_stop(s, f);
+
+ /* Otherwise need to wait untill the operation is done. */
+ break;
+
case SERVICE_STOP:
/* Need to wait until the operation is
* done */
@@ -4269,7 +4278,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
if (getpeername_pretty(fd, true, &peer) >= 0) {
if (UNIT(s)->description) {
- _cleanup_free_ char *a;
+ _cleanup_free_ char *a = NULL;
a = strjoin(UNIT(s)->description, " (", peer, ")");
if (!a)
diff --git a/src/core/system.conf.in b/src/core/system.conf.in
index fa6fb690c7..39b1c74ff9 100644
--- a/src/core/system.conf.in
+++ b/src/core/system.conf.in
@@ -72,3 +72,4 @@
#DefaultLimitNICE=
#DefaultLimitRTPRIO=
#DefaultLimitRTTIME=
+#DefaultOOMPolicy=stop
diff --git a/src/core/timer.c b/src/core/timer.c
index 32abdb74d7..b0caaf3850 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -124,7 +124,7 @@ static int timer_add_trigger_dependencies(Timer *t) {
assert(t);
- if (!hashmap_isempty(UNIT(t)->dependencies[UNIT_TRIGGERS]))
+ if (UNIT_TRIGGER(UNIT(t)))
return 0;
r = unit_load_related_unit(UNIT(t), ".service", &x);
diff --git a/src/core/unit.c b/src/core/unit.c
index 2c5dc54379..864bcd3d6e 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -11,6 +11,7 @@
#include "all-units.h"
#include "alloc-util.h"
#include "bpf-firewall.h"
+#include "bpf-foreign.h"
#include "bus-common-errors.h"
#include "bus-util.h"
#include "cgroup-setup.h"
@@ -556,7 +557,7 @@ static void unit_free_requires_mounts_for(Unit *u) {
assert(u);
for (;;) {
- _cleanup_free_ char *path;
+ _cleanup_free_ char *path = NULL;
path = hashmap_steal_first_key(u->requires_mounts_for);
if (!path)
@@ -723,6 +724,8 @@ Unit* unit_free(Unit *u) {
set_free(u->ip_bpf_custom_ingress_installed);
set_free(u->ip_bpf_custom_egress_installed);
+ hashmap_free(u->bpf_foreign_by_key);
+
bpf_program_unref(u->bpf_device_control_installed);
condition_free_list(u->conditions);
@@ -1060,7 +1063,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
char **dp;
STRV_FOREACH(dp, c->directories[dt].paths) {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = path_join(u->manager->prefix[dt], *dp);
if (!p)
@@ -2181,7 +2184,7 @@ static int unit_log_resources(Unit *u) {
if (n_message_parts == 0)
t = strjoina("MESSAGE=", u->id, ": Completed.");
else {
- _cleanup_free_ char *joined;
+ _cleanup_free_ char *joined = NULL;
message_parts[n_message_parts] = NULL;
diff --git a/src/core/unit.h b/src/core/unit.h
index 6d38e66680..88d1074aac 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -305,6 +305,10 @@ typedef struct Unit {
Set *ip_bpf_custom_egress;
Set *ip_bpf_custom_egress_installed;
+ /* BPF programs managed (e.g. loaded to kernel) by an entity external to systemd,
+ * attached to unit cgroup by provided program fd and attach type. */
+ Hashmap *bpf_foreign_by_key;
+
uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX];
/* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new
@@ -915,7 +919,7 @@ int unit_thaw_vtable_common(Unit *u);
/* Macros which append UNIT= or USER_UNIT= to the message */
-#define log_unit_full_errno(unit, level, error, ...) \
+#define log_unit_full_errno_zerook(unit, level, error, ...) \
({ \
const Unit *_u = (unit); \
(log_get_max_level() < LOG_PRI(level)) ? -ERRNO_VALUE(error) : \
@@ -923,9 +927,16 @@ int unit_thaw_vtable_common(Unit *u);
log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
})
-#define log_unit_full(unit, level, ...) (void) log_unit_full_errno(unit, level, 0, __VA_ARGS__)
+#define log_unit_full_errno(unit, level, error, ...) \
+ ({ \
+ int _error = (error); \
+ ASSERT_NON_ZERO(_error); \
+ log_unit_full_errno_zerook(unit, level, _error, ##__VA_ARGS__); \
+ })
+
+#define log_unit_full(unit, level, ...) (void) log_unit_full_errno_zerook(unit, level, 0, __VA_ARGS__)
-#define log_unit_debug(unit, ...) log_unit_full_errno(unit, LOG_DEBUG, 0, __VA_ARGS__)
+#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, __VA_ARGS__)
#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, __VA_ARGS__)
#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, __VA_ARGS__)
#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, __VA_ARGS__)
diff --git a/src/core/user.conf.in b/src/core/user.conf.in
index d3887e1c87..5f0ca4cb02 100644
--- a/src/core/user.conf.in
+++ b/src/core/user.conf.in
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the user.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# See systemd-user.conf(5) for details.
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 2fb2404500..62467d4cf9 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -588,7 +588,7 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
static int get_process_ns(pid_t pid, const char *namespace, ino_t *ns) {
const char *p;
struct stat stbuf;
- _cleanup_close_ int proc_ns_dir_fd;
+ _cleanup_close_ int proc_ns_dir_fd = -1;
p = procfs_file_alloca(pid, "ns");
@@ -703,14 +703,16 @@ static int submit_coredump(
struct iovec_wrapper *iovw,
int input_fd) {
+ _cleanup_(json_variant_unrefp) JsonVariant *json_metadata = NULL;
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
_cleanup_free_ char *filename = NULL, *coredump_data = NULL;
_cleanup_free_ char *stacktrace = NULL;
char *core_message;
+ const char *module_name;
uint64_t coredump_size = UINT64_MAX;
bool truncated = false;
+ JsonVariant *module_json;
int r;
-
assert(context);
assert(iovw);
assert(input_fd >= 0);
@@ -757,7 +759,7 @@ static int submit_coredump(
"than %"PRIu64" (the configured maximum)",
coredump_size, arg_process_size_max);
} else
- coredump_make_stack_trace(coredump_fd, context->meta[META_EXE], &stacktrace);
+ coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata);
#endif
log:
@@ -781,6 +783,34 @@ log:
if (truncated)
(void) iovw_put_string_field(iovw, "COREDUMP_TRUNCATED=", "1");
+ /* If we managed to parse any ELF metadata (build-id, ELF package meta),
+ * attach it as journal metadata. */
+ if (json_metadata) {
+ _cleanup_free_ char *formatted_json = NULL;
+
+ r = json_variant_format(json_metadata, 0, &formatted_json);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format JSON package metadata: %m");
+
+ (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_JSON=", formatted_json);
+ }
+
+ JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, json_metadata) {
+ JsonVariant *package_name, *package_version;
+
+ /* We only add structured fields for the 'main' ELF module */
+ if (!path_equal_filename(module_name, context->meta[META_EXE]))
+ continue;
+
+ package_name = json_variant_by_key(module_json, "name");
+ if (package_name)
+ (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_NAME=", json_variant_string(package_name));
+
+ package_version = json_variant_by_key(module_json, "version");
+ if (package_version)
+ (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_VERSION=", json_variant_string(package_version));
+ }
+
/* Optionally store the entire coredump in the journal */
if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
if (coredump_size <= arg_journal_size_max) {
diff --git a/src/coredump/coredump.conf b/src/coredump/coredump.conf
index 56c1008245..1f75d48d33 100644
--- a/src/coredump/coredump.conf
+++ b/src/coredump/coredump.conf
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the coredump.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# Use 'systemd-analyze cat-config systemd/coredump.conf' to display the full config.
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index 0c4ef2e123..9a577d47c8 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -139,7 +139,7 @@ static int acquire_journal(sd_journal **ret, char **matches) {
return r;
if (DEBUG_LOGGING) {
- _cleanup_free_ char *filter;
+ _cleanup_free_ char *filter = NULL;
filter = journal_make_match_string(j);
log_debug("Journal filter: %s", filter);
@@ -545,7 +545,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
*boot_id = NULL, *machine_id = NULL, *hostname = NULL,
*slice = NULL, *cgroup = NULL, *owner_uid = NULL,
*message = NULL, *timestamp = NULL, *filename = NULL,
- *truncated = NULL, *coredump = NULL;
+ *truncated = NULL, *coredump = NULL,
+ *pkgmeta_name = NULL, *pkgmeta_version = NULL, *pkgmeta_json = NULL;
const void *d;
size_t l;
bool normal_coredump;
@@ -574,6 +575,9 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
RETRIEVE(d, l, "COREDUMP", coredump);
+ RETRIEVE(d, l, "COREDUMP_PACKAGE_NAME", pkgmeta_name);
+ RETRIEVE(d, l, "COREDUMP_PACKAGE_VERSION", pkgmeta_version);
+ RETRIEVE(d, l, "COREDUMP_PACKAGE_JSON", pkgmeta_json);
RETRIEVE(d, l, "_BOOT_ID", boot_id);
RETRIEVE(d, l, "_MACHINE_ID", machine_id);
RETRIEVE(d, l, "MESSAGE", message);
@@ -716,6 +720,37 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
else
fprintf(file, " Storage: none\n");
+ if (pkgmeta_name && pkgmeta_version)
+ fprintf(file, " Package: %s/%s\n", pkgmeta_name, pkgmeta_version);
+
+ /* Print out the build-id of the 'main' ELF module, by matching the JSON key
+ * with the 'exe' field. */
+ if (exe && pkgmeta_json) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ r = json_parse(pkgmeta_json, 0, &v, NULL, NULL);
+ if (r < 0)
+ log_warning_errno(r, "json_parse on %s failed, ignoring: %m", pkgmeta_json);
+ else {
+ const char *module_name;
+ JsonVariant *module_json;
+
+ JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, v) {
+ JsonVariant *build_id;
+
+ /* We only print the build-id for the 'main' ELF module */
+ if (!path_equal_filename(module_name, exe))
+ continue;
+
+ build_id = json_variant_by_key(module_json, "buildId");
+ if (build_id)
+ fprintf(file, " build-id: %s\n", json_variant_string(build_id));
+
+ break;
+ }
+ }
+ }
+
if (message) {
_cleanup_free_ char *m = NULL;
@@ -944,7 +979,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp)
if (filename) {
#if HAVE_COMPRESSION
- _cleanup_close_ int fdf;
+ _cleanup_close_ int fdf = -1;
fdf = open(filename, O_RDONLY | O_CLOEXEC);
if (fdf < 0) {
diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c
index a29ab1211c..102ad2e65b 100644
--- a/src/coredump/stacktrace.c
+++ b/src/coredump/stacktrace.c
@@ -1,7 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <dwarf.h>
+#include <elfutils/libdwelf.h>
#include <elfutils/libdwfl.h>
+#include <libelf.h>
#include <sys/types.h>
#include <unistd.h>
@@ -9,6 +11,7 @@
#include "fileio.h"
#include "fd-util.h"
#include "format-util.h"
+#include "hexdecoct.h"
#include "macro.h"
#include "stacktrace.h"
#include "string-util.h"
@@ -16,6 +19,7 @@
#define FRAMES_MAX 64
#define THREADS_MAX 64
+#define ELF_PACKAGE_METADATA_ID 0xcafe1a7e
struct stack_context {
FILE *f;
@@ -23,6 +27,8 @@ struct stack_context {
Elf *elf;
unsigned n_thread;
unsigned n_frame;
+ JsonVariant **package_metadata;
+ Set **modules;
};
static int frame_callback(Dwfl_Frame *frame, void *userdata) {
@@ -111,14 +117,212 @@ static int thread_callback(Dwfl_Thread *thread, void *userdata) {
return DWARF_CB_OK;
}
-static int make_stack_trace(int fd, const char *executable, char **ret) {
+static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *elf, struct stack_context *c) {
+ size_t n_program_headers;
+ int r;
+
+ assert(name);
+ assert(elf);
+ assert(c);
+
+ /* When iterating over PT_LOAD we will visit modules more than once */
+ if (set_contains(*c->modules, name))
+ return DWARF_CB_OK;
+
+ r = elf_getphdrnum(elf, &n_program_headers);
+ if (r < 0) /* Not the handle we are looking for - that's ok, skip it */
+ return DWARF_CB_OK;
+
+ /* Iterate over all program headers in that ELF object. These will have been copied by
+ * the kernel verbatim when the core file is generated. */
+ for (size_t i = 0; i < n_program_headers; ++i) {
+ size_t note_offset = 0, name_offset, desc_offset;
+ GElf_Phdr mem, *program_header;
+ GElf_Nhdr note_header;
+ Elf_Data *data;
+
+ /* Package metadata is in PT_NOTE headers. */
+ program_header = gelf_getphdr(elf, i, &mem);
+ if (!program_header || program_header->p_type != PT_NOTE)
+ continue;
+
+ /* Fortunately there is an iterator we can use to walk over the
+ * elements of a PT_NOTE program header. We are interested in the
+ * note with type. */
+ data = elf_getdata_rawchunk(elf,
+ program_header->p_offset,
+ program_header->p_filesz,
+ ELF_T_NHDR);
+
+ while (note_offset < data->d_size &&
+ (note_offset = gelf_getnote(data, note_offset, &note_header, &name_offset, &desc_offset)) > 0) {
+ const char *note_name = (const char *)data->d_buf + name_offset;
+ const char *payload = (const char *)data->d_buf + desc_offset;
+
+ if (note_header.n_namesz == 0 || note_header.n_descsz == 0)
+ continue;
+
+ /* Package metadata might have different owners, but the
+ * magic ID is always the same. */
+ if (note_header.n_type == ELF_PACKAGE_METADATA_ID) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
+
+ r = json_parse(payload, 0, &v, NULL, NULL);
+ if (r < 0) {
+ log_error_errno(r, "json_parse on %s failed: %m", payload);
+ return DWARF_CB_ABORT;
+ }
+
+ /* First pretty-print to the buffer, so that the metadata goes as
+ * plaintext in the journal. */
+ fprintf(c->f, "Metadata for module %s owned by %s found: ",
+ name, note_name);
+ json_variant_dump(v, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, c->f, NULL);
+ fputc('\n', c->f);
+
+ /* Secondly, if we have a build-id, merge it in the same JSON object
+ * so that it appears all nicely together in the logs/metadata. */
+ if (id_json) {
+ r = json_variant_merge(&v, id_json);
+ if (r < 0) {
+ log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
+ return DWARF_CB_ABORT;
+ }
+ }
+
+ /* Then we build a new object using the module name as the key, and merge it
+ * with the previous parses, so that in the end it all fits together in a single
+ * JSON blob. */
+ r = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(name, JSON_BUILD_VARIANT(v))));
+ if (r < 0) {
+ log_error_errno(r, "Failed to build JSON object: %m");
+ return DWARF_CB_ABORT;
+ }
+ r = json_variant_merge(c->package_metadata, w);
+ if (r < 0) {
+ log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
+ return DWARF_CB_ABORT;
+ }
+
+ /* Finally stash the name, so we avoid double visits. */
+ r = set_put_strdup(c->modules, name);
+ if (r < 0) {
+ log_error_errno(r, "set_put_strdup failed: %m");
+ return DWARF_CB_ABORT;
+ }
+
+ return DWARF_CB_OK;
+ }
+ }
+ }
+
+ /* Didn't find package metadata for this module - that's ok, just go to the next. */
+ return DWARF_CB_OK;
+}
+
+static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, Dwarf_Addr start, void *arg) {
+ _cleanup_(json_variant_unrefp) JsonVariant *id_json = NULL;
+ struct stack_context *c = arg;
+ size_t n_program_headers;
+ GElf_Addr id_vaddr, bias;
+ const unsigned char *id;
+ int id_len, r;
+ Elf *elf;
+
+ assert(mod);
+ assert(c);
+
+ if (!name)
+ name = "(unnamed)"; /* For logging purposes */
+
+ /* We are iterating on each "module", which is what dwfl calls ELF objects contained in the
+ * core file, and extracting the build-id first and then the package metadata.
+ * We proceed in a best-effort fashion - not all ELF objects might contain both or either.
+ * The build-id is easy, as libdwfl parses it during the dwfl_core_file_report() call and
+ * stores it separately in an internal library struct. */
+ id_len = dwfl_module_build_id(mod, &id, &id_vaddr);
+ if (id_len <= 0)
+ /* If we don't find a build-id, note it in the journal message, and try
+ * anyway to find the package metadata. It's unlikely to have the latter
+ * without the former, but there's no hard rule. */
+ fprintf(c->f, "Found module %s without build-id.\n", name);
+ else {
+ JsonVariant *build_id;
+
+ /* We will later parse package metadata json and pass it to our caller. Prepare the
+ * build-id in json format too, so that it can be appended and parsed cleanly. It
+ * will then be added as metadata to the journal message with the stack trace. */
+ r = json_build(&id_json, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("buildId", JSON_BUILD_HEX(id, id_len))));
+ if (r < 0) {
+ log_error_errno(r, "json_build on build-id failed: %m");
+ return DWARF_CB_ABORT;
+ }
+
+ build_id = json_variant_by_key(id_json, "buildId");
+ assert_se(build_id);
+ fprintf(c->f, "Found module %s with build-id: %s\n", name, json_variant_string(build_id));
+ }
+
+ /* The .note.package metadata is more difficult. From the module, we need to get a reference
+ * to the ELF object first. We might be lucky and just get it from elfutils. */
+ elf = dwfl_module_getelf(mod, &bias);
+ if (elf)
+ return parse_package_metadata(name, id_json, elf, c);
+
+ /* We did not get the ELF object. That is likely because we didn't get direct
+ * access to the executable, and the version of elfutils does not yet support
+ * parsing it out of the core file directly.
+ * So fallback to manual extraction - get the PT_LOAD section from the core,
+ * and if it's the right one we can interpret it as an Elf object, and parse
+ * its notes manually. */
+
+ r = elf_getphdrnum(c->elf, &n_program_headers);
+ if (r < 0) {
+ log_warning("Could not parse number of program headers from core file: %s",
+ elf_errmsg(-1)); /* -1 retrieves the most recent error */
+ return DWARF_CB_OK;
+ }
+
+ for (size_t i = 0; i < n_program_headers; ++i) {
+ GElf_Phdr mem, *program_header;
+ Elf_Data *data;
+
+ /* The core file stores the ELF files in the PT_LOAD segment .*/
+ program_header = gelf_getphdr(c->elf, i, &mem);
+ if (!program_header || program_header->p_type != PT_LOAD)
+ continue;
+
+ /* Now get a usable Elf reference, and parse the notes from it. */
+ data = elf_getdata_rawchunk(c->elf,
+ program_header->p_offset,
+ program_header->p_filesz,
+ ELF_T_NHDR);
+
+ Elf *memelf = elf_memory(data->d_buf, data->d_size);
+ if (!memelf)
+ continue;
+ r = parse_package_metadata(name, id_json, memelf, c);
+ if (r != DWARF_CB_OK)
+ return r;
+ }
+
+ return DWARF_CB_OK;
+}
+
+static int parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
static const Dwfl_Callbacks callbacks = {
.find_elf = dwfl_build_id_find_elf,
+ .section_address = dwfl_offline_section_address,
.find_debuginfo = dwfl_standard_find_debuginfo,
};
- struct stack_context c = {};
+ _cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
+ _cleanup_(set_freep) Set *modules = NULL;
+ struct stack_context c = {
+ .package_metadata = &package_metadata,
+ .modules = &modules,
+ };
char *buf = NULL;
size_t sz = 0;
int r;
@@ -157,6 +361,11 @@ static int make_stack_trace(int fd, const char *executable, char **ret) {
goto finish;
}
+ if (dwfl_getmodules(c.dwfl, &module_callback, &c, 0) < 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
r = -EINVAL;
goto finish;
@@ -170,6 +379,8 @@ static int make_stack_trace(int fd, const char *executable, char **ret) {
c.f = safe_fclose(c.f);
*ret = TAKE_PTR(buf);
+ if (ret_package_metadata)
+ *ret_package_metadata = TAKE_PTR(package_metadata);
r = 0;
@@ -187,10 +398,10 @@ finish:
return r;
}
-void coredump_make_stack_trace(int fd, const char *executable, char **ret) {
+void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
int r;
- r = make_stack_trace(fd, executable, ret);
+ r = parse_core(fd, executable, ret, ret_package_metadata);
if (r == -EINVAL)
log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
else if (r < 0)
diff --git a/src/coredump/stacktrace.h b/src/coredump/stacktrace.h
index b935748404..5039b934dd 100644
--- a/src/coredump/stacktrace.h
+++ b/src/coredump/stacktrace.h
@@ -1,4 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-void coredump_make_stack_trace(int fd, const char *executable, char **ret);
+#include "json.h"
+
+void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata);
diff --git a/src/cryptsetup/cryptsetup-keyfile.c b/src/cryptsetup/cryptsetup-keyfile.c
index a6281fbdee..55c1442ed6 100644
--- a/src/cryptsetup/cryptsetup-keyfile.c
+++ b/src/cryptsetup/cryptsetup-keyfile.c
@@ -33,7 +33,7 @@ int find_key_file(
}
STRV_FOREACH(i, search_path) {
- _cleanup_free_ char *joined;
+ _cleanup_free_ char *joined = NULL;
joined = path_join(*i, key_file);
if (!joined)
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index ee31400e3f..c9cebb4637 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -1008,7 +1008,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_free_ char *friendly = NULL;
int keyslot = arg_key_slot, r;
- size_t decrypted_key_size = 0; /* Silence gcc warning about unitialized variable */
+ size_t decrypted_key_size = 0; /* avoid false maybe-uninitialized warning */
assert(cd);
assert(name);
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index 65ddb1d149..f1288b41a7 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -770,7 +770,7 @@ static int run(int argc, char *argv[]) {
r = loop_device_make_by_path(
arg_image,
- FLAGS_SET(arg_flags, DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR,
+ FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
&d);
if (r < 0)
@@ -781,6 +781,8 @@ static int run(int argc, char *argv[]) {
arg_image,
&arg_verity_settings,
NULL,
+ d->uevent_seqnum_not_before,
+ d->timestamp_not_before,
arg_flags,
&m);
if (r < 0)
diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c
index 1c51cf6b2c..852e29f11d 100644
--- a/src/environment-d-generator/environment-d-generator.c
+++ b/src/environment-d-generator/environment-d-generator.c
@@ -29,7 +29,7 @@ static int environment_dirs(char ***ret) {
return r;
if (DEBUG_LOGGING) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
t = strv_join(dirs, "\n\t");
log_debug("Looking for environment.d files in (higher priority first):\n\t%s", strna(t));
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 8c1087a9a3..110a9bd1ba 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -433,6 +433,11 @@ static int add_mount(
if (r < 0)
return r;
+ /* Order the mount unit we generate relative to the post unit, so that DefaultDependencies= on the
+ * target unit won't affect us. */
+ if (post && !FLAGS_SET(flags, AUTOMOUNT) && !FLAGS_SET(flags, NOAUTO))
+ fprintf(f, "Before=%s\n", post);
+
if (passno != 0) {
r = generator_write_fsck_deps(f, dest, what, where, fstype);
if (r < 0)
@@ -460,7 +465,7 @@ static int add_mount(
return r;
if (!isempty(fstype) && !streq(fstype, "auto")) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
t = specifier_escape(fstype);
if (!t)
@@ -721,7 +726,7 @@ static int add_sysroot_mount(void) {
else
opts = arg_root_options;
- log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
+ log_debug("Found entry what=%s where=/sysroot type=%s opts=%s", what, strna(arg_root_fstype), strempty(opts));
if (is_device_path(what)) {
r = generator_write_initrd_root_device_deps(arg_dest, what);
@@ -744,6 +749,10 @@ static int add_sysroot_mount(void) {
static int add_sysroot_usr_mount(void) {
_cleanup_free_ char *what = NULL;
const char *opts;
+ int r;
+
+ /* Returns 0 if we didn't do anything, > 0 if we either generated a unit for the /usr/ mount, or we
+ * know for sure something else did */
if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options)
return 0;
@@ -767,8 +776,23 @@ static int add_sysroot_usr_mount(void) {
return log_oom();
}
- if (!arg_usr_what)
+ if (isempty(arg_usr_what)) {
+ log_debug("Could not find a usr= entry on the kernel command line.");
return 0;
+ }
+
+ if (streq(arg_usr_what, "gpt-auto")) {
+ /* This is handled by the gpt-auto generator */
+ log_debug("Skipping /usr/ directory handling, as gpt-auto was requested.");
+ return 1; /* systemd-gpt-auto-generator will generate a unit for this, hence report that a
+ * unit file is being created for the host /usr/ mount. */
+ }
+
+ if (path_equal(arg_usr_what, "/dev/nfs")) {
+ /* This is handled by the initrd (if at all supported, that is) */
+ log_debug("Skipping /usr/ directory handling, as /dev/nfs was requested.");
+ return 1; /* As above, report that NFS code will create the unit */
+ }
what = fstab_node_to_udev_node(arg_usr_what);
if (!what)
@@ -781,17 +805,62 @@ static int add_sysroot_usr_mount(void) {
else
opts = arg_usr_options;
- log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
- return add_mount(arg_dest,
- what,
- "/sysroot/usr",
- NULL,
- arg_usr_fstype,
- opts,
- is_device_path(what) ? 1 : 0, /* passno */
- 0,
- SPECIAL_INITRD_FS_TARGET,
- "/proc/cmdline");
+ /* When mounting /usr from the initrd, we add an extra level of indirection: we first mount the /usr/
+ * partition to /sysusr/usr/, and then afterwards bind mount that to /sysroot/usr/. We do this so
+ * that we can cover for systems that initially only have a /usr/ around and where the root fs needs
+ * to be synthesized, based on configuration included in /usr/, e.g. systemd-repart. Software like
+ * this should order itself after initrd-usr-fs.target and before initrd-fs.target; and it should
+ * look into both /sysusr/ and /sysroot/ for the configuration data to apply. */
+
+ log_debug("Found entry what=%s where=/sysusr/usr type=%s opts=%s", what, strna(arg_usr_fstype), strempty(opts));
+
+ r = add_mount(arg_dest,
+ what,
+ "/sysusr/usr",
+ NULL,
+ arg_usr_fstype,
+ opts,
+ is_device_path(what) ? 1 : 0, /* passno */
+ 0,
+ SPECIAL_INITRD_USR_FS_TARGET,
+ "/proc/cmdline");
+ if (r < 0)
+ return r;
+
+ log_debug("Synthesizing entry what=/sysusr/usr where=/sysrootr/usr opts=bind");
+
+ r = add_mount(arg_dest,
+ "/sysusr/usr",
+ "/sysroot/usr",
+ NULL,
+ NULL,
+ "bind",
+ 0,
+ 0,
+ SPECIAL_INITRD_FS_TARGET,
+ "/proc/cmdline");
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int add_sysroot_usr_mount_or_fallback(void) {
+ int r;
+
+ r = add_sysroot_usr_mount();
+ if (r != 0)
+ return r;
+
+ /* OK, so we didn't write anything out for /sysusr/usr/ nor /sysroot/usr/. In this case, let's make
+ * sure that initrd-usr-fs.target is at least ordered after sysroot.mount so that services that order
+ * themselves get the guarantee that /usr/ is definitely mounted somewhere. */
+
+ return generator_add_symlink(
+ arg_dest,
+ SPECIAL_INITRD_USR_FS_TARGET,
+ "requires",
+ "sysroot.mount");
}
static int add_volatile_root(void) {
@@ -953,7 +1022,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
if (in_initrd()) {
r = add_sysroot_mount();
- r2 = add_sysroot_usr_mount();
+ r2 = add_sysroot_usr_mount_or_fallback();
r3 = add_volatile_root();
} else
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index dda9b18815..d3af814136 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -105,6 +105,7 @@ static int open_parent_block_device(dev_t devnum, int *ret_fd) {
}
static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
+#if HAVE_LIBCRYPTSETUP
_cleanup_free_ char *e = NULL, *n = NULL, *d = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -182,6 +183,9 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, bool requir
}
return 0;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Partition is encrypted, but the project was compiled without libcryptsetup support");
+#endif
}
static int add_mount(
@@ -668,6 +672,8 @@ static int enumerate_partitions(dev_t devnum) {
r = dissect_image(
fd,
NULL, NULL,
+ UINT64_MAX,
+ USEC_INFINITY,
DISSECT_IMAGE_GPT_ONLY|
DISSECT_IMAGE_NO_UDEV|
DISSECT_IMAGE_USR_NO_ROOT,
diff --git a/src/home/homectl.c b/src/home/homectl.c
index cf1a2d9f9b..a187a75d86 100644
--- a/src/home/homectl.c
+++ b/src/home/homectl.c
@@ -3362,4 +3362,4 @@ static int run(int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, NULL);
}
-DEFINE_MAIN_FUNCTION(run);
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/home/homed-bus.c b/src/home/homed-bus.c
index d70fda5f44..8f7a646d4a 100644
--- a/src/home/homed-bus.c
+++ b/src/home/homed-bus.c
@@ -59,7 +59,7 @@ int bus_message_read_home_record(sd_bus_message *m, UserRecordLoadFlags flags, U
r = user_record_load(hr, v, flags);
if (r < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "JSON data is not a valid identity record");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "JSON data is not a valid identity record");
*ret = TAKE_PTR(hr);
return 0;
diff --git a/src/home/homed-home.c b/src/home/homed-home.c
index b0c5ce4232..dc9a446c71 100644
--- a/src/home/homed-home.c
+++ b/src/home/homed-home.c
@@ -437,19 +437,19 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
switch (e) {
case -EMSGSIZE:
- return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrunk");
+ return sd_bus_error_set(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrunk");
case -ETXTBSY:
- return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrunk offline");
+ return sd_bus_error_set(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrunk offline");
case -ERANGE:
- return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File system size too small");
+ return sd_bus_error_set(error, BUS_ERROR_BAD_HOME_SIZE, "File system size too small");
case -ENOLINK:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected storage backend");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected storage backend");
case -EPROTONOSUPPORT:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected file system");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected file system");
case -ENOTTY:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on storage backend");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on storage backend");
case -ESOCKTNOSUPPORT:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on file system");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on file system");
case -ENOKEY:
return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD, "Password for home %s is incorrect or not sufficient for authentication.", h->user_name);
case -EBADSLT:
@@ -457,21 +457,21 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
case -EREMOTEIO:
return sd_bus_error_setf(error, BUS_ERROR_BAD_RECOVERY_KEY, "Recovery key for home %s is incorrect or not sufficient for authentication.", h->user_name);
case -ENOANO:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
case -ERFKILL:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
case -EMEDIUMTYPE:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence.");
case -ENOSTR:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)");
case -EOWNERDEAD:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
case -ENOLCK:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN, "Bad PIN of security token.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_BAD_PIN, "Bad PIN of security token.");
case -ETOOMANYREFS:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, "Bad PIN of security token, and only a few tries left.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, "Bad PIN of security token, and only a few tries left.");
case -EUCLEAN:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT, "Bad PIN of security token, and only one try left.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT, "Bad PIN of security token, and only one try left.");
case -EBUSY:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
case -ENOEXEC:
@@ -1104,7 +1104,7 @@ static int home_ratelimit(Home *h, sd_bus_error *error) {
return sd_bus_error_setf(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again in %s!",
format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC));
- return sd_bus_error_setf(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again later.");
+ return sd_bus_error_set(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again later.");
}
return 0;
@@ -1402,10 +1402,10 @@ static int home_update_internal(
assert(hr);
if (!user_record_compatible(hr, h->record))
- return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Updated user record is not compatible with existing one.");
+ return sd_bus_error_set(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Updated user record is not compatible with existing one.");
c = user_record_compare_last_change(hr, h->record); /* refuse downgrades */
if (c < 0)
- return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_DOWNGRADE, "Refusing to update to older home record.");
+ return sd_bus_error_set(error, BUS_ERROR_HOME_RECORD_DOWNGRADE, "Refusing to update to older home record.");
if (!secret && FLAGS_SET(hr->mask, USER_RECORD_SECRET)) {
r = user_record_clone(hr, USER_RECORD_EXTRACT_SECRET, &saved_secret);
@@ -1454,7 +1454,7 @@ static int home_update_internal(
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Home record different but timestamp remained the same, refusing.");
+ return sd_bus_error_set(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Home record different but timestamp remained the same, refusing.");
}
r = home_start_work(h, verb, new_hr, secret);
@@ -1528,7 +1528,7 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
if (disk_size == UINT64_MAX || disk_size == h->record->disk_size) {
if (h->record->disk_size == UINT64_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "No disk size to resize to specified.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "No disk size to resize to specified.");
c = user_record_ref(h->record); /* Shortcut if size is unspecified or matches the record */
} else {
@@ -2349,6 +2349,8 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
assert(o);
assert(o->type == OPERATION_ACQUIRE);
+ assert(!h->current_operation);
+
switch (home_get_state(h)) {
case HOME_UNFIXATED:
@@ -2357,8 +2359,9 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
break;
case HOME_ABSENT:
- r = sd_bus_error_setf(&error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
- break;
+ r = sd_bus_error_setf(&error, BUS_ERROR_HOME_ABSENT,
+ "Home %s is currently missing or not plugged in.", h->user_name);
+ goto check;
case HOME_INACTIVE:
case HOME_DIRTY:
@@ -2382,14 +2385,11 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
return 0;
}
- assert(!h->current_operation);
-
- if (call) {
- r = home_ratelimit(h, &error);
- if (r >= 0)
- r = call(h, o->secret, for_state, &error);
- }
+ r = home_ratelimit(h, &error);
+ if (r >= 0)
+ r = call(h, o->secret, for_state, &error);
+ check:
if (r != 0) /* failure or completed */
operation_result(o, r, &error);
else /* ongoing */
@@ -2660,7 +2660,7 @@ int home_schedule_operation(Home *h, Operation *o, sd_bus_error *error) {
if (o) {
if (ordered_set_size(h->pending_operations) >= PENDING_OPERATIONS_MAX)
- return sd_bus_error_setf(error, BUS_ERROR_TOO_MANY_OPERATIONS, "Too many client operations requested");
+ return sd_bus_error_set(error, BUS_ERROR_TOO_MANY_OPERATIONS, "Too many client operations requested");
r = ordered_set_ensure_put(&h->pending_operations, &operation_hash_ops, o);
if (r < 0)
diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c
index 85fdc88962..bd104f6811 100644
--- a/src/home/homed-manager.c
+++ b/src/home/homed-manager.c
@@ -1457,7 +1457,7 @@ int manager_sign_user_record(Manager *m, UserRecord *u, UserRecord **ret, sd_bus
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_KEY, "Can't sign without local key.");
+ return sd_bus_error_set(error, BUS_ERROR_NO_PRIVATE_KEY, "Can't sign without local key.");
return user_record_sign(u, m->private_key, ret);
}
diff --git a/src/home/homed.conf b/src/home/homed.conf
index ba854641df..d3a685e816 100644
--- a/src/home/homed.conf
+++ b/src/home/homed.conf
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the homed.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# Use 'systemd-analyze cat-config systemd/homed.conf' to display the full config.
diff --git a/src/home/user-record-util.c b/src/home/user-record-util.c
index b8f59c55ec..e244ba5772 100644
--- a/src/home/user-record-util.c
+++ b/src/home/user-record-util.c
@@ -1351,16 +1351,16 @@ int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
assert(hr);
if (hr->disposition >= 0 && hr->disposition != USER_REGULAR)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
if (hr->storage >= 0 && !IN_SET(hr->storage, USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
if (gid_is_valid(hr->gid) && hr->uid != (uid_t) hr->gid)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
if (hr->service && !streq(hr->service, "io.systemd.Home"))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
return 0;
}
diff --git a/src/import/curl-util.c b/src/import/curl-util.c
index e6db810635..ed2ac0a654 100644
--- a/src/import/curl-util.c
+++ b/src/import/curl-util.c
@@ -3,11 +3,11 @@
#include <fcntl.h>
#include "alloc-util.h"
-#include "build.h"
#include "curl-util.h"
#include "fd-util.h"
#include "locale-util.h"
#include "string-util.h"
+#include "version.h"
static void curl_glue_check_finished(CurlGlue *g) {
CURLMsg *msg;
diff --git a/src/import/importd.c b/src/import/importd.c
index fa8ff68a46..f0f61ca784 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -1080,7 +1080,7 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er
if (r < 0)
return r;
if (id <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
if (!t)
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 4cefe3918c..a2f166a881 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -311,7 +311,7 @@ static int request_parse_range(
colon2 = strchr(colon + 1, ':');
if (colon2) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
t = strndup(colon + 1, colon2 - colon - 1);
if (!t)
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index 6f71248aaf..9600e5f732 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -40,7 +40,7 @@ static int open_output(RemoteServer *s, Writer *w, const char* host) {
break;
case JOURNAL_WRITE_SPLIT_HOST: {
- _cleanup_free_ char *name;
+ _cleanup_free_ char *name = NULL;
assert(host);
diff --git a/src/journal-remote/journal-remote.conf.in b/src/journal-remote/journal-remote.conf.in
index 1e4c3b9710..4c1b78ebc1 100644
--- a/src/journal-remote/journal-remote.conf.in
+++ b/src/journal-remote/journal-remote.conf.in
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the journal-remote.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# See journal-remote.conf(5) for details.
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index a8f1f7e511..d7e45364a6 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -9,7 +9,6 @@
#include "sd-daemon.h"
#include "alloc-util.h"
-#include "build.h"
#include "conf-parser.h"
#include "daemon-util.h"
#include "def.h"
@@ -34,6 +33,7 @@
#include "strv.h"
#include "tmpfile-util.h"
#include "util.h"
+#include "version.h"
#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem"
diff --git a/src/journal-remote/journal-upload.conf.in b/src/journal-remote/journal-upload.conf.in
index 7d2bdc7501..29b623bdfa 100644
--- a/src/journal-remote/journal-upload.conf.in
+++ b/src/journal-remote/journal-upload.conf.in
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the journal-upload.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# See journal-upload.conf(5) for details.
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 4b3e697855..b4a8bd1bfb 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -1158,7 +1158,7 @@ static int add_matches(sd_journal *j, char **args) {
if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
if (executable_is_script(p, &interpreter) > 0) {
- _cleanup_free_ char *comm;
+ _cleanup_free_ char *comm = NULL;
comm = strndup(basename(p), 15);
if (!comm)
@@ -1537,7 +1537,7 @@ static int get_possible_units(
char **patterns,
Set **units) {
- _cleanup_set_free_free_ Set *found;
+ _cleanup_set_free_free_ Set *found = NULL;
const char *field;
int r;
@@ -2182,7 +2182,7 @@ int main(int argc, char *argv[]) {
case ACTION_LIST_CATALOG:
case ACTION_DUMP_CATALOG:
case ACTION_UPDATE_CATALOG: {
- _cleanup_free_ char *database;
+ _cleanup_free_ char *database = NULL;
database = path_join(arg_root, CATALOG_DATABASE);
if (!database) {
@@ -2436,7 +2436,7 @@ int main(int argc, char *argv[]) {
goto finish;
if (DEBUG_LOGGING) {
- _cleanup_free_ char *filter;
+ _cleanup_free_ char *filter = NULL;
filter = journal_make_match_string(j);
if (!filter)
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index e7255b0355..c96b84e61f 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -14,6 +14,7 @@
#include "escape.h"
#include "fd-util.h"
#include "format-util.h"
+#include "fs-util.h"
#include "io-util.h"
#include "journald-kmsg.h"
#include "journald-server.h"
@@ -376,8 +377,8 @@ int server_open_dev_kmsg(Server *s) {
s->dev_kmsg_fd = open("/dev/kmsg", mode);
if (s->dev_kmsg_fd < 0) {
- log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
- "Failed to open /dev/kmsg, ignoring: %m");
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
+ errno, "Failed to open /dev/kmsg, ignoring: %m");
return 0;
}
@@ -436,8 +437,8 @@ int server_open_kernel_seqnum(Server *s) {
return 0;
}
- r = posix_fallocate(fd, 0, sizeof(uint64_t));
- if (r != 0) {
+ r = posix_fallocate_loop(fd, 0, sizeof(uint64_t));
+ if (r < 0) {
log_error_errno(r, "Failed to allocate sequential number file, ignoring: %m");
return 0;
}
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index 7bc26097f3..85723f5b30 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -39,6 +39,12 @@
#define STDOUT_STREAMS_MAX 4096
+/* During the "setup" protocol phase of the stream logic let's define a different maximum line length than
+ * during the actual operational phase. We want to allow users to specify very short line lengths after all,
+ * but the unit name we embed in the setup protocol might be longer than that. Hence, during the setup phase
+ * let's enforce a line length matching the maximum unit name length (255) */
+#define STDOUT_STREAM_SETUP_PROTOCOL_LINE_MAX (UNIT_NAME_MAX-1U)
+
typedef enum StdoutStreamState {
STDOUT_STREAM_IDENTIFIER,
STDOUT_STREAM_UNIT_ID,
@@ -47,7 +53,7 @@ typedef enum StdoutStreamState {
STDOUT_STREAM_FORWARD_TO_SYSLOG,
STDOUT_STREAM_FORWARD_TO_KMSG,
STDOUT_STREAM_FORWARD_TO_CONSOLE,
- STDOUT_STREAM_RUNNING
+ STDOUT_STREAM_RUNNING,
} StdoutStreamState;
/* The different types of log record terminators: a real \n was read, a NUL character was read, the maximum line length
@@ -189,7 +195,7 @@ static int stdout_stream_save(StdoutStream *s) {
s->id_field + STRLEN("_STREAM_ID="));
if (!isempty(s->identifier)) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->identifier);
if (!escaped) {
@@ -201,7 +207,7 @@ static int stdout_stream_save(StdoutStream *s) {
}
if (!isempty(s->unit_id)) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->unit_id);
if (!escaped) {
@@ -468,6 +474,18 @@ static int stdout_stream_found(
return r;
}
+static size_t stdout_stream_line_max(StdoutStream *s) {
+ assert(s);
+
+ /* During the "setup" phase of our protocol, let's ensure we use a line length where a full unit name
+ * can fit in */
+ if (s->state != STDOUT_STREAM_RUNNING)
+ return STDOUT_STREAM_SETUP_PROTOCOL_LINE_MAX;
+
+ /* After the protocol's "setup" phase is complete, let's use whatever the user configured */
+ return s->server->line_max;
+}
+
static int stdout_stream_scan(
StdoutStream *s,
char *p,
@@ -475,19 +493,22 @@ static int stdout_stream_scan(
LineBreak force_flush,
size_t *ret_consumed) {
- size_t consumed = 0;
+ size_t consumed = 0, line_max;
int r;
assert(s);
assert(p);
+ line_max = stdout_stream_line_max(s);
+
for (;;) {
LineBreak line_break;
size_t skip, found;
char *end1, *end2;
+ size_t tmp_remaining = MIN(remaining, line_max);
- end1 = memchr(p, '\n', remaining);
- end2 = memchr(p, 0, end1 ? (size_t) (end1 - p) : remaining);
+ end1 = memchr(p, '\n', tmp_remaining);
+ end2 = memchr(p, 0, end1 ? (size_t) (end1 - p) : tmp_remaining);
if (end2) {
/* We found a NUL terminator */
@@ -499,9 +520,9 @@ static int stdout_stream_scan(
found = end1 - p;
skip = found + 1;
line_break = LINE_BREAK_NEWLINE;
- } else if (remaining >= s->server->line_max) {
+ } else if (remaining >= line_max) {
/* Force a line break after the maximum line length */
- found = skip = s->server->line_max;
+ found = skip = line_max;
line_break = LINE_BREAK_LINE_MAX;
} else
break;
@@ -563,7 +584,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
/* Try to make use of the allocated buffer in full, but never read more than the configured line size. Also,
* always leave room for a terminating NUL we might need to add. */
- limit = MIN(s->allocated - 1, s->server->line_max);
+ limit = MIN(s->allocated - 1, MAX(s->server->line_max, STDOUT_STREAM_SETUP_PROTOCOL_LINE_MAX));
assert(s->length <= limit);
iovec = IOVEC_MAKE(s->buffer + s->length, limit - s->length);
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 18d6b30773..5a60a9d39c 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the journald.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# Use 'systemd-analyze cat-config systemd/journald.conf' to display the full config.
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h
index c5c851c575..e5be7c5a63 100644
--- a/src/libsystemd-network/dhcp-internal.h
+++ b/src/libsystemd-network/dhcp-internal.h
@@ -66,15 +66,13 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui
#define DHCP_CLIENT_DONT_DESTROY(client) \
_cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
-#define log_dhcp_client_errno(client, error, fmt, ...) \
- ({ \
- int _e = (error); \
- if (DEBUG_LOGGING) \
- log_interface_full_errno( \
- sd_dhcp_client_get_ifname(client), \
- LOG_DEBUG, _e, "DHCPv4 client: " fmt, \
- ##__VA_ARGS__); \
- -ERRNO_VALUE(_e); \
- })
+#define log_dhcp_client_errno(client, error, fmt, ...) \
+ log_interface_prefix_full_errno( \
+ "DHCPv4 client: ", \
+ sd_dhcp_client_get_ifname(client), \
+ error, fmt, ##__VA_ARGS__)
#define log_dhcp_client(client, fmt, ...) \
- log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__)
+ log_interface_prefix_full_errno_zerook( \
+ "DHCPv4 client: ", \
+ sd_dhcp_client_get_ifname(client), \
+ 0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index 656482bf83..85059102b1 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -186,15 +186,18 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int
return r;
}
- if (address == INADDR_ANY) {
- r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
- if (r < 0)
- return r;
-
+ if (port == DHCP_PORT_SERVER) {
r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true);
if (r < 0)
return r;
-
+ if (address == INADDR_ANY) {
+ /* IP_PKTINFO filter should not be applied when packets are
+ allowed to enter/leave through the interface other than
+ DHCP server sits on(BindToInterface option). */
+ r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
+ if (r < 0)
+ return r;
+ }
} else {
r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true);
if (r < 0)
diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c
index faa075cbd7..8899e8a552 100644
--- a/src/libsystemd-network/dhcp-option.c
+++ b/src/libsystemd-network/dhcp-option.c
@@ -17,6 +17,7 @@
static int option_append(uint8_t options[], size_t size, size_t *offset,
uint8_t code, size_t optlen, const void *optval) {
assert(options);
+ assert(size > 0);
assert(offset);
if (code != SD_DHCP_OPTION_END)
@@ -165,7 +166,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
} else if (r == -ENOBUFS && use_sname) {
/* did not fit, but we have more buffers to try
close the file array and move the offset to its end */
- r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
+ r = option_append(message->file, sizeof(message->file), &file_offset, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h
index 33e236627f..3628223ae9 100644
--- a/src/libsystemd-network/dhcp-server-internal.h
+++ b/src/libsystemd-network/dhcp-server-internal.h
@@ -39,16 +39,20 @@ typedef struct DHCPLease {
} DHCPLease;
struct sd_dhcp_server {
+ struct in_addr relay_target;
unsigned n_ref;
sd_event *event;
int event_priority;
sd_event_source *receive_message;
+ sd_event_source *receive_broadcast;
int fd;
int fd_raw;
+ int fd_broadcast;
int ifindex;
char *ifname;
+ bool bind_to_interface;
be32_t address;
be32_t netmask;
be32_t subnet;
@@ -95,15 +99,13 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
void client_id_hash_func(const DHCPClientId *p, struct siphash *state);
int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b);
-#define log_dhcp_server_errno(server, error, fmt, ...) \
- ({ \
- int _e = (error); \
- if (DEBUG_LOGGING) \
- log_interface_full_errno( \
- sd_dhcp_server_get_ifname(server), \
- LOG_DEBUG, _e, "DHCPv4 server: " fmt, \
- ##__VA_ARGS__); \
- -ERRNO_VALUE(_e); \
- })
+#define log_dhcp_server_errno(server, error, fmt, ...) \
+ log_interface_prefix_full_errno( \
+ "DHCPv4 server: ", \
+ sd_dhcp_server_get_ifname(server), \
+ error, fmt, ##__VA_ARGS__)
#define log_dhcp_server(server, fmt, ...) \
- log_dhcp_server_errno(server, 0, fmt, ##__VA_ARGS__)
+ log_interface_prefix_full_errno_zerook( \
+ "DHCPv4 server: ", \
+ sd_dhcp_server_get_ifname(server), \
+ 0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index 274b14b056..1e5a6b0e37 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -119,15 +119,13 @@ int dhcp6_message_type_from_string(const char *s) _pure_;
const char *dhcp6_message_status_to_string(int s) _const_;
int dhcp6_message_status_from_string(const char *s) _pure_;
-#define log_dhcp6_client_errno(client, error, fmt, ...) \
- ({ \
- int _e = (error); \
- if (DEBUG_LOGGING) \
- log_interface_full_errno( \
- sd_dhcp6_client_get_ifname(client), \
- LOG_DEBUG, _e, "DHCPv6 client: " fmt, \
- ##__VA_ARGS__); \
- -ERRNO_VALUE(_e); \
- })
-#define log_dhcp6_client(client, fmt, ...) \
- log_dhcp6_client_errno(client, 0, fmt, ##__VA_ARGS__)
+#define log_dhcp6_client_errno(client, error, fmt, ...) \
+ log_interface_prefix_full_errno( \
+ "DHCPv6 client: ", \
+ sd_dhcp6_client_get_ifname(client), \
+ error, fmt, ##__VA_ARGS__)
+#define log_dhcp6_client(client, fmt, ...) \
+ log_interface_prefix_full_errno_zerook( \
+ "DHCPv6 client: ", \
+ sd_dhcp6_client_get_ifname(client), \
+ 0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index ff51758e0b..6aca898d33 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -602,7 +602,7 @@ int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6I
case SD_DHCP6_OPTION_IA_PD_PREFIX:
- if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD))
+ if (ia->type != SD_DHCP6_OPTION_IA_PD)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA PD Prefix option not in IA PD option");
diff --git a/src/libsystemd-network/fuzz-dhcp6-client.c b/src/libsystemd-network/fuzz-dhcp6-client.c
index acb8d9b98c..7ebe01286d 100644
--- a/src/libsystemd-network/fuzz-dhcp6-client.c
+++ b/src/libsystemd-network/fuzz-dhcp6-client.c
@@ -23,7 +23,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
}
static void fuzz_client(const uint8_t *data, size_t size, bool is_information_request_enabled) {
- _cleanup_(sd_event_unrefp) sd_event *e;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h
index f13555d35c..cf0578c5c2 100644
--- a/src/libsystemd-network/lldp-internal.h
+++ b/src/libsystemd-network/lldp-internal.h
@@ -36,15 +36,13 @@ struct sd_lldp {
const char* lldp_event_to_string(sd_lldp_event_t e) _const_;
sd_lldp_event_t lldp_event_from_string(const char *s) _pure_;
-#define log_lldp_errno(lldp, error, fmt, ...) \
- ({ \
- int _e = (error); \
- if (DEBUG_LOGGING) \
- log_interface_full_errno( \
- sd_lldp_get_ifname(lldp), \
- LOG_DEBUG, _e, "LLDP: " fmt, \
- ##__VA_ARGS__); \
- -ERRNO_VALUE(_e); \
- })
-#define log_lldp(lldp, fmt, ...) \
- log_lldp_errno(lldp, 0, fmt, ##__VA_ARGS__)
+#define log_lldp_errno(lldp, error, fmt, ...) \
+ log_interface_prefix_full_errno( \
+ "LLDP: ", \
+ sd_lldp_get_ifname(lldp), \
+ error, fmt, ##__VA_ARGS__)
+#define log_lldp(lldp, fmt, ...) \
+ log_interface_prefix_full_errno_zerook( \
+ "LLDP: ", \
+ sd_lldp_get_ifname(lldp), \
+ 0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/ndisc-internal.h b/src/libsystemd-network/ndisc-internal.h
index 44a7e76c21..d43b575352 100644
--- a/src/libsystemd-network/ndisc-internal.h
+++ b/src/libsystemd-network/ndisc-internal.h
@@ -41,15 +41,13 @@ struct sd_ndisc {
const char* ndisc_event_to_string(sd_ndisc_event_t e) _const_;
sd_ndisc_event_t ndisc_event_from_string(const char *s) _pure_;
-#define log_ndisc_errno(ndisc, error, fmt, ...) \
- ({ \
- int _e = (error); \
- if (DEBUG_LOGGING) \
- log_interface_full_errno( \
- sd_ndisc_get_ifname(ndisc), \
- LOG_DEBUG, _e, "NDISC: " fmt, \
- ##__VA_ARGS__); \
- -ERRNO_VALUE(_e); \
- })
-#define log_ndisc(ndisc, fmt, ...) \
- log_ndisc_errno(ndisc, 0, fmt, ##__VA_ARGS__)
+#define log_ndisc_errno(ndisc, error, fmt, ...) \
+ log_interface_prefix_full_errno( \
+ "NDISC: ", \
+ sd_ndisc_get_ifname(ndisc), \
+ error, fmt, ##__VA_ARGS__)
+#define log_ndisc(ndisc, fmt, ...) \
+ log_interface_prefix_full_errno_zerook( \
+ "NDISC: ", \
+ sd_ndisc_get_ifname(ndisc), \
+ 0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index e5b853c0cd..9e7a67cbe3 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -27,5 +27,5 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
/* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */
int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
-int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
+int dhcp_lease_save(const sd_dhcp_lease *lease, const char *lease_file);
int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index fe5d74fda4..209425548b 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -125,15 +125,13 @@ struct sd_radv_route_prefix {
LIST_FIELDS(struct sd_radv_route_prefix, prefix);
};
-#define log_radv_errno(radv, error, fmt, ...) \
- ({ \
- int _e = (error); \
- if (DEBUG_LOGGING) \
- log_interface_full_errno( \
- sd_radv_get_ifname(radv), \
- LOG_DEBUG, _e, "RADV: " fmt, \
- ##__VA_ARGS__); \
- -ERRNO_VALUE(_e); \
- })
-#define log_radv(radv, fmt, ...) \
- log_radv_errno(radv, 0, fmt, ##__VA_ARGS__)
+#define log_radv_errno(radv, error, fmt, ...) \
+ log_interface_prefix_full_errno( \
+ "RADV: ", \
+ sd_radv_get_ifname(radv), \
+ error, fmt, ##__VA_ARGS__)
+#define log_radv(radv, fmt, ...) \
+ log_interface_prefix_full_errno_zerook( \
+ "RADV: ", \
+ sd_radv_get_ifname(radv), \
+ 0, fmt, ##__VA_ARGS__)
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index d516162266..11492bca97 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -2052,6 +2052,13 @@ int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
return client_initialize_time_events(client);
}
+int sd_dhcp_client_is_running(const sd_dhcp_client *client) {
+ if (!client)
+ return 0;
+
+ return !IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED);
+}
+
int sd_dhcp_client_start(sd_dhcp_client *client) {
int r;
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 6d88c88e6b..37e37ef780 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -31,7 +31,7 @@
#include "tmpfile-util.h"
#include "unaligned.h"
-int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_address(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
@@ -42,7 +42,7 @@ int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
return 0;
}
-int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_broadcast(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
@@ -53,7 +53,7 @@ int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
return 0;
}
-int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
+int sd_dhcp_lease_get_lifetime(const sd_dhcp_lease *lease, uint32_t *lifetime) {
assert_return(lease, -EINVAL);
assert_return(lifetime, -EINVAL);
@@ -64,7 +64,7 @@ int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
return 0;
}
-int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) {
+int sd_dhcp_lease_get_t1(const sd_dhcp_lease *lease, uint32_t *t1) {
assert_return(lease, -EINVAL);
assert_return(t1, -EINVAL);
@@ -75,7 +75,7 @@ int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) {
return 0;
}
-int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) {
+int sd_dhcp_lease_get_t2(const sd_dhcp_lease *lease, uint32_t *t2) {
assert_return(lease, -EINVAL);
assert_return(t2, -EINVAL);
@@ -86,7 +86,7 @@ int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) {
return 0;
}
-int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
+int sd_dhcp_lease_get_mtu(const sd_dhcp_lease *lease, uint16_t *mtu) {
assert_return(lease, -EINVAL);
assert_return(mtu, -EINVAL);
@@ -98,7 +98,7 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
}
int sd_dhcp_lease_get_servers(
- sd_dhcp_lease *lease,
+ const sd_dhcp_lease *lease,
sd_dhcp_lease_server_type_t what,
const struct in_addr **addr) {
@@ -114,26 +114,26 @@ int sd_dhcp_lease_get_servers(
return (int) lease->servers[what].size;
}
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_dns(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_DNS, addr);
}
-int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_ntp(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_NTP, addr);
}
-int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_sip(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr);
}
-int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_pop3(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr);
}
-int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_smtp(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr);
}
-int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_lpr(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr);
}
-int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
+int sd_dhcp_lease_get_domainname(const sd_dhcp_lease *lease, const char **domainname) {
assert_return(lease, -EINVAL);
assert_return(domainname, -EINVAL);
@@ -144,7 +144,7 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname)
return 0;
}
-int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
+int sd_dhcp_lease_get_hostname(const sd_dhcp_lease *lease, const char **hostname) {
assert_return(lease, -EINVAL);
assert_return(hostname, -EINVAL);
@@ -155,7 +155,7 @@ int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
return 0;
}
-int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
+int sd_dhcp_lease_get_root_path(const sd_dhcp_lease *lease, const char **root_path) {
assert_return(lease, -EINVAL);
assert_return(root_path, -EINVAL);
@@ -166,7 +166,7 @@ int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
return 0;
}
-int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_router(const sd_dhcp_lease *lease, const struct in_addr **addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
@@ -177,7 +177,7 @@ int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr)
return (int) lease->router_size;
}
-int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_netmask(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
@@ -188,7 +188,7 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
return 0;
}
-int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_server_identifier(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
@@ -199,7 +199,7 @@ int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *ad
return 0;
}
-int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_next_server(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
@@ -214,7 +214,7 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
* The returned routes array must be freed by the caller.
* Route objects have the same lifetime of the lease and must not be freed.
*/
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
+int sd_dhcp_lease_get_routes(const sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
sd_dhcp_route **ret;
unsigned i;
@@ -235,7 +235,7 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
return (int) lease->static_route_size;
}
-int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
+int sd_dhcp_lease_get_search_domains(const sd_dhcp_lease *lease, char ***domains) {
size_t r;
assert_return(lease, -EINVAL);
@@ -250,7 +250,7 @@ int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
return -ENODATA;
}
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
+int sd_dhcp_lease_get_vendor_specific(const sd_dhcp_lease *lease, const void **data, size_t *data_len) {
assert_return(lease, -EINVAL);
assert_return(data, -EINVAL);
assert_return(data_len, -EINVAL);
@@ -868,7 +868,7 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {
return 0;
}
-int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
+int dhcp_lease_save(const sd_dhcp_lease *lease, const char *lease_file) {
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
struct sd_dhcp_raw_option *option;
@@ -1313,7 +1313,7 @@ int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
return 0;
}
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
+int sd_dhcp_lease_get_client_id(const sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
assert_return(lease, -EINVAL);
assert_return(client_id, -EINVAL);
assert_return(client_id_len, -EINVAL);
@@ -1348,7 +1348,7 @@ int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t
return 0;
}
-int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
+int sd_dhcp_lease_get_timezone(const sd_dhcp_lease *lease, const char **tz) {
assert_return(lease, -EINVAL);
assert_return(tz, -EINVAL);
@@ -1359,7 +1359,7 @@ int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
return 0;
}
-int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
+int sd_dhcp_route_get_destination(const sd_dhcp_route *route, struct in_addr *destination) {
assert_return(route, -EINVAL);
assert_return(destination, -EINVAL);
@@ -1367,7 +1367,7 @@ int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destinat
return 0;
}
-int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
+int sd_dhcp_route_get_destination_prefix_length(const sd_dhcp_route *route, uint8_t *length) {
assert_return(route, -EINVAL);
assert_return(length, -EINVAL);
@@ -1375,7 +1375,7 @@ int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *l
return 0;
}
-int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
+int sd_dhcp_route_get_gateway(const sd_dhcp_route *route, struct in_addr *gateway) {
assert_return(route, -EINVAL);
assert_return(gateway, -EINVAL);
@@ -1383,7 +1383,7 @@ int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
return 0;
}
-int sd_dhcp_route_get_option(sd_dhcp_route *route) {
+int sd_dhcp_route_get_option(const sd_dhcp_route *route) {
assert_return(route, -EINVAL);
return route->option;
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index 0036cddbf9..e2ad99ebcb 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -114,6 +114,12 @@ int sd_dhcp_server_is_running(sd_dhcp_server *server) {
return !!server->receive_message;
}
+int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) {
+ assert_return(server, -EINVAL);
+
+ return in4_addr_is_set(&server->relay_target);
+}
+
void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
assert(id);
assert(id->length);
@@ -180,9 +186,11 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
.n_ref = 1,
.fd_raw = -1,
.fd = -1,
+ .fd_broadcast = -1,
.address = htobe32(INADDR_ANY),
.netmask = htobe32(INADDR_ANY),
.ifindex = ifindex,
+ .bind_to_interface = true,
.default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC),
.max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC),
};
@@ -250,11 +258,12 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) {
if (!server)
return 0;
- server->receive_message =
- sd_event_source_unref(server->receive_message);
+ server->receive_message = sd_event_source_unref(server->receive_message);
+ server->receive_broadcast = sd_event_source_unref(server->receive_broadcast);
server->fd_raw = safe_close(server->fd_raw);
server->fd = safe_close(server->fd);
+ server->fd_broadcast = safe_close(server->fd_broadcast);
log_dhcp_server(server, "STOPPED");
@@ -303,8 +312,6 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
.msg_namelen = sizeof(dest.in),
.msg_iov = &iov,
.msg_iovlen = 1,
- .msg_control = &control,
- .msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
struct in_pktinfo *pktinfo;
@@ -314,22 +321,27 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
assert(message);
assert(len > sizeof(DHCPMessage));
- cmsg = CMSG_FIRSTHDR(&msg);
- assert(cmsg);
+ if (server->bind_to_interface) {
+ msg.msg_control = &control;
+ msg.msg_controllen = sizeof(control);
- cmsg->cmsg_level = IPPROTO_IP;
- cmsg->cmsg_type = IP_PKTINFO;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ cmsg = CMSG_FIRSTHDR(&msg);
+ assert(cmsg);
- /* we attach source interface and address info to the message
- rather than binding the socket. This will be mostly useful
- when we gain support for arbitrary number of server addresses
- */
- pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
- assert(pktinfo);
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+
+ /* we attach source interface and address info to the message
+ rather than binding the socket. This will be mostly useful
+ when we gain support for arbitrary number of server addresses
+ */
+ pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
+ assert(pktinfo);
- pktinfo->ipi_ifindex = server->ifindex;
- pktinfo->ipi_spec_dst.s_addr = server->address;
+ pktinfo->ipi_ifindex = server->ifindex;
+ pktinfo->ipi_spec_dst.s_addr = server->address;
+ }
if (sendmsg(server->fd, &msg, 0) < 0)
return -errno;
@@ -337,10 +349,27 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
return 0;
}
-static bool requested_broadcast(DHCPRequest *req) {
- assert(req);
+static bool requested_broadcast(DHCPMessage *message) {
+ assert(message);
+ return message->flags & htobe16(0x8000);
+}
- return req->message->flags & htobe16(0x8000);
+static int dhcp_server_send(sd_dhcp_server *server, be32_t destination, uint16_t destination_port,
+ DHCPPacket *packet, size_t optoffset, bool l2_broadcast) {
+ if (destination != INADDR_ANY)
+ return dhcp_server_send_udp(server, destination,
+ destination_port, &packet->dhcp,
+ sizeof(DHCPMessage) + optoffset);
+ else if (l2_broadcast)
+ return dhcp_server_send_udp(server, INADDR_BROADCAST,
+ destination_port, &packet->dhcp,
+ sizeof(DHCPMessage) + optoffset);
+ else
+ /* we cannot send UDP packet to specific MAC address when the
+ address is not yet configured, so must fall back to raw
+ packets */
+ return dhcp_server_send_unicast_raw(server, packet,
+ sizeof(DHCPPacket) + optoffset);
}
int dhcp_server_send_packet(sd_dhcp_server *server,
@@ -398,20 +427,8 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
} else if (req->message->ciaddr && type != DHCP_NAK)
destination = req->message->ciaddr;
- if (destination != INADDR_ANY)
- return dhcp_server_send_udp(server, destination,
- destination_port, &packet->dhcp,
- sizeof(DHCPMessage) + optoffset);
- else if (requested_broadcast(req) || type == DHCP_NAK)
- return dhcp_server_send_udp(server, INADDR_BROADCAST,
- destination_port, &packet->dhcp,
- sizeof(DHCPMessage) + optoffset);
- else
- /* we cannot send UDP packet to specific MAC address when the
- address is not yet configured, so must fall back to raw
- packets */
- return dhcp_server_send_unicast_raw(server, packet,
- sizeof(DHCPPacket) + optoffset);
+ bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK;
+ return dhcp_server_send(server, destination, destination_port, packet, optoffset, l2_broadcast);
}
static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
@@ -695,6 +712,47 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
}
+static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length) {
+ _cleanup_free_ DHCPPacket *packet = NULL;
+
+ assert(server);
+ assert(message);
+ assert(sd_dhcp_server_is_in_relay_mode(server));
+
+ if (message->op == BOOTREQUEST) {
+ log_dhcp_server(server, "(relay agent) BOOTREQUEST (0x%x)", be32toh(message->xid));
+ if (message->hops >= 16)
+ return -ETIME;
+ message->hops++;
+
+ /* https://tools.ietf.org/html/rfc1542#section-4.1.1 */
+ if (message->giaddr == 0)
+ message->giaddr = server->address;
+
+ return dhcp_server_send_udp(server, server->relay_target.s_addr, DHCP_PORT_SERVER, message, sizeof(DHCPMessage) + opt_length);
+ } else if (message->op == BOOTREPLY) {
+ log_dhcp_server(server, "(relay agent) BOOTREPLY (0x%x)", be32toh(message->xid));
+ if (message->giaddr != server->address) {
+ return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG),
+ "(relay agent) BOOTREPLY giaddr mismatch, discarding");
+ }
+
+ int message_type = dhcp_option_parse(message, sizeof(DHCPMessage) + opt_length, NULL, NULL, NULL);
+ if (message_type < 0)
+ return message_type;
+
+ packet = malloc0(sizeof(DHCPPacket) + opt_length);
+ if (!packet)
+ return -ENOMEM;
+ memcpy(&packet->dhcp, message, sizeof(DHCPMessage) + opt_length);
+
+ bool l2_broadcast = requested_broadcast(message) || message_type == DHCP_NAK;
+ const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message->ciaddr;
+ return dhcp_server_send(server, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast);
+ }
+ return -EBADMSG;
+}
+
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
@@ -993,10 +1051,15 @@ static int server_receive_message(sd_event_source *s, int fd,
}
}
- r = dhcp_server_handle_message(server, message, (size_t) len);
- if (r < 0)
- log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
-
+ if (sd_dhcp_server_is_in_relay_mode(server)) {
+ r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage));
+ if (r < 0)
+ log_dhcp_server_errno(server, r, "Couldn't relay message: %m");
+ } else {
+ r = dhcp_server_handle_message(server, message, (size_t) len);
+ if (r < 0)
+ log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
+ }
return 0;
}
@@ -1013,36 +1076,55 @@ int sd_dhcp_server_start(sd_dhcp_server *server) {
r = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (r < 0) {
r = -errno;
- sd_dhcp_server_stop(server);
- return r;
+ goto on_error;
}
server->fd_raw = r;
- r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER, -1);
- if (r < 0) {
- sd_dhcp_server_stop(server);
- return r;
- }
+ if (server->bind_to_interface)
+ r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER, -1);
+ else
+ r = dhcp_network_bind_udp_socket(0, server->address, DHCP_PORT_SERVER, -1);
+ if (r < 0)
+ goto on_error;
server->fd = r;
r = sd_event_add_io(server->event, &server->receive_message,
server->fd, EPOLLIN,
server_receive_message, server);
- if (r < 0) {
- sd_dhcp_server_stop(server);
- return r;
- }
+ if (r < 0)
+ goto on_error;
r = sd_event_source_set_priority(server->receive_message,
server->event_priority);
- if (r < 0) {
- sd_dhcp_server_stop(server);
- return r;
+ if (r < 0)
+ goto on_error;
+
+ if (!server->bind_to_interface) {
+ r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_BROADCAST, DHCP_PORT_SERVER, -1);
+ if (r < 0)
+ goto on_error;
+
+ server->fd_broadcast = r;
+
+ r = sd_event_add_io(server->event, &server->receive_broadcast,
+ server->fd_broadcast, EPOLLIN,
+ server_receive_message, server);
+ if (r < 0)
+ goto on_error;
+
+ r = sd_event_source_set_priority(server->receive_broadcast,
+ server->event_priority);
+ if (r < 0)
+ goto on_error;
}
log_dhcp_server(server, "STARTED");
return 0;
+
+on_error:
+ sd_dhcp_server_stop(server);
+ return r;
}
int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
@@ -1069,6 +1151,18 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
return r;
}
+int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled) {
+ assert_return(server, -EINVAL);
+ assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
+
+ if (!!enabled == server->bind_to_interface)
+ return 0;
+
+ server->bind_to_interface = enabled;
+
+ return 1;
+}
+
int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
int r;
@@ -1201,3 +1295,14 @@ int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_
return 0;
}
+
+int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr* address) {
+ assert_return(server, -EINVAL);
+ assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
+
+ if (memcmp(address, &server->relay_target, sizeof(struct in_addr)) == 0)
+ return 0;
+
+ server->relay_target = *address;
+ return 1;
+}
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index c8a4c79ffe..10f82996c8 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -161,7 +161,7 @@ int sd_dhcp6_client_set_callback(
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
assert_return(client, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->ifindex = ifindex;
return 0;
@@ -191,8 +191,7 @@ int sd_dhcp6_client_set_local_address(
assert_return(client, -EINVAL);
assert_return(local_address, -EINVAL);
assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL);
-
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->local_address = *local_address;
@@ -207,8 +206,7 @@ int sd_dhcp6_client_set_mac(
assert_return(client, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
-
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
if (arp_type == ARPHRD_ETHER)
assert_return(addr_len == ETH_ALEN, -EINVAL);
@@ -238,8 +236,7 @@ int sd_dhcp6_client_set_prefix_delegation_hint(
assert_return(client, -EINVAL);
assert_return(pd_address, -EINVAL);
-
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->hint_pd_prefix.iapdprefix.address = *pd_address;
client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen;
@@ -284,7 +281,7 @@ static int dhcp6_client_set_duid_internal(
assert_return(client, -EINVAL);
assert_return(duid_len == 0 || duid != NULL, -EINVAL);
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
if (duid) {
r = dhcp_validate_duid_len(duid_type, duid_len, true);
@@ -393,7 +390,7 @@ int sd_dhcp6_client_duid_as_string(
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->ia_na.ia_na.id = htobe32(iaid);
client->ia_pd.ia_pd.id = htobe32(iaid);
@@ -430,7 +427,7 @@ int sd_dhcp6_client_set_fqdn(
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->information_request = enabled;
@@ -1705,7 +1702,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
assert_return(client->ifindex > 0, -EINVAL);
assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
- if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
+ if (client->state != DHCP6_STATE_STOPPED)
return -EBUSY;
if (!client->information_request && !client->request)
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c
index 643bdc4ba9..b4af60741a 100644
--- a/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/libsystemd-network/sd-ipv4acd.c
@@ -75,18 +75,16 @@ struct sd_ipv4acd {
void* userdata;
};
-#define log_ipv4acd_errno(acd, error, fmt, ...) \
- ({ \
- int _e = (error); \
- if (DEBUG_LOGGING) \
- log_interface_full_errno( \
- sd_ipv4acd_get_ifname(acd), \
- LOG_DEBUG, _e, "IPv4ACD: " fmt, \
- ##__VA_ARGS__); \
- -ERRNO_VALUE(_e); \
- })
+#define log_ipv4acd_errno(acd, error, fmt, ...) \
+ log_interface_prefix_full_errno( \
+ "IPv4ACD: ", \
+ sd_ipv4acd_get_ifname(acd), \
+ error, fmt, ##__VA_ARGS__)
#define log_ipv4acd(acd, fmt, ...) \
- log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__)
+ log_interface_prefix_full_errno_zerook( \
+ "IPv4ACD: ", \
+ sd_ipv4acd_get_ifname(acd), \
+ 0, fmt, ##__VA_ARGS__)
static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = {
[IPV4ACD_STATE_INIT] = "init",
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 49e9350fba..8053afee93 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -49,18 +49,16 @@ struct sd_ipv4ll {
void* userdata;
};
-#define log_ipv4ll_errno(ll, error, fmt, ...) \
- ({ \
- int _e = (error); \
- if (DEBUG_LOGGING) \
- log_interface_full_errno( \
- sd_ipv4ll_get_ifname(ll), \
- LOG_DEBUG, _e, "IPv4LL: " fmt, \
- ##__VA_ARGS__); \
- -ERRNO_VALUE(_e); \
- })
+#define log_ipv4ll_errno(ll, error, fmt, ...) \
+ log_interface_prefix_full_errno( \
+ "IPv4LL: ", \
+ sd_ipv4ll_get_ifname(ll), \
+ error, fmt, ##__VA_ARGS__)
#define log_ipv4ll(ll, fmt, ...) \
- log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__)
+ log_interface_prefix_full_errno_zerook( \
+ "IPv4LL: ", \
+ sd_ipv4ll_get_ifname(ll), \
+ 0, fmt, ##__VA_ARGS__)
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index e91b440fe9..f051420708 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -20,7 +20,7 @@ static void test_pool(struct in_addr *address, unsigned size, int ret) {
assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret);
}
-static int test_basic(sd_event *event) {
+static int test_basic(sd_event *event, bool bind_to_interface) {
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
struct in_addr address_lo = {
.s_addr = htobe32(INADDR_LOOPBACK),
@@ -33,6 +33,7 @@ static int test_basic(sd_event *event) {
/* attach to loopback interface */
assert_se(sd_dhcp_server_new(&server, 1) >= 0);
assert_se(server);
+ server->bind_to_interface = bind_to_interface;
assert_se(sd_dhcp_server_attach_event(server, event, 0) >= 0);
assert_se(sd_dhcp_server_attach_event(server, event, 0) == -EBUSY);
@@ -234,9 +235,13 @@ int main(int argc, char *argv[]) {
assert_se(sd_event_new(&e) >= 0);
- r = test_basic(e);
+ r = test_basic(e, true);
if (r != 0)
- return log_tests_skipped("cannot start dhcp server");
+ return log_tests_skipped("cannot start dhcp server(bound to interface)");
+
+ r = test_basic(e, false);
+ if (r != 0)
+ return log_tests_skipped("cannot start dhcp server(non-bound to interface)");
test_message_handler();
test_client_id_hash();
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index b025b55026..996bf25d6c 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -756,4 +756,5 @@ LIBSYSTEMD_249 {
global:
sd_device_monitor_filter_add_match_sysattr;
sd_device_monitor_filter_add_match_parent;
+ sd_device_get_usec_initialized;
} LIBSYSTEMD_248;
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c
index 7747600b83..7483b46a11 100644
--- a/src/libsystemd/sd-bus/bus-error.c
+++ b/src/libsystemd/sd-bus/bus-error.c
@@ -212,27 +212,29 @@ _public_ void sd_bus_error_free(sd_bus_error *e) {
}
_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
+ int r;
if (!name)
return 0;
- if (!e)
- goto finish;
- assert_return(!bus_error_is_dirty(e), -EINVAL);
+ if (e) {
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
- e->name = strdup(name);
- if (!e->name) {
- *e = BUS_ERROR_OOM;
- return -ENOMEM;
- }
+ e->name = strdup(name);
+ if (!e->name) {
+ *e = BUS_ERROR_OOM;
+ return -ENOMEM;
+ }
- if (message)
- e->message = strdup(message);
+ if (message)
+ e->message = strdup(message);
- e->_need_free = 1;
+ e->_need_free = 1;
+ }
-finish:
- return -bus_error_name_to_errno(name);
+ r = bus_error_name_to_errno(name);
+ assert(r > 0);
+ return -r;
}
int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index e719c74370..8bcf4f6c50 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -1800,7 +1800,9 @@ void bus_enter_closing(sd_bus *bus) {
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus, sd_bus, bus_free);
_public_ int sd_bus_is_open(sd_bus *bus) {
- assert_return(bus, -EINVAL);
+ if (!bus)
+ return 0;
+
assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -1808,7 +1810,9 @@ _public_ int sd_bus_is_open(sd_bus *bus) {
}
_public_ int sd_bus_is_ready(sd_bus *bus) {
- assert_return(bus, -EINVAL);
+ if (!bus)
+ return 0;
+
assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
@@ -2410,7 +2414,7 @@ _public_ int sd_bus_call(
return 1;
}
- return sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry.");
} else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR)
return sd_bus_error_copy(error, &incoming->error);
diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h
index c1a81e3b41..c7f9b59087 100644
--- a/src/libsystemd/sd-device/device-internal.h
+++ b/src/libsystemd/sd-device/device-internal.h
@@ -69,7 +69,7 @@ struct sd_device {
char *id_filename;
- uint64_t usec_initialized;
+ usec_t usec_initialized;
mode_t devmode;
uid_t devuid;
diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h
index 026bdcd644..e2adfe8132 100644
--- a/src/libsystemd/sd-device/device-util.h
+++ b/src/libsystemd/sd-device/device-util.h
@@ -45,22 +45,29 @@
device; \
device = sd_device_enumerator_get_subsystem_next(enumerator))
-#define log_device_full_errno(device, level, error, ...) \
+#define log_device_full_errno_zerook(device, level, error, ...) \
({ \
const char *_sysname = NULL; \
sd_device *_d = (device); \
- int _level = (level), _error = (error); \
+ int _level = (level), _e = (error); \
\
if (_d && _unlikely_(log_get_max_level() >= LOG_PRI(_level))) \
(void) sd_device_get_sysname(_d, &_sysname); \
- log_object_internal(_level, _error, PROJECT_FILE, __LINE__, __func__, \
+ log_object_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, \
_sysname ? "DEVICE=" : NULL, _sysname, \
NULL, NULL, __VA_ARGS__); \
})
-#define log_device_full(device, level, ...) (void) log_device_full_errno(device, level, 0, __VA_ARGS__)
+#define log_device_full_errno(device, level, error, ...) \
+ ({ \
+ int _error = (error); \
+ ASSERT_NON_ZERO(_error); \
+ log_device_full_errno_zerook(device, level, _error, __VA_ARGS__); \
+ })
+
+#define log_device_full(device, level, ...) (void) log_device_full_errno_zerook(device, level, 0, __VA_ARGS__)
-#define log_device_debug(device, ...) log_device_full_errno(device, LOG_DEBUG, 0, __VA_ARGS__)
+#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__)
#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__)
#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__)
#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__)
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index e556a6f838..4b7f22e207 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -178,13 +178,12 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
/* all 'devices' require an 'uevent' file */
path = strjoina(syspath, "/uevent");
- r = access(path, F_OK);
- if (r < 0) {
+ if (access(path, F_OK) < 0) {
if (errno == ENOENT)
/* this is not a valid device */
return -ENODEV;
- return log_debug_errno(errno, "sd-device: %s does not have an uevent file: %m", syspath);
+ return log_debug_errno(errno, "sd-device: cannot access uevent file for %s: %m", syspath);
}
} else {
/* everything else just needs to be a directory */
@@ -491,7 +490,6 @@ int device_read_uevent_file(sd_device *device) {
const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
char *path;
size_t uevent_len;
- unsigned i;
int r;
enum {
@@ -527,7 +525,7 @@ int device_read_uevent_file(sd_device *device) {
device->uevent_loaded = true;
- for (i = 0; i < uevent_len; i++)
+ for (size_t i = 0; i < uevent_len; i++)
switch (state) {
case PRE_KEY:
if (!strchr(NEWLINE, uevent[i])) {
@@ -706,7 +704,7 @@ static int device_new_from_child(sd_device **ret, sd_device *child) {
pos = strrchr(subdir, '/');
if (!pos || pos < subdir + 2)
- break;
+ return -ENODEV;
*pos = '\0';
@@ -716,8 +714,6 @@ static int device_new_from_child(sd_device **ret, sd_device *child) {
return 0;
}
-
- return -ENODEV;
}
_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
@@ -1321,7 +1317,7 @@ int device_get_id_filename(sd_device *device, const char **ret) {
int device_read_db_internal_filename(sd_device *device, const char *filename) {
_cleanup_free_ char *db = NULL;
const char *value;
- size_t db_len, i;
+ size_t db_len;
char key;
int r;
@@ -1349,7 +1345,7 @@ int device_read_db_internal_filename(sd_device *device, const char *filename) {
device->db_loaded = true;
- for (i = 0; i < db_len; i++) {
+ for (size_t i = 0; i < db_len; i++) {
switch (state) {
case PRE_KEY:
if (!strchr(NEWLINE, db[i])) {
@@ -1387,7 +1383,8 @@ int device_read_db_internal_filename(sd_device *device, const char *filename) {
db[i] = '\0';
r = handle_db_line(device, key, value);
if (r < 0)
- log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m", key, value);
+ log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m",
+ key, value);
state = PRE_KEY;
}
@@ -1431,6 +1428,27 @@ _public_ int sd_device_get_is_initialized(sd_device *device) {
return device->is_initialized;
}
+_public_ int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret) {
+ int r;
+
+ assert_return(device, -EINVAL);
+
+ r = device_read_db(device);
+ if (r < 0)
+ return r;
+
+ if (!device->is_initialized)
+ return -EBUSY;
+
+ if (device->usec_initialized == 0)
+ return -ENODATA;
+
+ if (ret)
+ *ret = device->usec_initialized;
+
+ return 0;
+}
+
_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) {
usec_t now_ts;
int r;
@@ -1444,10 +1462,10 @@ _public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *u
if (!device->is_initialized)
return -EBUSY;
- if (!device->usec_initialized)
+ if (device->usec_initialized == 0)
return -ENODATA;
- now_ts = now(clock_boottime_or_monotonic());
+ now_ts = now(CLOCK_MONOTONIC);
if (now_ts < device->usec_initialized)
return -EIO;
@@ -1895,10 +1913,11 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
return r;
path = prefix_roota(syspath, sysattr);
- r = lstat(path, &statbuf);
- if (r < 0) {
+ if (lstat(path, &statbuf) < 0) {
int k;
+ r = -errno;
+
/* remember that we could not access the sysattr */
k = device_cache_sysattr_value(device, sysattr, NULL);
if (k < 0)
diff --git a/src/libsystemd/sd-journal/generate-audit_type-list.sh b/src/libsystemd/sd-journal/generate-audit_type-list.sh
index d5b145f31d..aad653c128 100755
--- a/src/libsystemd/sd-journal/generate-audit_type-list.sh
+++ b/src/libsystemd/sd-journal/generate-audit_type-list.sh
@@ -1,16 +1,17 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
-cpp="$1"
+cpp="${1:?}"
shift
-includes=""
+includes=()
for i in "$@"; do
- includes="$includes -include $i"
+ includes+=(-include "$i")
done
-$cpp -dM $includes - </dev/null | \
- grep -vE 'AUDIT_.*(FIRST|LAST)_' | \
- sed -r -n 's/^#define\s+AUDIT_(\w+)\s+([0-9]{4})\s*$$/\1\t\2/p' | \
- sort -k2
+$cpp -dM "${includes[@]}" - </dev/null | \
+ grep -vE 'AUDIT_.*(FIRST|LAST)_' | \
+ sed -r -n 's/^#define\s+AUDIT_(\w+)\s+([0-9]{4})\s*$$/\1\t\2/p' | \
+ sort -k2
diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c
index f8bb708e58..3cf26f8e44 100644
--- a/src/libsystemd/sd-journal/journal-file.c
+++ b/src/libsystemd/sd-journal/journal-file.c
@@ -707,9 +707,9 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
/* Note that the glibc fallocate() fallback is very
inefficient, hence we try to minimize the allocation area
as we can. */
- r = posix_fallocate(f->fd, old_size, new_size - old_size);
- if (r != 0)
- return -r;
+ r = posix_fallocate_loop(f->fd, old_size, new_size - old_size);
+ if (r < 0)
+ return r;
f->header->arena_size = htole64(new_size - old_header_size);
diff --git a/src/libsystemd/sd-journal/journal-vacuum.c b/src/libsystemd/sd-journal/journal-vacuum.c
index c173664146..0f1c9eb8f7 100644
--- a/src/libsystemd/sd-journal/journal-vacuum.c
+++ b/src/libsystemd/sd-journal/journal-vacuum.c
@@ -88,7 +88,7 @@ static void patch_realtime(
}
static int journal_file_empty(int dir_fd, const char *name) {
- _cleanup_close_ int fd;
+ _cleanup_close_ int fd = -1;
struct stat st;
le64_t n_entries;
ssize_t n;
diff --git a/src/libsystemd/sd-journal/sd-journal.c b/src/libsystemd/sd-journal/sd-journal.c
index be92f803c9..c90b4c926e 100644
--- a/src/libsystemd/sd-journal/sd-journal.c
+++ b/src/libsystemd/sd-journal/sd-journal.c
@@ -1860,7 +1860,7 @@ static int add_current_paths(sd_journal *j) {
* treat them as fatal. */
ORDERED_HASHMAP_FOREACH(f, j->files) {
- _cleanup_free_ char *dir;
+ _cleanup_free_ char *dir = NULL;
int r;
dir = dirname_malloc(f->path);
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index a3da2e3f24..b4e010e74c 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -775,7 +775,7 @@ _public_ int sd_get_sessions(char ***sessions) {
}
_public_ int sd_get_uids(uid_t **users) {
- _cleanup_closedir_ DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
unsigned n = 0;
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index ed7b9a8cd1..5ae15d67bc 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -683,13 +683,13 @@ static const NLType rtnl_address_types[] = {
[IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR },
[IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
[IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
- [IFA_BROADCAST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
+ [IFA_BROADCAST] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFA_ANYCAST] = { .type = NETLINK_TYPE_IN_ADDR },
[IFA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) },
-/*
- [IFA_ANYCAST],
- [IFA_MULTICAST],
-*/
+ [IFA_MULTICAST] = { .type = NETLINK_TYPE_IN_ADDR },
[IFA_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [IFA_RT_PRIORITY] = { .type = NETLINK_TYPE_U32 },
+ [IFA_TARGET_NETNSID] = { .type = NETLINK_TYPE_S32 },
};
static const NLTypeSystem rtnl_address_type_system = {
diff --git a/src/libsystemd/sd-network/network-util.c b/src/libsystemd/sd-network/network-util.c
index acf7500970..c1d3a9f6a2 100644
--- a/src/libsystemd/sd-network/network-util.c
+++ b/src/libsystemd/sd-network/network-util.c
@@ -56,6 +56,15 @@ static const char* const link_carrier_state_table[_LINK_CARRIER_STATE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(link_carrier_state, LinkCarrierState);
+static const char* const link_required_address_family_table[_ADDRESS_FAMILY_MAX] = {
+ [ADDRESS_FAMILY_NO] = "any",
+ [ADDRESS_FAMILY_IPV4] = "ipv4",
+ [ADDRESS_FAMILY_IPV6] = "ipv6",
+ [ADDRESS_FAMILY_YES] = "both",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(link_required_address_family, AddressFamily);
+
static const char* const link_address_state_table[_LINK_ADDRESS_STATE_MAX] = {
[LINK_ADDRESS_STATE_OFF] = "off",
[LINK_ADDRESS_STATE_DEGRADED] = "degraded",
diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h
index dd6a7dbe88..3bd10c42de 100644
--- a/src/libsystemd/sd-network/network-util.h
+++ b/src/libsystemd/sd-network/network-util.h
@@ -11,6 +11,16 @@
bool network_is_online(void);
+typedef enum AddressFamily {
+ /* This is a bitmask, though it usually doesn't feel that way! */
+ ADDRESS_FAMILY_NO = 0,
+ ADDRESS_FAMILY_IPV4 = 1 << 0,
+ ADDRESS_FAMILY_IPV6 = 1 << 1,
+ ADDRESS_FAMILY_YES = ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6,
+ _ADDRESS_FAMILY_MAX,
+ _ADDRESS_FAMILY_INVALID = -EINVAL,
+} AddressFamily;
+
typedef enum LinkOperationalState {
LINK_OPERSTATE_MISSING,
LINK_OPERSTATE_OFF,
@@ -50,6 +60,9 @@ LinkOperationalState link_operstate_from_string(const char *s) _pure_;
const char* link_carrier_state_to_string(LinkCarrierState s) _const_;
LinkCarrierState link_carrier_state_from_string(const char *s) _pure_;
+const char* link_required_address_family_to_string(AddressFamily s) _const_;
+AddressFamily link_required_address_family_from_string(const char *s) _pure_;
+
const char* link_address_state_to_string(LinkAddressState s) _const_;
LinkAddressState link_address_state_from_string(const char *s) _pure_;
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index ccdfde9137..b190e8f881 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -48,6 +48,14 @@ _public_ int sd_network_get_address_state(char **state) {
return network_get_string("ADDRESS_STATE", state);
}
+_public_ int sd_network_get_ipv4_address_state(char **state) {
+ return network_get_string("IPV4_ADDRESS_STATE", state);
+}
+
+_public_ int sd_network_get_ipv6_address_state(char **state) {
+ return network_get_string("IPV6_ADDRESS_STATE", state);
+}
+
static int network_get_strv(const char *key, char ***ret) {
_cleanup_strv_free_ char **a = NULL;
_cleanup_free_ char *s = NULL;
@@ -160,6 +168,26 @@ _public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "OPER_STATE", state);
}
+_public_ int sd_network_link_get_required_family_for_online(int ifindex, char **state) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ assert_return(state, -EINVAL);
+
+ r = network_link_get_string(ifindex, "REQUIRED_FAMILY_FOR_ONLINE", &s);
+ if (r < 0) {
+ if (r != -ENODATA)
+ return r;
+
+ s = strdup("any");
+ if (!s)
+ return -ENOMEM;
+ }
+
+ *state = TAKE_PTR(s);
+ return 0;
+}
+
_public_ int sd_network_link_get_carrier_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "CARRIER_STATE", state);
}
@@ -168,6 +196,14 @@ _public_ int sd_network_link_get_address_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "ADDRESS_STATE", state);
}
+_public_ int sd_network_link_get_ipv4_address_state(int ifindex, char **state) {
+ return network_link_get_string(ifindex, "IPV4_ADDRESS_STATE", state);
+}
+
+_public_ int sd_network_link_get_ipv6_address_state(int ifindex, char **state) {
+ return network_link_get_string(ifindex, "IPV6_ADDRESS_STATE", state);
+}
+
_public_ int sd_network_link_get_dhcp6_client_iaid_string(int ifindex, char **iaid) {
return network_link_get_string(ifindex, "DHCP6_CLIENT_IAID", iaid);
}
diff --git a/src/libudev/test-libudev.c b/src/libudev/test-libudev.c
index 12bd0d6299..a751056795 100644
--- a/src/libudev/test-libudev.c
+++ b/src/libudev/test-libudev.c
@@ -6,7 +6,6 @@
#include <unistd.h>
#include "alloc-util.h"
-#include "build.h"
#include "fd-util.h"
#include "libudev-list-internal.h"
#include "libudev-util.h"
@@ -15,6 +14,7 @@
#include "stdio-util.h"
#include "string-util.h"
#include "tests.h"
+#include "version.h"
static bool arg_monitor = false;
diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c
index e8de1b789a..d2f0566dbc 100644
--- a/src/locale/keymap-util.c
+++ b/src/locale/keymap-util.c
@@ -535,7 +535,7 @@ int vconsole_convert_to_x11(Context *c) {
int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap) {
const char *dir;
- _cleanup_free_ char *n;
+ _cleanup_free_ char *n = NULL;
if (x11_variant)
n = strjoin(x11_layout, "-", x11_variant);
diff --git a/src/locale/localed.c b/src/locale/localed.c
index f44c0bab2a..df0eb030d4 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -399,7 +399,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
r = locale_read_data(c, m);
if (r < 0) {
log_error_errno(r, "Failed to read locale data: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to read locale data");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read locale data");
}
/* Merge with the current settings */
@@ -457,7 +457,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
(void) locale_update_system_manager(c, sd_bus_message_get_bus(m));
if (settings) {
- _cleanup_free_ char *line;
+ _cleanup_free_ char *line = NULL;
line = strv_join(settings, ", ");
log_info("Changed locale to %s.", strnull(line));
@@ -673,7 +673,7 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
r = x11_read_data(c, m);
if (r < 0) {
log_error_errno(r, "Failed to read x11 keyboard layout data: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to read x11 keyboard layout data");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read x11 keyboard layout data");
}
if (streq_ptr(layout, c->x11_layout) &&
@@ -686,7 +686,7 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
(model && !string_is_safe(model)) ||
(variant && !string_is_safe(variant)) ||
(options && !string_is_safe(options)))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keyboard data");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keyboard data");
r = verify_xkb_rmlvo(model, layout, variant, options);
if (r < 0) {
@@ -694,7 +694,7 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
strempty(model), strempty(layout), strempty(variant), strempty(options));
if (r == -EOPNOTSUPP)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system.");
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid.");
}
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index 2ecf2120fd..cd3a374201 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -485,7 +485,7 @@ int config_parse_n_autovts(
static int vt_is_busy(unsigned vtnr) {
struct vt_stat vt_stat;
int r;
- _cleanup_close_ int fd;
+ _cleanup_close_ int fd = -1;
assert(vtnr >= 1);
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 526bf21d97..feeacc2d99 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -695,9 +695,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
return r;
if (!uid_is_valid(uid))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
if (leader < 0 || leader == 1 || leader == getpid_cached())
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
if (isempty(type))
t = _SESSION_TYPE_INVALID;
@@ -831,7 +831,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
vtnr < m->seat0->position_count &&
m->seat0->positions[vtnr] &&
m->seat0->positions[vtnr]->class != SESSION_GREETER)
- return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session");
+ return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session");
if (hashmap_size(m->sessions) >= m->sessions_max)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED,
@@ -1371,7 +1371,7 @@ static int attach_device(Manager *m, const char *seat, const char *sysfs) {
}
static int flush_devices(Manager *m) {
- _cleanup_closedir_ DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
assert(m);
@@ -1879,9 +1879,9 @@ static int method_do_shutdown_or_sleep(
if (r < 0)
return r;
if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
if (!streq(unit_name, SPECIAL_REBOOT_TARGET) && (flags & SD_LOGIND_REBOOT_VIA_KEXEC))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec is only applicable with reboot operations");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec is only applicable with reboot operations");
} else {
/* Old style method: no flags parameter, but interactive bool passed as boolean in
* payload. Let's convert this argument to the new-style flags parameter for our internal
@@ -2073,7 +2073,7 @@ static int update_schedule_file(Manager *m) {
m->scheduled_shutdown_type);
if (!isempty(m->wall_message)) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
t = cescape(m->wall_message);
if (!t) {
@@ -2140,6 +2140,8 @@ static int manager_scheduled_shutdown_handler(
target = SPECIAL_POWEROFF_TARGET;
else if (streq(m->scheduled_shutdown_type, "reboot"))
target = SPECIAL_REBOOT_TARGET;
+ else if (streq(m->scheduled_shutdown_type, "kexec"))
+ target = SPECIAL_KEXEC_TARGET;
else if (streq(m->scheduled_shutdown_type, "halt"))
target = SPECIAL_HALT_TARGET;
else
@@ -2205,7 +2207,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
action = "org.freedesktop.login1.power-off";
action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions";
action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit";
- } else if (streq(type, "reboot")) {
+ } else if (STR_IN_SET(type, "reboot", "kexec")) {
action = "org.freedesktop.login1.reboot";
action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions";
action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit";
@@ -2214,7 +2216,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions";
action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit";
} else
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, action, action_multiple_sessions,
action_ignore_inhibit, 0, error);
@@ -2672,7 +2674,7 @@ static int method_set_reboot_to_firmware_setup(
r = efi_reboot_to_firmware_supported();
if (r == -EOPNOTSUPP)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
if (r < 0)
return r;
@@ -2684,7 +2686,7 @@ static int method_set_reboot_to_firmware_setup(
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
} else
/* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to on */
use_efi = false;
@@ -2840,7 +2842,7 @@ static int method_set_reboot_to_boot_loader_menu(
if (r < 0)
log_warning_errno(r, "Failed to determine whether reboot to boot loader menu is supported: %m");
if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
use_efi = true;
@@ -2850,7 +2852,7 @@ static int method_set_reboot_to_boot_loader_menu(
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
} else
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to on */
use_efi = false;
@@ -3042,7 +3044,7 @@ static int method_set_reboot_to_boot_loader_entry(
if (r < 0)
log_warning_errno(r, "Failed to determine whether reboot into boot loader entry is supported: %m");
if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_ENTRY_ONESHOT))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
use_efi = true;
@@ -3052,7 +3054,7 @@ static int method_set_reboot_to_boot_loader_entry(
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
} else
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to on */
use_efi = false;
diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c
index 9c2625cdcc..cceb3b1d2d 100644
--- a/src/login/logind-seat-dbus.c
+++ b/src/login/logind-seat-dbus.c
@@ -206,7 +206,7 @@ static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_erro
return r;
if (to <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid virtual terminal");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid virtual terminal");
r = check_polkit_chvt(message, s->manager, error);
if (r < 0)
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index d342dc4193..afaef1123d 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -256,11 +256,11 @@ static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_
return r;
if (uid != 0 && uid != s->user->user_record->uid)
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint");
r = session_set_idle_hint(s, b);
if (r == -ENOTTY)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical sessions.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical sessions.");
if (r < 0)
return r;
@@ -289,7 +289,7 @@ static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bu
return r;
if (uid != 0 && uid != s->user->user_record->uid)
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint");
session_set_locked_hint(s, b);
@@ -364,7 +364,7 @@ static int method_take_control(sd_bus_message *message, void *userdata, sd_bus_e
return r;
if (uid != 0 && (force || uid != s->user->user_record->uid))
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
r = session_set_controller(s, sd_bus_message_get_sender(message), force, true);
if (r < 0)
@@ -380,7 +380,7 @@ static int method_release_control(sd_bus_message *message, void *userdata, sd_bu
assert(s);
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
session_drop_controller(s);
@@ -406,7 +406,7 @@ static int method_set_type(sd_bus_message *message, void *userdata, sd_bus_error
"Invalid session type '%s'", t);
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You must be in control of this session to set type");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You must be in control of this session to set type");
session_set_type(s, type);
@@ -428,10 +428,10 @@ static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_er
return r;
if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
dev = makedev(major, minor);
sd = hashmap_get(s->devices, &dev);
@@ -441,7 +441,7 @@ static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_er
* The caller should use dup() if it requires more
* than one fd (it would be functionally
* equivalent). */
- return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken");
+ return sd_bus_error_set(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken");
r = session_device_new(s, dev, true, &sd);
if (r < 0)
@@ -478,15 +478,15 @@ static int method_release_device(sd_bus_message *message, void *userdata, sd_bus
return r;
if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
dev = makedev(major, minor);
sd = hashmap_get(s->devices, &dev);
if (!sd)
- return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
+ return sd_bus_error_set(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
session_device_free(sd);
session_save(s);
@@ -509,15 +509,15 @@ static int method_pause_device_complete(sd_bus_message *message, void *userdata,
return r;
if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
dev = makedev(major, minor);
sd = hashmap_get(s->devices, &dev);
if (!sd)
- return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
+ return sd_bus_error_set(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
session_device_complete_pause(sd);
@@ -546,9 +546,9 @@ static int method_set_brightness(sd_bus_message *message, void *userdata, sd_bus
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not a valid device name %s, refusing.", name);
if (!s->seat)
- return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Your session has no seat, refusing.");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_YOUR_DEVICE, "Your session has no seat, refusing.");
if (s->seat->active != s)
- return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Session is not in foreground, refusing.");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_YOUR_DEVICE, "Session is not in foreground, refusing.");
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
@@ -559,7 +559,7 @@ static int method_set_brightness(sd_bus_message *message, void *userdata, sd_bus
return r;
if (uid != 0 && uid != s->user->user_record->uid)
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change brightness.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change brightness.");
r = sd_device_new_from_subsystem_sysname(&d, subsystem, name);
if (r < 0)
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 34fcde92aa..6a3dd860db 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -266,7 +266,7 @@ int session_save(Session *s) {
fprintf(f, "DISPLAY=%s\n", s->display);
if (s->remote_host) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->remote_host);
if (!escaped) {
@@ -278,7 +278,7 @@ int session_save(Session *s) {
}
if (s->remote_user) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->remote_user);
if (!escaped) {
@@ -290,7 +290,7 @@ int session_save(Session *s) {
}
if (s->service) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->service);
if (!escaped) {
@@ -302,7 +302,7 @@ int session_save(Session *s) {
}
if (s->desktop) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->desktop);
if (!escaped) {
diff --git a/src/login/logind.c b/src/login/logind.c
index 4887889fac..b782938a5c 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -268,8 +268,8 @@ static int manager_enumerate_seats(Manager *m) {
s = hashmap_get(m->seats, de->d_name);
if (!s) {
if (unlinkat(dirfd(d), de->d_name, 0) < 0)
- log_warning("Failed to remove /run/systemd/seats/%s: %m",
- de->d_name);
+ log_warning_errno(errno, "Failed to remove /run/systemd/seats/%s, ignoring: %m",
+ de->d_name);
continue;
}
diff --git a/src/login/logind.conf.in b/src/login/logind.conf.in
index 76f529c176..564868e7ae 100644
--- a/src/login/logind.conf.in
+++ b/src/login/logind.conf.in
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the logind.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# Use 'systemd-analyze cat-config systemd/logind.conf' to display the full config.
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 309de0b116..e6ffb52924 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -41,7 +41,7 @@ int bus_image_method_remove(
assert(image);
if (m->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = bus_verify_polkit_async(
message,
@@ -146,7 +146,7 @@ int bus_image_method_clone(
assert(m);
if (m->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = sd_bus_message_read(message, "sb", &new_name, &read_only);
if (r < 0)
@@ -252,7 +252,7 @@ int bus_image_method_set_limit(
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_verify_polkit_async(
message,
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index e7c4ed3c7c..0022a980c5 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -331,12 +331,12 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
break;
}
default:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
}
r = sd_bus_message_close_container(reply);
@@ -415,15 +415,15 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r == EXIT_NOT_FOUND)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
if (r != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
break;
}
default:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
}
return bus_reply_pair_array(message, l);
@@ -628,7 +628,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
if (!strv_env_is_valid(env))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
const char *details[] = {
"machine", m->name,
@@ -820,19 +820,19 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
assert(m);
if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines.");
r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory);
if (r < 0)
return r;
if (!path_is_absolute(src) || !path_is_normalized(src))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
if (isempty(dest))
dest = src;
else if (!path_is_absolute(dest) || !path_is_normalized(dest))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
r = bus_verify_polkit_async(
message,
@@ -852,7 +852,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
if (uid != 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
propagate_directory = strjoina("/run/systemd/nspawn/propagate/", m->name);
r = bind_mount_in_namespace(m->leader,
@@ -881,22 +881,22 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
assert(m);
if (m->manager->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines.");
r = sd_bus_message_read(message, "ss", &src, &dest);
if (r < 0)
return r;
if (!path_is_absolute(src))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute.");
if (isempty(dest))
dest = src;
else if (!path_is_absolute(dest))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
r = bus_verify_polkit_async(
message,
@@ -1074,7 +1074,7 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
fd = receive_one_fd(pair[0], MSG_DONTWAIT);
if (fd < 0)
@@ -1084,7 +1084,7 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
}
default:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines.");
}
return sd_bus_reply_method_return(message, "h", fd);
@@ -1105,7 +1105,7 @@ int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd
return sd_bus_reply_method_return(message, "u", UINT32_C(0));
if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines.");
r = machine_get_uid_shift(m, &shift);
if (r == -ENXIO)
diff --git a/src/machine/machine.c b/src/machine/machine.c
index 537b0cd779..6215b29c27 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -134,7 +134,7 @@ int machine_save(Machine *m) {
m->name);
if (m->unit) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(m->unit);
if (!escaped) {
@@ -149,7 +149,7 @@ int machine_save(Machine *m) {
fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
if (m->service) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(m->service);
if (!escaped) {
@@ -160,7 +160,7 @@ int machine_save(Machine *m) {
}
if (m->root_directory) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(m->root_directory);
if (!escaped) {
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 0a6ae96bc6..342b18a8df 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -241,7 +241,7 @@ static int method_create_or_register_machine(Manager *manager, sd_bus_message *m
if (r < 0)
return r;
if (!hostname_is_valid(name, 0))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
r = sd_bus_message_read_array(message, 'y', &v, &n);
if (r < 0)
@@ -251,7 +251,7 @@ static int method_create_or_register_machine(Manager *manager, sd_bus_message *m
else if (n == 16)
memcpy(&id, v, n);
else
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory);
if (r < 0)
@@ -275,14 +275,14 @@ static int method_create_or_register_machine(Manager *manager, sd_bus_message *m
else {
c = machine_class_from_string(class);
if (c < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
}
if (leader == 1)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
if (!isempty(root_directory) && !path_is_absolute(root_directory))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
if (leader == 0) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
@@ -701,7 +701,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
assert(message);
if (m->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = sd_bus_message_read(message, "s", &mm);
if (r < 0)
@@ -842,7 +842,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_verify_polkit_async(
message,
@@ -867,7 +867,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
r = btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, limit);
if (r == -ENOTTY)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
@@ -898,7 +898,7 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata,
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
if (machine->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
r = machine_translate_uid(machine, uid, &converted);
if (r == -ESRCH)
@@ -957,7 +957,7 @@ static int method_map_from_machine_group(sd_bus_message *message, void *userdata
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
if (machine->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
r = machine_translate_gid(machine, gid, &converted);
if (r == -ESRCH)
diff --git a/src/machine/operation.c b/src/machine/operation.c
index 34565e3e69..42432f1f7f 100644
--- a/src/machine/operation.c
+++ b/src/machine/operation.c
@@ -22,14 +22,14 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
o->pid = 0;
if (si->si_code != CLD_EXITED) {
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ r = sd_bus_error_set(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
goto fail;
}
if (si->si_status == EXIT_SUCCESS)
r = 0;
else if (read(o->errno_fd, &r, sizeof(r)) != sizeof(r)) { /* Try to acquire error code for failed operation */
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");
+ r = sd_bus_error_set(&error, SD_BUS_ERROR_FAILED, "Child failed.");
goto fail;
}
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index b31f0fa81a..81d9260c12 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -747,8 +747,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
r = netdev_get_mac(netdev->ifname, &netdev->mac);
if (r < 0)
return log_netdev_error_errno(netdev, r,
- "Failed to generate predictable MAC address for %s: %m",
- netdev->ifname);
+ "Failed to generate predictable MAC address: %m");
}
r = hashmap_ensure_put(&netdev->manager->netdevs, &string_hash_ops, netdev->ifname, netdev);
@@ -760,8 +759,8 @@ int netdev_load_one(Manager *manager, const char *filename) {
assert(n);
if (!streq(netdev->filename, n->filename))
log_netdev_warning_errno(netdev, r,
- "The setting Name=%s in %s conflicts with the one in %s, ignoring",
- netdev->ifname, netdev->filename, n->filename);
+ "Device was already configured by file %s, ignoring %s.",
+ n->filename, netdev->filename);
/* Clear ifname before netdev_free() is called. Otherwise, the NetDev object 'n' is
* removed from the hashmap 'manager->netdevs'. */
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index 6d149da2e9..08070abd0a 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -5,6 +5,7 @@
#include "conf-parser.h"
#include "list.h"
+#include "log-link.h"
#include "networkd-link.h"
#include "time-util.h"
@@ -223,24 +224,32 @@ const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, GPERF_
/* Macros which append INTERFACE= to the message */
-#define log_netdev_full(netdev, level, error, ...) \
+#define log_netdev_full_errno_zerook(netdev, level, error, ...) \
({ \
const NetDev *_n = (netdev); \
- _n ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "INTERFACE=", _n->ifname, NULL, NULL, ##__VA_ARGS__) : \
- log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
+ log_interface_full_errno_zerook(_n ? _n->ifname : NULL, level, error, __VA_ARGS__); \
})
-#define log_netdev_debug(netdev, ...) log_netdev_full(netdev, LOG_DEBUG, 0, ##__VA_ARGS__)
-#define log_netdev_info(netdev, ...) log_netdev_full(netdev, LOG_INFO, 0, ##__VA_ARGS__)
-#define log_netdev_notice(netdev, ...) log_netdev_full(netdev, LOG_NOTICE, 0, ##__VA_ARGS__)
-#define log_netdev_warning(netdev, ...) log_netdev_full(netdev, LOG_WARNING, 0, ## __VA_ARGS__)
-#define log_netdev_error(netdev, ...) log_netdev_full(netdev, LOG_ERR, 0, ##__VA_ARGS__)
-
-#define log_netdev_debug_errno(netdev, error, ...) log_netdev_full(netdev, LOG_DEBUG, error, ##__VA_ARGS__)
-#define log_netdev_info_errno(netdev, error, ...) log_netdev_full(netdev, LOG_INFO, error, ##__VA_ARGS__)
-#define log_netdev_notice_errno(netdev, error, ...) log_netdev_full(netdev, LOG_NOTICE, error, ##__VA_ARGS__)
-#define log_netdev_warning_errno(netdev, error, ...) log_netdev_full(netdev, LOG_WARNING, error, ##__VA_ARGS__)
-#define log_netdev_error_errno(netdev, error, ...) log_netdev_full(netdev, LOG_ERR, error, ##__VA_ARGS__)
+#define log_netdev_full_errno(netdev, level, error, ...) \
+ ({ \
+ int _error = (error); \
+ ASSERT_NON_ZERO(_error); \
+ log_netdev_full_errno_zerook(netdev, level, _error, __VA_ARGS__); \
+ })
+
+#define log_netdev_full(netdev, level, ...) (void) log_netdev_full_errno_zerook(netdev, level, 0, __VA_ARGS__)
+
+#define log_netdev_debug(netdev, ...) log_netdev_full(netdev, LOG_DEBUG, __VA_ARGS__)
+#define log_netdev_info(netdev, ...) log_netdev_full(netdev, LOG_INFO, __VA_ARGS__)
+#define log_netdev_notice(netdev, ...) log_netdev_full(netdev, LOG_NOTICE, __VA_ARGS__)
+#define log_netdev_warning(netdev, ...) log_netdev_full(netdev, LOG_WARNING, __VA_ARGS__)
+#define log_netdev_error(netdev, ...) log_netdev_full(netdev, LOG_ERR, __VA_ARGS__)
+
+#define log_netdev_debug_errno(netdev, error, ...) log_netdev_full_errno(netdev, LOG_DEBUG, error, __VA_ARGS__)
+#define log_netdev_info_errno(netdev, error, ...) log_netdev_full_errno(netdev, LOG_INFO, error, __VA_ARGS__)
+#define log_netdev_notice_errno(netdev, error, ...) log_netdev_full_errno(netdev, LOG_NOTICE, error, __VA_ARGS__)
+#define log_netdev_warning_errno(netdev, error, ...) log_netdev_full_errno(netdev, LOG_WARNING, error, __VA_ARGS__)
+#define log_netdev_error_errno(netdev, error, ...) log_netdev_full_errno(netdev, LOG_ERR, error, __VA_ARGS__)
#define LOG_NETDEV_MESSAGE(netdev, fmt, ...) "MESSAGE=%s: " fmt, (netdev)->ifname, ##__VA_ARGS__
#define LOG_NETDEV_INTERFACE(netdev) "INTERFACE=%s", (netdev)->ifname
diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c
index d9d654495e..0e13c4f88a 100644
--- a/src/network/netdev/tuntap.c
+++ b/src/network/netdev/tuntap.c
@@ -46,7 +46,7 @@ static int netdev_fill_tuntap_message(NetDev *netdev, struct ifreq *ifr) {
}
static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) {
- _cleanup_close_ int fd;
+ _cleanup_close_ int fd = -1;
TunTap *t = NULL;
const char *user;
const char *group;
diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c
index 75d6d376a5..b8fc492d4f 100644
--- a/src/network/netdev/wireguard.c
+++ b/src/network/netdev/wireguard.c
@@ -914,14 +914,13 @@ static int wireguard_verify(NetDev *netdev, const char *filename) {
r = wireguard_read_key_file(w->private_key_file, w->private_key);
if (r < 0)
return log_netdev_error_errno(netdev, r,
- "Failed to read private key from %s. Dropping network device %s.",
- w->private_key_file, netdev->ifname);
+ "Failed to read private key from %s. Ignoring network device.",
+ w->private_key_file);
if (eqzero(w->private_key))
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"%s: Missing PrivateKey= or PrivateKeyFile=, "
- "Dropping network device %s.",
- filename, netdev->ifname);
+ "Ignoring network device.", filename);
LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
if (wireguard_peer_verify(peer) < 0)
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index faaa6a7b6c..52abcddd64 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -77,35 +77,47 @@ static bool arg_full = false;
static unsigned arg_lines = 10;
static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
- assert(on);
- assert(off);
-
if (STRPTR_IN_SET(state, "routable", "enslaved") ||
(streq_ptr(name, "lo") && streq_ptr(state, "carrier"))) {
- *on = ansi_highlight_green();
- *off = ansi_normal();
+ if (on)
+ *on = ansi_highlight_green();
+ if (off)
+ *off = ansi_normal();
} else if (streq_ptr(state, "degraded")) {
- *on = ansi_highlight_yellow();
- *off = ansi_normal();
- } else
- *on = *off = "";
+ if (on)
+ *on = ansi_highlight_yellow();
+ if (off)
+ *off = ansi_normal();
+ } else {
+ if (on)
+ *on = "";
+ if (off)
+ *off = "";
+ }
}
static void setup_state_to_color(const char *state, const char **on, const char **off) {
- assert(on);
- assert(off);
-
if (streq_ptr(state, "configured")) {
- *on = ansi_highlight_green();
- *off = ansi_normal();
+ if (on)
+ *on = ansi_highlight_green();
+ if (off)
+ *off = ansi_normal();
} else if (streq_ptr(state, "configuring")) {
- *on = ansi_highlight_yellow();
- *off = ansi_normal();
+ if (on)
+ *on = ansi_highlight_yellow();
+ if (off)
+ *off = ansi_normal();
} else if (STRPTR_IN_SET(state, "failed", "linger")) {
- *on = ansi_highlight_red();
- *off = ansi_normal();
- } else
- *on = *off = "";
+ if (on)
+ *on = ansi_highlight_red();
+ if (off)
+ *off = ansi_normal();
+ } else {
+ if (on)
+ *on = "";
+ if (off)
+ *off = "";
+ }
}
typedef struct VxLanInfo {
@@ -692,17 +704,16 @@ static int list_links(int argc, char *argv[], void *userdata) {
for (int i = 0; i < c; i++) {
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
- const char *on_color_operational, *off_color_operational,
- *on_color_setup, *off_color_setup;
+ const char *on_color_operational, *on_color_setup;
_cleanup_free_ char *t = NULL;
(void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
- operational_state_to_color(links[i].name, operational_state, &on_color_operational, &off_color_operational);
+ operational_state_to_color(links[i].name, operational_state, &on_color_operational, NULL);
r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
setup_state = strdup("unmanaged");
- setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
+ setup_state_to_color(setup_state, &on_color_setup, NULL);
t = link_get_type_string(links[i].sd_device, links[i].iftype);
@@ -2145,7 +2156,7 @@ static int link_status_one(
static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
_cleanup_free_ char *operational_state = NULL;
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
- const char *on_color_operational, *off_color_operational;
+ const char *on_color_operational;
_cleanup_(table_unrefp) Table *table = NULL;
TableCell *cell;
int r;
@@ -2153,7 +2164,7 @@ static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
assert(rtnl);
(void) sd_network_get_operational_state(&operational_state);
- operational_state_to_color(NULL, operational_state, &on_color_operational, &off_color_operational);
+ operational_state_to_color(NULL, operational_state, &on_color_operational, NULL);
table = table_new("dot", "key", "value");
if (!table)
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 58b089901b..e589fff150 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -918,6 +918,10 @@ int address_configure(
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFA_CACHEINFO attribute: %m");
+ r = sd_netlink_message_append_u32(req, IFA_RT_PRIORITY, address->route_metric);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFA_RT_PRIORITY attribute: %m");
+
k = address_add(link, address, &a);
if (k < 0)
return log_link_error_errno(link, k, "Could not add address: %m");
@@ -1091,6 +1095,7 @@ int link_set_addresses(Link *link) {
return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m");
address->family = AF_INET6;
+ address->route_metric = p->route_metric;
r = static_address_configure(address, link);
if (r < 0)
return r;
@@ -1801,6 +1806,48 @@ int config_parse_address_scope(
return 0;
}
+int config_parse_address_route_metric(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = address_new_static(network, filename, section_line, &n);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to allocate new address, ignoring assignment: %m");
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &n->route_metric);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Could not parse %s=, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(n);
+ return 0;
+}
+
int config_parse_duplicate_address_detection(
const char *unit,
const char *filename,
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index e3ca868c2e..58f44cd4cd 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -28,6 +28,7 @@ typedef struct Address {
unsigned char prefixlen;
unsigned char scope;
uint32_t flags;
+ uint32_t route_metric; /* route metric for prefix route */
char *label;
int set_broadcast;
@@ -83,6 +84,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_label);
CONFIG_PARSER_PROTOTYPE(config_parse_lifetime);
CONFIG_PARSER_PROTOTYPE(config_parse_address_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_address_scope);
+CONFIG_PARSER_PROTOTYPE(config_parse_address_route_metric);
CONFIG_PARSER_PROTOTYPE(config_parse_duplicate_address_detection);
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c
index c413f16739..7907fa8140 100644
--- a/src/network/networkd-conf.c
+++ b/src/network/networkd-conf.c
@@ -3,20 +3,11 @@
Copyright © 2014 Vinay Kulkarni <kulkarniv@vmware.com>
***/
-#include <ctype.h>
-#include <netinet/ip.h>
-
#include "conf-parser.h"
#include "def.h"
-#include "dhcp-identifier.h"
-#include "extract-word.h"
-#include "hexdecoct.h"
#include "networkd-conf.h"
#include "networkd-manager.h"
-#include "networkd-network.h"
#include "networkd-speed-meter.h"
-#include "networkd-dhcp4.h"
-#include "string-table.h"
int manager_parse_config_file(Manager *m) {
int r;
@@ -45,147 +36,3 @@ int manager_parse_config_file(Manager *m) {
return 0;
}
-
-static const char* const duid_type_table[_DUID_TYPE_MAX] = {
- [DUID_TYPE_LLT] = "link-layer-time",
- [DUID_TYPE_EN] = "vendor",
- [DUID_TYPE_LL] = "link-layer",
- [DUID_TYPE_UUID] = "uuid",
-};
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType);
-
-int config_parse_duid_type(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- _cleanup_free_ char *type_string = NULL;
- const char *p = rvalue;
- DUID *duid = data;
- DUIDType type;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(duid);
-
- r = extract_first_word(&p, &type_string, ":", 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Invalid syntax, ignoring: %s", rvalue);
- return 0;
- }
- if (r == 0) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Failed to extract DUID type from '%s', ignoring.", rvalue);
- return 0;
- }
-
- type = duid_type_from_string(type_string);
- if (type < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, type,
- "Failed to parse DUID type '%s', ignoring.", type_string);
- return 0;
- }
-
- if (!isempty(p)) {
- usec_t u;
-
- if (type != DUID_TYPE_LLT) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Invalid syntax, ignoring: %s", rvalue);
- return 0;
- }
-
- r = parse_timestamp(p, &u);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse timestamp, ignoring: %s", p);
- return 0;
- }
-
- duid->llt_time = u;
- }
-
- duid->type = type;
-
- return 0;
-}
-
-int config_parse_duid_rawdata(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- DUID *ret = data;
- uint8_t raw_data[MAX_DUID_LEN];
- unsigned count = 0;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(ret);
-
- /* RawData contains DUID in format "NN:NN:NN..." */
- for (const char *p = rvalue;;) {
- int n1, n2, len, r;
- uint32_t byte;
- _cleanup_free_ char *cbyte = NULL;
-
- r = extract_first_word(&p, &cbyte, ":", 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue);
- return 0;
- }
- if (r == 0)
- break;
-
- if (count >= MAX_DUID_LEN) {
- log_syntax(unit, LOG_WARNING, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
- return 0;
- }
-
- len = strlen(cbyte);
- if (!IN_SET(len, 1, 2)) {
- log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
- return 0;
- }
- n1 = unhexchar(cbyte[0]);
- if (len == 2)
- n2 = unhexchar(cbyte[1]);
- else
- n2 = 0;
-
- if (n1 < 0 || n2 < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
- return 0;
- }
-
- byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2;
- raw_data[count++] = byte;
- }
-
- assert_cc(sizeof(raw_data) == sizeof(ret->raw_data));
- memcpy(ret->raw_data, raw_data, count);
- ret->raw_data_len = count;
- return 0;
-}
diff --git a/src/network/networkd-conf.h b/src/network/networkd-conf.h
index b485e9e541..6f8612ac91 100644
--- a/src/network/networkd-conf.h
+++ b/src/network/networkd-conf.h
@@ -12,6 +12,3 @@ typedef struct Manager Manager;
int manager_parse_config_file(Manager *m);
const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
-
-CONFIG_PARSER_PROTOTYPE(config_parse_duid_type);
-CONFIG_PARSER_PROTOTYPE(config_parse_duid_rawdata);
diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c
index 0de1892fc9..8357b2b99d 100644
--- a/src/network/networkd-dhcp-common.c
+++ b/src/network/networkd-dhcp-common.c
@@ -4,9 +4,11 @@
#include <linux/if_arp.h>
#include "bus-error.h"
+#include "dhcp-identifier.h"
#include "dhcp-internal.h"
#include "dhcp6-internal.h"
#include "escape.h"
+#include "hexdecoct.h"
#include "in-addr-util.h"
#include "networkd-dhcp-common.h"
#include "networkd-link.h"
@@ -57,33 +59,79 @@ void network_adjust_dhcp(Network *network) {
"Disabling DHCPv6 client.", network->filename);
SET_FLAG(network->dhcp, ADDRESS_FAMILY_IPV6, false);
}
+
+ network_adjust_dhcp4(network);
+}
+
+static bool duid_needs_product_uuid(const DUID *duid) {
+ assert(duid);
+
+ return duid->type == DUID_TYPE_UUID && duid->raw_data_len == 0;
}
-static struct DUID fallback_duid = { .type = DUID_TYPE_EN };
-DUID* link_get_duid(Link *link) {
- if (link->network->duid.type != _DUID_TYPE_INVALID)
- return &link->network->duid;
- else if (link->hw_addr.length == 0 && IN_SET(link->manager->duid.type, DUID_TYPE_LLT, DUID_TYPE_LL))
+static const struct DUID fallback_duid = { .type = DUID_TYPE_EN };
+
+const DUID *link_get_duid(Link *link, int family) {
+ const DUID *duid;
+
+ assert(link);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+
+ if (link->network) {
+ duid = family == AF_INET ? &link->network->dhcp_duid : &link->network->dhcp6_duid;
+ if (duid->type != _DUID_TYPE_INVALID) {
+ if (duid_needs_product_uuid(duid))
+ return &link->manager->duid_product_uuid;
+ else
+ return duid;
+ }
+ }
+
+ duid = family == AF_INET ? &link->manager->dhcp_duid : &link->manager->dhcp6_duid;
+ if (link->hw_addr.length == 0 && IN_SET(duid->type, DUID_TYPE_LLT, DUID_TYPE_LL))
/* Fallback to DUID that works without MAC address.
* This is useful for tunnel devices without MAC address. */
return &fallback_duid;
- else
- return &link->manager->duid;
+
+ return duid;
}
-static int duid_set_uuid(DUID *duid, sd_id128_t uuid) {
- assert(duid);
+static int link_configure_and_start_dhcp_delayed(Link *link) {
+ int r;
- if (duid->raw_data_len > 0)
+ assert(link);
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return 0;
- if (duid->type != DUID_TYPE_UUID)
- return -EINVAL;
+ if (!link->dhcp_client) {
+ r = dhcp4_configure(link);
+ if (r < 0)
+ return r;
+ }
- memcpy(&duid->raw_data, &uuid, sizeof(sd_id128_t));
- duid->raw_data_len = sizeof(sd_id128_t);
+ if (!link->dhcp6_client) {
+ r = dhcp6_configure(link);
+ if (r < 0)
+ return r;
+ }
- return 1;
+ if (!link_has_carrier(link))
+ return 0;
+
+ r = dhcp4_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start DHCPv4 client: %m");
+
+ r = ndisc_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
+
+ r = dhcp6_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
+
+ return 0;
}
static int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
@@ -91,7 +139,6 @@ static int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_er
const sd_bus_error *e;
const void *a;
size_t sz;
- DUID *duid;
Link *link;
int r;
@@ -117,57 +164,38 @@ static int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_er
goto configure;
}
- memcpy(&manager->product_uuid, a, sz);
- while ((duid = set_steal_first(manager->duids_requesting_uuid)))
- (void) duid_set_uuid(duid, manager->product_uuid);
-
- manager->duids_requesting_uuid = set_free(manager->duids_requesting_uuid);
+ memcpy(&manager->duid_product_uuid.raw_data, a, sz);
+ manager->duid_product_uuid.raw_data_len = sz;
configure:
- while ((link = set_steal_first(manager->links_requesting_uuid))) {
- link_unref(link);
+ /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
+ * even if the method fails. */
+ manager->has_product_uuid = true;
- r = link_configure(link);
+ while ((link = set_steal_first(manager->links_requesting_uuid))) {
+ r = link_configure_and_start_dhcp_delayed(link);
if (r < 0)
link_enter_failed(link);
+
+ link_unref(link);
}
manager->links_requesting_uuid = set_free(manager->links_requesting_uuid);
- /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
- * even if the method fails. */
- manager->has_product_uuid = true;
-
return 1;
}
-int manager_request_product_uuid(Manager *m, Link *link) {
+int manager_request_product_uuid(Manager *m) {
int r;
assert(m);
- if (m->has_product_uuid)
+ if (m->product_uuid_requested)
return 0;
log_debug("Requesting product UUID");
- if (link) {
- DUID *duid;
-
- assert_se(duid = link_get_duid(link));
-
- r = set_ensure_put(&m->links_requesting_uuid, NULL, link);
- if (r < 0)
- return log_oom();
- if (r > 0)
- link_ref(link);
-
- r = set_ensure_put(&m->duids_requesting_uuid, NULL, duid);
- if (r < 0)
- return log_oom();
- }
-
- if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
+ if (sd_bus_is_ready(m->bus) <= 0) {
log_debug("Not connected to system bus, requesting product UUID later.");
return 0;
}
@@ -186,71 +214,40 @@ int manager_request_product_uuid(Manager *m, Link *link) {
if (r < 0)
return log_warning_errno(r, "Failed to get product UUID: %m");
- return 0;
-}
-
-static bool link_requires_uuid(Link *link) {
- const DUID *duid;
-
- assert(link);
- assert(link->manager);
- assert(link->network);
-
- duid = link_get_duid(link);
- if (duid->type != DUID_TYPE_UUID || duid->raw_data_len != 0)
- return false;
-
- if (link_dhcp4_enabled(link) && IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
- return true;
-
- if (link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link))
- return true;
+ m->product_uuid_requested = true;
- return false;
+ return 0;
}
-int link_configure_duid(Link *link) {
+int dhcp_configure_duid(Link *link, const DUID *duid) {
Manager *m;
- DUID *duid;
int r;
assert(link);
assert(link->manager);
- assert(link->network);
+ assert(duid);
m = link->manager;
- duid = link_get_duid(link);
- if (!link_requires_uuid(link))
+ if (!duid_needs_product_uuid(duid))
return 1;
- if (m->has_product_uuid) {
- (void) duid_set_uuid(duid, m->product_uuid);
+ if (m->has_product_uuid)
return 1;
- }
- if (!m->links_requesting_uuid) {
- r = manager_request_product_uuid(m, link);
- if (r < 0) {
- if (r == -ENOMEM)
- return r;
-
- log_link_warning_errno(link, r,
- "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
- return 1;
- }
- } else {
- r = set_put(m->links_requesting_uuid, link);
- if (r < 0)
- return log_oom();
- if (r > 0)
- link_ref(link);
-
- r = set_put(m->duids_requesting_uuid, duid);
- if (r < 0)
- return log_oom();
+ r = manager_request_product_uuid(m);
+ if (r < 0) {
+ log_link_warning_errno(link, r,
+ "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
+ return 1;
}
+ r = set_ensure_put(&m->links_requesting_uuid, NULL, link);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ link_ref(link);
+
return 0;
}
@@ -330,14 +327,14 @@ int config_parse_dhcp_route_metric(
if (streq_ptr(section, "DHCPv4")) {
network->dhcp_route_metric = metric;
network->dhcp_route_metric_set = true;
- } else if (streq_ptr(section, "DHCPv6")) {
- network->dhcp6_route_metric = metric;
- network->dhcp6_route_metric_set = true;
+ } else if (STRPTR_IN_SET(section, "DHCPv6", "IPv6AcceptRA")) {
+ network->ipv6_accept_ra_route_metric = metric;
+ network->ipv6_accept_ra_route_metric_set = true;
} else { /* [DHCP] section */
if (!network->dhcp_route_metric_set)
network->dhcp_route_metric = metric;
- if (!network->dhcp6_route_metric_set)
- network->dhcp6_route_metric = metric;
+ if (!network->ipv6_accept_ra_route_metric_set)
+ network->ipv6_accept_ra_route_metric = metric;
}
return 0;
@@ -521,6 +518,7 @@ int config_parse_iaid(const char *unit,
const char *rvalue,
void *data,
void *userdata) {
+
Network *network = userdata;
uint32_t iaid;
int r;
@@ -529,6 +527,7 @@ int config_parse_iaid(const char *unit,
assert(lvalue);
assert(rvalue);
assert(network);
+ assert(IN_SET(ltype, AF_INET, AF_INET6));
r = safe_atou32(rvalue, &iaid);
if (r < 0) {
@@ -537,8 +536,21 @@ int config_parse_iaid(const char *unit,
return 0;
}
- network->iaid = iaid;
- network->iaid_set = true;
+ if (ltype == AF_INET) {
+ network->dhcp_iaid = iaid;
+ network->dhcp_iaid_set = true;
+ if (!network->dhcp6_iaid_set_explicitly) {
+ /* Backward compatibility. Previously, IAID is shared by DHCP4 and DHCP6.
+ * If DHCP6 IAID is not specified explicitly, then use DHCP4 IAID for DHCP6. */
+ network->dhcp6_iaid = iaid;
+ network->dhcp6_iaid_set = true;
+ }
+ } else {
+ assert(ltype == AF_INET6);
+ network->dhcp6_iaid = iaid;
+ network->dhcp6_iaid_set = true;
+ network->dhcp6_iaid_set_explicitly = true;
+ }
return 0;
}
@@ -922,3 +934,260 @@ static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);
+
+static const char* const duid_type_table[_DUID_TYPE_MAX] = {
+ [DUID_TYPE_LLT] = "link-layer-time",
+ [DUID_TYPE_EN] = "vendor",
+ [DUID_TYPE_LL] = "link-layer",
+ [DUID_TYPE_UUID] = "uuid",
+};
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType);
+
+int config_parse_duid_type(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *type_string = NULL;
+ const char *p = rvalue;
+ bool force = ltype;
+ DUID *duid = data;
+ DUIDType type;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(duid);
+
+ if (!force && duid->set)
+ return 0;
+
+ r = extract_first_word(&p, &type_string, ":", 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Failed to extract DUID type from '%s', ignoring.", rvalue);
+ return 0;
+ }
+
+ type = duid_type_from_string(type_string);
+ if (type < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, type,
+ "Failed to parse DUID type '%s', ignoring.", type_string);
+ return 0;
+ }
+
+ if (!isempty(p)) {
+ usec_t u;
+
+ if (type != DUID_TYPE_LLT) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ r = parse_timestamp(p, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse timestamp, ignoring: %s", p);
+ return 0;
+ }
+
+ duid->llt_time = u;
+ }
+
+ duid->type = type;
+ duid->set = force;
+
+ return 0;
+}
+
+int config_parse_manager_duid_type(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Manager *manager = userdata;
+ int r;
+
+ assert(manager);
+
+ /* For backward compatibility. Setting both DHCP4 and DHCP6 DUID if they are not specified explicitly. */
+
+ r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
+ if (r < 0)
+ return r;
+
+ return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp6_duid, manager);
+}
+
+int config_parse_network_duid_type(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ int r;
+
+ assert(network);
+
+ r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, true, rvalue, &network->dhcp_duid, network);
+ if (r < 0)
+ return r;
+
+ /* For backward compatibility, also set DHCP6 DUID if not specified explicitly. */
+ return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
+}
+
+int config_parse_duid_rawdata(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ uint8_t raw_data[MAX_DUID_LEN];
+ unsigned count = 0;
+ bool force = ltype;
+ DUID *duid = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(duid);
+
+ if (!force && duid->set)
+ return 0;
+
+ /* RawData contains DUID in format "NN:NN:NN..." */
+ for (const char *p = rvalue;;) {
+ int n1, n2, len, r;
+ uint32_t byte;
+ _cleanup_free_ char *cbyte = NULL;
+
+ r = extract_first_word(&p, &cbyte, ":", 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
+
+ if (count >= MAX_DUID_LEN) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
+ return 0;
+ }
+
+ len = strlen(cbyte);
+ if (!IN_SET(len, 1, 2)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
+ return 0;
+ }
+ n1 = unhexchar(cbyte[0]);
+ if (len == 2)
+ n2 = unhexchar(cbyte[1]);
+ else
+ n2 = 0;
+
+ if (n1 < 0 || n2 < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
+ return 0;
+ }
+
+ byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2;
+ raw_data[count++] = byte;
+ }
+
+ assert_cc(sizeof(raw_data) == sizeof(duid->raw_data));
+ memcpy(duid->raw_data, raw_data, count);
+ duid->raw_data_len = count;
+ duid->set = force;
+
+ return 0;
+}
+
+int config_parse_manager_duid_rawdata(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Manager *manager = userdata;
+ int r;
+
+ assert(manager);
+
+ /* For backward compatibility. Setting both DHCP4 and DHCP6 DUID if they are not specified explicitly. */
+
+ r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
+ if (r < 0)
+ return r;
+
+ return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp6_duid, manager);
+}
+
+int config_parse_network_duid_rawdata(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ int r;
+
+ assert(network);
+
+ r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, true, rvalue, &network->dhcp_duid, network);
+ if (r < 0)
+ return r;
+
+ /* For backward compatibility, also set DHCP6 DUID if not specified explicitly. */
+ return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
+}
diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h
index acf80e6255..316f5cf10b 100644
--- a/src/network/networkd-dhcp-common.h
+++ b/src/network/networkd-dhcp-common.h
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <netinet/in.h>
+
#include "conf-parser.h"
#include "dhcp-identifier.h"
#include "time-util.h"
@@ -37,6 +39,7 @@ typedef struct DUID {
uint8_t raw_data_len;
uint8_t raw_data[MAX_DUID_LEN];
usec_t llt_time;
+ bool set;
} DUID;
bool link_dhcp_enabled(Link *link, int family);
@@ -49,9 +52,16 @@ static inline bool link_dhcp6_enabled(Link *link) {
void network_adjust_dhcp(Network *network);
-DUID* link_get_duid(Link *link);
-int link_configure_duid(Link *link);
-int manager_request_product_uuid(Manager *m, Link *link);
+const DUID *link_get_duid(Link *link, int family);
+static inline const DUID *link_get_dhcp4_duid(Link *link) {
+ return link_get_duid(link, AF_INET);
+}
+static inline const DUID *link_get_dhcp6_duid(Link *link) {
+ return link_get_duid(link, AF_INET6);
+}
+
+int dhcp_configure_duid(Link *link, const DUID *duid);
+int manager_request_product_uuid(Manager *m);
const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
@@ -69,3 +79,9 @@ CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_or_vendor_class);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_option);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_request_options);
+CONFIG_PARSER_PROTOTYPE(config_parse_duid_type);
+CONFIG_PARSER_PROTOTYPE(config_parse_manager_duid_type);
+CONFIG_PARSER_PROTOTYPE(config_parse_network_duid_type);
+CONFIG_PARSER_PROTOTYPE(config_parse_duid_rawdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_manager_duid_rawdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_network_duid_rawdata);
diff --git a/src/network/networkd-dhcp-server-bus.c b/src/network/networkd-dhcp-server-bus.c
index 32f4baed78..91a10dc33a 100644
--- a/src/network/networkd-dhcp-server-bus.c
+++ b/src/network/networkd-dhcp-server-bus.c
@@ -31,6 +31,9 @@ static int property_get_leases(
if (!s)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Link %s has no DHCP server.", l->ifname);
+ if (sd_dhcp_server_is_in_relay_mode(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Link %s has DHCP relay agent active.", l->ifname);
+
r = sd_bus_message_open_container(reply, 'a', "(uayayayayt)");
if (r < 0)
return r;
diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c
index ad979fb2c4..bd368672da 100644
--- a/src/network/networkd-dhcp-server.c
+++ b/src/network/networkd-dhcp-server.c
@@ -344,10 +344,18 @@ int dhcp4_server_configure(Link *link) {
dhcp_lease_server_type_to_string(type));
}
+ r = sd_dhcp_server_set_bind_to_interface(link->dhcp_server, link->network->dhcp_server_bind_to_interface);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to set interface binding for DHCP server: %m");
+
r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set router emission for DHCP server: %m");
+ r = sd_dhcp_server_set_relay_target(link->dhcp_server, &link->network->dhcp_server_relay_target);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to set relay target for DHCP server: %m");
+
if (link->network->dhcp_server_emit_timezone) {
_cleanup_free_ char *buffer = NULL;
const char *tz;
@@ -394,6 +402,32 @@ int dhcp4_server_configure(Link *link) {
return 0;
}
+int config_parse_dhcp_server_relay_target(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ union in_addr_union a;
+ int r;
+
+ r = in_addr_from_string(AF_INET, rvalue, &a);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s= address '%s', ignoring: %m", lvalue, rvalue);
+ return 0;
+ }
+ network->dhcp_server_relay_target = a.in;
+ return r;
+}
+
int config_parse_dhcp_server_emit(
const char *unit,
const char *filename,
diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h
index 4bd5120ea0..9e5d24fbe8 100644
--- a/src/network/networkd-dhcp-server.h
+++ b/src/network/networkd-dhcp-server.h
@@ -9,4 +9,5 @@ typedef struct Link Link;
int dhcp4_server_configure(Link *link);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_target);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit);
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 6f06e2218d..9856f397c2 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -26,6 +26,31 @@
static int dhcp4_update_address(Link *link, bool announce);
static int dhcp4_remove_all(Link *link);
+void network_adjust_dhcp4(Network *network) {
+ assert(network);
+
+ if (!FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
+ return;
+
+ if (network->dhcp_use_gateway < 0)
+ network->dhcp_use_gateway = network->dhcp_use_routes;
+
+ /* RFC7844 section 3.: MAY contain the Client Identifier option
+ * Section 3.5: clients MUST use client identifiers based solely on the link-layer address
+ * NOTE: Using MAC, as it does not reveal extra information, and some servers might not answer
+ * if this option is not sent */
+ if (network->dhcp_anonymize &&
+ network->dhcp_client_identifier >= 0 &&
+ network->dhcp_client_identifier != DHCP_CLIENT_ID_MAC) {
+ log_warning("%s: ClientIdentifier= is set, although Anonymize=yes. Using ClientIdentifier=mac.",
+ network->filename);
+ network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
+ }
+
+ if (network->dhcp_client_identifier < 0)
+ network->dhcp_client_identifier = network->dhcp_anonymize ? DHCP_CLIENT_ID_MAC : DHCP_CLIENT_ID_DUID;
+}
+
static int dhcp4_release_old_lease(Link *link) {
Route *route;
int k, r = 0;
@@ -131,12 +156,6 @@ static int route_scope_from_address(const Route *route, const struct in_addr *se
return RT_SCOPE_UNIVERSE;
}
-static bool link_prefixroute(Link *link) {
- return !link->network->dhcp_route_table_set ||
- link->network->dhcp_route_table == RT_TABLE_MAIN ||
- link->manager->dhcp4_prefix_root_cannot_set_table;
-}
-
static int dhcp_route_configure(Route *route, Link *link) {
Route *ret;
int r;
@@ -159,64 +178,259 @@ static int dhcp_route_configure(Route *route, Link *link) {
return 0;
}
-static int link_set_dns_routes(Link *link, const struct in_addr *address) {
- const struct in_addr *dns;
- uint32_t table;
+static bool link_prefixroute(Link *link) {
+ return !link->network->dhcp_route_table_set ||
+ link->network->dhcp_route_table == RT_TABLE_MAIN ||
+ link->manager->dhcp4_prefix_root_cannot_set_table;
+}
+
+static int link_set_dhcp_prefix_route(Link *link) {
+ _cleanup_(route_freep) Route *route = NULL;
+ struct in_addr address, netmask;
+ int r;
+
+ assert(link);
+ assert(link->dhcp_lease);
+
+ if (link_prefixroute(link))
+ /* When true, the route will be created by kernel. See dhcp4_update_address(). */
+ return 0;
+
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
+ if (r < 0)
+ return r;
+
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ route->family = AF_INET;
+ route->dst.in.s_addr = address.s_addr & netmask.s_addr;
+ route->dst_prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
+ route->prefsrc.in = address;
+ route->scope = RT_SCOPE_LINK;
+ route->protocol = RTPROT_DHCP;
+ route->table = link_get_dhcp_route_table(link);
+
+ return dhcp_route_configure(route, link);
+}
+
+static int link_set_dhcp_route_to_gateway(Link *link, const struct in_addr *gw) {
+ _cleanup_(route_freep) Route *route = NULL;
+ struct in_addr address;
+ int r;
+
+ assert(link);
+ assert(link->dhcp_lease);
+ assert(gw);
+
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ if (r < 0)
+ return r;
+
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ route->family = AF_INET;
+ route->dst.in = *gw;
+ route->dst_prefixlen = 32;
+ route->prefsrc.in = address;
+ route->scope = RT_SCOPE_LINK;
+ route->protocol = RTPROT_DHCP;
+ route->priority = link->network->dhcp_route_metric;
+ route->table = link_get_dhcp_route_table(link);
+ route->mtu = link->network->dhcp_route_mtu;
+
+ return dhcp_route_configure(route, link);
+}
+
+static int link_set_dhcp_static_routes(Link *link) {
+ _cleanup_free_ sd_dhcp_route **static_routes = NULL;
+ bool classless_route = false, static_route = false;
+ _cleanup_(route_freep) Route *route = NULL;
+ struct in_addr address;
int n, r;
assert(link);
assert(link->dhcp_lease);
- assert(link->network);
- if (!link->network->dhcp_use_dns ||
- !link->network->dhcp_routes_to_dns)
+ if (!link->network->dhcp_use_routes)
return 0;
- n = sd_dhcp_lease_get_dns(link->dhcp_lease, &dns);
- if (IN_SET(n, 0, -ENODATA))
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ if (r < 0)
+ return r;
+
+ n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
+ if (IN_SET(n, 0, -ENODATA)) {
+ log_link_debug(link, "DHCP: No static routes received from DHCP server.");
return 0;
+ }
if (n < 0)
- return log_link_warning_errno(link, n, "DHCP error: could not get DNS servers: %m");
+ return n;
- table = link_get_dhcp_route_table(link);
+ for (int i = 0; i < n; i++) {
+ switch (sd_dhcp_route_get_option(static_routes[i])) {
+ case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
+ classless_route = true;
+ break;
+ case SD_DHCP_OPTION_STATIC_ROUTE:
+ static_route = true;
+ break;
+ }
+ }
- for (int i = 0; i < n; i ++) {
- _cleanup_(route_freep) Route *route = NULL;
+ /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
+ * the DHCP client MUST ignore the Static Routes option. */
+ if (classless_route && static_route)
+ log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option");
- r = route_new(&route);
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ route->family = AF_INET;
+ route->gw_family = AF_INET;
+ route->protocol = RTPROT_DHCP;
+ route->priority = link->network->dhcp_route_metric;
+ route->table = link_get_dhcp_route_table(link);
+ route->mtu = link->network->dhcp_route_mtu;
+
+ for (int i = 0; i < n; i++) {
+ if (sd_dhcp_route_get_option(static_routes[i]) !=
+ (classless_route ? SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE : SD_DHCP_OPTION_STATIC_ROUTE))
+ continue;
+
+ r = sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in);
if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate route: %m");
+ return r;
- /* Set routes to DNS servers. */
+ r = sd_dhcp_route_get_destination(static_routes[i], &route->dst.in);
+ if (r < 0)
+ return r;
- route->family = AF_INET;
- route->dst.in = dns[i];
- route->dst_prefixlen = 32;
- route->prefsrc.in = *address;
- route->scope = RT_SCOPE_LINK;
- route->protocol = RTPROT_DHCP;
- route->priority = link->network->dhcp_route_metric;
- route->table = table;
+ r = sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen);
+ if (r < 0)
+ return r;
+
+ route->scope = route_scope_from_address(route, &address);
+ if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
+ route->prefsrc.in = address;
+ else
+ route->prefsrc = IN_ADDR_NULL;
r = dhcp_route_configure(route, link);
if (r < 0)
- return log_link_error_errno(link, r, "Could not set route to DNS server: %m");
+ return r;
+ }
+
+ return classless_route;
+}
+
+static int link_set_dhcp_gateway(Link *link) {
+ _cleanup_(route_freep) Route *route = NULL;
+ const struct in_addr *router;
+ struct in_addr address;
+ Route *rt;
+ int r;
+
+ assert(link);
+ assert(link->dhcp_lease);
+
+ if (!link->network->dhcp_use_gateway)
+ return 0;
+
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
+ if (IN_SET(r, 0, -ENODATA)) {
+ log_link_debug(link, "DHCP: No gateway received from DHCP server.");
+ return 0;
+ }
+ if (r < 0)
+ return r;
+ if (in4_addr_is_null(&router[0])) {
+ log_link_debug(link, "DHCP: Received gateway address is null.");
+ return 0;
+ }
+
+ /* The dhcp netmask may mask out the gateway. First, add an explicit route for the gateway host
+ * so that we can route no matter the netmask or existing kernel route tables. */
+ r = link_set_dhcp_route_to_gateway(link, &router[0]);
+ if (r < 0)
+ return r;
+
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ /* Next, add a default gateway. */
+ route->family = AF_INET;
+ route->gw_family = AF_INET;
+ route->gw.in = router[0];
+ route->prefsrc.in = address;
+ route->protocol = RTPROT_DHCP;
+ route->priority = link->network->dhcp_route_metric;
+ route->table = link_get_dhcp_route_table(link);
+ route->mtu = link->network->dhcp_route_mtu;
+
+ r = dhcp_route_configure(route, link);
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(rt, link->network->routes_by_section) {
+ if (!rt->gateway_from_dhcp_or_ra)
+ continue;
+
+ if (rt->gw_family != AF_INET)
+ continue;
+
+ rt->gw.in = router[0];
+ if (!rt->protocol_set)
+ rt->protocol = RTPROT_DHCP;
+ if (!rt->priority_set)
+ rt->priority = link->network->dhcp_route_metric;
+ if (!rt->table_set)
+ rt->table = link_get_dhcp_route_table(link);
+ if (rt->mtu == 0)
+ rt->mtu = link->network->dhcp_route_mtu;
+
+ r = dhcp_route_configure(rt, link);
+ if (r < 0)
+ return r;
}
return 0;
}
-static int dhcp_prefix_route_from_lease(
- const sd_dhcp_lease *lease,
- uint32_t table,
- const struct in_addr *address,
- Route **ret_route) {
+static int link_set_dns_routes(Link *link) {
+ _cleanup_(route_freep) Route *route = NULL;
+ const struct in_addr *dns;
+ struct in_addr address;
+ int n, r;
- Route *route;
- struct in_addr netmask;
- int r;
+ assert(link);
+ assert(link->dhcp_lease);
+ assert(link->network);
- r = sd_dhcp_lease_get_netmask((sd_dhcp_lease*) lease, &netmask);
+ if (!link->network->dhcp_use_dns ||
+ !link->network->dhcp_routes_to_dns)
+ return 0;
+
+ n = sd_dhcp_lease_get_dns(link->dhcp_lease, &dns);
+ if (IN_SET(n, 0, -ENODATA))
+ return 0;
+ if (n < 0)
+ return n;
+
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
if (r < 0)
return r;
@@ -225,23 +439,27 @@ static int dhcp_prefix_route_from_lease(
return r;
route->family = AF_INET;
- route->dst.in.s_addr = address->s_addr & netmask.s_addr;
- route->dst_prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
- route->prefsrc.in = *address;
+ route->dst_prefixlen = 32;
+ route->prefsrc.in = address;
route->scope = RT_SCOPE_LINK;
route->protocol = RTPROT_DHCP;
- route->table = table;
- *ret_route = route;
+ route->priority = link->network->dhcp_route_metric;
+ route->table = link_get_dhcp_route_table(link);
+
+ for (int i = 0; i < n; i ++) {
+ route->dst.in = dns[i];
+
+ r = dhcp_route_configure(route, link);
+ if (r < 0)
+ return r;
+ }
+
return 0;
}
static int link_set_dhcp_routes(Link *link) {
- _cleanup_free_ sd_dhcp_route **static_routes = NULL;
- bool classless_route = false, static_route = false;
- struct in_addr address;
- uint32_t table;
Route *rt;
- int r, n;
+ int r;
assert(link);
@@ -262,160 +480,26 @@ static int link_set_dhcp_routes(Link *link) {
return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m");
}
- table = link_get_dhcp_route_table(link);
-
- r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ r = link_set_dhcp_prefix_route(link);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
-
- if (!link_prefixroute(link)) {
- _cleanup_(route_freep) Route *prefix_route = NULL;
+ return log_link_error_errno(link, r, "DHCP error: Could not set prefix route: %m");
- r = dhcp_prefix_route_from_lease(link->dhcp_lease, table, &address, &prefix_route);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not create prefix route: %m");
-
- r = dhcp_route_configure(prefix_route, link);
+ r = link_set_dhcp_static_routes(link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP error: Could not set static routes: %m");
+ if (r == 0) {
+ /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
+ * a Router option, the DHCP client MUST ignore the Router option. */
+ r = link_set_dhcp_gateway(link);
if (r < 0)
- return log_link_error_errno(link, r, "Could not set prefix route: %m");
- }
-
- n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
- if (n == -ENODATA)
- log_link_debug_errno(link, n, "DHCP: No routes received from DHCP server: %m");
- else if (n < 0)
- return log_link_error_errno(link, n, "DHCP: could not get routes: %m");
-
- for (int i = 0; i < n; i++) {
- switch (sd_dhcp_route_get_option(static_routes[i])) {
- case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
- classless_route = true;
- break;
- case SD_DHCP_OPTION_STATIC_ROUTE:
- static_route = true;
- break;
- }
+ return log_link_error_errno(link, r, "DHCP error: Could not set gateway: %m");
}
- if (link->network->dhcp_use_routes) {
- /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
- * the DHCP client MUST ignore the Static Routes option. */
- if (classless_route && static_route)
- log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option");
-
- for (int i = 0; i < n; i++) {
- _cleanup_(route_freep) Route *route = NULL;
-
- if (classless_route &&
- sd_dhcp_route_get_option(static_routes[i]) != SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
- continue;
-
- r = route_new(&route);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate route: %m");
-
- route->family = AF_INET;
- route->protocol = RTPROT_DHCP;
- route->gw_family = AF_INET;
- assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
- assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
- assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
- route->priority = link->network->dhcp_route_metric;
- route->table = table;
- route->mtu = link->network->dhcp_route_mtu;
- route->scope = route_scope_from_address(route, &address);
- if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
- route->prefsrc.in = address;
-
- if (set_contains(link->dhcp_routes, route))
- continue;
-
- r = dhcp_route_configure(route, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set route: %m");
- }
- }
-
- if (link->network->dhcp_use_gateway) {
- const struct in_addr *router;
-
- r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
- if (IN_SET(r, 0, -ENODATA))
- log_link_info(link, "DHCP: No gateway received from DHCP server.");
- else if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m");
- else if (in4_addr_is_null(&router[0]))
- log_link_info(link, "DHCP: Received gateway is null.");
- else if (classless_route)
- /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
- * a Router option, the DHCP client MUST ignore the Router option. */
- log_link_warning(link, "Classless static routes received from DHCP server: ignoring router option");
- else {
- _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL;
-
- r = route_new(&route_gw);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate route: %m");
-
- /* The dhcp netmask may mask out the gateway. Add an explicit
- * route for the gw host so that we can route no matter the
- * netmask or existing kernel route tables. */
- route_gw->family = AF_INET;
- route_gw->dst.in = router[0];
- route_gw->dst_prefixlen = 32;
- route_gw->prefsrc.in = address;
- route_gw->scope = RT_SCOPE_LINK;
- route_gw->protocol = RTPROT_DHCP;
- route_gw->priority = link->network->dhcp_route_metric;
- route_gw->table = table;
- route_gw->mtu = link->network->dhcp_route_mtu;
-
- r = dhcp_route_configure(route_gw, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set host route: %m");
-
- r = route_new(&route);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate route: %m");
-
- route->family = AF_INET;
- route->gw_family = AF_INET;
- route->gw.in = router[0];
- route->prefsrc.in = address;
- route->protocol = RTPROT_DHCP;
- route->priority = link->network->dhcp_route_metric;
- route->table = table;
- route->mtu = link->network->dhcp_route_mtu;
-
- r = dhcp_route_configure(route, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set router: %m");
-
- HASHMAP_FOREACH(rt, link->network->routes_by_section) {
- if (!rt->gateway_from_dhcp_or_ra)
- continue;
-
- if (rt->gw_family != AF_INET)
- continue;
-
- rt->gw.in = router[0];
- if (!rt->protocol_set)
- rt->protocol = RTPROT_DHCP;
- if (!rt->priority_set)
- rt->priority = link->network->dhcp_route_metric;
- if (!rt->table_set)
- rt->table = table;
- if (rt->mtu == 0)
- rt->mtu = link->network->dhcp_route_mtu;
-
- r = dhcp_route_configure(rt, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set gateway: %m");
- }
- }
- }
+ r = link_set_dns_routes(link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP error: Could not set routes to DNS servers: %m");
- return link_set_dns_routes(link, &address);
+ return 0;
}
static int dhcp_reset_mtu(Link *link) {
@@ -868,6 +952,7 @@ static int dhcp4_update_address(Link *link, bool announce) {
if (prefixlen <= 30)
addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr;
SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link));
+ addr->route_metric = link->network->dhcp_route_metric;
/* allow reusing an existing address and simply update its lifetime
* in case it already exists */
@@ -1199,17 +1284,17 @@ static int dhcp4_set_client_identifier(Link *link) {
switch (link->network->dhcp_client_identifier) {
case DHCP_CLIENT_ID_DUID: {
/* If configured, apply user specified DUID and IAID */
- const DUID *duid = link_get_duid(link);
+ const DUID *duid = link_get_dhcp4_duid(link);
if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
r = sd_dhcp_client_set_iaid_duid_llt(link->dhcp_client,
- link->network->iaid_set,
- link->network->iaid,
+ link->network->dhcp_iaid_set,
+ link->network->dhcp_iaid,
duid->llt_time);
else
r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
- link->network->iaid_set,
- link->network->iaid,
+ link->network->dhcp_iaid_set,
+ link->network->dhcp_iaid,
duid->type,
duid->raw_data_len > 0 ? duid->raw_data : NULL,
duid->raw_data_len);
@@ -1219,7 +1304,7 @@ static int dhcp4_set_client_identifier(Link *link) {
}
case DHCP_CLIENT_ID_DUID_ONLY: {
/* If configured, apply user specified DUID */
- const DUID *duid = link_get_duid(link);
+ const DUID *duid = link_get_dhcp4_duid(link);
if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
r = sd_dhcp_client_set_duid_llt(link->dhcp_client,
@@ -1258,6 +1343,15 @@ static int dhcp4_set_client_identifier(Link *link) {
return 0;
}
+static int dhcp4_configure_duid(Link *link) {
+ assert(link);
+
+ if (!IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
+ return 1;
+
+ return dhcp_configure_duid(link, link_get_dhcp4_duid(link));
+}
+
static int dhcp4_set_request_address(Link *link) {
Address *a;
@@ -1283,6 +1377,30 @@ static int dhcp4_set_request_address(Link *link) {
return sd_dhcp_client_set_request_address(link->dhcp_client, &a->in_addr.in);
}
+static bool link_needs_dhcp_broadcast(Link *link) {
+ const char *val;
+ int r;
+
+ assert(link);
+ assert(link->network);
+
+ /* Return the setting in DHCP[4].RequestBroadcast if specified. Otherwise return the device property
+ * ID_NET_DHCP_BROADCAST setting, which may be set for interfaces requiring that the DHCPOFFER message
+ * is being broadcast because they can't handle unicast messages while not fully configured.
+ * If neither is set or a failure occurs, return false, which is the default for this flag.
+ */
+ r = link->network->dhcp_broadcast;
+ if (r < 0 && link->sd_device && sd_device_get_property_value(link->sd_device, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
+ r = parse_boolean(val);
+ if (r < 0)
+ log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
+ else
+ log_link_debug(link, "DHCP4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
+
+ }
+ return r == true;
+}
+
int dhcp4_configure(Link *link) {
sd_dhcp_option *send_option;
void *request_options;
@@ -1294,15 +1412,20 @@ int dhcp4_configure(Link *link) {
if (!link_dhcp4_enabled(link))
return 0;
- if (!link->dhcp_client) {
- r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to allocate DHCP4 client: %m");
+ if (link->dhcp_client)
+ return -EBUSY; /* Already configured. */
- r = sd_dhcp_client_attach_event(link->dhcp_client, link->manager->event, 0);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to attach event to DHCP4 client: %m");
- }
+ r = dhcp4_configure_duid(link);
+ if (r <= 0)
+ return r;
+
+ r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to allocate DHCP4 client: %m");
+
+ r = sd_dhcp_client_attach_event(link->dhcp_client, link->manager->event, 0);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to attach event to DHCP4 client: %m");
r = sd_dhcp_client_set_mac(link->dhcp_client,
link->hw_addr.addr.bytes,
@@ -1319,7 +1442,7 @@ int dhcp4_configure(Link *link) {
if (r < 0)
return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set callback: %m");
- r = sd_dhcp_client_set_request_broadcast(link->dhcp_client, link->network->dhcp_broadcast);
+ r = sd_dhcp_client_set_request_broadcast(link->dhcp_client, link_needs_dhcp_broadcast(link));
if (r < 0)
return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for broadcast: %m");
@@ -1329,96 +1452,93 @@ int dhcp4_configure(Link *link) {
return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set MTU: %m");
}
- if (link->network->dhcp_use_mtu) {
- r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_INTERFACE_MTU);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for MTU: %m");
- }
-
- /* NOTE: even if this variable is called "use", it also "sends" PRL
- * options, maybe there should be a different configuration variable
- * to send or not route options?. */
- /* NOTE: when using Anonymize=yes, routes PRL options are sent
- * by default, so they don't need to be added here. */
- if (link->network->dhcp_use_routes && !link->network->dhcp_anonymize) {
- r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_STATIC_ROUTE);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for static route: %m");
+ if (!link->network->dhcp_anonymize) {
+ if (link->network->dhcp_use_mtu) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_INTERFACE_MTU);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for MTU: %m");
+ }
- r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for classless static route: %m");
- }
+ if (link->network->dhcp_use_routes) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_STATIC_ROUTE);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for static route: %m");
- if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO && !link->network->dhcp_anonymize) {
- r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for domain search list: %m");
- }
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for classless static route: %m");
+ }
- if (link->network->dhcp_use_ntp) {
- r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for NTP server: %m");
- }
+ if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for domain search list: %m");
+ }
- if (link->network->dhcp_use_sip) {
- r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_SIP_SERVER);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for SIP server: %m");
- }
+ if (link->network->dhcp_use_ntp) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for NTP server: %m");
+ }
- if (link->network->dhcp_use_timezone) {
- r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for timezone: %m");
- }
+ if (link->network->dhcp_use_sip) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_SIP_SERVER);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for SIP server: %m");
+ }
- SET_FOREACH(request_options, link->network->dhcp_request_options) {
- uint32_t option = PTR_TO_UINT32(request_options);
+ if (link->network->dhcp_use_timezone) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for timezone: %m");
+ }
- r = sd_dhcp_client_set_request_option(link->dhcp_client, option);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for '%u': %m", option);
- }
+ SET_FOREACH(request_options, link->network->dhcp_request_options) {
+ uint32_t option = PTR_TO_UINT32(request_options);
- ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_options) {
- r = sd_dhcp_client_add_option(link->dhcp_client, send_option);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
- }
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, option);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for '%u': %m", option);
+ }
- ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_vendor_options) {
- r = sd_dhcp_client_add_vendor_option(link->dhcp_client, send_option);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
- }
+ ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_options) {
+ r = sd_dhcp_client_add_option(link->dhcp_client, send_option);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
+ }
- r = dhcp4_set_hostname(link);
- if (r < 0)
- return r;
+ ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_vendor_options) {
+ r = sd_dhcp_client_add_vendor_option(link->dhcp_client, send_option);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
+ }
- if (link->network->dhcp_vendor_class_identifier) {
- r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
- link->network->dhcp_vendor_class_identifier);
+ r = dhcp4_set_hostname(link);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set vendor class identifier: %m");
- }
+ return r;
- if (link->network->dhcp_mudurl) {
- r = sd_dhcp_client_set_mud_url(link->dhcp_client, link->network->dhcp_mudurl);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set MUD URL: %m");
- }
+ if (link->network->dhcp_vendor_class_identifier) {
+ r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
+ link->network->dhcp_vendor_class_identifier);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set vendor class identifier: %m");
+ }
- if (link->network->dhcp_user_class) {
- r = sd_dhcp_client_set_user_class(link->dhcp_client, link->network->dhcp_user_class);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set user class: %m");
+ if (link->network->dhcp_mudurl) {
+ r = sd_dhcp_client_set_mud_url(link->dhcp_client, link->network->dhcp_mudurl);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set MUD URL: %m");
+ }
+
+ if (link->network->dhcp_user_class) {
+ r = sd_dhcp_client_set_user_class(link->dhcp_client, link->network->dhcp_user_class);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set user class: %m");
+ }
}
if (link->network->dhcp_client_port > 0) {
@@ -1481,6 +1601,20 @@ int dhcp4_update_mac(Link *link) {
return 0;
}
+int dhcp4_start(Link *link) {
+ assert(link);
+
+ if (!link->dhcp_client)
+ return 0;
+
+ if (sd_dhcp_client_is_running(link->dhcp_client) > 0)
+ return 0;
+
+ log_link_debug(link, "Acquiring DHCPv4 lease");
+
+ return sd_dhcp_client_start(link->dhcp_client);
+}
+
int config_parse_dhcp_max_attempts(
const char *unit,
const char *filename,
diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h
index 5ec2f88b7e..a33fe403be 100644
--- a/src/network/networkd-dhcp4.h
+++ b/src/network/networkd-dhcp4.h
@@ -17,8 +17,10 @@ typedef enum DHCPClientIdentifier {
_DHCP_CLIENT_ID_INVALID = -EINVAL,
} DHCPClientIdentifier;
+void network_adjust_dhcp4(Network *network);
int dhcp4_configure(Link *link);
int dhcp4_update_mac(Link *link);
+int dhcp4_start(Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_acl_ip_address);
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 72bb46b186..f4b309636f 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -25,6 +25,13 @@
#include "radv-internal.h"
#include "web-util.h"
+bool link_dhcp6_with_address_enabled(Link *link) {
+ if (!link_dhcp6_enabled(link))
+ return false;
+
+ return link->network->dhcp6_use_address;
+}
+
bool link_dhcp6_pd_is_enabled(Link *link) {
assert(link);
@@ -277,6 +284,7 @@ static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, con
route->dst = *prefix;
route->dst_prefixlen = 64;
route->protocol = RTPROT_DHCP;
+ route->priority = link->network->dhcp6_pd_route_metric;
r = route_configure(route, link, dhcp6_pd_route_handler, &ret);
if (r < 0)
@@ -356,6 +364,33 @@ static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Lin
return 1;
}
+static void log_dhcp6_pd_address(Link *link, const Address *address) {
+ char valid_buf[FORMAT_TIMESPAN_MAX], preferred_buf[FORMAT_TIMESPAN_MAX];
+ const char *valid_str = NULL, *preferred_str = NULL;
+ _cleanup_free_ char *buffer = NULL;
+ int log_level;
+
+ log_level = address_get(link, address, NULL) >= 0 ? LOG_DEBUG : LOG_INFO;
+
+ if (log_level < log_get_max_level())
+ return;
+
+ (void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &buffer);
+ if (address->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
+ valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
+ address->cinfo.ifa_valid * USEC_PER_SEC,
+ USEC_PER_SEC);
+ if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME)
+ preferred_str = format_timespan(preferred_buf, FORMAT_TIMESPAN_MAX,
+ address->cinfo.ifa_prefered * USEC_PER_SEC,
+ USEC_PER_SEC);
+
+ log_link_full(link, log_level, "DHCPv6-PD address %s (valid %s%s, preferred %s%s)",
+ strna(buffer),
+ valid_str ? "for " : "forever", strempty(valid_str),
+ preferred_str ? "for " : "forever", strempty(preferred_str));
+}
+
static int dhcp6_set_pd_address(
Link *link,
const union in_addr_union *prefix,
@@ -392,7 +427,9 @@ static int dhcp6_set_pd_address(
address->cinfo.ifa_prefered = lifetime_preferred;
address->cinfo.ifa_valid = lifetime_valid;
SET_FLAG(address->flags, IFA_F_MANAGETEMPADDR, link->network->dhcp6_pd_manage_temporary_address);
+ address->route_metric = link->network->dhcp6_pd_route_metric;
+ log_dhcp6_pd_address(link, address);
r = address_configure(address, link, dhcp6_pd_address_handler, &ret);
if (r < 0)
return log_link_error_errno(link, r, "Failed to set DHCPv6 delegated prefix address: %m");
@@ -645,6 +682,8 @@ static void dhcp6_pd_prefix_lost(Link *dhcp6_link) {
if (r < 0)
link_enter_failed(link);
}
+
+ set_clear(dhcp6_link->dhcp6_pd_prefixes);
}
static int dhcp6_remove_old(Link *link, bool force);
@@ -787,20 +826,12 @@ static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *ad
(void) in_addr_prefix_to_string(AF_INET6, addr, prefixlen, &buf);
- if (prefixlen > 64) {
- log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s", strna(buf));
- return 0;
- }
-
if (prefixlen == 64) {
log_link_debug(link, "Not adding a blocking route for DHCPv6 delegated subnet %s since distributed prefix is 64",
strna(buf));
- return 1;
+ return 0;
}
- if (prefixlen < 48)
- log_link_warning(link, "PD Prefix length < 48, looks unusual: %s", strna(buf));
-
r = route_new(&route);
if (r < 0)
return log_oom();
@@ -828,7 +859,45 @@ static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *ad
(void) set_remove(link->dhcp6_routes_old, ret);
- return 1;
+ return 0;
+}
+
+static int dhcp6_pd_prefix_add(Link *link, const union in_addr_union *prefix, uint8_t prefixlen) {
+ _cleanup_free_ struct in_addr_prefix *p = NULL;
+ _cleanup_free_ char *buf = NULL;
+ int r;
+
+ assert(link);
+ assert(prefix);
+
+ p = new(struct in_addr_prefix, 1);
+ if (!p)
+ return log_oom();
+
+ *p = (struct in_addr_prefix) {
+ .family = AF_INET6,
+ .prefixlen = prefixlen,
+ .address = *prefix,
+ };
+
+ (void) in_addr_prefix_to_string(p->family, &p->address, p->prefixlen, &buf);
+
+ log_link_full(link,
+ set_contains(link->dhcp6_pd_prefixes, p) ? LOG_DEBUG :
+ prefixlen > 64 || prefixlen < 48 ? LOG_WARNING : LOG_INFO,
+ "DHCP6: received PD Prefix %s%s",
+ strna(buf),
+ prefixlen > 64 ? " with prefix length > 64, ignoring." :
+ prefixlen < 48 ? " with prefix lenght < 48, looks unusual.": "");
+
+ /* Store PD prefix even if prefixlen > 64, not to make logged at warning level so frequently. */
+ r = set_ensure_put(&link->dhcp6_pd_prefixes, &in_addr_prefix_hash_ops_free, p);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to store DHCP6 PD prefix %s: %m", strna(buf));
+ if (r > 0)
+ TAKE_PTR(p);
+
+ return prefixlen <= 64;
}
static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
@@ -857,12 +926,16 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
if (r < 0)
break;
- r = dhcp6_set_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len);
+ r = dhcp6_pd_prefix_add(dhcp6_link, &pd_prefix, pd_prefix_len);
if (r < 0)
return r;
if (r == 0)
continue;
+ r = dhcp6_set_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len);
+ if (r < 0)
+ return r;
+
/* We are doing prefix allocation in two steps:
* 1. all those links that have a preferred subnet id will be assigned their subnet
* 2. all those links that remain will receive prefixes in sequential order. Prefixes
@@ -1281,6 +1354,31 @@ int dhcp6_request_address(Link *link, int ir) {
return 0;
}
+int dhcp6_start(Link *link) {
+ assert(link);
+
+ if (!link->dhcp6_client)
+ return 0;
+
+ if (!link_dhcp6_enabled(link))
+ return 0;
+
+ if (link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_NO)
+ return 0;
+
+ if (!in6_addr_is_link_local(&link->ipv6ll_address)) {
+ log_link_debug(link, "IPv6 link-local address is not set, delaying to start DHCPv6 client.");
+ return 0;
+ }
+
+ if (sd_dhcp6_client_is_running(link->dhcp6_client) > 0)
+ return 0;
+
+ log_link_debug(link, "Acquiring DHCPv6 lease");
+
+ return dhcp6_request_address(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
+}
+
int dhcp6_request_prefix_delegation(Link *link) {
Link *l;
@@ -1415,13 +1513,13 @@ static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) {
if (r < 0)
return r;
- if (link->network->iaid_set) {
- r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
+ if (link->network->dhcp6_iaid_set) {
+ r = sd_dhcp6_client_set_iaid(client, link->network->dhcp6_iaid);
if (r < 0)
return r;
}
- duid = link_get_duid(link);
+ duid = link_get_dhcp6_duid(link);
if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
else
@@ -1449,7 +1547,11 @@ int dhcp6_configure(Link *link) {
return 0;
if (link->dhcp6_client)
- return 0;
+ return -EBUSY;
+
+ r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
+ if (r <= 0)
+ return r;
r = sd_dhcp6_client_new(&client);
if (r == -ENOMEM)
diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h
index f74476d575..025bbb6188 100644
--- a/src/network/networkd-dhcp6.h
+++ b/src/network/networkd-dhcp6.h
@@ -26,10 +26,12 @@ typedef struct DHCP6DelegatedPrefix {
DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free);
+bool link_dhcp6_with_address_enabled(Link *link);
bool link_dhcp6_pd_is_enabled(Link *link);
int dhcp6_pd_remove(Link *link);
int dhcp6_configure(Link *link);
int dhcp6_update_mac(Link *link);
+int dhcp6_start(Link *link);
int dhcp6_request_address(Link *link, int ir);
int dhcp6_request_prefix_delegation(Link *link);
diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf
index b2a2f55790..20c33e8c80 100644
--- a/src/network/networkd-gperf.gperf
+++ b/src/network/networkd-gperf.gperf
@@ -6,6 +6,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include <stddef.h>
#include "conf-parser.h"
#include "networkd-conf.h"
+#include "networkd-dhcp-common.h"
#include "networkd-manager.h"
#include "networkd-route.h"
%}
@@ -20,9 +21,15 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Network.SpeedMeter, config_parse_bool, 0, offsetof(Manager, use_speed_meter)
-Network.SpeedMeterIntervalSec, config_parse_sec, 0, offsetof(Manager, speed_meter_interval_usec)
-Network.ManageForeignRoutes, config_parse_bool, 0, offsetof(Manager, manage_foreign_routes)
-Network.RouteTable, config_parse_route_table_names, 0, 0
-DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid)
-DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid)
+Network.SpeedMeter, config_parse_bool, 0, offsetof(Manager, use_speed_meter)
+Network.SpeedMeterIntervalSec, config_parse_sec, 0, offsetof(Manager, speed_meter_interval_usec)
+Network.ManageForeignRoutingPolicyRules, config_parse_bool, 0, offsetof(Manager, manage_foreign_rules)
+Network.ManageForeignRoutes, config_parse_bool, 0, offsetof(Manager, manage_foreign_routes)
+Network.RouteTable, config_parse_route_table_names, 0, 0
+DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp_duid)
+DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, dhcp_duid)
+DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp6_duid)
+DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, dhcp6_duid)
+/* Deprecated */
+DHCP.DUIDType, config_parse_manager_duid_type, 0, 0
+DHCP.DUIDRawData, config_parse_manager_duid_rawdata, 0, 0
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index 65266633e3..5390c53799 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -91,6 +91,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
ll_addr->prefixlen = 16;
ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen);
ll_addr->scope = RT_SCOPE_LINK;
+ ll_addr->route_metric = IPV4LL_ROUTE_METRIC;
r = address_configure(ll_addr, link, ipv4ll_address_handler, NULL);
if (r < 0)
@@ -151,15 +152,16 @@ int ipv4ll_configure(Link *link) {
if (!link_ipv4ll_enabled(link))
return 0;
- if (!link->ipv4ll) {
- r = sd_ipv4ll_new(&link->ipv4ll);
- if (r < 0)
- return r;
+ if (link->ipv4ll)
+ return -EBUSY;
- r = sd_ipv4ll_attach_event(link->ipv4ll, link->manager->event, 0);
- if (r < 0)
- return r;
- }
+ r = sd_ipv4ll_new(&link->ipv4ll);
+ if (r < 0)
+ return r;
+
+ r = sd_ipv4ll_attach_event(link->ipv4ll, link->manager->event, 0);
+ if (r < 0)
+ return r;
if (link->sd_device &&
net_get_unique_predictable_data(link->sd_device, true, &seed) >= 0) {
diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c
index 4d0e4815db..a999b05845 100644
--- a/src/network/networkd-link-bus.c
+++ b/src/network/networkd-link-bus.c
@@ -211,7 +211,7 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
if (!route_only && dns_name_is_root(name))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
r = dns_name_normalize(name, 0, &str);
if (r < 0)
@@ -684,6 +684,8 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Link, operstate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Link, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Link, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("IPv4AddressState", "s", property_get_address_state, offsetof(Link, ipv4_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("IPv6AddressState", "s", property_get_address_state, offsetof(Link, ipv6_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BitRates", "(tt)", property_get_bit_rates, 0, 0),
@@ -854,7 +856,7 @@ int link_send_changed_strv(Link *link, char **properties) {
assert(link->manager);
assert(properties);
- if (!link->manager->bus)
+ if (sd_bus_is_ready(link->manager->bus) <= 0)
return 0;
p = link_bus_path(link);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index de453fa72e..a46aa5b9c3 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -4,6 +4,7 @@
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_link.h>
+#include <sys/socket.h>
#include <unistd.h>
#include "alloc-util.h"
@@ -163,12 +164,25 @@ static void link_update_master_operstate(Link *link, NetDev *netdev) {
link_update_operstate(master, true);
}
+static LinkAddressState address_state_from_scope(uint8_t scope) {
+ if (scope < RT_SCOPE_SITE)
+ /* universally accessible addresses found */
+ return LINK_ADDRESS_STATE_ROUTABLE;
+
+ if (scope < RT_SCOPE_HOST)
+ /* only link or site local addresses found */
+ return LINK_ADDRESS_STATE_DEGRADED;
+
+ /* no useful addresses found */
+ return LINK_ADDRESS_STATE_OFF;
+}
+
void link_update_operstate(Link *link, bool also_update_master) {
LinkOperationalState operstate;
LinkCarrierState carrier_state;
- LinkAddressState address_state;
+ LinkAddressState ipv4_address_state, ipv6_address_state, address_state;
_cleanup_strv_free_ char **p = NULL;
- uint8_t scope = RT_SCOPE_NOWHERE;
+ uint8_t ipv4_scope = RT_SCOPE_NOWHERE, ipv6_scope = RT_SCOPE_NOWHERE;
bool changed = false;
Address *address;
@@ -201,8 +215,11 @@ void link_update_operstate(Link *link, bool also_update_master) {
if (!address_is_ready(address))
continue;
- if (address->scope < scope)
- scope = address->scope;
+ if (address->family == AF_INET)
+ ipv4_scope = MIN(ipv4_scope, address->scope);
+
+ if (address->family == AF_INET6)
+ ipv6_scope = MIN(ipv6_scope, address->scope);
}
/* for operstate we also take foreign addresses into account */
@@ -210,19 +227,16 @@ void link_update_operstate(Link *link, bool also_update_master) {
if (!address_is_ready(address))
continue;
- if (address->scope < scope)
- scope = address->scope;
+ if (address->family == AF_INET)
+ ipv4_scope = MIN(ipv4_scope, address->scope);
+
+ if (address->family == AF_INET6)
+ ipv6_scope = MIN(ipv6_scope, address->scope);
}
- if (scope < RT_SCOPE_SITE)
- /* universally accessible addresses found */
- address_state = LINK_ADDRESS_STATE_ROUTABLE;
- else if (scope < RT_SCOPE_HOST)
- /* only link or site local addresses found */
- address_state = LINK_ADDRESS_STATE_DEGRADED;
- else
- /* no useful addresses found */
- address_state = LINK_ADDRESS_STATE_OFF;
+ ipv4_address_state = address_state_from_scope(ipv4_scope);
+ ipv6_address_state = address_state_from_scope(ipv6_scope);
+ address_state = address_state_from_scope(MIN(ipv4_scope, ipv6_scope));
/* Mapping of address and carrier state vs operational state
* carrier state
@@ -256,6 +270,20 @@ void link_update_operstate(Link *link, bool also_update_master) {
log_oom();
}
+ if (link->ipv4_address_state != ipv4_address_state) {
+ link->ipv4_address_state = ipv4_address_state;
+ changed = true;
+ if (strv_extend(&p, "IPv4AddressState") < 0)
+ log_oom();
+ }
+
+ if (link->ipv6_address_state != ipv6_address_state) {
+ link->ipv6_address_state = ipv6_address_state;
+ changed = true;
+ if (strv_extend(&p, "IPv6AddressState") < 0)
+ log_oom();
+ }
+
if (link->operstate != operstate) {
link->operstate = operstate;
changed = true;
@@ -545,6 +573,8 @@ static Link *link_free(Link *link) {
link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old);
link->ndisc_addresses = set_free(link->ndisc_addresses);
+ link->dhcp6_pd_prefixes = set_free(link->dhcp6_pd_prefixes);
+
link_free_engines(link);
free(link->ifname);
@@ -779,7 +809,7 @@ void link_check_ready(Link *link) {
break;
}
- if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_ipv4ll_enabled(link)) &&
+ if ((link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) || link_ipv4ll_enabled(link)) &&
!link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address &&
!link->ipv4ll_address_configured)
/* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */
@@ -1149,14 +1179,6 @@ static int link_acquire_ipv6_conf(Link *link) {
assert(link);
- if (link->ndisc) {
- log_link_debug(link, "Discovering IPv6 routers");
-
- r = sd_ndisc_start(link->ndisc);
- if (r < 0 && r != -EBUSY)
- return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
- }
-
if (link->radv) {
assert(link->radv);
assert(in6_addr_is_link_local(&link->ipv6ll_address));
@@ -1172,18 +1194,13 @@ static int link_acquire_ipv6_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
}
- if (link_dhcp6_enabled(link) && IN_SET(link->network->dhcp6_without_ra,
- DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST,
- DHCP6_CLIENT_START_MODE_SOLICIT)) {
- assert(link->dhcp6_client);
- assert(in6_addr_is_link_local(&link->ipv6ll_address));
+ r = ndisc_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
- r = dhcp6_request_address(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
- if (r < 0 && r != -EBUSY)
- return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m");
- else
- log_link_debug(link, "Acquiring DHCPv6 lease");
- }
+ r = dhcp6_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
r = dhcp6_request_prefix_delegation(link);
if (r < 0)
@@ -1200,11 +1217,9 @@ static int link_acquire_ipv4_conf(Link *link) {
assert(link->manager->event);
if (link->dhcp_client) {
- log_link_debug(link, "Acquiring DHCPv4 lease");
-
- r = sd_dhcp_client_start(link->dhcp_client);
+ r = dhcp4_start(link);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m");
+ return log_link_warning_errno(link, r, "Failed to start DHCPv4 client: %m");
} else if (link->ipv4ll) {
log_link_debug(link, "Acquiring IPv4 link-local address");
@@ -2202,7 +2217,7 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for
return r;
if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) {
- log_link_debug(link, "State is %s, dropping config", link_state_to_string(link->state));
+ log_link_debug(link, "State is %s, dropping foreign config", link_state_to_string(link->state));
r = link_drop_foreign_config(link);
if (r < 0)
return r;
@@ -2224,12 +2239,6 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for
link_set_state(link, LINK_STATE_INITIALIZED);
link->activated = false;
- /* link_configure_duid() returns 0 if it requests product UUID. In that case,
- * link_configure() is called later asynchronously. */
- r = link_configure_duid(link);
- if (r <= 0)
- return r;
-
r = link_configure(link);
if (r < 0)
return r;
@@ -2344,12 +2353,6 @@ static int link_initialized_and_synced(Link *link) {
if (r < 0)
return r;
- /* link_configure_duid() returns 0 if it requests product UUID. In that case,
- * link_configure() is called later asynchronously. */
- r = link_configure_duid(link);
- if (r <= 0)
- return r;
-
r = link_configure(link);
if (r < 0)
return r;
@@ -2638,7 +2641,7 @@ static int link_carrier_lost(Link *link) {
return r;
if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) {
- log_link_debug(link, "State is %s, dropping config", link_state_to_string(link->state));
+ log_link_debug(link, "State is %s, dropping foreign config", link_state_to_string(link->state));
r = link_drop_foreign_config(link);
if (r < 0)
return r;
@@ -2806,6 +2809,12 @@ static int link_update(Link *link, sd_netlink_message *m) {
return log_link_warning_errno(link, r, "Could not update MAC for NDisc: %m");
}
+ if (link->lldp) {
+ r = sd_lldp_set_filter_address(link->lldp, &link->hw_addr.addr.ether);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update MAC address for LLDP: %m");
+ }
+
r = ipv4_dad_update_mac(link);
if (r < 0)
return log_link_warning_errno(link, r, "Could not update MAC address in IPv4 ACD client: %m");
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index d3353a1c4f..a9f6cf61eb 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -75,6 +75,8 @@ typedef struct Link {
LinkOperationalState operstate;
LinkCarrierState carrier_state;
LinkAddressState address_state;
+ LinkAddressState ipv4_address_state;
+ LinkAddressState ipv6_address_state;
unsigned address_messages;
unsigned address_remove_messages;
@@ -149,6 +151,7 @@ typedef struct Link {
sd_dhcp6_lease *dhcp6_lease;
Set *dhcp6_addresses, *dhcp6_addresses_old;
Set *dhcp6_routes, *dhcp6_routes_old;
+ Set *dhcp6_pd_prefixes;
Set *dhcp6_pd_addresses, *dhcp6_pd_addresses_old;
Set *dhcp6_pd_routes, *dhcp6_pd_routes_old;
unsigned dhcp6_address_messages;
diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c
index bf1dd045b8..5b1b90cdd3 100644
--- a/src/network/networkd-lldp-rx.c
+++ b/src/network/networkd-lldp-rx.c
@@ -73,15 +73,16 @@ int link_lldp_rx_configure(Link *link) {
if (!link_lldp_rx_enabled(link))
return 0;
- if (!link->lldp) {
- r = sd_lldp_new(&link->lldp);
- if (r < 0)
- return r;
+ if (link->lldp)
+ return -EBUSY;
- r = sd_lldp_attach_event(link->lldp, link->manager->event, 0);
- if (r < 0)
- return r;
- }
+ r = sd_lldp_new(&link->lldp);
+ if (r < 0)
+ return r;
+
+ r = sd_lldp_attach_event(link->lldp, link->manager->event, 0);
+ if (r < 0)
+ return r;
r = sd_lldp_set_ifindex(link->lldp, link->ifindex);
if (r < 0)
diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c
index 1b97e6584e..bd77b78852 100644
--- a/src/network/networkd-lldp-tx.c
+++ b/src/network/networkd-lldp-tx.c
@@ -378,7 +378,7 @@ int link_lldp_emit_start(Link *link) {
link->lldp_tx_fast = LLDP_TX_FAST_INIT;
next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC),
- (usec_t) random_u64() % LLDP_JITTER_USEC);
+ (usec_t) random_u64() % LLDP_JITTER_USEC);
if (link->lldp_emit_event_source) {
usec_t old;
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index fce10a7e7d..0acaeb2bed 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -235,6 +235,8 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Manager, operational_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Manager, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Manager, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("IPv4AddressState", "s", property_get_address_state, offsetof(Manager, ipv4_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("IPv6AddressState", "s", property_get_address_state, offsetof(Manager, ipv6_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_METHOD_WITH_ARGS("ListLinks",
SD_BUS_NO_ARGS,
@@ -339,7 +341,7 @@ int manager_send_changed_strv(Manager *manager, char **properties) {
assert(manager);
assert(properties);
- if (!manager->bus)
+ if (sd_bus_is_ready(manager->bus) <= 0)
return 0;
return sd_bus_emit_properties_changed_strv(
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index bfdb1f8c96..562ce5ca54 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -100,8 +100,8 @@ static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *r
(void) manager_set_hostname(m, m->dynamic_hostname);
if (m->dynamic_timezone)
(void) manager_set_timezone(m, m->dynamic_timezone);
- if (m->links_requesting_uuid)
- (void) manager_request_product_uuid(m, NULL);
+ if (!set_isempty(m->links_requesting_uuid))
+ (void) manager_request_product_uuid(m);
return 0;
}
@@ -380,7 +380,11 @@ int manager_new(Manager **ret) {
*m = (Manager) {
.speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
.manage_foreign_routes = true,
+ .manage_foreign_rules = true,
.ethtool_fd = -1,
+ .dhcp_duid.type = DUID_TYPE_EN,
+ .dhcp6_duid.type = DUID_TYPE_EN,
+ .duid_product_uuid.type = DUID_TYPE_UUID,
};
m->state_file = strdup("/run/systemd/netif/state");
@@ -426,8 +430,6 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
- m->duid.type = DUID_TYPE_EN;
-
*ret = TAKE_PTR(m);
return 0;
@@ -451,7 +453,6 @@ Manager* manager_free(Manager *m) {
m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref);
m->links = hashmap_free_with_destructor(m->links, link_unref);
- m->duids_requesting_uuid = set_free(m->duids_requesting_uuid);
m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref);
m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
@@ -655,6 +656,9 @@ static int manager_enumerate_rules(Manager *m) {
assert(m);
assert(m->rtnl);
+ if (!m->manage_foreign_rules)
+ return 0;
+
r = sd_rtnl_message_new_routing_policy_rule(m->rtnl, &req, RTM_GETRULE, 0);
if (r < 0)
return r;
@@ -767,8 +771,8 @@ int manager_set_hostname(Manager *m, const char *hostname) {
if (r < 0)
return r;
- if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
- log_debug("Not connected to system bus, setting hostname later.");
+ if (sd_bus_is_ready(m->bus) <= 0) {
+ log_debug("Not connected to system bus, setting system hostname later.");
return 0;
}
@@ -784,7 +788,6 @@ int manager_set_hostname(Manager *m, const char *hostname) {
"sb",
hostname,
false);
-
if (r < 0)
return log_error_errno(r, "Could not set transient hostname: %m");
@@ -817,8 +820,8 @@ int manager_set_timezone(Manager *m, const char *tz) {
if (r < 0)
return r;
- if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
- log_debug("Not connected to system bus, setting timezone later.");
+ if (sd_bus_is_ready(m->bus) <= 0) {
+ log_debug("Not connected to system bus, setting system timezone later.");
return 0;
}
diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
index 929855daaf..e02c4ab59e 100644
--- a/src/network/networkd-manager.h
+++ b/src/network/networkd-manager.h
@@ -28,10 +28,11 @@ struct Manager {
Hashmap *polkit_registry;
int ethtool_fd;
- bool enumerating:1;
- bool dirty:1;
- bool restarting:1;
+ bool enumerating;
+ bool dirty;
+ bool restarting;
bool manage_foreign_routes;
+ bool manage_foreign_rules;
Set *dirty_links;
@@ -39,6 +40,8 @@ struct Manager {
LinkOperationalState operational_state;
LinkCarrierState carrier_state;
LinkAddressState address_state;
+ LinkAddressState ipv4_address_state;
+ LinkAddressState ipv6_address_state;
Hashmap *links;
Hashmap *netdevs;
@@ -49,11 +52,12 @@ struct Manager {
usec_t network_dirs_ts_usec;
- DUID duid;
- sd_id128_t product_uuid;
+ DUID dhcp_duid;
+ DUID dhcp6_duid;
+ DUID duid_product_uuid;
bool has_product_uuid;
+ bool product_uuid_requested;
Set *links_requesting_uuid;
- Set *duids_requesting_uuid;
char* dynamic_hostname;
char* dynamic_timezone;
diff --git a/src/network/networkd-mdb.c b/src/network/networkd-mdb.c
index f5aff72248..b3d583e6e6 100644
--- a/src/network/networkd-mdb.c
+++ b/src/network/networkd-mdb.c
@@ -208,8 +208,10 @@ int link_set_bridge_mdb(Link *link) {
if (hashmap_isempty(link->network->mdb_entries_by_section))
goto finish;
- if (!link_has_carrier(link))
- return log_link_debug(link, "Link does not have carrier yet, setting MDB entries later.");
+ if (!link_has_carrier(link)) {
+ log_link_debug(link, "Link does not have carrier yet, setting MDB entries later.");
+ return 0;
+ }
if (link->network->bridge) {
Link *master;
@@ -218,8 +220,10 @@ int link_set_bridge_mdb(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Failed to get Link object for Bridge=%s", link->network->bridge->ifname);
- if (!link_has_carrier(master))
- return log_link_debug(link, "Bridge interface %s does not have carrier yet, setting MDB entries later.", link->network->bridge->ifname);
+ if (!link_has_carrier(master)) {
+ log_link_debug(link, "Bridge interface %s does not have carrier yet, setting MDB entries later.", link->network->bridge->ifname);
+ return 0;
+ }
} else if (!streq_ptr(link->kind, "bridge")) {
log_link_warning(link, "Link is neither a bridge master nor a bridge port, ignoring [BridgeMDB] sections.");
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index ba7c184b66..af2d6a3ef7 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -530,7 +530,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
route->family = AF_INET6;
route->table = table;
- route->priority = link->network->dhcp6_route_metric;
+ route->priority = link->network->ipv6_accept_ra_route_metric;
route->protocol = RTPROT_RA;
route->pref = preference;
route->gw_family = AF_INET6;
@@ -554,7 +554,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
if (!route_gw->table_set)
route_gw->table = table;
if (!route_gw->priority_set)
- route_gw->priority = link->network->dhcp6_route_metric;
+ route_gw->priority = link->network->ipv6_accept_ra_route_metric;
if (!route_gw->protocol_set)
route_gw->protocol = RTPROT_RA;
if (!route_gw->pref_set)
@@ -814,7 +814,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
route->family = AF_INET6;
route->table = link_get_ipv6_accept_ra_route_table(link);
- route->priority = link->network->dhcp6_route_metric;
+ route->priority = link->network->ipv6_accept_ra_route_metric;
route->protocol = RTPROT_RA;
route->flags = RTM_F_PREFIX;
route->dst_prefixlen = prefixlen;
@@ -899,7 +899,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
route->family = AF_INET6;
route->table = link_get_ipv6_accept_ra_route_table(link);
- route->priority = link->network->dhcp6_route_metric;
+ route->priority = link->network->ipv6_accept_ra_route_metric;
route->protocol = RTPROT_RA;
route->pref = preference;
route->gw = gateway;
@@ -1315,15 +1315,16 @@ int ndisc_configure(Link *link) {
if (!link_ipv6_accept_ra_enabled(link))
return 0;
- if (!link->ndisc) {
- r = sd_ndisc_new(&link->ndisc);
- if (r < 0)
- return r;
+ if (link->ndisc)
+ return -EBUSY; /* Already configured. */
- r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0);
- if (r < 0)
- return r;
- }
+ r = sd_ndisc_new(&link->ndisc);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0);
+ if (r < 0)
+ return r;
r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.addr.ether);
if (r < 0)
@@ -1340,6 +1341,17 @@ int ndisc_configure(Link *link) {
return 0;
}
+int ndisc_start(Link *link) {
+ assert(link);
+
+ if (!link->ndisc || !link->dhcp6_client)
+ return 0;
+
+ log_link_debug(link, "Discovering IPv6 routers");
+
+ return sd_ndisc_start(link->ndisc);
+}
+
void ndisc_vacuum(Link *link) {
NDiscRDNSS *r;
NDiscDNSSL *d;
diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h
index 13e9547311..2ff9a8969d 100644
--- a/src/network/networkd-ndisc.h
+++ b/src/network/networkd-ndisc.h
@@ -73,6 +73,7 @@ bool link_ipv6_accept_ra_enabled(Link *link);
void network_adjust_ipv6_accept_ra(Network *network);
int ndisc_configure(Link *link);
+int ndisc_start(Link *link);
void ndisc_vacuum(Link *link);
void ndisc_flush(Link *link);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 7d38d7077a..19d52a9296 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -10,7 +10,6 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "networkd-address-label.h"
#include "networkd-address.h"
#include "networkd-can.h"
-#include "networkd-conf.h"
#include "networkd-dhcp-common.h"
#include "networkd-dhcp-server.h"
#include "networkd-dhcp4.h"
@@ -67,6 +66,7 @@ Link.Promiscuous, config_parse_tristate,
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
Link.ActivationPolicy, config_parse_activation_policy, 0, offsetof(Network, activation_policy)
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
+Link.RequiredFamilyForOnline, config_parse_required_family_for_online, 0, offsetof(Network, required_family_for_online)
SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, 0
SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, 0
SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, 0
@@ -145,6 +145,7 @@ Address.AddPrefixRoute, config_parse_address_flags,
Address.AutoJoin, config_parse_address_flags, IFA_F_MCAUTOJOIN, 0
Address.DuplicateAddressDetection, config_parse_duplicate_address_detection, 0, 0
Address.Scope, config_parse_address_scope, 0, 0
+Address.RouteMetric, config_parse_address_route_metric, 0, 0
IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0
IPv6AddressLabel.Label, config_parse_address_label, 0, 0
Neighbor.Address, config_parse_neighbor_address, 0, 0
@@ -206,17 +207,17 @@ DHCPv4.RequestOptions, config_parse_dhcp_request_options,
DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
DHCPv4.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
DHCPv4.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
-DHCPv4.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
+DHCPv4.RequestBroadcast, config_parse_tristate, 0, offsetof(Network, dhcp_broadcast)
DHCPv4.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
DHCPv4.MUDURL, config_parse_dhcp_mud_url, 0, 0
DHCPv4.MaxAttempts, config_parse_dhcp_max_attempts, 0, 0
DHCPv4.UserClass, config_parse_dhcp_user_or_vendor_class, AF_INET, offsetof(Network, dhcp_user_class)
-DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid)
-DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid)
+DHCPv4.IAID, config_parse_iaid, AF_INET, 0
+DHCPv4.DUIDType, config_parse_network_duid_type, 0, 0
+DHCPv4.DUIDRawData, config_parse_network_duid_rawdata, 0, 0
DHCPv4.RouteMetric, config_parse_dhcp_route_metric, 0, 0
DHCPv4.RouteTable, config_parse_section_route_table, 0, 0
DHCPv4.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
-DHCPv4.IAID, config_parse_iaid, 0, 0
DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release)
DHCPv4.SendDecline, config_parse_bool, 0, offsetof(Network, dhcp_send_decline)
@@ -242,19 +243,23 @@ DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool,
DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0
DHCPv6.WithoutRA, config_parse_dhcp6_client_start_mode, 0, offsetof(Network, dhcp6_without_ra)
DHCPv6.SendOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_options)
-DHCPv6.RouteMetric, config_parse_dhcp_route_metric, 0, 0
+DHCPv6.IAID, config_parse_iaid, AF_INET6, 0
+DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Network, dhcp6_duid)
+DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, dhcp6_duid)
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_ipv6_accept_ra_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.DHCPv6Client, config_parse_ipv6_accept_ra_start_dhcp6_client, 0, offsetof(Network, ipv6_accept_ra_start_dhcp6_client)
IPv6AcceptRA.RouteTable, config_parse_section_route_table, 0, 0
+IPv6AcceptRA.RouteMetric, config_parse_dhcp_route_metric, 0, 0
IPv6AcceptRA.RouterAllowList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_allow_listed_router)
IPv6AcceptRA.RouterDenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_router)
IPv6AcceptRA.PrefixAllowList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_allow_listed_prefix)
IPv6AcceptRA.PrefixDenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_prefix)
IPv6AcceptRA.RouteAllowList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_allow_listed_route_prefix)
IPv6AcceptRA.RouteDenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_route_prefix)
+DHCPServer.RelayTarget, config_parse_dhcp_server_relay_target, 0, 0
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_DNS].emit)
@@ -276,6 +281,7 @@ DHCPServer.PoolOffset, config_parse_uint32,
DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
DHCPServer.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_vendor_options)
DHCPServer.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_options)
+DHCPServer.BindToInterface, config_parse_bool, 0, offsetof(Network, dhcp_server_bind_to_interface)
Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost)
Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu)
Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin)
@@ -305,6 +311,7 @@ DHCPv6PrefixDelegation.Announce, config_parse_bool,
DHCPv6PrefixDelegation.Assign, config_parse_bool, 0, offsetof(Network, dhcp6_pd_assign)
DHCPv6PrefixDelegation.ManageTemporaryAddress, config_parse_bool, 0, offsetof(Network, dhcp6_pd_manage_temporary_address)
DHCPv6PrefixDelegation.Token, config_parse_dhcp6_pd_token, 0, offsetof(Network, dhcp6_pd_token)
+DHCPv6PrefixDelegation.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp6_pd_route_metric)
IPv6SendRA.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec)
IPv6SendRA.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
IPv6SendRA.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
@@ -320,6 +327,7 @@ IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags,
IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6Prefix.Assign, config_parse_prefix_assign, 0, 0
+IPv6Prefix.RouteMetric, config_parse_prefix_metric, 0, 0
IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0
IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0
LLDP.MUDURL, config_parse_lldp_mud, 0, 0
@@ -473,21 +481,22 @@ DHCP.UseRoutes, config_parse_bool,
DHCP.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
-DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
+DHCP.RequestBroadcast, config_parse_tristate, 0, offsetof(Network, dhcp_broadcast)
DHCP.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
DHCP.UserClass, config_parse_dhcp_user_or_vendor_class, AF_INET, offsetof(Network, dhcp_user_class)
-DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid)
-DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid)
+DHCP.IAID, config_parse_iaid, AF_INET, 0
+DHCP.DUIDType, config_parse_network_duid_type, 0, 0
+DHCP.DUIDRawData, config_parse_network_duid_rawdata, 0, 0
DHCP.RouteMetric, config_parse_dhcp_route_metric, 0, 0
DHCP.RouteTable, config_parse_section_route_table, 0, 0
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
-DHCP.IAID, config_parse_iaid, 0, 0
DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_rapid_commit)
DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, 0
DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
+DHCPv6.RouteMetric, config_parse_dhcp_route_metric, 0, 0
IPv6AcceptRA.DenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_prefix)
IPv6AcceptRA.BlackList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_prefix)
TrafficControlQueueingDiscipline.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index f532536f1c..9833722f71 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -41,45 +41,6 @@
/* Let's assume that anything above this number is a user misconfiguration. */
#define MAX_NTP_SERVERS 128
-/* Set defaults following RFC7844 */
-void network_apply_anonymize_if_set(Network *network) {
- if (!network->dhcp_anonymize)
- return;
- /* RFC7844 3.7
- SHOULD NOT send the Host Name option */
- network->dhcp_send_hostname = false;
- /* RFC7844 section 3.:
- MAY contain the Client Identifier option
- Section 3.5:
- clients MUST use client identifiers based solely
- on the link-layer address */
- /* NOTE: Using MAC, as it does not reveal extra information,
- * and some servers might not answer if this option is not sent */
- network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
- /* RFC 7844 3.10:
- SHOULD NOT use the Vendor Class Identifier option */
- network->dhcp_vendor_class_identifier = mfree(network->dhcp_vendor_class_identifier);
- /* RFC7844 section 3.6.:
- The client intending to protect its privacy SHOULD only request a
- minimal number of options in the PRL and SHOULD also randomly shuffle
- the ordering of option codes in the PRL. If this random ordering
- cannot be implemented, the client MAY order the option codes in the
- PRL by option code number (lowest to highest).
- */
- /* NOTE: dhcp_use_mtu is false by default,
- * though it was not initiallized to any value in network_load_one.
- * Maybe there should be another var called *send*?
- * (to use the MTU sent by the server but to do not send
- * the option in the PRL). */
- network->dhcp_use_mtu = false;
- /* NOTE: when Anonymize=yes, the PRL route options are sent by default,
- * but this is needed to use them. */
- network->dhcp_use_routes = true;
- /* RFC7844 section 3.6.
- * same comments as previous option */
- network->dhcp_use_timezone = false;
-}
-
static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret_netdev) {
const char *kind_string;
NetDev *netdev;
@@ -223,9 +184,6 @@ int network_verify(Network *network) {
network->dhcp_use_mtu = false;
}
- if (network->dhcp_use_gateway < 0)
- network->dhcp_use_gateway = network->dhcp_use_routes;
-
if (network->dhcp_critical >= 0) {
if (network->keep_configuration >= 0)
log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
@@ -340,12 +298,10 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.allmulticast = -1,
.promiscuous = -1,
- .configure_without_carrier = false,
.ignore_carrier_loss = -1,
.keep_configuration = _KEEP_CONFIGURATION_INVALID,
- .dhcp = ADDRESS_FAMILY_NO,
- .duid.type = _DUID_TYPE_INVALID,
+ .dhcp_duid.type = _DUID_TYPE_INVALID,
.dhcp_critical = -1,
.dhcp_use_ntp = true,
.dhcp_use_sip = true,
@@ -353,28 +309,20 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.dhcp_use_hostname = true,
.dhcp_use_routes = true,
.dhcp_use_gateway = -1,
- /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
.dhcp_send_hostname = true,
.dhcp_send_release = true,
- /* To enable/disable RFC7844 Anonymity Profiles */
- .dhcp_anonymize = false,
.dhcp_route_metric = DHCP_ROUTE_METRIC,
- /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
- .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
+ .dhcp_client_identifier = _DHCP_CLIENT_ID_INVALID,
.dhcp_route_table = RT_TABLE_MAIN,
- .dhcp_route_table_set = false,
- /* NOTE: from man: UseMTU=... Defaults to false*/
- .dhcp_use_mtu = false,
- /* NOTE: from man: UseTimezone=... Defaults to "no".*/
- .dhcp_use_timezone = false,
.dhcp_ip_service_type = -1,
+ .dhcp_broadcast = -1,
.dhcp6_use_address = true,
.dhcp6_use_dns = true,
.dhcp6_use_hostname = true,
.dhcp6_use_ntp = true,
.dhcp6_rapid_commit = true,
- .dhcp6_route_metric = DHCP_ROUTE_METRIC,
+ .dhcp6_duid.type = _DUID_TYPE_INVALID,
.dhcp6_pd = -1,
.dhcp6_pd_announce = true,
@@ -382,10 +330,10 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.dhcp6_pd_manage_temporary_address = true,
.dhcp6_pd_subnet_id = -1,
+ .dhcp_server_bind_to_interface = true,
.dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
.dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
.dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true,
-
.dhcp_server_emit_router = true,
.dhcp_server_emit_timezone = true,
@@ -432,7 +380,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.ipv6_accept_ra_use_autonomous_prefix = true,
.ipv6_accept_ra_use_onlink_prefix = true,
.ipv6_accept_ra_route_table = RT_TABLE_MAIN,
- .ipv6_accept_ra_route_table_set = false,
+ .ipv6_accept_ra_route_metric = DHCP_ROUTE_METRIC,
.ipv6_accept_ra_start_dhcp6_client = IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES,
.can_triple_sampling = -1,
@@ -505,8 +453,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
if (r < 0)
return r;
- network_apply_anonymize_if_set(network);
-
r = network_add_ipv4ll_route(network);
if (r < 0)
log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename);
@@ -655,10 +601,6 @@ static Network *network_free(Network *network) {
ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
- if (network->manager &&
- network->manager->duids_requesting_uuid)
- set_remove(network->manager->duids_requesting_uuid, &network->duid);
-
free(network->name);
free(network->dhcp_server_timezone);
@@ -1196,6 +1138,9 @@ int config_parse_required_for_online(
return 0;
}
+DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
+ "Failed to parse RequiredFamilyForOnline= setting");
+
DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
"Failed to parse KeepConfiguration= setting");
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 6fe8a76c13..0013de5518 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -104,6 +104,7 @@ struct Network {
bool unmanaged;
bool required_for_online; /* Is this network required to be considered online? */
LinkOperationalStateRange required_operstate_for_online;
+ AddressFamily required_family_for_online;
ActivationPolicy activation_policy;
/* misc settings */
@@ -117,9 +118,9 @@ struct Network {
/* DHCP Client Support */
AddressFamily dhcp;
DHCPClientIdentifier dhcp_client_identifier;
- DUID duid;
- uint32_t iaid;
- bool iaid_set;
+ DUID dhcp_duid;
+ uint32_t dhcp_iaid;
+ bool dhcp_iaid_set;
char *dhcp_vendor_class_identifier;
char *dhcp_mudurl;
char **dhcp_user_class;
@@ -135,7 +136,7 @@ struct Network {
int dhcp_ip_service_type;
bool dhcp_anonymize;
bool dhcp_send_hostname;
- bool dhcp_broadcast;
+ int dhcp_broadcast;
bool dhcp_use_dns;
bool dhcp_use_dns_set;
bool dhcp_routes_to_dns;
@@ -168,9 +169,11 @@ struct Network {
bool dhcp6_rapid_commit;
DHCPUseDomains dhcp6_use_domains;
bool dhcp6_use_domains_set;
+ uint32_t dhcp6_iaid;
+ bool dhcp6_iaid_set;
+ bool dhcp6_iaid_set_explicitly;
+ DUID dhcp6_duid;
uint8_t dhcp6_pd_length;
- uint32_t dhcp6_route_metric;
- bool dhcp6_route_metric_set;
char *dhcp6_mudurl;
char **dhcp6_user_class;
char **dhcp6_vendor_class;
@@ -184,6 +187,8 @@ struct Network {
/* DHCP Server Support */
bool dhcp_server;
+ bool dhcp_server_bind_to_interface;
+ struct in_addr dhcp_server_relay_target;
NetworkDHCPServerEmitAddress dhcp_server_emit[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
bool dhcp_server_emit_router;
bool dhcp_server_emit_timezone;
@@ -218,6 +223,7 @@ struct Network {
bool dhcp6_pd_assign;
bool dhcp6_pd_manage_temporary_address;
int64_t dhcp6_pd_subnet_id;
+ uint32_t dhcp6_pd_route_metric;
union in_addr_union dhcp6_pd_token;
/* Bridge Support */
@@ -274,10 +280,12 @@ struct Network {
bool ipv6_accept_ra_use_onlink_prefix;
bool active_slave;
bool primary_slave;
- bool ipv6_accept_ra_route_table_set;
DHCPUseDomains ipv6_accept_ra_use_domains;
IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client;
uint32_t ipv6_accept_ra_route_table;
+ bool ipv6_accept_ra_route_table_set;
+ uint32_t ipv6_accept_ra_route_metric;
+ bool ipv6_accept_ra_route_metric_set;
Set *ndisc_deny_listed_router;
Set *ndisc_allow_listed_router;
Set *ndisc_deny_listed_prefix;
@@ -347,6 +355,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors);
CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
+CONFIG_PARSER_PROTOTYPE(config_parse_required_family_for_online);
CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);
diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c
index 470095c897..c32cc70798 100644
--- a/src/network/networkd-nexthop.c
+++ b/src/network/networkd-nexthop.c
@@ -711,8 +711,10 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: could not get nexthop family, ignoring: %m");
return 0;
- } else if (!IN_SET(tmp->family, AF_INET, AF_INET6))
- return log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
+ } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
+ log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
+ return 0;
+ }
r = sd_rtnl_message_nexthop_get_protocol(message, &tmp->protocol);
if (r < 0) {
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index 8d8c21c0d8..52c9afcc5e 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -382,6 +382,45 @@ int config_parse_prefix_assign(
return 0;
}
+int config_parse_prefix_metric(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return log_oom();
+
+ r = safe_atou32(rvalue, &p->route_metric);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(p);
+
+ return 0;
+}
+
int config_parse_route_prefix(
const char *unit,
const char *filename,
@@ -644,6 +683,9 @@ int radv_configure(Link *link) {
if (!link_radv_enabled(link))
return 0;
+ if (link->radv)
+ return -EBUSY;
+
r = sd_radv_new(&link->radv);
if (r < 0)
return r;
diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h
index 73d2f24545..f6efd32697 100644
--- a/src/network/networkd-radv.h
+++ b/src/network/networkd-radv.h
@@ -33,6 +33,7 @@ typedef struct Prefix {
sd_radv_prefix *radv_prefix;
bool assign;
+ uint32_t route_metric;
} Prefix;
typedef struct RoutePrefix {
@@ -64,6 +65,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_assign);
+CONFIG_PARSER_PROTOTYPE(config_parse_prefix_metric);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains);
CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix);
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index a74541a6c9..9ed30d69ed 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -628,11 +628,11 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin
(void) route_protocol_full_to_string_alloc(route->protocol, &proto);
log_link_debug(link,
- "%s route: dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s, nexthop: %"PRIu32,
+ "%s route: dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32,
str, strna(dst), strna(src), strna(gw), strna(prefsrc),
strna(scope), strna(table), strna(proto),
strna(route_type_to_string(route->type)),
- route->nexthop_id);
+ route->nexthop_id, route->priority);
}
}
diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c
index a7fddfd58f..03bdd4e640 100644
--- a/src/network/networkd-routing-policy-rule.c
+++ b/src/network/networkd-routing-policy-rule.c
@@ -977,6 +977,8 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
case RTM_NEWRULE:
if (rule)
log_routing_policy_rule_debug(tmp, tmp->family, "Received remembered", NULL, m);
+ else if (!m->manage_foreign_routes)
+ log_routing_policy_rule_debug(tmp, tmp->family, "Ignoring received foreign", NULL, m);
else {
log_routing_policy_rule_debug(tmp, tmp->family, "Remembering foreign", NULL, m);
r = routing_policy_rule_consume_foreign(m, TAKE_PTR(tmp));
diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c
index 96232018dd..f8243cc3ee 100644
--- a/src/network/networkd-state-file.c
+++ b/src/network/networkd-state-file.c
@@ -105,10 +105,11 @@ static int ordered_set_put_in4_addrv(
int manager_save(Manager *m) {
_cleanup_ordered_set_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL;
- const char *operstate_str, *carrier_state_str, *address_state_str;
+ const char *operstate_str, *carrier_state_str, *address_state_str, *ipv4_address_state_str, *ipv6_address_state_str;
LinkOperationalState operstate = LINK_OPERSTATE_OFF;
LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF;
- LinkAddressState address_state = LINK_ADDRESS_STATE_OFF;
+ LinkAddressState ipv4_address_state = LINK_ADDRESS_STATE_OFF, ipv6_address_state = LINK_ADDRESS_STATE_OFF,
+ address_state = LINK_ADDRESS_STATE_OFF;
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_strv_free_ char **p = NULL;
_cleanup_fclose_ FILE *f = NULL;
@@ -124,14 +125,11 @@ int manager_save(Manager *m) {
if (link->flags & IFF_LOOPBACK)
continue;
- if (link->operstate > operstate)
- operstate = link->operstate;
-
- if (link->carrier_state > carrier_state)
- carrier_state = link->carrier_state;
-
- if (link->address_state > address_state)
- address_state = link->address_state;
+ operstate = MAX(operstate, link->operstate);
+ carrier_state = MAX(carrier_state, link->carrier_state);
+ address_state = MAX(address_state, link->address_state);
+ ipv4_address_state = MAX(ipv4_address_state, link->ipv4_address_state);
+ ipv6_address_state = MAX(ipv6_address_state, link->ipv6_address_state);
if (!link->network)
continue;
@@ -226,6 +224,12 @@ int manager_save(Manager *m) {
address_state_str = link_address_state_to_string(address_state);
assert(address_state_str);
+ ipv4_address_state_str = link_address_state_to_string(ipv4_address_state);
+ assert(ipv4_address_state_str);
+
+ ipv6_address_state_str = link_address_state_to_string(ipv6_address_state);
+ assert(ipv6_address_state_str);
+
r = fopen_temporary(m->state_file, &f, &temp_path);
if (r < 0)
return r;
@@ -236,8 +240,10 @@ int manager_save(Manager *m) {
"# This is private data. Do not parse.\n"
"OPER_STATE=%s\n"
"CARRIER_STATE=%s\n"
- "ADDRESS_STATE=%s\n",
- operstate_str, carrier_state_str, address_state_str);
+ "ADDRESS_STATE=%s\n"
+ "IPV4_ADDRESS_STATE=%s\n"
+ "IPV6_ADDRESS_STATE=%s\n",
+ operstate_str, carrier_state_str, address_state_str, ipv4_address_state_str, ipv6_address_state_str);
ordered_set_print(f, "DNS=", dns);
ordered_set_print(f, "NTP=", ntp);
@@ -273,6 +279,18 @@ int manager_save(Manager *m) {
log_oom();
}
+ if (m->ipv4_address_state != ipv4_address_state) {
+ m->ipv4_address_state = ipv4_address_state;
+ if (strv_extend(&p, "IPv4AddressState") < 0)
+ log_oom();
+ }
+
+ if (m->ipv6_address_state != ipv6_address_state) {
+ m->ipv6_address_state = ipv6_address_state;
+ if (strv_extend(&p, "IPv6AddressState") < 0)
+ log_oom();
+ }
+
if (p) {
r = manager_send_changed_strv(m, p);
if (r < 0)
@@ -376,7 +394,7 @@ static void serialize_addresses(
}
int link_save(Link *link) {
- const char *admin_state, *oper_state, *carrier_state, *address_state;
+ const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state;
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -403,6 +421,12 @@ int link_save(Link *link) {
address_state = link_address_state_to_string(link->address_state);
assert(address_state);
+ ipv4_address_state = link_address_state_to_string(link->ipv4_address_state);
+ assert(ipv4_address_state);
+
+ ipv6_address_state = link_address_state_to_string(link->ipv6_address_state);
+ assert(ipv6_address_state);
+
r = fopen_temporary(link->state_file, &f, &temp_path);
if (r < 0)
return r;
@@ -414,8 +438,10 @@ int link_save(Link *link) {
"ADMIN_STATE=%s\n"
"OPER_STATE=%s\n"
"CARRIER_STATE=%s\n"
- "ADDRESS_STATE=%s\n",
- admin_state, oper_state, carrier_state, address_state);
+ "ADDRESS_STATE=%s\n"
+ "IPV4_ADDRESS_STATE=%s\n"
+ "IPV6_ADDRESS_STATE=%s\n",
+ admin_state, oper_state, carrier_state, address_state, ipv4_address_state, ipv6_address_state);
if (link->network) {
char **dhcp6_domains = NULL, **dhcp_domains = NULL;
@@ -431,6 +457,9 @@ int link_save(Link *link) {
st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
+ fprintf(f, "REQUIRED_FAMILY_FOR_ONLINE=%s\n",
+ link_required_address_family_to_string(link->network->required_family_for_online));
+
fprintf(f, "ACTIVATION_POLICY=%s\n",
activation_policy_to_string(link->network->activation_policy));
diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h
index 01675e8b5c..4926fc5eb7 100644
--- a/src/network/networkd-util.h
+++ b/src/network/networkd-util.h
@@ -8,18 +8,9 @@
#include "hashmap.h"
#include "log.h"
#include "macro.h"
+#include "network-util.h"
#include "string-util.h"
-typedef enum AddressFamily {
- /* This is a bitmask, though it usually doesn't feel that way! */
- ADDRESS_FAMILY_NO = 0,
- ADDRESS_FAMILY_IPV4 = 1 << 0,
- ADDRESS_FAMILY_IPV6 = 1 << 1,
- ADDRESS_FAMILY_YES = ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6,
- _ADDRESS_FAMILY_MAX,
- _ADDRESS_FAMILY_INVALID = -EINVAL,
-} AddressFamily;
-
typedef struct NetworkConfigSection {
unsigned line;
bool invalid;
diff --git a/src/network/networkd.conf b/src/network/networkd.conf
index 4850ba61f2..38dc9f1f79 100644
--- a/src/network/networkd.conf
+++ b/src/network/networkd.conf
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the networkd.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# See networkd.conf(5) for details.
@@ -15,8 +15,14 @@
[Network]
#SpeedMeter=no
#SpeedMeterIntervalSec=10sec
+#ManageForeignRoutingPolicyRules=yes
#ManageForeignRoutes=yes
+#RouteTable=
-[DHCP]
+[DHCPv4]
+#DUIDType=vendor
+#DUIDRawData=
+
+[DHCPv6]
#DUIDType=vendor
#DUIDRawData=
diff --git a/src/network/wait-online/link.c b/src/network/wait-online/link.c
index f2d556f099..5a33d563c2 100644
--- a/src/network/wait-online/link.c
+++ b/src/network/wait-online/link.c
@@ -97,7 +97,8 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
}
int link_update_monitor(Link *l) {
- _cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *state = NULL;
+ _cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *required_family = NULL,
+ *ipv4_address_state = NULL, *ipv6_address_state = NULL, *state = NULL;
int r, ret = 0;
assert(l);
@@ -135,6 +136,47 @@ int link_update_monitor(Link *l) {
l->operational_state = s;
}
+ r = sd_network_link_get_required_family_for_online(l->ifindex, &required_family);
+ if (r < 0)
+ ret = log_link_debug_errno(l, r, "Failed to get required address family, ignoring: %m");
+ else if (isempty(required_family))
+ l->required_family = ADDRESS_FAMILY_NO;
+ else {
+ AddressFamily f;
+
+ f = link_required_address_family_from_string(required_family);
+ if (f < 0)
+ ret = log_link_debug_errno(l, f, "Failed to parse required address family, ignoring: %m");
+ else
+ l->required_family = f;
+ }
+
+ r = sd_network_link_get_ipv4_address_state(l->ifindex, &ipv4_address_state);
+ if (r < 0)
+ ret = log_link_debug_errno(l, r, "Failed to get IPv4 address state, ignoring: %m");
+ else {
+ LinkAddressState s;
+
+ s = link_address_state_from_string(ipv4_address_state);
+ if (s < 0)
+ ret = log_link_debug_errno(l, s, "Failed to parse IPv4 address state, ignoring: %m");
+ else
+ l->ipv4_address_state = s;
+ }
+
+ r = sd_network_link_get_ipv6_address_state(l->ifindex, &ipv6_address_state);
+ if (r < 0)
+ ret = log_link_debug_errno(l, r, "Failed to get IPv6 address state, ignoring: %m");
+ else {
+ LinkAddressState s;
+
+ s = link_address_state_from_string(ipv6_address_state);
+ if (s < 0)
+ ret = log_link_debug_errno(l, s, "Failed to parse IPv6 address state, ignoring: %m");
+ else
+ l->ipv6_address_state = s;
+ }
+
r = sd_network_link_get_setup_state(l->ifindex, &state);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get setup state, ignoring: %m");
diff --git a/src/network/wait-online/link.h b/src/network/wait-online/link.h
index 3aa8357293..3072a91e56 100644
--- a/src/network/wait-online/link.h
+++ b/src/network/wait-online/link.h
@@ -19,6 +19,9 @@ struct Link {
bool required_for_online;
LinkOperationalStateRange required_operstate;
LinkOperationalState operational_state;
+ AddressFamily required_family;
+ LinkAddressState ipv4_address_state;
+ LinkAddressState ipv6_address_state;
char *state;
};
diff --git a/src/network/wait-online/manager.c b/src/network/wait-online/manager.c
index 1438b27445..e1df0345c0 100644
--- a/src/network/wait-online/manager.c
+++ b/src/network/wait-online/manager.c
@@ -32,6 +32,13 @@ static bool manager_ignore_link(Manager *m, Link *link) {
}
static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
+ AddressFamily required_family;
+ bool needs_ipv4;
+ bool needs_ipv6;
+
+ assert(m);
+ assert(l);
+
/* This returns the following:
* -EAGAIN: not processed by udev or networkd
* 0: operstate is not enough
@@ -60,6 +67,34 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange
return 0;
}
+ required_family = m->required_family > 0 ? m->required_family : l->required_family;
+ needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4;
+ needs_ipv6 = required_family & ADDRESS_FAMILY_IPV6;
+
+ if (s.min >= LINK_OPERSTATE_DEGRADED) {
+ if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED) {
+ log_link_debug(l, "No routable or link-local IPv4 address is configured.");
+ return 0;
+ }
+
+ if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_DEGRADED) {
+ log_link_debug(l, "No routable or link-local IPv6 address is configured.");
+ return 0;
+ }
+ }
+
+ if (s.min >= LINK_OPERSTATE_ROUTABLE) {
+ if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_ROUTABLE) {
+ log_link_debug(l, "No routable IPv4 address is configured.");
+ return 0;
+ }
+
+ if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_ROUTABLE) {
+ log_link_debug(l, "No routable IPv6 address is configured.");
+ return 0;
+ }
+ }
+
return 1;
}
@@ -298,6 +333,7 @@ static int manager_network_monitor_listen(Manager *m) {
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
LinkOperationalStateRange required_operstate,
+ AddressFamily required_family,
bool any, usec_t timeout) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
@@ -312,6 +348,7 @@ int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
.interfaces = interfaces,
.ignore = ignore,
.required_operstate = required_operstate,
+ .required_family = required_family,
.any = any,
};
diff --git a/src/network/wait-online/manager.h b/src/network/wait-online/manager.h
index 9892a43dc9..f2e091638c 100644
--- a/src/network/wait-online/manager.h
+++ b/src/network/wait-online/manager.h
@@ -21,6 +21,7 @@ struct Manager {
char **ignore;
LinkOperationalStateRange required_operstate;
+ AddressFamily required_family;
bool any;
sd_netlink *rtnl;
@@ -35,6 +36,7 @@ struct Manager {
Manager* manager_free(Manager *m);
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
LinkOperationalStateRange required_operstate,
+ AddressFamily required_family,
bool any, usec_t timeout);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
diff --git a/src/network/wait-online/wait-online.c b/src/network/wait-online/wait-online.c
index ca0116e7f3..c65b885d2c 100644
--- a/src/network/wait-online/wait-online.c
+++ b/src/network/wait-online/wait-online.c
@@ -19,6 +19,7 @@ static usec_t arg_timeout = 120 * USEC_PER_SEC;
static Hashmap *arg_interfaces = NULL;
static char **arg_ignore = NULL;
static LinkOperationalStateRange arg_required_operstate = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
+static AddressFamily arg_required_family = ADDRESS_FAMILY_NO;
static bool arg_any = false;
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
@@ -42,6 +43,8 @@ static int help(void) {
" --ignore=INTERFACE Don't take these interfaces into account\n"
" -o --operational-state=MIN_OPERSTATE[:MAX_OPERSTATE]\n"
" Required operational state\n"
+ " -4 --ipv4 Requires at least one IPv4 address\n"
+ " -6 --ipv6 Requires at least one IPv6 address\n"
" --any Wait until at least one of the interfaces is online\n"
" --timeout=SECS Maximum time to wait for network connectivity\n"
"\nSee the %s for details.\n",
@@ -53,7 +56,7 @@ static int help(void) {
static int parse_interface_with_operstate_range(const char *str) {
_cleanup_free_ char *ifname = NULL;
- _cleanup_free_ LinkOperationalStateRange *range;
+ _cleanup_free_ LinkOperationalStateRange *range = NULL;
const char *p;
int r;
@@ -111,6 +114,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "interface", required_argument, NULL, 'i' },
{ "ignore", required_argument, NULL, ARG_IGNORE },
{ "operational-state", required_argument, NULL, 'o' },
+ { "ipv4", no_argument, NULL, '4' },
+ { "ipv6", no_argument, NULL, '6' },
{ "any", no_argument, NULL, ARG_ANY },
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
{}
@@ -121,7 +126,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hi:qo:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hi:qo:46", options, NULL)) >= 0)
switch (c) {
@@ -159,6 +164,15 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
+
+ case '4':
+ arg_required_family |= ADDRESS_FAMILY_IPV4;
+ break;
+
+ case '6':
+ arg_required_family |= ADDRESS_FAMILY_IPV6;
+ break;
+
case ARG_ANY:
arg_any = true;
break;
@@ -197,7 +211,7 @@ static int run(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
- r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_any, arg_timeout);
+ r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_required_family, arg_any, arg_timeout);
if (r < 0)
return log_error_errno(r, "Could not create manager: %m");
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 164a330207..ed9b31e63b 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -5483,6 +5483,8 @@ static int run(int argc, char *argv[]) {
arg_image,
&arg_verity_settings,
NULL,
+ loop->uevent_seqnum_not_before,
+ loop->timestamp_not_before,
dissect_image_flags,
&dissected_image);
if (r == -ENOPKG) {
diff --git a/src/oom/oomd-manager.c b/src/oom/oomd-manager.c
index c3e84aadde..49dc5eb26e 100644
--- a/src/oom/oomd-manager.c
+++ b/src/oom/oomd-manager.c
@@ -299,8 +299,7 @@ static int acquire_managed_oom_connect(Manager *m) {
return 0;
}
-static int monitor_cgroup_contexts_handler(sd_event_source *s, uint64_t usec, void *userdata) {
- _cleanup_set_free_ Set *targets = NULL;
+static int monitor_swap_contexts_handler(sd_event_source *s, uint64_t usec, void *userdata) {
Manager *m = userdata;
usec_t usec_now;
int r;
@@ -313,7 +312,7 @@ static int monitor_cgroup_contexts_handler(sd_event_source *s, uint64_t usec, vo
if (r < 0)
return log_error_errno(r, "Failed to reset event timer: %m");
- r = sd_event_source_set_time_relative(s, INTERVAL_USEC);
+ r = sd_event_source_set_time_relative(s, SWAP_INTERVAL_USEC);
if (r < 0)
return log_error_errno(r, "Failed to set relative time for timer: %m");
@@ -324,97 +323,29 @@ static int monitor_cgroup_contexts_handler(sd_event_source *s, uint64_t usec, vo
return log_error_errno(r, "Failed to acquire varlink connection: %m");
}
- /* Update the cgroups used for detection/action */
- r = update_monitored_cgroup_contexts(&m->monitored_swap_cgroup_contexts);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_debug_errno(r, "Failed to update monitored swap cgroup contexts, ignoring: %m");
-
- r = update_monitored_cgroup_contexts(&m->monitored_mem_pressure_cgroup_contexts);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_debug_errno(r, "Failed to update monitored memory pressure cgroup contexts, ignoring: %m");
-
- r = update_monitored_cgroup_contexts_candidates(
- m->monitored_mem_pressure_cgroup_contexts, &m->monitored_mem_pressure_cgroup_contexts_candidates);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_debug_errno(r, "Failed to update monitored memory pressure candidate cgroup contexts, ignoring: %m");
-
+ /* We still try to acquire swap information for oomctl even if no units want swap monitoring */
r = oomd_system_context_acquire("/proc/swaps", &m->system_context);
- /* If there aren't units depending on swap actions, the only error we exit on is ENOMEM.
+ /* If there are no units depending on swap actions, the only error we exit on is ENOMEM.
* Allow ENOENT in the event that swap is disabled on the system. */
- if (r == -ENOMEM || (r < 0 && r != -ENOENT && !hashmap_isempty(m->monitored_swap_cgroup_contexts)))
- return log_error_errno(r, "Failed to acquire system context: %m");
- else if (r == -ENOENT)
+ if (r == -ENOENT) {
zero(m->system_context);
+ return 0;
+ } else if (r == -ENOMEM || (r < 0 && !hashmap_isempty(m->monitored_swap_cgroup_contexts)))
+ return log_error_errno(r, "Failed to acquire system context: %m");
- if (oomd_memory_reclaim(m->monitored_mem_pressure_cgroup_contexts))
- m->last_reclaim_at = usec_now;
+ /* Return early if nothing is requesting swap monitoring */
+ if (hashmap_isempty(m->monitored_swap_cgroup_contexts))
+ return 0;
- /* If we're still recovering from a kill, don't try to kill again yet */
- if (m->post_action_delay_start > 0) {
- if (m->post_action_delay_start + POST_ACTION_DELAY_USEC > usec_now)
- return 0;
- else
- m->post_action_delay_start = 0;
- }
-
- r = oomd_pressure_above(m->monitored_mem_pressure_cgroup_contexts, m->default_mem_pressure_duration_usec, &targets);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_debug_errno(r, "Failed to check if memory pressure exceeded limits, ignoring: %m");
- else if (r == 1) {
- /* Check if there was reclaim activity in the given interval. The concern is the following case:
- * Pressure climbed, a lot of high-frequency pages were reclaimed, and we killed the offending
- * cgroup. Even after this, well-behaved processes will fault in recently resident pages and
- * this will cause pressure to remain high. Thus if there isn't any reclaim pressure, no need
- * to kill something (it won't help anyways). */
- if ((usec_now - m->last_reclaim_at) <= RECLAIM_DURATION_USEC) {
- OomdCGroupContext *t;
-
- SET_FOREACH(t, targets) {
- _cleanup_free_ char *selected = NULL;
- char ts[FORMAT_TIMESPAN_MAX];
-
- log_debug("Memory pressure for %s is %lu.%02lu%% > %lu.%02lu%% for > %s with reclaim activity",
- t->path,
- LOAD_INT(t->memory_pressure.avg10), LOAD_FRAC(t->memory_pressure.avg10),
- LOAD_INT(t->mem_pressure_limit), LOAD_FRAC(t->mem_pressure_limit),
- format_timespan(ts, sizeof ts,
- m->default_mem_pressure_duration_usec,
- USEC_PER_SEC));
-
- r = oomd_kill_by_pgscan_rate(m->monitored_mem_pressure_cgroup_contexts_candidates, t->path, m->dry_run, &selected);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_notice_errno(r, "Failed to kill any cgroup(s) under %s based on pressure: %m", t->path);
- else {
- /* Don't act on all the high pressure cgroups at once; return as soon as we kill one */
- m->post_action_delay_start = usec_now;
- if (selected)
- log_notice("Killed %s due to memory pressure for %s being %lu.%02lu%% > %lu.%02lu%%"
- " for > %s with reclaim activity",
- selected, t->path,
- LOAD_INT(t->memory_pressure.avg10), LOAD_FRAC(t->memory_pressure.avg10),
- LOAD_INT(t->mem_pressure_limit), LOAD_FRAC(t->mem_pressure_limit),
- format_timespan(ts, sizeof ts,
- m->default_mem_pressure_duration_usec,
- USEC_PER_SEC));
- return 0;
- }
- }
- }
- }
+ /* Note that m->monitored_swap_cgroup_contexts does not need to be updated every interval because only the
+ * system context is used for deciding whether the swap threshold is hit. m->monitored_swap_cgroup_contexts
+ * is only used to decide which cgroups to kill (and even then only the resource usages of its descendent
+ * nodes are the ones that matter). */
if (oomd_swap_free_below(&m->system_context, 10000 - m->swap_used_limit_permyriad)) {
_cleanup_hashmap_free_ Hashmap *candidates = NULL;
_cleanup_free_ char *selected = NULL;
+ uint64_t threshold;
log_debug("Swap used (%"PRIu64") / total (%"PRIu64") is more than " PERMYRIAD_AS_PERCENT_FORMAT_STR,
m->system_context.swap_used, m->system_context.swap_total,
@@ -426,13 +357,13 @@ static int monitor_cgroup_contexts_handler(sd_event_source *s, uint64_t usec, vo
if (r < 0)
log_debug_errno(r, "Failed to get monitored swap cgroup candidates, ignoring: %m");
- r = oomd_kill_by_swap_usage(candidates, m->dry_run, &selected);
+ threshold = m->system_context.swap_total * THRESHOLD_SWAP_USED_PERCENT / 100;
+ r = oomd_kill_by_swap_usage(candidates, threshold, m->dry_run, &selected);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
log_notice_errno(r, "Failed to kill any cgroup(s) based on swap: %m");
else {
- m->post_action_delay_start = usec_now;
if (selected)
log_notice("Killed %s due to swap used (%"PRIu64") / total (%"PRIu64") being more than "
PERMYRIAD_AS_PERCENT_FORMAT_STR,
@@ -445,14 +376,183 @@ static int monitor_cgroup_contexts_handler(sd_event_source *s, uint64_t usec, vo
return 0;
}
-static int monitor_cgroup_contexts(Manager *m) {
+static void clear_candidate_hashmapp(Manager **m) {
+ if (*m)
+ hashmap_clear((*m)->monitored_mem_pressure_cgroup_contexts_candidates);
+}
+
+static int monitor_memory_pressure_contexts_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+ /* Don't want to use stale candidate data. Setting this will clear the candidate hashmap on return unless we
+ * update the candidate data (in which case clear_candidates will be NULL). */
+ _cleanup_(clear_candidate_hashmapp) Manager *clear_candidates = userdata;
+ _cleanup_set_free_ Set *targets = NULL;
+ bool in_post_action_delay = false;
+ Manager *m = userdata;
+ usec_t usec_now;
+ int r;
+
+ assert(s);
+ assert(userdata);
+
+ /* Reset timer */
+ r = sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &usec_now);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reset event timer: %m");
+
+ r = sd_event_source_set_time_relative(s, MEM_PRESSURE_INTERVAL_USEC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set relative time for timer: %m");
+
+ /* Reconnect if our connection dropped */
+ if (!m->varlink) {
+ r = acquire_managed_oom_connect(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire varlink connection: %m");
+ }
+
+ /* Return early if nothing is requesting memory pressure monitoring */
+ if (hashmap_isempty(m->monitored_mem_pressure_cgroup_contexts))
+ return 0;
+
+ /* Update the cgroups used for detection/action */
+ r = update_monitored_cgroup_contexts(&m->monitored_mem_pressure_cgroup_contexts);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to update monitored memory pressure cgroup contexts, ignoring: %m");
+
+ r = update_monitored_cgroup_contexts_candidates(
+ m->monitored_mem_pressure_cgroup_contexts, &m->monitored_mem_pressure_cgroup_contexts_candidates);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to update monitored memory pressure candidate cgroup contexts, ignoring: %m");
+
+ /* Since pressure counters are lagging, we need to wait a bit after a kill to ensure we don't read stale
+ * values and go on a kill storm. */
+ if (m->mem_pressure_post_action_delay_start > 0) {
+ if (m->mem_pressure_post_action_delay_start + POST_ACTION_DELAY_USEC > usec_now)
+ in_post_action_delay = true;
+ else
+ m->mem_pressure_post_action_delay_start = 0;
+ }
+
+ r = oomd_pressure_above(m->monitored_mem_pressure_cgroup_contexts, m->default_mem_pressure_duration_usec, &targets);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to check if memory pressure exceeded limits, ignoring: %m");
+ else if (r == 1 && !in_post_action_delay) {
+ OomdCGroupContext *t;
+ SET_FOREACH(t, targets) {
+ _cleanup_free_ char *selected = NULL;
+ char ts[FORMAT_TIMESPAN_MAX];
+
+ /* Check if there was reclaim activity in the given interval. The concern is the following case:
+ * Pressure climbed, a lot of high-frequency pages were reclaimed, and we killed the offending
+ * cgroup. Even after this, well-behaved processes will fault in recently resident pages and
+ * this will cause pressure to remain high. Thus if there isn't any reclaim pressure, no need
+ * to kill something (it won't help anyways). */
+ if ((now(CLOCK_MONOTONIC) - t->last_had_mem_reclaim) > RECLAIM_DURATION_USEC)
+ continue;
+
+ log_debug("Memory pressure for %s is %lu.%02lu%% > %lu.%02lu%% for > %s with reclaim activity",
+ t->path,
+ LOAD_INT(t->memory_pressure.avg10), LOAD_FRAC(t->memory_pressure.avg10),
+ LOAD_INT(t->mem_pressure_limit), LOAD_FRAC(t->mem_pressure_limit),
+ format_timespan(ts, sizeof ts,
+ m->default_mem_pressure_duration_usec,
+ USEC_PER_SEC));
+
+ r = update_monitored_cgroup_contexts_candidates(
+ m->monitored_mem_pressure_cgroup_contexts, &m->monitored_mem_pressure_cgroup_contexts_candidates);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to update monitored memory pressure candidate cgroup contexts, ignoring: %m");
+ else
+ clear_candidates = NULL;
+
+ r = oomd_kill_by_pgscan_rate(m->monitored_mem_pressure_cgroup_contexts_candidates, t->path, m->dry_run, &selected);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_notice_errno(r, "Failed to kill any cgroup(s) under %s based on pressure: %m", t->path);
+ else {
+ /* Don't act on all the high pressure cgroups at once; return as soon as we kill one */
+ m->mem_pressure_post_action_delay_start = usec_now;
+ if (selected)
+ log_notice("Killed %s due to memory pressure for %s being %lu.%02lu%% > %lu.%02lu%%"
+ " for > %s with reclaim activity",
+ selected, t->path,
+ LOAD_INT(t->memory_pressure.avg10), LOAD_FRAC(t->memory_pressure.avg10),
+ LOAD_INT(t->mem_pressure_limit), LOAD_FRAC(t->mem_pressure_limit),
+ format_timespan(ts, sizeof ts,
+ m->default_mem_pressure_duration_usec,
+ USEC_PER_SEC));
+ return 0;
+ }
+ }
+ } else {
+ /* If any monitored cgroup is over their pressure limit, get all the kill candidates for every
+ * monitored cgroup. This saves CPU cycles from doing it every interval by only doing it when a kill
+ * might happen.
+ * Candidate cgroup data will continue to get updated during the post-action delay period in case
+ * pressure continues to be high after a kill. */
+ OomdCGroupContext *c;
+ HASHMAP_FOREACH(c, m->monitored_mem_pressure_cgroup_contexts) {
+ if (c->mem_pressure_limit_hit_start == 0)
+ continue;
+
+ r = update_monitored_cgroup_contexts_candidates(
+ m->monitored_mem_pressure_cgroup_contexts, &m->monitored_mem_pressure_cgroup_contexts_candidates);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to update monitored memory pressure candidate cgroup contexts, ignoring: %m");
+ else {
+ clear_candidates = NULL;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int monitor_swap_contexts(Manager *m) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ int r;
+
+ assert(m);
+ assert(m->event);
+
+ r = sd_event_add_time(m->event, &s, CLOCK_MONOTONIC, 0, 0, monitor_swap_contexts_handler, m);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_exit_on_failure(s, true);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(s, SD_EVENT_ON);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(s, "oomd-swap-timer");
+
+ m->swap_context_event_source = TAKE_PTR(s);
+ return 0;
+}
+
+static int monitor_memory_pressure_contexts(Manager *m) {
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
int r;
assert(m);
assert(m->event);
- r = sd_event_add_time(m->event, &s, CLOCK_MONOTONIC, 0, 0, monitor_cgroup_contexts_handler, m);
+ r = sd_event_add_time(m->event, &s, CLOCK_MONOTONIC, 0, 0, monitor_memory_pressure_contexts_handler, m);
if (r < 0)
return r;
@@ -464,9 +564,9 @@ static int monitor_cgroup_contexts(Manager *m) {
if (r < 0)
return r;
- (void) sd_event_source_set_description(s, "oomd-timer");
+ (void) sd_event_source_set_description(s, "oomd-memory-pressure-timer");
- m->cgroup_context_event_source = TAKE_PTR(s);
+ m->mem_pressure_context_event_source = TAKE_PTR(s);
return 0;
}
@@ -474,7 +574,8 @@ Manager* manager_free(Manager *m) {
assert(m);
varlink_close_unref(m->varlink);
- sd_event_source_unref(m->cgroup_context_event_source);
+ sd_event_source_unref(m->swap_context_event_source);
+ sd_event_source_unref(m->mem_pressure_context_event_source);
sd_event_unref(m->event);
bus_verify_polkit_async_registry_free(m->polkit_registry);
@@ -596,7 +697,11 @@ int manager_start(
if (r < 0)
return r;
- r = monitor_cgroup_contexts(m);
+ r = monitor_memory_pressure_contexts(m);
+ if (r < 0)
+ return r;
+
+ r = monitor_swap_contexts(m);
if (r < 0)
return r;
diff --git a/src/oom/oomd-manager.h b/src/oom/oomd-manager.h
index 9c580c8a24..dc170f2bda 100644
--- a/src/oom/oomd-manager.h
+++ b/src/oom/oomd-manager.h
@@ -7,10 +7,9 @@
#include "varlink.h"
/* Polling interval for monitoring stats */
-#define INTERVAL_USEC (1 * USEC_PER_SEC)
-
-/* Used to weight the averages */
-#define AVERAGE_SIZE_DECAY 4
+#define SWAP_INTERVAL_USEC 150000 /* 0.15 seconds */
+/* Pressure counters are lagging (~2 seconds) compared to swap so polling too frequently just wastes CPU */
+#define MEM_PRESSURE_INTERVAL_USEC (1 * USEC_PER_SEC)
/* Take action if 10s of memory pressure > 60 for more than 30s. We use the "full" value from PSI so this is the
* percentage of time all tasks were delayed (i.e. unproductive).
@@ -20,6 +19,9 @@
#define DEFAULT_MEM_PRESSURE_LIMIT_PERCENT 60
#define DEFAULT_SWAP_USED_LIMIT_PERCENT 90
+/* Only tackle candidates with large swap usage. */
+#define THRESHOLD_SWAP_USED_PERCENT 5
+
#define RECLAIM_DURATION_USEC (30 * USEC_PER_SEC)
#define POST_ACTION_DELAY_USEC (15 * USEC_PER_SEC)
@@ -44,10 +46,10 @@ struct Manager {
OomdSystemContext system_context;
- usec_t last_reclaim_at;
- usec_t post_action_delay_start;
+ usec_t mem_pressure_post_action_delay_start;
- sd_event_source *cgroup_context_event_source;
+ sd_event_source *swap_context_event_source;
+ sd_event_source *mem_pressure_context_event_source;
Varlink *varlink;
};
diff --git a/src/oom/oomd-util.c b/src/oom/oomd-util.c
index 894d23a83a..5bf81479c9 100644
--- a/src/oom/oomd-util.c
+++ b/src/oom/oomd-util.c
@@ -82,17 +82,17 @@ int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret) {
if (ctx->memory_pressure.avg10 > ctx->mem_pressure_limit) {
usec_t diff;
- if (ctx->last_hit_mem_pressure_limit == 0)
- ctx->last_hit_mem_pressure_limit = now(CLOCK_MONOTONIC);
+ if (ctx->mem_pressure_limit_hit_start == 0)
+ ctx->mem_pressure_limit_hit_start = now(CLOCK_MONOTONIC);
- diff = now(CLOCK_MONOTONIC) - ctx->last_hit_mem_pressure_limit;
+ diff = now(CLOCK_MONOTONIC) - ctx->mem_pressure_limit_hit_start;
if (diff >= duration) {
r = set_put(targets, ctx);
if (r < 0)
return -ENOMEM;
}
} else
- ctx->last_hit_mem_pressure_limit = 0;
+ ctx->mem_pressure_limit_hit_start = 0;
}
if (!set_isempty(targets)) {
@@ -104,34 +104,21 @@ int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret) {
return 0;
}
-bool oomd_memory_reclaim(Hashmap *h) {
- uint64_t pgscan = 0, pgscan_of = 0, last_pgscan = 0, last_pgscan_of = 0;
- OomdCGroupContext *ctx;
-
- assert(h);
-
- /* If sum of all the current pgscan values are greater than the sum of all the last_pgscan values,
- * there was reclaim activity. Used along with pressure checks to decide whether to take action. */
+uint64_t oomd_pgscan_rate(const OomdCGroupContext *c) {
+ uint64_t last_pgscan;
- HASHMAP_FOREACH(ctx, h) {
- uint64_t sum;
+ assert(c);
- sum = pgscan + ctx->pgscan;
- if (sum < pgscan || sum < ctx->pgscan)
- pgscan_of++; /* count overflows */
- pgscan = sum;
-
- sum = last_pgscan + ctx->last_pgscan;
- if (sum < last_pgscan || sum < ctx->last_pgscan)
- last_pgscan_of++; /* count overflows */
- last_pgscan = sum;
+ /* If last_pgscan > pgscan, assume the cgroup was recreated and reset last_pgscan to zero.
+ * pgscan is monotonic and in practice should not decrease (except in the recreation case). */
+ last_pgscan = c->last_pgscan;
+ if (c->last_pgscan > c->pgscan) {
+ log_debug("Last pgscan %"PRIu64" greater than current pgscan %"PRIu64" for %s. Using last pgscan of zero.",
+ c->last_pgscan, c->pgscan, c->path);
+ last_pgscan = 0;
}
- /* overflow counts are the same, return sums comparison */
- if (last_pgscan_of == pgscan_of)
- return pgscan > last_pgscan;
-
- return pgscan_of > last_pgscan_of;
+ return c->pgscan - last_pgscan;
}
bool oomd_swap_free_below(const OomdSystemContext *ctx, int threshold_permyriad) {
@@ -246,7 +233,7 @@ int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char
return ret;
}
-int oomd_kill_by_swap_usage(Hashmap *h, bool dry_run, char **ret_selected) {
+int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, char **ret_selected) {
_cleanup_free_ OomdCGroupContext **sorted = NULL;
int n, r, ret = 0;
@@ -257,12 +244,12 @@ int oomd_kill_by_swap_usage(Hashmap *h, bool dry_run, char **ret_selected) {
if (n < 0)
return n;
- /* Try to kill cgroups with non-zero swap usage until we either succeed in
- * killing or we get to a cgroup with no swap usage. */
+ /* Try to kill cgroups with non-zero swap usage until we either succeed in killing or we get to a cgroup with
+ * no swap usage. Threshold killing only cgroups with more than threshold swap usage. */
for (int i = 0; i < n; i++) {
- /* Skip over cgroups with no resource usage.
- * Continue break since there might be "avoid" cgroups at the end. */
- if (sorted[i]->swap_usage == 0)
+ /* Skip over cgroups with not enough swap usage. Don't break since there might be "avoid"
+ * cgroups at the end. */
+ if (sorted[i]->swap_usage <= threshold_usage)
continue;
r = oomd_cgroup_kill(sorted[i]->path, true, dry_run);
@@ -430,9 +417,13 @@ int oomd_insert_cgroup_context(Hashmap *old_h, Hashmap *new_h, const char *path)
if (old_ctx) {
curr_ctx->last_pgscan = old_ctx->pgscan;
curr_ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
- curr_ctx->last_hit_mem_pressure_limit = old_ctx->last_hit_mem_pressure_limit;
+ curr_ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
+ curr_ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
}
+ if (oomd_pgscan_rate(curr_ctx) > 0)
+ curr_ctx->last_had_mem_reclaim = now(CLOCK_MONOTONIC);
+
r = hashmap_put(new_h, curr_ctx->path, curr_ctx);
if (r < 0)
return r;
@@ -456,7 +447,11 @@ void oomd_update_cgroup_contexts_between_hashmaps(Hashmap *old_h, Hashmap *curr_
ctx->last_pgscan = old_ctx->pgscan;
ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
- ctx->last_hit_mem_pressure_limit = old_ctx->last_hit_mem_pressure_limit;
+ ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
+ ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
+
+ if (oomd_pgscan_rate(ctx) > 0)
+ ctx->last_had_mem_reclaim = now(CLOCK_MONOTONIC);
}
}
diff --git a/src/oom/oomd-util.h b/src/oom/oomd-util.h
index 51423130d1..81fdc5e088 100644
--- a/src/oom/oomd-util.h
+++ b/src/oom/oomd-util.h
@@ -32,10 +32,10 @@ struct OomdCGroupContext {
ManagedOOMPreference preference;
- /* These are only used by oomd_pressure_above for acting on high memory pressure. */
+ /* These are only used for acting on high memory pressure. */
loadavg_t mem_pressure_limit;
- usec_t mem_pressure_duration_usec;
- usec_t last_hit_mem_pressure_limit;
+ usec_t mem_pressure_limit_hit_start;
+ usec_t last_had_mem_reclaim;
};
struct OomdSystemContext {
@@ -51,23 +51,22 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(OomdCGroupContext*, oomd_cgroup_context_free);
/* Scans all the OomdCGroupContexts in `h` and returns 1 and a set of pointers to those OomdCGroupContexts in `ret`
* if any of them have exceeded their supplied memory pressure limits for the `duration` length of time.
- * `last_hit_mem_pressure_limit` is updated accordingly for each entry when the limit is exceeded, and when it returns
+ * `mem_pressure_limit_hit_start` is updated accordingly for the first time the limit is exceeded, and when it returns
* below the limit.
* Returns 0 and sets `ret` to an empty set if no entries exceeded limits for `duration`.
* Returns -ENOMEM for allocation errors. */
int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret);
-/* Sum up current OomdCGroupContexts' pgscan values and last interval's pgscan values in `h`. Returns true if the
- * current sum is higher than the last interval's sum (there was some reclaim activity). */
-bool oomd_memory_reclaim(Hashmap *h);
-
/* Returns true if the amount of swap free is below the permyriad of swap specified by `threshold_permyriad`. */
bool oomd_swap_free_below(const OomdSystemContext *ctx, int threshold_permyriad);
+/* Returns pgscan - last_pgscan, accounting for corner cases. */
+uint64_t oomd_pgscan_rate(const OomdCGroupContext *c);
+
/* The compare functions will sort from largest to smallest, putting all the contexts with "avoid" at the end
* (after the smallest values). */
static inline int compare_pgscan_rate_and_memory_usage(OomdCGroupContext * const *c1, OomdCGroupContext * const *c2) {
- uint64_t last1, last2;
+ uint64_t diff1, diff2;
int r;
assert(c1);
@@ -77,22 +76,9 @@ static inline int compare_pgscan_rate_and_memory_usage(OomdCGroupContext * const
if (r != 0)
return r;
- /* If last_pgscan > pgscan, assume the cgroup was recreated and reset last_pgscan to zero. */
- last2 = (*c2)->last_pgscan;
- if ((*c2)->last_pgscan > (*c2)->pgscan) {
- log_info("Last pgscan %" PRIu64 "greater than current pgscan %" PRIu64 "for %s. Using last pgscan of zero.",
- (*c2)->last_pgscan, (*c2)->pgscan, (*c2)->path);
- last2 = 0;
- }
-
- last1 = (*c1)->last_pgscan;
- if ((*c1)->last_pgscan > (*c1)->pgscan) {
- log_info("Last pgscan %" PRIu64 "greater than current pgscan %" PRIu64 "for %s. Using last pgscan of zero.",
- (*c1)->last_pgscan, (*c1)->pgscan, (*c1)->path);
- last1 = 0;
- }
-
- r = CMP((*c2)->pgscan - last2, (*c1)->pgscan - last1);
+ diff1 = oomd_pgscan_rate(*c1);
+ diff2 = oomd_pgscan_rate(*c2);
+ r = CMP(diff2, diff1);
if (r != 0)
return r;
@@ -125,7 +111,7 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run);
* everything in `h` is a candidate.
* Returns the killed cgroup in ret_selected. */
int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char **ret_selected);
-int oomd_kill_by_swap_usage(Hashmap *h, bool dry_run, char **ret_selected);
+int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, char **ret_selected);
int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret);
int oomd_system_context_acquire(const char *proc_swaps_path, OomdSystemContext *ret);
diff --git a/src/oom/oomd.c b/src/oom/oomd.c
index 6e2a5889d1..deb7b094d5 100644
--- a/src/oom/oomd.c
+++ b/src/oom/oomd.c
@@ -155,6 +155,9 @@ static int run(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
+ if (arg_mem_pressure_usec > 0 && arg_mem_pressure_usec < 1 * USEC_PER_SEC)
+ log_error_errno(SYNTHETIC_ERRNO(EINVAL), "DefaultMemoryPressureDurationSec= must be 0 or at least 1s");
+
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Failed to create manager: %m");
diff --git a/src/oom/oomd.conf b/src/oom/oomd.conf
index d1dca0d64d..b3a457f887 100644
--- a/src/oom/oomd.conf
+++ b/src/oom/oomd.conf
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the oomd.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# Use 'systemd-analyze cat-config systemd/oomd.conf' to display the full config.
diff --git a/src/oom/test-oomd-util.c b/src/oom/test-oomd-util.c
index 278fc305b3..7ebea29c1a 100644
--- a/src/oom/test-oomd-util.c
+++ b/src/oom/test-oomd-util.c
@@ -160,9 +160,10 @@ static void test_oomd_cgroup_context_acquire_and_insert(void) {
assert_se(oomd_insert_cgroup_context(NULL, h1, cgroup) == -EEXIST);
/* make sure certain values from h1 get updated in h2 */
- c1->pgscan = 5555;
+ c1->pgscan = UINT64_MAX;
c1->mem_pressure_limit = 6789;
- c1->last_hit_mem_pressure_limit = 42;
+ c1->mem_pressure_limit_hit_start = 42;
+ c1->last_had_mem_reclaim = 888;
assert_se(h2 = hashmap_new(&oomd_cgroup_ctx_hash_ops));
assert_se(oomd_insert_cgroup_context(h1, h2, cgroup) == 0);
c1 = hashmap_get(h1, cgroup);
@@ -170,9 +171,10 @@ static void test_oomd_cgroup_context_acquire_and_insert(void) {
assert_se(c1);
assert_se(c2);
assert_se(c1 != c2);
- assert_se(c2->last_pgscan == 5555);
+ assert_se(c2->last_pgscan == UINT64_MAX);
assert_se(c2->mem_pressure_limit == 6789);
- assert_se(c2->last_hit_mem_pressure_limit == 42);
+ assert_se(c2->mem_pressure_limit_hit_start == 42);
+ assert_se(c2->last_had_mem_reclaim == 888); /* assumes the live pgscan is less than UINT64_MAX */
/* Assert that avoid/omit are not set if the cgroup is not owned by root */
if (test_xattrs) {
@@ -189,20 +191,22 @@ static void test_oomd_update_cgroup_contexts_between_hashmaps(void) {
char **paths = STRV_MAKE("/0.slice",
"/1.slice");
- OomdCGroupContext ctx_old[3] = {
+ OomdCGroupContext ctx_old[2] = {
{ .path = paths[0],
.mem_pressure_limit = 5,
- .last_hit_mem_pressure_limit = 777,
+ .mem_pressure_limit_hit_start = 777,
+ .last_had_mem_reclaim = 888,
.pgscan = 57 },
{ .path = paths[1],
.mem_pressure_limit = 6,
- .last_hit_mem_pressure_limit = 888,
+ .mem_pressure_limit_hit_start = 888,
+ .last_had_mem_reclaim = 888,
.pgscan = 42 },
};
- OomdCGroupContext ctx_new[3] = {
+ OomdCGroupContext ctx_new[2] = {
{ .path = paths[0],
- .pgscan = 100 },
+ .pgscan = 57 },
{ .path = paths[1],
.pgscan = 101 },
};
@@ -221,13 +225,15 @@ static void test_oomd_update_cgroup_contexts_between_hashmaps(void) {
assert_se(c_new = hashmap_get(h_new, "/0.slice"));
assert_se(c_old->pgscan == c_new->last_pgscan);
assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit);
- assert_se(c_old->last_hit_mem_pressure_limit == c_new->last_hit_mem_pressure_limit);
+ assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
+ assert_se(c_old->last_had_mem_reclaim == c_new->last_had_mem_reclaim);
assert_se(c_old = hashmap_get(h_old, "/1.slice"));
assert_se(c_new = hashmap_get(h_new, "/1.slice"));
assert_se(c_old->pgscan == c_new->last_pgscan);
assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit);
- assert_se(c_old->last_hit_mem_pressure_limit == c_new->last_hit_mem_pressure_limit);
+ assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
+ assert_se(c_new->last_had_mem_reclaim > c_old->last_had_mem_reclaim);
}
static void test_oomd_system_context_acquire(void) {
@@ -290,7 +296,7 @@ static void test_oomd_pressure_above(void) {
assert_se(oomd_pressure_above(h1, 0 /* duration */, &t1) == 1);
assert_se(set_contains(t1, &ctx[0]) == true);
assert_se(c = hashmap_get(h1, "/herp.slice"));
- assert_se(c->last_hit_mem_pressure_limit > 0);
+ assert_se(c->mem_pressure_limit_hit_start > 0);
/* Low memory pressure */
assert_se(h2 = hashmap_new(&string_hash_ops));
@@ -298,7 +304,7 @@ static void test_oomd_pressure_above(void) {
assert_se(oomd_pressure_above(h2, 0 /* duration */, &t2) == 0);
assert_se(t2 == NULL);
assert_se(c = hashmap_get(h2, "/derp.slice"));
- assert_se(c->last_hit_mem_pressure_limit == 0);
+ assert_se(c->mem_pressure_limit_hit_start == 0);
/* High memory pressure w/ multiple cgroups */
assert_se(hashmap_put(h1, "/derp.slice", &ctx[1]) >= 0);
@@ -306,50 +312,9 @@ static void test_oomd_pressure_above(void) {
assert_se(set_contains(t3, &ctx[0]) == true);
assert_se(set_size(t3) == 1);
assert_se(c = hashmap_get(h1, "/herp.slice"));
- assert_se(c->last_hit_mem_pressure_limit > 0);
+ assert_se(c->mem_pressure_limit_hit_start > 0);
assert_se(c = hashmap_get(h1, "/derp.slice"));
- assert_se(c->last_hit_mem_pressure_limit == 0);
-}
-
-static void test_oomd_memory_reclaim(void) {
- _cleanup_hashmap_free_ Hashmap *h1 = NULL;
- char **paths = STRV_MAKE("/0.slice",
- "/1.slice",
- "/2.slice",
- "/3.slice",
- "/4.slice");
-
- OomdCGroupContext ctx[5] = {
- { .path = paths[0],
- .last_pgscan = 100,
- .pgscan = 100 },
- { .path = paths[1],
- .last_pgscan = 100,
- .pgscan = 100 },
- { .path = paths[2],
- .last_pgscan = 77,
- .pgscan = 33 },
- { .path = paths[3],
- .last_pgscan = UINT64_MAX,
- .pgscan = 100 },
- { .path = paths[4],
- .last_pgscan = 100,
- .pgscan = UINT64_MAX },
- };
-
- assert_se(h1 = hashmap_new(&string_hash_ops));
- assert_se(hashmap_put(h1, paths[0], &ctx[0]) >= 0);
- assert_se(hashmap_put(h1, paths[1], &ctx[1]) >= 0);
- assert_se(oomd_memory_reclaim(h1) == false);
-
- assert_se(hashmap_put(h1, paths[2], &ctx[2]) >= 0);
- assert_se(oomd_memory_reclaim(h1) == false);
-
- assert_se(hashmap_put(h1, paths[4], &ctx[4]) >= 0);
- assert_se(oomd_memory_reclaim(h1) == true);
-
- assert_se(hashmap_put(h1, paths[3], &ctx[3]) >= 0);
- assert_se(oomd_memory_reclaim(h1) == false);
+ assert_se(c->mem_pressure_limit_hit_start == 0);
}
static void test_oomd_swap_free_below(void) {
@@ -468,7 +433,6 @@ int main(void) {
test_oomd_update_cgroup_contexts_between_hashmaps();
test_oomd_system_context_acquire();
test_oomd_pressure_above();
- test_oomd_memory_reclaim();
test_oomd_swap_free_below();
test_oomd_sort_cgroups();
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 195a909bf3..3b31109952 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -26,6 +26,7 @@
#include "conf-parser.h"
#include "cryptsetup-util.h"
#include "def.h"
+#include "dirent-util.h"
#include "efivars.h"
#include "errno-util.h"
#include "fd-util.h"
@@ -44,6 +45,7 @@
#include "mkdir.h"
#include "mkfs-util.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "parse-argument.h"
#include "parse-util.h"
#include "path-util.h"
@@ -76,12 +78,6 @@
/* LUKS2 takes off 16M of the partition size with its metadata by default */
#define LUKS2_METADATA_SIZE (16*1024*1024)
-#if !HAVE_LIBCRYPTSETUP
-struct crypt_device;
-static inline void sym_crypt_free(struct crypt_device* cd) {}
-static inline void sym_crypt_freep(struct crypt_device** cd) {}
-#endif
-
/* Note: When growing and placing new partitions we always align to 4K sector size. It's how newer hard disks
* are designed, and if everything is aligned to that performance is best. And for older hard disks with 512B
* sector size devices were generally assumed to have an even number of sectors, hence at the worst we'll
@@ -98,6 +94,7 @@ static enum {
static bool arg_dry_run = true;
static const char *arg_node = NULL;
static char *arg_root = NULL;
+static char *arg_image = NULL;
static char *arg_definitions = NULL;
static bool arg_discard = true;
static bool arg_can_factory_reset = false;
@@ -116,6 +113,7 @@ static char *arg_tpm2_device = NULL;
static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
@@ -161,13 +159,18 @@ struct Partition {
FreeArea *allocated_to_area;
char *copy_blocks_path;
+ bool copy_blocks_auto;
int copy_blocks_fd;
uint64_t copy_blocks_size;
char *format;
char **copy_files;
+ char **make_directories;
EncryptMode encrypt;
+ uint64_t gpt_flags;
+ int read_only;
+
LIST_FIELDS(Partition, partitions);
};
@@ -239,6 +242,7 @@ static Partition *partition_new(void) {
.offset = UINT64_MAX,
.copy_blocks_fd = -1,
.copy_blocks_size = UINT64_MAX,
+ .read_only = -1,
};
return p;
@@ -262,6 +266,7 @@ static Partition* partition_free(Partition *p) {
free(p->format);
strv_free(p->copy_files);
+ strv_free(p->make_directories);
return mfree(p);
}
@@ -967,6 +972,9 @@ static int config_parse_label(
assert(rvalue);
assert(label);
+ /* Nota bene: the empty label is a totally valid one. Let's hence not follow our usual rule of
+ * assigning the empty string to reset to default here, but really accept it as label to set. */
+
r = specifier_printf(rvalue, specifier_table, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
@@ -1139,11 +1147,9 @@ static int config_parse_copy_files(
return 0;
}
- if (!path_is_absolute(resolved_source) || !path_is_normalized(resolved_source)) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Invalid path name in CopyFiles= source, ignoring: %s", resolved_source);
+ r = path_simplify_and_warn(resolved_source, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
return 0;
- }
r = specifier_printf(target, specifier_table, NULL, &resolved_target);
if (r < 0) {
@@ -1152,11 +1158,9 @@ static int config_parse_copy_files(
return 0;
}
- if (!path_is_absolute(resolved_target) || !path_is_normalized(resolved_target)) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Invalid path name in CopyFiles= source, ignoring: %s", resolved_target);
+ r = path_simplify_and_warn(resolved_target, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
return 0;
- }
r = strv_consume_pair(&partition->copy_files, TAKE_PTR(resolved_source), TAKE_PTR(resolved_target));
if (r < 0)
@@ -1165,26 +1169,153 @@ static int config_parse_copy_files(
return 0;
}
+static int config_parse_copy_blocks(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *d = NULL;
+ Partition *partition = data;
+ int r;
+
+ assert(rvalue);
+ assert(partition);
+
+ if (isempty(rvalue)) {
+ partition->copy_blocks_path = mfree(partition->copy_blocks_path);
+ partition->copy_blocks_auto = false;
+ return 0;
+ }
+
+ if (streq(rvalue, "auto")) {
+ partition->copy_blocks_path = mfree(partition->copy_blocks_path);
+ partition->copy_blocks_auto = true;
+ return 0;
+ }
+
+ r = specifier_printf(rvalue, specifier_table, NULL, &d);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to expand specifiers in CopyBlocks= source path, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ return 0;
+
+ free_and_replace(partition->copy_blocks_path, d);
+ partition->copy_blocks_auto = false;
+ return 0;
+}
+
+static int config_parse_make_dirs(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Partition *partition = data;
+ const char *p = rvalue;
+ int r;
+
+ assert(rvalue);
+ assert(partition);
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *d = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+
+ r = specifier_printf(word, specifier_table, NULL, &d);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to expand specifiers in MakeDirectories= parameter, ignoring: %s", word);
+ continue;
+ }
+
+ r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ continue;
+
+ r = strv_consume(&partition->make_directories, TAKE_PTR(d));
+ if (r < 0)
+ return log_oom();
+ }
+}
+
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt, encrypt_mode, EncryptMode, ENCRYPT_OFF, "Invalid encryption mode");
+static int config_parse_gpt_flags(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ uint64_t *gpt_flags = data;
+ int r;
+
+ assert(rvalue);
+ assert(gpt_flags);
+
+ r = safe_atou64(rvalue, gpt_flags);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse Flags= value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
static int partition_read_definition(Partition *p, const char *path) {
ConfigTableItem table[] = {
- { "Partition", "Type", config_parse_type, 0, &p->type_uuid },
- { "Partition", "Label", config_parse_label, 0, &p->new_label },
- { "Partition", "UUID", config_parse_id128, 0, &p->new_uuid },
- { "Partition", "Priority", config_parse_int32, 0, &p->priority },
- { "Partition", "Weight", config_parse_weight, 0, &p->weight },
- { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
- { "Partition", "SizeMinBytes", config_parse_size4096, 1, &p->size_min },
- { "Partition", "SizeMaxBytes", config_parse_size4096, -1, &p->size_max },
- { "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min },
- { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max },
- { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
- { "Partition", "CopyBlocks", config_parse_path, 0, &p->copy_blocks_path },
- { "Partition", "Format", config_parse_fstype, 0, &p->format },
- { "Partition", "CopyFiles", config_parse_copy_files, 0, p },
- { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
+ { "Partition", "Type", config_parse_type, 0, &p->type_uuid },
+ { "Partition", "Label", config_parse_label, 0, &p->new_label },
+ { "Partition", "UUID", config_parse_id128, 0, &p->new_uuid },
+ { "Partition", "Priority", config_parse_int32, 0, &p->priority },
+ { "Partition", "Weight", config_parse_weight, 0, &p->weight },
+ { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
+ { "Partition", "SizeMinBytes", config_parse_size4096, 1, &p->size_min },
+ { "Partition", "SizeMaxBytes", config_parse_size4096, -1, &p->size_max },
+ { "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min },
+ { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max },
+ { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
+ { "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p },
+ { "Partition", "Format", config_parse_fstype, 0, &p->format },
+ { "Partition", "CopyFiles", config_parse_copy_files, 0, p },
+ { "Partition", "MakeDirectories", config_parse_make_dirs, 0, p },
+ { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
+ { "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags },
+ { "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only },
{}
};
int r;
@@ -1210,21 +1341,28 @@ static int partition_read_definition(Partition *p, const char *path) {
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Type= not defined, refusing.");
- if (p->copy_blocks_path && (p->format || !strv_isempty(p->copy_files)))
+ if ((p->copy_blocks_path || p->copy_blocks_auto) &&
+ (p->format || !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
- "Format= and CopyBlocks= cannot be combined, refusing.");
+ "Format=/CopyFiles=/MakeDirectories= and CopyBlocks= cannot be combined, refusing.");
- if (!strv_isempty(p->copy_files) && streq_ptr(p->format, "swap"))
+ if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && streq_ptr(p->format, "swap"))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Format=swap and CopyFiles= cannot be combined, refusing.");
- if (!p->format && (!strv_isempty(p->copy_files) || (p->encrypt != ENCRYPT_OFF && !p->copy_blocks_path))) {
+ if (!p->format && (!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || (p->encrypt != ENCRYPT_OFF && !(p->copy_blocks_path || p->copy_blocks_auto)))) {
/* Pick "ext4" as file system if we are configured to copy files or encrypt the device */
p->format = strdup("ext4");
if (!p->format)
return log_oom();
}
+ /* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
+ if ((gpt_partition_type_is_root_verity(p->type_uuid) ||
+ gpt_partition_type_is_usr_verity(p->type_uuid)) &&
+ p->read_only < 0)
+ p->read_only = true;
+
return 0;
}
@@ -1881,7 +2019,7 @@ static int context_dump_partitions(Context *context, const char *node) {
r = table_add_many(
t,
TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type_uuid, uuid_buffer),
- TABLE_STRING, label ?: "-", TABLE_SET_COLOR, label ? NULL : ansi_grey(),
+ TABLE_STRING, empty_to_null(label) ?: "-", TABLE_SET_COLOR, empty_to_null(label) ? NULL : ansi_grey(),
TABLE_UUID, sd_id128_is_null(p->new_uuid) ? p->current_uuid : p->new_uuid,
TABLE_STRING, p->definition_path ? basename(p->definition_path) : "-", TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
TABLE_STRING, partname ?: "-", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
@@ -2235,6 +2373,9 @@ static int context_discard_range(
range[0] = round_up_size(offset, 512);
+ if (offset > UINT64_MAX - size)
+ return -ERANGE;
+
end = offset + size;
if (end <= range[0])
return 0;
@@ -2274,6 +2415,11 @@ static int context_discard_partition(Context *context, Partition *p) {
log_info("Storage does not support discard, not discarding data in future partition %" PRIu64 ".", p->partno);
return 0;
}
+ if (r == -EBUSY) {
+ /* Let's handle this gracefully: https://bugzilla.kernel.org/show_bug.cgi?id=211167 */
+ log_info("Block device is busy, not discarding partition %" PRIu64 " because it probably is mounted.", p->partno);
+ return 0;
+ }
if (r == 0) {
log_info("Partition %" PRIu64 " too short for discard, skipping.", p->partno);
return 0;
@@ -2444,7 +2590,7 @@ static int partition_encrypt(
volume_key,
volume_key_size,
&(struct crypt_params_luks2) {
- .label = p->new_label,
+ .label = strempty(p->new_label),
.sector_size = 512U,
});
if (r < 0)
@@ -2655,15 +2801,6 @@ static int do_copy_files(Partition *p, const char *fs) {
STRV_FOREACH_PAIR(source, target, p->copy_files) {
_cleanup_close_ int sfd = -1, pfd = -1, tfd = -1;
- _cleanup_free_ char *dn = NULL, *fn = NULL;
-
- r = path_extract_directory(*target, &dn);
- if (r < 0)
- return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
-
- r = path_extract_filename(*target, &fn);
- if (r < 0)
- return log_error_errno(r, "Failed to extract filename from '%s': %m", *target);
sfd = chase_symlinks_and_open(*source, arg_root, CHASE_PREFIX_ROOT|CHASE_WARN, O_CLOEXEC|O_NOCTTY, NULL);
if (sfd < 0)
@@ -2677,9 +2814,19 @@ static int do_copy_files(Partition *p, const char *fs) {
/* We are looking at a directory */
tfd = chase_symlinks_and_open(*target, fs, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
if (tfd < 0) {
+ _cleanup_free_ char *dn = NULL, *fn = NULL;
+
if (tfd != -ENOENT)
return log_error_errno(tfd, "Failed to open target directory '%s': %m", *target);
+ r = path_extract_filename(*target, &fn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from '%s': %m", *target);
+
+ r = path_extract_directory(*target, &dn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
+
r = mkdir_p_root(fs, dn, UID_INVALID, GID_INVALID, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create parent directory '%s': %m", dn);
@@ -2700,25 +2847,38 @@ static int do_copy_files(Partition *p, const char *fs) {
UID_INVALID, GID_INVALID,
COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
if (r < 0)
- return log_error_errno(r, "Failed to copy %s%s to %s: %m", strempty(arg_root), *source, *target);
+ return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
} else {
+ _cleanup_free_ char *dn = NULL, *fn = NULL;
+
/* We are looking at a regular file */
+ r = path_extract_filename(*target, &fn);
+ if (r == -EADDRNOTAVAIL || r == O_DIRECTORY)
+ return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
+ "Target path '%s' refers to a directory, but source path '%s' refers to regular file, can't copy.", *target, *source);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from '%s': %m", *target);
+
+ r = path_extract_directory(*target, &dn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
+
r = mkdir_p_root(fs, dn, UID_INVALID, GID_INVALID, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create parent directory: %m");
pfd = chase_symlinks_and_open(dn, fs, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
if (pfd < 0)
- return log_error_errno(tfd, "Failed to open parent directory of target: %m");
+ return log_error_errno(pfd, "Failed to open parent directory of target: %m");
- tfd = openat(pfd, basename(*target), O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC, 0700);
+ tfd = openat(pfd, fn, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC, 0700);
if (tfd < 0)
return log_error_errno(errno, "Failed to create target file '%s': %m", *target);
r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_SIGINT);
if (r < 0)
- return log_error_errno(r, "Failed to copy '%s%s' to '%s': %m", strempty(arg_root), *source, *target);
+ return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
(void) copy_xattr(sfd, tfd);
(void) copy_access(sfd, tfd);
@@ -2729,13 +2889,30 @@ static int do_copy_files(Partition *p, const char *fs) {
return 0;
}
-static int partition_copy_files(Partition *p, const char *node) {
+static int do_make_directories(Partition *p, const char *fs) {
+ char **d;
+ int r;
+
+ assert(p);
+ assert(fs);
+
+ STRV_FOREACH(d, p->make_directories) {
+
+ r = mkdir_p_root(fs, *d, UID_INVALID, GID_INVALID, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
+ }
+
+ return 0;
+}
+
+static int partition_populate(Partition *p, const char *node) {
int r;
assert(p);
assert(node);
- if (strv_isempty(p->copy_files))
+ if (strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
return 0;
log_info("Populating partition %" PRIu64 " with files.", p->partno);
@@ -2763,6 +2940,9 @@ static int partition_copy_files(Partition *p, const char *node) {
if (do_copy_files(p, fs) < 0)
_exit(EXIT_FAILURE);
+ if (do_make_directories(p, fs) < 0)
+ _exit(EXIT_FAILURE);
+
r = syncfs_path(AT_FDCWD, fs);
if (r < 0) {
log_error_errno(r, "Failed to synchronize written files: %m");
@@ -2838,7 +3018,7 @@ static int context_mkfs(Context *context) {
if (r < 0)
return r;
- r = make_filesystem(fsdev, p->format, p->new_label, fs_uuid, arg_discard);
+ r = make_filesystem(fsdev, p->format, strempty(p->new_label), fs_uuid, arg_discard);
if (r < 0) {
encrypted_dev_fd = safe_close(encrypted_dev_fd);
(void) deactivate_luks(cd, encrypted);
@@ -2852,7 +3032,7 @@ static int context_mkfs(Context *context) {
if (flock(encrypted_dev_fd, LOCK_UN) < 0)
return log_error_errno(errno, "Failed to unlock LUKS device: %m");
- r = partition_copy_files(p, fsdev);
+ r = partition_populate(p, fsdev);
if (r < 0) {
encrypted_dev_fd = safe_close(encrypted_dev_fd);
(void) deactivate_luks(cd, encrypted);
@@ -2949,9 +3129,8 @@ static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *re
if (p == q)
continue;
- if (sd_id128_equal(q->current_uuid, result.id) ||
- sd_id128_equal(q->new_uuid, result.id)) {
- log_warning("Partition UUID calculated from seed for partition %" PRIu64 " exists already, reverting to randomized UUID.", p->partno);
+ if (sd_id128_in_set(result.id, q->current_uuid, q->new_uuid)) {
+ log_warning("Partition UUID calculated from seed for partition %" PRIu64 " already used, reverting to randomized UUID.", p->partno);
r = sd_id128_randomize(&result.id);
if (r < 0)
@@ -3024,10 +3203,9 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
p->new_uuid = p->current_uuid;
if (p->current_label) {
- free(p->new_label);
- p->new_label = strdup(p->current_label);
- if (!p->new_label)
- return log_oom();
+ r = free_and_strdup_warn(&p->new_label, strempty(p->current_label));
+ if (r < 0)
+ return r;
}
continue;
@@ -3043,10 +3221,10 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
}
if (!isempty(p->current_label)) {
- free(p->new_label);
- p->new_label = strdup(p->current_label); /* never change initialized labels */
- if (!p->new_label)
- return log_oom();
+ /* never change initialized labels */
+ r = free_and_strdup_warn(&p->new_label, p->current_label);
+ if (r < 0)
+ return r;
} else if (!p->new_label) {
/* Not explicitly set by user! */
@@ -3059,6 +3237,24 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
return 0;
}
+static int set_gpt_flags(struct fdisk_partition *q, uint64_t flags) {
+ _cleanup_free_ char *a = NULL;
+
+ for (unsigned i = 0; i < sizeof(flags) * 8; i++) {
+ uint64_t bit = UINT64_C(1) << i;
+ char buf[DECIMAL_STR_MAX(unsigned)+1];
+
+ if (!FLAGS_SET(flags, bit))
+ continue;
+
+ xsprintf(buf, "%u", i);
+ if (!strextend_with_separator(&a, ",", buf))
+ return -ENOMEM;
+ }
+
+ return fdisk_partition_set_attrs(q, a);
+}
+
static int context_mangle_partitions(Context *context) {
Partition *p;
int r;
@@ -3108,9 +3304,7 @@ static int context_mangle_partitions(Context *context) {
}
if (!streq_ptr(p->new_label, p->current_label)) {
- assert(!isempty(p->new_label));
-
- r = fdisk_partition_set_name(p->current_partition, p->new_label);
+ r = fdisk_partition_set_name(p->current_partition, strempty(p->new_label));
if (r < 0)
return log_error_errno(r, "Failed to set partition label: %m");
@@ -3129,12 +3323,13 @@ static int context_mangle_partitions(Context *context) {
_cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
_cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
char ids[ID128_UUID_STRING_MAX];
+ uint64_t f;
assert(!p->new_partition);
assert(p->offset % 512 == 0);
assert(p->new_size % 512 == 0);
assert(!sd_id128_is_null(p->new_uuid));
- assert(!isempty(p->new_label));
+ assert(p->new_label);
t = fdisk_new_parttype();
if (!t)
@@ -3172,10 +3367,26 @@ static int context_mangle_partitions(Context *context) {
if (r < 0)
return log_error_errno(r, "Failed to set partition UUID: %m");
- r = fdisk_partition_set_name(q, p->new_label);
+ r = fdisk_partition_set_name(q, strempty(p->new_label));
if (r < 0)
return log_error_errno(r, "Failed to set partition label: %m");
+ /* Merge the read only setting with the literal flags */
+ f = p->gpt_flags;
+ if (p->read_only >= 0) {
+ if (gpt_partition_type_knows_read_only(p->type_uuid))
+ SET_FLAG(f, GPT_FLAG_READ_ONLY, p->read_only);
+ else {
+ char buffer[ID128_UUID_STRING_MAX];
+ log_warning("Configured ReadOnly=yes for partition type '%s' that doesn't support it, ignoring.",
+ gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer));
+ }
+ }
+
+ r = set_gpt_flags(q, f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set GPT partition flags: %m");
+
log_info("Adding new partition %" PRIu64 " to partition table.", p->partno);
r = fdisk_add_partition(context->fdisk_context, q, NULL);
@@ -3387,7 +3598,311 @@ static int context_can_factory_reset(Context *context) {
return false;
}
-static int context_open_copy_block_paths(Context *context) {
+static int resolve_copy_blocks_auto_candidate(
+ dev_t partition_devno,
+ sd_id128_t partition_type_uuid,
+ dev_t restrict_devno,
+ sd_id128_t *ret_uuid) {
+
+ _cleanup_(blkid_free_probep) blkid_probe b = NULL;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *pttype, *t;
+ sd_id128_t pt_parsed, u;
+ blkid_partition pp;
+ dev_t whole_devno;
+ blkid_partlist pl;
+ struct stat st;
+ int r;
+
+ /* Checks if the specified partition has the specified GPT type UUID, and is located on the specified
+ * 'restrict_devno' device. The type check is particularly relevant if we have Verity volume which is
+ * backed by two separate partitions: the data and the hash partitions, and we need to find the right
+ * one of the two. */
+
+ r = block_get_whole_disk(partition_devno, &whole_devno);
+ if (r < 0)
+ return log_error_errno(
+ r,
+ "Unable to determine containing block device of partition %u:%u: %m",
+ major(partition_devno), minor(partition_devno));
+
+ if (restrict_devno != (dev_t) -1 &&
+ restrict_devno != whole_devno)
+ return log_error_errno(
+ SYNTHETIC_ERRNO(EPERM),
+ "Partition %u:%u is located outside of block device %u:%u, refusing.",
+ major(partition_devno), minor(partition_devno),
+ major(restrict_devno), minor(restrict_devno));
+
+ r = device_path_make_major_minor(S_IFBLK, whole_devno, &p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert block device to device node path: %m");
+
+ fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0)
+ return log_error_errno(r, "Failed to open '%s': %m", p);
+
+ if (fstat(fd, &st) < 0)
+ return log_error_errno(r, "Failed to stat '%s': %m", p);
+
+ if (!S_ISBLK(st.st_mode) || st.st_rdev != whole_devno)
+ return log_error_errno(
+ SYNTHETIC_ERRNO(EPERM),
+ "Opened and determined block device don't match, refusing.");
+
+ b = blkid_new_probe();
+ if (!b)
+ return log_oom();
+
+ errno = 0;
+ r = blkid_probe_set_device(b, fd, 0, 0);
+ if (r != 0)
+ return log_error_errno(errno_or_else(ENOMEM), "Failed to open block device '%s': %m", p);
+
+ (void) blkid_probe_enable_partitions(b, 1);
+ (void) blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+ errno = 0;
+ r = blkid_do_safeprobe(b);
+ if (IN_SET(r, -2, 1)) { /* nothing found or ambiguous result */
+ log_debug("Didn't find partition table on block device '%s'.", p);
+ return false;
+ }
+ if (r != 0)
+ return log_error_errno(errno_or_else(EIO), "Unable to probe for partition table of '%s': %m", p);
+
+ (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
+ if (!streq_ptr(pttype, "gpt")) {
+ log_debug("Didn't find a GPT partition table on '%s'.", p);
+ return false;
+ }
+
+ errno = 0;
+ pl = blkid_probe_get_partitions(b);
+ if (!pl)
+ return log_error_errno(errno_or_else(EIO), "Unable read partition table of '%s': %m", p);
+ errno = 0;
+
+ pp = blkid_partlist_devno_to_partition(pl, partition_devno);
+ if (!pp) {
+ log_debug("Partition %u:%u has no matching partition table entry on '%s'.",
+ major(partition_devno), minor(partition_devno), p);
+ return false;
+ }
+
+ t = blkid_partition_get_type_string(pp);
+ if (isempty(t)) {
+ log_debug("Partition %u:%u has no type on '%s'.",
+ major(partition_devno), minor(partition_devno), p);
+ return false;
+ }
+
+ r = sd_id128_from_string(t, &pt_parsed);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse partition type \"%s\": %m", t);
+ return false;
+ }
+
+ if (!sd_id128_equal(pt_parsed, partition_type_uuid)) {
+ log_debug("Partition %u:%u has non-matching partition type " SD_ID128_FORMAT_STR " (needed: " SD_ID128_FORMAT_STR "), ignoring.",
+ major(partition_devno), minor(partition_devno),
+ SD_ID128_FORMAT_VAL(pt_parsed), SD_ID128_FORMAT_VAL(partition_type_uuid));
+ return false;
+ }
+
+ t = blkid_partition_get_uuid(pp);
+ if (isempty(t)) {
+ log_debug("Partition %u:%u has no UUID.",
+ major(partition_devno), minor(partition_devno));
+ return false;
+ }
+
+ r = sd_id128_from_string(t, &u);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse partition UUID \"%s\": %m", t);
+ return false;
+ }
+
+ log_debug("Automatically found partition %u:%u of right type " SD_ID128_FORMAT_STR ".",
+ major(partition_devno), minor(partition_devno),
+ SD_ID128_FORMAT_VAL(pt_parsed));
+
+ if (ret_uuid)
+ *ret_uuid = u;
+
+ return true;
+}
+
+static int find_backing_devno(
+ const char *path,
+ const char *root,
+ dev_t *ret) {
+
+ _cleanup_free_ char *resolved = NULL;
+ int r;
+
+ assert(path);
+
+ r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &resolved, NULL);
+ if (r < 0)
+ return r;
+
+ r = path_is_mount_point(resolved, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r == 0) /* Not a mount point, then it's not a partition of its own, let's not automatically use it. */
+ return -ENOENT;
+
+ r = get_block_device(resolved, ret);
+ if (r < 0)
+ return r;
+ if (r == 0) /* Not backed by physical file system, we can't use this */
+ return -ENOENT;
+
+ return 0;
+}
+
+static int resolve_copy_blocks_auto(
+ sd_id128_t type_uuid,
+ const char *root,
+ dev_t restrict_devno,
+ char **ret_path,
+ sd_id128_t *ret_uuid) {
+
+ const char *try1 = NULL, *try2 = NULL;
+ char p[SYS_BLOCK_PATH_MAX("/slaves")];
+ _cleanup_(closedirp) DIR *d = NULL;
+ sd_id128_t found_uuid = SD_ID128_NULL;
+ dev_t devno, found = 0;
+ int r;
+
+ assert(ret_path);
+
+ /* Enforce some security restrictions: CopyBlocks=auto should not be an avenue to get outside of the
+ * --root=/--image= confinement. Specifically, refuse CopyBlocks= in combination with --root= at all,
+ * and restrict block device references in the --image= case to loopback block device we set up.
+ *
+ * restrict_devno contain the dev_t of the loop back device we operate on in case of --image=, and
+ * thus declares which device (and its partition subdevices) we shall limit access to. If
+ * restrict_devno is zero no device probing access shall be allowed at all (used for --root=) and if
+ * it is (dev_t) -1 then free access shall be allowed (if neither switch is used). */
+
+ if (restrict_devno == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Automatic discovery of backing block devices not permitted in --root= mode, refusing.");
+
+ /* Handles CopyBlocks=auto, and finds the right source partition to copy from. We look for matching
+ * partitions in the host, using the appropriate directory as key and ensuring that the partition
+ * type matches. */
+
+ if (gpt_partition_type_is_root(type_uuid))
+ try1 = "/";
+ else if (gpt_partition_type_is_usr(type_uuid))
+ try1 = "/usr/";
+ else if (gpt_partition_type_is_root_verity(type_uuid))
+ try1 = "/";
+ else if (gpt_partition_type_is_usr_verity(type_uuid))
+ try1 = "/usr/";
+ else if (sd_id128_equal(type_uuid, GPT_ESP)) {
+ try1 = "/efi/";
+ try2 = "/boot/";
+ } else if (sd_id128_equal(type_uuid, GPT_XBOOTLDR))
+ try1 = "/boot/";
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Partition type " SD_ID128_FORMAT_STR " not supported from automatic source block device discovery.",
+ SD_ID128_FORMAT_VAL(type_uuid));
+
+ r = find_backing_devno(try1, root, &devno);
+ if (r == -ENOENT && try2)
+ r = find_backing_devno(try2, root, &devno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve automatic CopyBlocks= path for partition type " SD_ID128_FORMAT_STR ", sorry: %m",
+ SD_ID128_FORMAT_VAL(type_uuid));
+
+ xsprintf_sys_block_path(p, "/slaves", devno);
+ d = opendir(p);
+ if (d) {
+ struct dirent *de;
+
+ for (;;) {
+ _cleanup_free_ char *q = NULL, *t = NULL;
+ sd_id128_t u;
+ dev_t sl;
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (!de) {
+ if (errno != 0)
+ return log_error_errno(errno, "Failed to read directory '%s': %m", p);
+
+ break;
+ }
+
+ if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
+ continue;
+
+ q = path_join(p, de->d_name, "/dev");
+ if (!q)
+ return log_oom();
+
+ r = read_one_line_file(q, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read %s: %m", q);
+
+ r = parse_dev(t, &sl);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse %s, ignoring: %m", q);
+ continue;
+ }
+ if (major(sl) == 0) {
+ log_debug_errno(r, "Device backing %s is special, ignoring: %m", q);
+ continue;
+ }
+
+ r = resolve_copy_blocks_auto_candidate(sl, type_uuid, restrict_devno, &u);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* We found a matching one! */
+ if (found != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
+ "Multiple matching partitions found, refusing.");
+
+ found = sl;
+ found_uuid = u;
+ }
+ }
+ } else if (errno != ENOENT)
+ return log_error_errno(errno, "Failed open %s: %m", p);
+ else {
+ r = resolve_copy_blocks_auto_candidate(devno, type_uuid, restrict_devno, &found_uuid);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ found = devno;
+ }
+
+ if (found == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
+ "Unable to automatically discover suitable partition to copy blocks from.");
+
+ r = device_path_make_major_minor(S_IFBLK, found, ret_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert dev_t to device node path: %m");
+
+ if (ret_uuid)
+ *ret_uuid = found_uuid;
+
+ return 0;
+}
+
+static int context_open_copy_block_paths(
+ Context *context,
+ const char *root,
+ dev_t restrict_devno) {
+
Partition *p;
int r;
@@ -3395,6 +3910,8 @@ static int context_open_copy_block_paths(Context *context) {
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_close_ int source_fd = -1;
+ _cleanup_free_ char *opened = NULL;
+ sd_id128_t uuid = SD_ID128_NULL;
uint64_t size;
struct stat st;
@@ -3404,15 +3921,38 @@ static int context_open_copy_block_paths(Context *context) {
if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */
continue;
- if (!p->copy_blocks_path)
- continue;
+ if (p->copy_blocks_path) {
- source_fd = open(p->copy_blocks_path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (source_fd < 0)
- return log_error_errno(errno, "Failed to open block copy file '%s': %m", p->copy_blocks_path);
+ source_fd = chase_symlinks_and_open(p->copy_blocks_path, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &opened);
+ if (source_fd < 0)
+ return log_error_errno(source_fd, "Failed to open '%s': %m", p->copy_blocks_path);
+
+ if (fstat(source_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat block copy file '%s': %m", opened);
+
+ if (!S_ISREG(st.st_mode) && restrict_devno != (dev_t) -1)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Copying from block device node is not permitted in --image=/--root= mode, refusing.");
+
+ } else if (p->copy_blocks_auto) {
+
+ r = resolve_copy_blocks_auto(p->type_uuid, root, restrict_devno, &opened, &uuid);
+ if (r < 0)
+ return r;
+
+ source_fd = open(opened, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (source_fd < 0)
+ return log_error_errno(errno, "Failed to open automatically determined source block copy device '%s': %m", opened);
- if (fstat(source_fd, &st) < 0)
- return log_error_errno(errno, "Failed to stat block copy file '%s': %m", p->copy_blocks_path);
+ if (fstat(source_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat block copy file '%s': %m", opened);
+
+ /* If we found it automatically, it must be a block device, let's enforce that */
+ if (!S_ISBLK(st.st_mode))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADF),
+ "Automatically detected source block copy device '%s' is not a block device, refusing: %m", opened);
+ } else
+ continue;
if (S_ISDIR(st.st_mode)) {
_cleanup_free_ char *bdev = NULL;
@@ -3428,14 +3968,14 @@ static int context_open_copy_block_paths(Context *context) {
r = btrfs_get_block_device_fd(source_fd, &devt);
if (r == -EUCLEAN)
- return btrfs_log_dev_root(LOG_ERR, r, p->copy_blocks_path);
+ return btrfs_log_dev_root(LOG_ERR, r, opened);
if (r < 0)
- return log_error_errno(r, "Unable to determine backing block device of '%s': %m", p->copy_blocks_path);
+ return log_error_errno(r, "Unable to determine backing block device of '%s': %m", opened);
r = device_path_make_major_minor(S_IFBLK, devt, &bdev);
}
if (r < 0)
- return log_error_errno(r, "Failed to determine block device path for block device backing '%s': %m", p->copy_blocks_path);
+ return log_error_errno(r, "Failed to determine block device path for block device backing '%s': %m", opened);
safe_close(source_fd);
@@ -3456,15 +3996,21 @@ static int context_open_copy_block_paths(Context *context) {
if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
} else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", p->copy_blocks_path);
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", opened);
if (size <= 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", p->copy_blocks_path);
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", opened);
if (size % 512 != 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has size that is not multiple of 512, refusing.", p->copy_blocks_path);
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has size that is not multiple of 512, refusing.", opened);
p->copy_blocks_fd = TAKE_FD(source_fd);
p->copy_blocks_size = size;
+
+ free_and_replace(p->copy_blocks_path, opened);
+
+ /* When copying from an existing partition copy that partitions UUID if none is configured explicitly */
+ if (sd_id128_is_null(p->new_uuid) && !sd_id128_is_null(uuid))
+ p->new_uuid = uuid;
}
return 0;
@@ -3493,6 +4039,7 @@ static int help(void) {
" them\n"
" --can-factory-reset Test whether factory reset is defined\n"
" --root=PATH Operate relative to root path\n"
+ " --image=PATH Operate relative to image file\n"
" --definitions=DIR Find partition definitions in specified directory\n"
" --key-file=PATH Key to use when encrypting partitions\n"
" --tpm2-device=PATH Path to TPM2 device node to use\n"
@@ -3523,6 +4070,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FACTORY_RESET,
ARG_CAN_FACTORY_RESET,
ARG_ROOT,
+ ARG_IMAGE,
ARG_SEED,
ARG_PRETTY,
ARG_DEFINITIONS,
@@ -3544,6 +4092,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "factory-reset", required_argument, NULL, ARG_FACTORY_RESET },
{ "can-factory-reset", no_argument, NULL, ARG_CAN_FACTORY_RESET },
{ "root", required_argument, NULL, ARG_ROOT },
+ { "image", required_argument, NULL, ARG_IMAGE },
{ "seed", required_argument, NULL, ARG_SEED },
{ "pretty", required_argument, NULL, ARG_PRETTY },
{ "definitions", required_argument, NULL, ARG_DEFINITIONS },
@@ -3623,7 +4172,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ROOT:
- r = parse_path_argument(optarg, false, &arg_root);
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_IMAGE:
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
if (r < 0)
return r;
break;
@@ -3773,9 +4328,28 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"If --empty=create is specified, --size= must be specified, too.");
+ if (arg_image && arg_root)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+ else if (!arg_image && !arg_root && in_initrd()) {
+
+ /* By default operate on /sysusr/ or /sysroot/ when invoked in the initrd. We prefer the
+ * former, if it is mounted, so that we have deterministic behaviour on systems where /usr/
+ * is vendor-supplied but the root fs formatted on first boot. */
+ r = path_is_mount_point("/sysusr/usr", NULL, 0);
+ if (r <= 0) {
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Unable to determine whether /sysusr/usr is a mount point, assuming it is not: %m");
+
+ arg_root = strdup("/sysroot");
+ } else
+ arg_root = strdup("/sysusr");
+ if (!arg_root)
+ return log_oom();
+ }
+
arg_node = argc > optind ? argv[optind] : NULL;
- if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node)
+ if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node && !arg_image)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"A path to a device node or loopback file must be specified when --empty=force, --empty=require or --empty=create are used.");
@@ -3830,7 +4404,7 @@ static int parse_efi_variable_factory_reset(void) {
arg_factory_reset = r;
if (r)
- log_notice("Honouring factory reset requested via EFI variable FactoryReset: %m");
+ log_notice("Factory reset requested via EFI variable FactoryReset.");
return 0;
}
@@ -3848,39 +4422,44 @@ static int remove_efi_variable_factory_reset(void) {
return 0;
}
-static int acquire_root_devno(const char *p, int mode, char **ret, int *ret_fd) {
+static int acquire_root_devno(
+ const char *p,
+ const char *root,
+ int mode,
+ char **ret,
+ int *ret_fd) {
+
+ _cleanup_free_ char *found_path = NULL;
+ dev_t devno, fd_devno = MODE_INVALID;
_cleanup_close_ int fd = -1;
struct stat st;
- dev_t devno, fd_devno = MODE_INVALID;
int r;
assert(p);
assert(ret);
assert(ret_fd);
- fd = open(p, mode);
+ fd = chase_symlinks_and_open(p, root, CHASE_PREFIX_ROOT, mode, &found_path);
if (fd < 0)
- return -errno;
+ return fd;
if (fstat(fd, &st) < 0)
return -errno;
if (S_ISREG(st.st_mode)) {
- char *s;
-
- s = strdup(p);
- if (!s)
- return log_oom();
-
- *ret = s;
+ *ret = TAKE_PTR(found_path);
*ret_fd = TAKE_FD(fd);
-
return 0;
}
- if (S_ISBLK(st.st_mode))
+ if (S_ISBLK(st.st_mode)) {
+ /* Refuse referencing explicit block devices if a root dir is specified, after all we should
+ * not be able to leave the image the root path constrains us to. */
+ if (root)
+ return -EPERM;
+
fd_devno = devno = st.st_rdev;
- else if (S_ISDIR(st.st_mode)) {
+ } else if (S_ISDIR(st.st_mode)) {
devno = st.st_dev;
if (major(devno) == 0) {
@@ -3895,7 +4474,9 @@ static int acquire_root_devno(const char *p, int mode, char **ret, int *ret_fd)
/* From dm-crypt to backing partition */
r = block_get_originating(devno, &devno);
- if (r < 0)
+ if (r == -ENOENT)
+ log_debug_errno(r, "Device '%s' has no dm-crypt/dm-verity device, no need to look for underlying block device.", p);
+ else if (r < 0)
log_debug_errno(r, "Failed to find underlying block device for '%s', ignoring: %m", p);
/* From partition to whole disk containing it */
@@ -3913,8 +4494,43 @@ static int acquire_root_devno(const char *p, int mode, char **ret, int *ret_fd)
return 0;
}
+static int find_os_prefix(const char **ret) {
+ int r;
+
+ assert(ret);
+
+ /* Searches for the right place to look for the OS root. This is relevant in the initrd: in the
+ * initrd the host OS is typically mounted to /sysroot/ — except in setups where /usr/ is a separate
+ * partition, in which case it is mounted to /sysusr/usr/ before being moved to /sysroot/usr/. */
+
+ if (!in_initrd()) {
+ *ret = NULL; /* no prefix */
+ return 0;
+ }
+
+ r = path_is_mount_point("/sysroot", NULL, 0);
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to determine whether /sysroot/ is a mount point, assuming it is not: %m");
+ else if (r > 0) {
+ log_debug("/sysroot/ is a mount point, assuming it's the prefix.");
+ *ret = "/sysroot";
+ return 0;
+ }
+
+ r = path_is_mount_point("/sysusr/usr", NULL, 0);
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to determine whether /sysusr/usr is a mount point, assuming it is not: %m");
+ else if (r > 0) {
+ log_debug("/sysusr/usr/ is a mount point, assuming /sysusr/ is the prefix.");
+ *ret = "/sysusr";
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
static int find_root(char **ret, int *ret_fd) {
- const char *t;
+ const char *t, *prefix;
int r;
assert(ret);
@@ -3938,7 +4554,9 @@ static int find_root(char **ret, int *ret_fd) {
return 0;
}
- r = acquire_root_devno(arg_node, O_RDONLY|O_CLOEXEC, ret, ret_fd);
+ /* Note that we don't specify a root argument here: if the user explicitly configured a node
+ * we'll take it relative to the host, not the image */
+ r = acquire_root_devno(arg_node, NULL, O_RDONLY|O_CLOEXEC, ret, ret_fd);
if (r == -EUCLEAN)
return btrfs_log_dev_root(LOG_ERR, r, arg_node);
if (r < 0)
@@ -3953,12 +4571,16 @@ static int find_root(char **ret, int *ret_fd) {
* latter we check for cases where / is a tmpfs and only /usr is an actual persistent block device
* (think: volatile setups) */
+ r = find_os_prefix(&prefix);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine OS prefix: %m");
+
FOREACH_STRING(t, "/", "/usr") {
_cleanup_free_ char *j = NULL;
const char *p;
- if (in_initrd()) {
- j = path_join("/sysroot", t);
+ if (prefix) {
+ j = path_join(prefix, t);
if (!j)
return log_oom();
@@ -3966,7 +4588,7 @@ static int find_root(char **ret, int *ret_fd) {
} else
p = t;
- r = acquire_root_devno(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret, ret_fd);
+ r = acquire_root_devno(p, arg_root, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret, ret_fd);
if (r < 0) {
if (r == -EUCLEAN)
return btrfs_log_dev_root(LOG_ERR, r, p);
@@ -4013,9 +4635,15 @@ static int resize_pt(int fd) {
return 1;
}
-static int resize_backing_fd(const char *node, int *fd) {
+static int resize_backing_fd(
+ const char *node, /* The primary way we access the disk image to operate on */
+ int *fd, /* An O_RDONLY fd referring to that inode */
+ const char *backing_file, /* If the above refers to a loopback device, the backing regular file for that, which we can grow */
+ LoopDevice *loop_device) {
+
char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
_cleanup_close_ int writable_fd = -1;
+ uint64_t current_size;
struct stat st;
int r;
@@ -4036,25 +4664,64 @@ static int resize_backing_fd(const char *node, int *fd) {
if (fstat(*fd, &st) < 0)
return log_error_errno(errno, "Failed to stat '%s': %m", node);
- r = stat_verify_regular(&st);
- if (r < 0)
- return log_error_errno(r, "Specified path '%s' is not a regular file, cannot resize: %m", node);
+ if (S_ISBLK(st.st_mode)) {
+ if (!backing_file)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Cannot resize block device '%s'.", node);
- assert_se(format_bytes(buf1, sizeof(buf1), st.st_size));
+ assert(loop_device);
+
+ if (ioctl(*fd, BLKGETSIZE64, &current_size) < 0)
+ return log_error_errno(errno, "Failed to determine size of block device %s: %m", node);
+ } else {
+ r = stat_verify_regular(&st);
+ if (r < 0)
+ return log_error_errno(r, "Specified path '%s' is not a regular file or loopback block device, cannot resize: %m", node);
+
+ assert(!backing_file);
+ assert(!loop_device);
+ current_size = st.st_size;
+ }
+
+ assert_se(format_bytes(buf1, sizeof(buf1), current_size));
assert_se(format_bytes(buf2, sizeof(buf2), arg_size));
- if ((uint64_t) st.st_size >= arg_size) {
+ if (current_size >= arg_size) {
log_info("File '%s' already is of requested size or larger, not growing. (%s >= %s)", node, buf1, buf2);
return 0;
}
- /* The file descriptor is read-only. In order to grow the file we need to have a writable fd. We
- * reopen the file for that temporarily. We keep the writable fd only open for this operation though,
- * as fdisk can't accept it anyway. */
+ if (S_ISBLK(st.st_mode)) {
+ assert(backing_file);
+
+ /* This is a loopback device. We can't really grow those directly, but we can grow the
+ * backing file, hence let's do that. */
+
+ writable_fd = open(backing_file, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
+ if (writable_fd < 0)
+ return log_error_errno(errno, "Failed to open backing file '%s': %m", backing_file);
+
+ if (fstat(writable_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat() backing file '%s': %m", backing_file);
+
+ r = stat_verify_regular(&st);
+ if (r < 0)
+ return log_error_errno(r, "Backing file '%s' of block device is not a regular file: %m", backing_file);
+
+ if ((uint64_t) st.st_size != current_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Size of backing file '%s' of loopback block device '%s' don't match, refusing.", node, backing_file);
+ } else {
+ assert(S_ISREG(st.st_mode));
+ assert(!backing_file);
+
+ /* The file descriptor is read-only. In order to grow the file we need to have a writable fd. We
+ * reopen the file for that temporarily. We keep the writable fd only open for this operation though,
+ * as fdisk can't accept it anyway. */
- writable_fd = fd_reopen(*fd, O_WRONLY|O_CLOEXEC);
- if (writable_fd < 0)
- return log_error_errno(writable_fd, "Failed to reopen backing file '%s' writable: %m", node);
+ writable_fd = fd_reopen(*fd, O_WRONLY|O_CLOEXEC);
+ if (writable_fd < 0)
+ return log_error_errno(writable_fd, "Failed to reopen backing file '%s' writable: %m", node);
+ }
if (!arg_discard) {
if (fallocate(writable_fd, 0, 0, arg_size) < 0) {
@@ -4065,16 +4732,12 @@ static int resize_backing_fd(const char *node, int *fd) {
/* Fallback to truncation, if fallocate() is not supported. */
log_debug("Backing file system does not support fallocate(), falling back to ftruncate().");
} else {
- r = resize_pt(writable_fd);
- if (r < 0)
- return r;
-
- if (st.st_size == 0) /* Likely regular file just created by us */
+ if (current_size == 0) /* Likely regular file just created by us */
log_info("Allocated %s for '%s'.", buf2, node);
else
log_info("File '%s' grown from %s to %s by allocation.", node, buf1, buf2);
- return 1;
+ goto done;
}
}
@@ -4082,14 +4745,21 @@ static int resize_backing_fd(const char *node, int *fd) {
return log_error_errno(errno, "Failed to grow '%s' from %s to %s by truncation: %m",
node, buf1, buf2);
+ if (current_size == 0) /* Likely regular file just created by us */
+ log_info("Sized '%s' to %s.", node, buf2);
+ else
+ log_info("File '%s' grown from %s to %s by truncation.", node, buf1, buf2);
+
+done:
r = resize_pt(writable_fd);
if (r < 0)
return r;
- if (st.st_size == 0) /* Likely regular file just created by us */
- log_info("Sized '%s' to %s.", node, buf2);
- else
- log_info("File '%s' grown from %s to %s by truncation.", node, buf1, buf2);
+ if (loop_device) {
+ r = loop_device_refresh_size(loop_device, UINT64_MAX, arg_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update loop device size: %m");
+ }
return 1;
}
@@ -4100,8 +4770,6 @@ static int determine_auto_size(Context *c) {
Partition *p;
assert_se(c);
- assert_se(arg_size == UINT64_MAX);
- assert_se(arg_size_auto);
LIST_FOREACH(partitions, p, c->partitions) {
uint64_t m;
@@ -4124,23 +4792,19 @@ static int determine_auto_size(Context *c) {
}
static int run(int argc, char *argv[]) {
+ _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+ _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
_cleanup_(context_freep) Context* context = NULL;
_cleanup_free_ char *node = NULL;
_cleanup_close_ int backing_fd = -1;
- bool from_scratch;
+ bool from_scratch, node_is_our_loop = false;
int r;
log_show_color(true);
log_parse_environment();
log_open();
- if (in_initrd()) {
- /* Default to operation on /sysroot when invoked in the initrd! */
- arg_root = strdup("/sysroot");
- if (!arg_root)
- return log_oom();
- }
-
r = parse_argv(argc, argv);
if (r <= 0)
return r;
@@ -4153,6 +4817,40 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
+ if (arg_image) {
+ assert(!arg_root);
+
+ /* Mount this strictly read-only: we shall modify the partition table, not the file
+ * systems */
+ r = mount_image_privately_interactively(
+ arg_image,
+ DISSECT_IMAGE_MOUNT_READ_ONLY |
+ (arg_node ? DISSECT_IMAGE_DEVICE_READ_ONLY : 0) | /* If a different node to make changes to is specified let's open the device in read-only mode) */
+ DISSECT_IMAGE_GPT_ONLY |
+ DISSECT_IMAGE_RELAX_VAR_CHECK |
+ DISSECT_IMAGE_USR_NO_ROOT |
+ DISSECT_IMAGE_REQUIRE_ROOT,
+ &mounted_dir,
+ &loop_device,
+ &decrypted_image);
+ if (r < 0)
+ return r;
+
+ arg_root = strdup(mounted_dir);
+ if (!arg_root)
+ return log_oom();
+
+ if (!arg_node) {
+ arg_node = strdup(loop_device->node);
+ if (!arg_node)
+ return log_oom();
+
+ /* Remember that the the device we are about to manipulate is actually the one we
+ * allocated here, and thus to increase its backing file we know what to do */
+ node_is_our_loop = true;
+ }
+ }
+
context = context_new(arg_seed);
if (!context)
return log_oom();
@@ -4171,7 +4869,11 @@ static int run(int argc, char *argv[]) {
return r;
if (arg_size != UINT64_MAX) {
- r = resize_backing_fd(node, &backing_fd);
+ r = resize_backing_fd(
+ node,
+ &backing_fd,
+ node_is_our_loop ? arg_image : NULL,
+ node_is_our_loop ? loop_device : NULL);
if (r < 0)
return r;
}
@@ -4220,7 +4922,12 @@ static int run(int argc, char *argv[]) {
return r;
/* Open all files to copy blocks from now, since we want to take their size into consideration */
- r = context_open_copy_block_paths(context);
+ r = context_open_copy_block_paths(
+ context,
+ arg_root,
+ loop_device ? loop_device->devno : /* if --image= is specified, only allow partitions on the loopback device*/
+ arg_root && !arg_image ? 0 : /* if --root= is specified, don't accept any block device */
+ (dev_t) -1); /* if neither is specified, make no restrictions */
if (r < 0)
return r;
@@ -4233,7 +4940,11 @@ static int run(int argc, char *argv[]) {
context_unload_partition_table(context);
assert_se(arg_size != UINT64_MAX);
- r = resize_backing_fd(node, &backing_fd);
+ r = resize_backing_fd(
+ node,
+ &backing_fd,
+ node_is_our_loop ? arg_image : NULL,
+ node_is_our_loop ? loop_device : NULL);
if (r < 0)
return r;
@@ -4247,9 +4958,13 @@ static int run(int argc, char *argv[]) {
if (context_allocate_partitions(context))
break; /* Success! */
- if (!context_drop_one_priority(context))
- return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
- "Can't fit requested partitions into free space, refusing.");
+ if (!context_drop_one_priority(context)) {
+ r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
+ "Can't fit requested partitions into free space, refusing.");
+
+ determine_auto_size(context);
+ return r;
+ }
}
/* Now assign free space according to the weight logic */
diff --git a/src/partition/test-repart.sh b/src/partition/test-repart.sh
index d21865dd64..8598a99887 100755
--- a/src/partition/test-repart.sh
+++ b/src/partition/test-repart.sh
@@ -1,25 +1,28 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
+set -eux
+set -o pipefail
[[ -e /dev/loop-control ]] || exit 77
-repart=$1
-test -x $repart
+repart="${1:?}"
+test -x "$repart"
-D=$(mktemp --tmpdir --directory "test-repart.XXXXXXXXXX")
+D="$(mktemp --tmpdir --directory "test-repart.XXXXXXXXXX")"
+
+# shellcheck disable=SC2064
trap "rm -rf '$D'" EXIT INT QUIT PIPE
-mkdir -p $D/definitions
+mkdir -p "$D/definitions"
SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8
echo "### Testing systemd-repart --empty=create ###"
-$repart $D/zzz --empty=create --size=1G --seed=$SEED
+"$repart" "$D/zzz" --empty=create --size=1G --seed="$SEED"
-sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/empty
+sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/empty"
-cmp $D/empty - <<EOF
+cmp "$D/empty" - <<EOF
label: gpt
label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
device: $D/zzz
@@ -30,32 +33,32 @@ EOF
echo "### Testing with root, root2, home, & swap ###"
-cat >$D/definitions/root.conf <<EOF
+cat >"$D/definitions/root.conf" <<EOF
[Partition]
Type=root-x86-64
EOF
-ln -s root.conf $D/definitions/root2.conf
+ln -s root.conf "$D/definitions/root2.conf"
-cat >$D/definitions/home.conf <<EOF
+cat >"$D/definitions/home.conf" <<EOF
[Partition]
Type=home
Label=home-first
Label=home-always-too-long-xxxxxxxxxxxxxx-%v
EOF
-cat >$D/definitions/swap.conf <<EOF
+cat >"$D/definitions/swap.conf" <<EOF
[Partition]
Type=swap
SizeMaxBytes=64M
PaddingMinBytes=92M
EOF
-$repart $D/zzz --dry-run=no --seed=$SEED --definitions=$D/definitions
+"$repart" "$D/zzz" --dry-run=no --seed="$SEED" --definitions="$D/definitions"
-sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated
+sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated"
-cmp $D/populated - <<EOF
+cmp "$D/populated" - <<EOF
label: gpt
label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
device: $D/zzz
@@ -70,27 +73,27 @@ EOF
echo "### Testing with root, root2, home, swap, & another partition ###"
-cat >$D/definitions/swap.conf <<EOF
+cat >"$D/definitions/swap.conf" <<EOF
[Partition]
Type=swap
SizeMaxBytes=64M
EOF
-cat >$D/definitions/extra.conf <<EOF
+cat >"$D/definitions/extra.conf" <<EOF
[Partition]
Type=linux-generic
Label=custom_label
UUID=a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
EOF
-echo "Label=ignored_label" >>$D/definitions/home.conf
-echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>$D/definitions/home.conf
+echo "Label=ignored_label" >>"$D/definitions/home.conf"
+echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$D/definitions/home.conf"
-$repart $D/zzz --dry-run=no --seed=$SEED --definitions=$D/definitions
+"$repart" "$D/zzz" --dry-run=no --seed="$SEED" --definitions="$D/definitions"
-sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated2
+sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated2"
-cmp $D/populated2 - <<EOF
+cmp "$D/populated2" - <<EOF
label: gpt
label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
device: $D/zzz
@@ -106,11 +109,11 @@ EOF
echo "### Resizing to 2G ###"
-$repart $D/zzz --size=2G --dry-run=no --seed=$SEED --definitions=$D/definitions
+"$repart" "$D/zzz" --size=2G --dry-run=no --seed="$SEED" --definitions="$D/definitions"
-sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated3
+sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated3"
-cmp $D/populated3 - <<EOF
+cmp "$D/populated3" - <<EOF
label: gpt
label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
device: $D/zzz
@@ -124,11 +127,11 @@ $D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-09
$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
EOF
-dd if=/dev/urandom of=$D/block-copy bs=4096 count=10240
+dd if=/dev/urandom of="$D/block-copy" bs=4096 count=10240
echo "### Testing with root, root2, home, swap, another partition, & partition copy ###"
-cat >$D/definitions/extra2.conf <<EOF
+cat >"$D/definitions/extra2.conf" <<EOF
[Partition]
Type=linux-generic
Label=block-copy
@@ -136,11 +139,11 @@ UUID=2a1d97e1d0a346cca26eadc643926617
CopyBlocks=$D/block-copy
EOF
-$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions
+"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions"
-sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated4
+sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated4"
-cmp $D/populated4 - <<EOF
+cmp "$D/populated4" - <<EOF
label: gpt
label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
device: $D/zzz
@@ -155,14 +158,14 @@ $D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D
$D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name="block-copy"
EOF
-cmp --bytes=41943040 --ignore-initial=0:$((512*4194264)) $D/block-copy $D/zzz
+cmp --bytes=41943040 --ignore-initial=0:$((512*4194264)) "$D/block-copy" "$D/zzz"
-if [ `id -u` == 0 ] && type -P cryptsetup diff losetup > /dev/null ; then
+if [ "$(id -u)" -eq 0 ] && type -P cryptsetup diff losetup >/dev/null ; then
echo "### Testing Format=/Encrypt=/CopyFiles="
# These tests require privileges unfortunately
- cat >$D/definitions/extra3.conf <<EOF
+ cat >"$D/definitions/extra3.conf" <<EOF
[Partition]
Type=linux-generic
Label=luks-format-copy
@@ -173,11 +176,11 @@ CopyFiles=$D/definitions:/def
SizeMinBytes=48M
EOF
- $repart $D/zzz --size=auto --dry-run=no --seed=$SEED --definitions=$D/definitions
+ "$repart" "$D/zzz" --size=auto --dry-run=no --seed="$SEED" --definitions="$D/definitions"
- sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated5
+ sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated5"
- cmp $D/populated5 - <<EOF
+ cmp "$D/populated5" - <<EOF
label: gpt
label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
device: $D/zzz
@@ -193,22 +196,23 @@ $D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D
$D/zzz7 : start= 6291416, size= 98304, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=7B93D1F2-595D-4CE3-B0B9-837FBD9E63B0, name="luks-format-copy"
EOF
- LOOP=`losetup -P --show --find $D/zzz`
- VOLUME=test-repart-$RANDOM
-
- touch $D/empty-password
- cryptsetup open --type=luks2 --key-file=$D/empty-password ${LOOP}p7 $VOLUME
- mkdir $D/mount
- mount -t ext4 /dev/mapper/$VOLUME $D/mount
- diff -r $D/mount/def $D/definitions > /dev/null
- umount $D/mount
- cryptsetup close $VOLUME
- losetup -d $LOOP
+ LOOP="$(losetup -P --show --find "$D/zzz")"
+ VOLUME="test-repart-$RANDOM"
+
+ touch "$D/empty-password"
+ cryptsetup open --type=luks2 --key-file="$D/empty-password" "${LOOP}p7" "$VOLUME"
+ mkdir "$D/mount"
+ mount -t ext4 "/dev/mapper/$VOLUME" "$D/mount"
+ # Use deferred closing on the mapper and autoclear on the loop, so they are cleaned up on umount
+ cryptsetup close --deferred "$VOLUME"
+ losetup -d "$LOOP"
+ diff -r "$D/mount/def" "$D/definitions" >/dev/null
+ umount "$D/mount"
else
echo "### Skipping Format=/Encrypt=/CopyFiles= test, lacking privileges or missing cryptsetup/diff/losetup"
fi
echo "### Testing json output ###"
-$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions --json=help
-$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions --json=pretty
-$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions --json=short
+"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --json=help
+"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --json=pretty
+"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --json=short
diff --git a/src/portable/portable.c b/src/portable/portable.c
index 02d1d64195..0799bff53d 100644
--- a/src/portable/portable.c
+++ b/src/portable/portable.c
@@ -395,6 +395,8 @@ static int portable_extract_by_path(
r = dissect_image(
d->fd,
NULL, NULL,
+ d->uevent_seqnum_not_before,
+ d->timestamp_not_before,
DISSECT_IMAGE_READ_ONLY |
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
@@ -861,7 +863,7 @@ static int find_profile(const char *name, const char *unit, char **ret) {
assert_se(dot = strrchr(unit, '.'));
NULSTR_FOREACH(p, profile_dirs) {
- _cleanup_free_ char *joined;
+ _cleanup_free_ char *joined = NULL;
joined = strjoin(p, "/", name, "/", dot + 1, ".conf");
if (!joined)
diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c
index fa6df9054a..2d8079ad97 100644
--- a/src/portable/portablectl.c
+++ b/src/portable/portablectl.c
@@ -341,7 +341,7 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
nl = true;
} else {
_cleanup_free_ char *pretty_portable = NULL, *pretty_os = NULL;
- _cleanup_fclose_ FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
f = fmemopen_unlocked((void*) data, sz, "re");
if (!f)
diff --git a/src/portable/portabled-bus.c b/src/portable/portabled-bus.c
index 72f685f76d..5b992d9df8 100644
--- a/src/portable/portabled-bus.c
+++ b/src/portable/portabled-bus.c
@@ -357,7 +357,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_verify_polkit_async(
message,
@@ -377,7 +377,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
r = btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit);
if (r == -ENOTTY)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c
index 88d8f914e0..595fe8a60a 100644
--- a/src/portable/portabled-image-bus.c
+++ b/src/portable/portabled-image-bus.c
@@ -443,7 +443,7 @@ int bus_image_common_remove(
}
if (m->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = bus_image_acquire(m,
message,
@@ -781,7 +781,7 @@ int bus_image_common_set_limit(
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_image_acquire(m,
message,
diff --git a/src/portable/portabled-operation.c b/src/portable/portabled-operation.c
index 848b784908..6f06367d20 100644
--- a/src/portable/portabled-operation.c
+++ b/src/portable/portabled-operation.c
@@ -20,14 +20,14 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
o->pid = 0;
if (si->si_code != CLD_EXITED) {
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ r = sd_bus_error_set(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
goto fail;
}
if (si->si_status == EXIT_SUCCESS)
r = 0;
else if (read(o->errno_fd, &r, sizeof(r)) != sizeof(r)) { /* Try to acquire error code for failed operation */
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");
+ r = sd_bus_error_set(&error, SD_BUS_ERROR_FAILED, "Child failed.");
goto fail;
}
diff --git a/src/pstore/pstore.conf b/src/pstore/pstore.conf
index 7b7b1e8493..68fb04ae0a 100644
--- a/src/pstore/pstore.conf
+++ b/src/pstore/pstore.conf
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the pstore.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# See pstore.conf(5) for details.
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 4caf967807..e003ca60e3 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -262,7 +262,7 @@ static int run(int argc, char *argv[]) {
if (k < 0)
log_debug_errno(errno, "Failed to read random data with getrandom(), falling back to /dev/urandom: %m");
else if ((size_t) k < buf_size)
- log_debug("Short read from getrandom(), falling back to /dev/urandom: %m");
+ log_debug("Short read from getrandom(), falling back to /dev/urandom.");
else
getrandom_worked = true;
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index c3624669ce..31c96706a2 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -299,7 +299,7 @@ static int validate_and_mangle_flags(
SD_RESOLVED_NO_TRUST_ANCHOR|
SD_RESOLVED_NO_NETWORK|
ok))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */
*flags |= SD_RESOLVED_PROTOCOLS_ALL;
@@ -420,7 +420,7 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
return r;
if (ifindex < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
@@ -581,7 +581,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
return r;
if (ifindex < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
r = validate_and_mangle_flags(NULL, &flags, 0, error);
if (r < 0)
@@ -738,7 +738,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
return r;
if (ifindex < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
r = dns_name_is_valid(name);
if (r < 0)
@@ -749,7 +749,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
if (!dns_type_is_valid_query(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified resource record type %" PRIu16 " may not be used in a query.", type);
if (dns_type_is_zone_transer(type))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface.");
if (dns_type_is_obsolete(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type);
@@ -765,7 +765,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
if (!key)
return -ENOMEM;
- r = dns_question_add(question, key);
+ r = dns_question_add(question, key, 0);
if (r < 0)
return r;
@@ -1267,7 +1267,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
return r;
if (ifindex < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
@@ -1289,7 +1289,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid domain '%s'", domain);
if (name && !type)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type.");
r = validate_and_mangle_flags(name, &flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS, error);
if (r < 0)
@@ -1861,7 +1861,7 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
assert(m);
if (m->mdns_support != RESOLVE_SUPPORT_YES)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for MulticastDNS is disabled");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for MulticastDNS is disabled");
service = new0(DnssdService, 1);
if (!service)
@@ -1928,7 +1928,7 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
return r;
if (isempty(key))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Keys in DNS-SD TXT RRs can't be empty");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Keys in DNS-SD TXT RRs can't be empty");
if (!ascii_is_valid(key))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TXT key '%s' contains non-ASCII symbols", key);
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 8de407d21a..a197f94bcf 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -627,7 +627,7 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, const DnsAnswer
if (r < 0)
goto fail;
- class = flags & DNS_ANSWER_CACHE_FLUSH ? k->class | MDNS_RR_CACHE_FLUSH : k->class;
+ class = flags & DNS_ANSWER_CACHE_FLUSH ? k->class | MDNS_RR_CACHE_FLUSH_OR_QU : k->class;
r = dns_packet_append_uint16(p, class, NULL);
if (r < 0)
goto fail;
@@ -1628,12 +1628,12 @@ static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t siz
int dns_packet_read_key(
DnsPacket *p,
DnsResourceKey **ret,
- bool *ret_cache_flush,
+ bool *ret_cache_flush_or_qu,
size_t *ret_start) {
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
_cleanup_free_ char *name = NULL;
- bool cache_flush = false;
+ bool cache_flush_or_qu = false;
uint16_t class, type;
int r;
@@ -1653,11 +1653,11 @@ int dns_packet_read_key(
return r;
if (p->protocol == DNS_PROTOCOL_MDNS) {
- /* See RFC6762, Section 10.2 */
+ /* See RFC6762, sections 5.4 and 10.2 */
- if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH)) {
- class &= ~MDNS_RR_CACHE_FLUSH;
- cache_flush = true;
+ if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH_OR_QU)) {
+ class &= ~MDNS_RR_CACHE_FLUSH_OR_QU;
+ cache_flush_or_qu = true;
}
}
@@ -1672,8 +1672,8 @@ int dns_packet_read_key(
*ret = key;
}
- if (ret_cache_flush)
- *ret_cache_flush = cache_flush;
+ if (ret_cache_flush_or_qu)
+ *ret_cache_flush_or_qu = cache_flush_or_qu;
if (ret_start)
*ret_start = rewinder.saved_rindex;
@@ -2221,15 +2221,12 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question)
for (i = 0; i < n; i++) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- bool cache_flush;
+ bool qu;
- r = dns_packet_read_key(p, &key, &cache_flush, NULL);
+ r = dns_packet_read_key(p, &key, &qu, NULL);
if (r < 0)
return r;
- if (cache_flush)
- return -EBADMSG;
-
if (!dns_type_is_valid_query(key->type))
return -EBADMSG;
@@ -2240,7 +2237,7 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question)
/* Already in the Question, let's skip */
continue;
- r = dns_question_add_raw(question, key);
+ r = dns_question_add_raw(question, key, qu ? DNS_QUESTION_WANTS_UNICAST_REPLY : 0);
if (r < 0)
return r;
}
@@ -2451,7 +2448,7 @@ int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) {
if (p->question->n_keys != 1)
return 0;
- return dns_resource_key_equal(p->question->keys[0], key);
+ return dns_resource_key_equal(dns_question_first_key(p->question), key);
}
int dns_packet_patch_max_udp_size(DnsPacket *p, uint16_t max_udp_size) {
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 7b2abe3e76..e9820795c0 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -233,7 +233,7 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start);
int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start);
int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start);
int dns_packet_read_name(DnsPacket *p, char **ret, bool allow_compression, size_t *start);
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start);
+int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush_or_qu, size_t *start);
int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start);
void dns_packet_rewind(DnsPacket *p, size_t idx);
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index ce58680cf6..5517db149d 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -231,7 +231,7 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
assert(dns_question_size(c->query->question_bypass->question) == 1);
- if (!dns_scope_good_key(c->scope, c->query->question_bypass->question->keys[0]))
+ if (!dns_scope_good_key(c->scope, dns_question_first_key(c->query->question_bypass->question)))
return 0;
r = dns_query_candidate_add_transaction(c, NULL, c->query->question_bypass);
@@ -502,7 +502,7 @@ int dns_query_new(
/* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
DNS_QUESTION_FOREACH(key, question_idna) {
- r = dns_question_contains(question_utf8, key);
+ r = dns_question_contains_key(question_utf8, key);
if (r < 0)
return r;
if (r > 0)
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index ef40932630..aefdaa0eeb 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -11,7 +11,7 @@ DnsQuestion *dns_question_new(size_t n) {
if (n > UINT16_MAX) /* We can only place 64K key in an question section at max */
n = UINT16_MAX;
- q = malloc0(offsetof(DnsQuestion, keys) + sizeof(DnsResourceKey*) * n);
+ q = malloc0(offsetof(DnsQuestion, items) + sizeof(DnsQuestionItem) * n);
if (!q)
return NULL;
@@ -22,18 +22,19 @@ DnsQuestion *dns_question_new(size_t n) {
}
static DnsQuestion *dns_question_free(DnsQuestion *q) {
- size_t i;
+ DnsResourceKey *key;
assert(q);
- for (i = 0; i < q->n_keys; i++)
- dns_resource_key_unref(q->keys[i]);
+ DNS_QUESTION_FOREACH(key, q)
+ dns_resource_key_unref(key);
+
return mfree(q);
}
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsQuestion, dns_question, dns_question_free);
-int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key) {
+int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags) {
/* Insert without checking for duplicates. */
assert(key);
@@ -42,11 +43,15 @@ int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key) {
if (q->n_keys >= q->n_allocated)
return -ENOSPC;
- q->keys[q->n_keys++] = dns_resource_key_ref(key);
+ q->items[q->n_keys++] = (DnsQuestionItem) {
+ .key = dns_resource_key_ref(key),
+ .flags = flags,
+ };
return 0;
}
-int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
+int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags) {
+ DnsQuestionItem *item;
int r;
assert(key);
@@ -54,19 +59,20 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
if (!q)
return -ENOSPC;
- for (size_t i = 0; i < q->n_keys; i++) {
- r = dns_resource_key_equal(q->keys[i], key);
+
+ DNS_QUESTION_FOREACH_ITEM(item, q) {
+ r = dns_resource_key_equal(item->key, key);
if (r < 0)
return r;
- if (r > 0)
+ if (r > 0 && item->flags == flags)
return 0;
}
- return dns_question_add_raw(q, key);
+ return dns_question_add_raw(q, key, flags);
}
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
- size_t i;
+ DnsResourceKey *key;
int r;
assert(rr);
@@ -74,8 +80,8 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *s
if (!q)
return 0;
- for (i = 0; i < q->n_keys; i++) {
- r = dns_resource_key_match_rr(q->keys[i], rr, search_domain);
+ DNS_QUESTION_FOREACH(key, q) {
+ r = dns_resource_key_match_rr(key, rr, search_domain);
if (r != 0)
return r;
}
@@ -84,7 +90,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *s
}
int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
- size_t i;
+ DnsResourceKey *key;
int r;
assert(rr);
@@ -95,12 +101,12 @@ int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, c
if (!IN_SET(rr->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME))
return 0;
- for (i = 0; i < q->n_keys; i++) {
+ DNS_QUESTION_FOREACH(key, q) {
/* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
- if (!dns_type_may_redirect(q->keys[i]->type))
+ if (!dns_type_may_redirect(key->type))
return 0;
- r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain);
+ r = dns_resource_key_match_cname_or_dname(key, rr->key, search_domain);
if (r != 0)
return r;
}
@@ -122,38 +128,39 @@ int dns_question_is_valid_for_query(DnsQuestion *q) {
if (q->n_keys > 65535)
return 0;
- name = dns_resource_key_name(q->keys[0]);
+ name = dns_resource_key_name(q->items[0].key);
if (!name)
return 0;
/* Check that all keys in this question bear the same name */
for (i = 0; i < q->n_keys; i++) {
- assert(q->keys[i]);
+ assert(q->items[i].key);
if (i > 0) {
- r = dns_name_equal(dns_resource_key_name(q->keys[i]), name);
+ r = dns_name_equal(dns_resource_key_name(q->items[i].key), name);
if (r <= 0)
return r;
}
- if (!dns_type_is_valid_query(q->keys[i]->type))
+ if (!dns_type_is_valid_query(q->items[i].key->type))
return 0;
}
return 1;
}
-int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) {
+int dns_question_contains_key(DnsQuestion *q, const DnsResourceKey *k) {
size_t j;
int r;
assert(k);
- if (!a)
+ if (!q)
return 0;
- for (j = 0; j < a->n_keys; j++) {
- r = dns_resource_key_equal(a->keys[j], k);
+
+ for (j = 0; j < q->n_keys; j++) {
+ r = dns_resource_key_equal(q->items[j].key, k);
if (r != 0)
return r;
}
@@ -161,8 +168,25 @@ int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) {
return 0;
}
+static int dns_question_contains_item(DnsQuestion *q, const DnsQuestionItem *i) {
+ DnsQuestionItem *item;
+ int r;
+
+ assert(i);
+
+ DNS_QUESTION_FOREACH_ITEM(item, q) {
+ if (item->flags != i->flags)
+ continue;
+ r = dns_resource_key_equal(item->key, i->key);
+ if (r != 0)
+ return r;
+ }
+
+ return false;
+}
+
int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
- size_t j;
+ DnsQuestionItem *item;
int r;
if (a == b)
@@ -173,16 +197,15 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
if (!b)
return a->n_keys == 0;
- /* Checks if all keys in a are also contained b, and vice versa */
+ /* Checks if all items in a are also contained b, and vice versa */
- for (j = 0; j < a->n_keys; j++) {
- r = dns_question_contains(b, a->keys[j]);
+ DNS_QUESTION_FOREACH_ITEM(item, a) {
+ r = dns_question_contains_item(b, item);
if (r <= 0)
return r;
}
-
- for (j = 0; j < b->n_keys; j++) {
- r = dns_question_contains(a, b->keys[j]);
+ DNS_QUESTION_FOREACH_ITEM(item, b) {
+ r = dns_question_contains_item(a, item);
if (r <= 0)
return r;
}
@@ -249,7 +272,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
if (!k)
return -ENOMEM;
- r = dns_question_add(n, k);
+ r = dns_question_add(n, k, 0);
if (r < 0)
return r;
}
@@ -267,7 +290,7 @@ const char *dns_question_first_name(DnsQuestion *q) {
if (q->n_keys < 1)
return NULL;
- return dns_resource_key_name(q->keys[0]);
+ return dns_resource_key_name(q->items[0].key);
}
int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) {
@@ -306,7 +329,7 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo
if (!key)
return -ENOMEM;
- r = dns_question_add(q, key);
+ r = dns_question_add(q, key, 0);
if (r < 0)
return r;
}
@@ -318,7 +341,7 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo
if (!key)
return -ENOMEM;
- r = dns_question_add(q, key);
+ r = dns_question_add(q, key, 0);
if (r < 0)
return r;
}
@@ -354,7 +377,7 @@ int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_
reverse = NULL;
- r = dns_question_add(q, key);
+ r = dns_question_add(q, key, 0);
if (r < 0)
return r;
@@ -426,7 +449,7 @@ int dns_question_new_service(
if (!key)
return -ENOMEM;
- r = dns_question_add(q, key);
+ r = dns_question_add(q, key, 0);
if (r < 0)
return r;
@@ -436,7 +459,7 @@ int dns_question_new_service(
if (!key)
return -ENOMEM;
- r = dns_question_add(q, key);
+ r = dns_question_add(q, key, 0);
if (r < 0)
return r;
}
diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h
index 8f9a84c82d..31b8a2ec3e 100644
--- a/src/resolve/resolved-dns-question.h
+++ b/src/resolve/resolved-dns-question.h
@@ -2,16 +2,26 @@
#pragma once
typedef struct DnsQuestion DnsQuestion;
+typedef struct DnsQuestionItem DnsQuestionItem;
#include "macro.h"
#include "resolved-dns-rr.h"
/* A simple array of resource keys */
+typedef enum DnsQuestionFlags {
+ DNS_QUESTION_WANTS_UNICAST_REPLY = 1 << 0, /* For mDNS: sender is willing to accept unicast replies */
+} DnsQuestionFlags;
+
+struct DnsQuestionItem {
+ DnsResourceKey *key;
+ DnsQuestionFlags flags;
+};
+
struct DnsQuestion {
unsigned n_ref;
size_t n_keys, n_allocated;
- DnsResourceKey* keys[0];
+ DnsQuestionItem items[0];
};
DnsQuestion *dns_question_new(size_t n);
@@ -22,13 +32,13 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo
int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a);
int dns_question_new_service(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool with_txt, bool convert_idna);
-int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key);
-int dns_question_add(DnsQuestion *q, DnsResourceKey *key);
+int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags);
+int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags);
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain);
int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain);
int dns_question_is_valid_for_query(DnsQuestion *q);
-int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k);
+int dns_question_contains_key(DnsQuestion *q, const DnsResourceKey *k);
int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret);
@@ -37,6 +47,10 @@ void dns_question_dump(DnsQuestion *q, FILE *f);
const char *dns_question_first_name(DnsQuestion *q);
+static inline DnsResourceKey *dns_question_first_key(DnsQuestion *q) {
+ return (q && q->n_keys > 0) ? q->items[0].key : NULL;
+}
+
static inline size_t dns_question_size(DnsQuestion *q) {
return q ? q->n_keys : 0;
}
@@ -47,12 +61,22 @@ static inline bool dns_question_isempty(DnsQuestion *q) {
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
-#define _DNS_QUESTION_FOREACH(u, key, q) \
- for (size_t UNIQ_T(i, u) = ({ \
- (key) = ((q) && (q)->n_keys > 0) ? (q)->keys[0] : NULL; \
- 0; \
- }); \
- (q) && (UNIQ_T(i, u) < (q)->n_keys); \
- UNIQ_T(i, u)++, (key) = (UNIQ_T(i, u) < (q)->n_keys ? (q)->keys[UNIQ_T(i, u)] : NULL))
+#define _DNS_QUESTION_FOREACH(u, k, q) \
+ for (size_t UNIQ_T(i, u) = ({ \
+ (k) = ((q) && (q)->n_keys > 0) ? (q)->items[0].key : NULL; \
+ 0; \
+ }); \
+ (q) && (UNIQ_T(i, u) < (q)->n_keys); \
+ UNIQ_T(i, u)++, (k) = (UNIQ_T(i, u) < (q)->n_keys ? (q)->items[UNIQ_T(i, u)].key : NULL))
#define DNS_QUESTION_FOREACH(key, q) _DNS_QUESTION_FOREACH(UNIQ, key, q)
+
+#define _DNS_QUESTION_FOREACH_ITEM(u, item, q) \
+ for (size_t UNIQ_T(i, u) = ({ \
+ (item) = dns_question_isempty(q) ? NULL : (q)->items; \
+ 0; \
+ }); \
+ UNIQ_T(i, u) < dns_question_size(q); \
+ UNIQ_T(i, u)++, (item) = (UNIQ_T(i, u) < dns_question_size(q) ? (q)->items + UNIQ_T(i, u) : NULL))
+
+#define DNS_QUESTION_FOREACH_ITEM(item, q) _DNS_QUESTION_FOREACH_ITEM(UNIQ, item, q)
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index ab48ea5f2a..43bbcb3073 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -16,12 +16,12 @@ typedef struct DnsResourceRecord DnsResourceRecord;
typedef struct DnsTxtItem DnsTxtItem;
/* DNSKEY RR flags */
-#define DNSKEY_FLAG_SEP (UINT16_C(1) << 0)
-#define DNSKEY_FLAG_REVOKE (UINT16_C(1) << 7)
-#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8)
+#define DNSKEY_FLAG_SEP (UINT16_C(1) << 0)
+#define DNSKEY_FLAG_REVOKE (UINT16_C(1) << 7)
+#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8)
/* mDNS RR flags */
-#define MDNS_RR_CACHE_FLUSH (UINT16_C(1) << 15)
+#define MDNS_RR_CACHE_FLUSH_OR_QU (UINT16_C(1) << 15)
/* DNSSEC algorithm identifiers, see
* http://tools.ietf.org/html/rfc4034#appendix-A.1 and
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 67c6f54dc5..e155df0efa 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -287,17 +287,23 @@ static int dns_scope_emit_one(DnsScope *s, int fd, int family, DnsPacket *p) {
return -EBUSY;
if (family == AF_INET) {
- addr.in = MDNS_MULTICAST_IPV4_ADDRESS;
+ if (in4_addr_is_null(&p->destination.in))
+ addr.in = MDNS_MULTICAST_IPV4_ADDRESS;
+ else
+ addr = p->destination;
fd = manager_mdns_ipv4_fd(s->manager);
} else if (family == AF_INET6) {
- addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS;
+ if (in6_addr_is_null(&p->destination.in6))
+ addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS;
+ else
+ addr = p->destination;
fd = manager_mdns_ipv6_fd(s->manager);
} else
return -EAFNOSUPPORT;
if (fd < 0)
return fd;
- r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, MDNS_PORT, NULL, p);
+ r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, p->destination_port ?: MDNS_PORT, NULL, p);
if (r < 0)
return r;
@@ -984,7 +990,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
}
assert(dns_question_size(p->question) == 1);
- key = p->question->keys[0];
+ key = dns_question_first_key(p->question);
r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
if (r < 0) {
@@ -1441,7 +1447,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
/* Since all the active services are in the zone make them discoverable now. */
SET_FOREACH(service_type, types) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr;
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR,
"_services._dns-sd._udp.local");
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 602720bf50..5b9d32f001 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -433,6 +433,7 @@ static int dns_stub_finish_reply_packet(
int rcode,
bool tc, /* set the Truncated bit? */
bool aa, /* set the Authoritative Answer bit? */
+ bool rd, /* set the Recursion Desired bit? */
bool add_opt, /* add an OPT RR to this packet? */
bool edns0_do, /* set the EDNS0 DNSSEC OK bit? */
bool ad, /* set the DNSSEC authenticated data bit? */
@@ -473,7 +474,7 @@ static int dns_stub_finish_reply_packet(
0 /* opcode */,
aa /* aa */,
tc /* tc */,
- 1 /* rd */,
+ rd /* rd */,
1 /* ra */,
ad /* ad */,
cd /* cd */,
@@ -581,6 +582,7 @@ static int dns_stub_send_reply(
rcode,
truncated,
dns_query_fully_authoritative(q),
+ DNS_PACKET_RD(q->request_packet),
!!q->request_packet->opt,
edns0_do,
DNS_PACKET_AD(q->request_packet) && dns_query_fully_authenticated(q),
@@ -622,6 +624,7 @@ static int dns_stub_send_failure(
rcode,
truncated,
false,
+ DNS_PACKET_RD(p),
!!p->opt,
DNS_PACKET_DO(p),
DNS_PACKET_AD(p) && authenticated,
@@ -879,13 +882,13 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
return;
}
- if (dns_type_is_obsolete(p->question->keys[0]->type)) {
+ if (dns_type_is_obsolete(dns_question_first_key(p->question)->type)) {
log_debug("Got message with obsolete key type, refusing.");
dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false);
return;
}
- if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
+ if (dns_type_is_zone_transer(dns_question_first_key(p->question)->type)) {
log_debug("Got request for zone transfer, refusing.");
dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false);
return;
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index a8ec6e18d5..c2d73cbedc 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -166,10 +166,7 @@ static inline DnsResourceKey *dns_transaction_key(DnsTransaction *t) {
assert(t->bypass);
- if (dns_question_isempty(t->bypass->question))
- return NULL;
-
- return t->bypass->question->keys[0];
+ return dns_question_first_key(t->bypass->question);
}
static inline uint64_t dns_transaction_source_to_query_flags(DnsTransactionSource s) {
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
index 8ba459b3e5..b036aa402c 100644
--- a/src/resolve/resolved-dns-trust-anchor.c
+++ b/src/resolve/resolved-dns-trust-anchor.c
@@ -160,7 +160,10 @@ static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
"lan\0"
"intranet\0"
"internal\0"
- "private\0";
+ "private\0"
+
+ /* Defined by RFC 8375. The most official choice. */
+ "home.arpa\0";
const char *name;
int r;
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
index d56d5de4df..8d533d7ecf 100644
--- a/src/resolve/resolved-link-bus.c
+++ b/src/resolve/resolved-link-bus.c
@@ -386,7 +386,7 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
if (!route_only && dns_name_is_root(name))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
if (route_only) {
prefixed = strjoin("~", name);
diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c
index 2857b58e89..0d19d08455 100644
--- a/src/resolve/resolved-mdns.c
+++ b/src/resolve/resolved-mdns.c
@@ -10,7 +10,7 @@
#include "resolved-mdns.h"
#include "sort-util.h"
-#define CLEAR_CACHE_FLUSH(x) (~MDNS_RR_CACHE_FLUSH & (x))
+#define CLEAR_CACHE_FLUSH(x) (~MDNS_RR_CACHE_FLUSH_OR_QU & (x))
void manager_mdns_stop(Manager *m) {
assert(m);
@@ -173,12 +173,69 @@ static int mdns_do_tiebreak(DnsResourceKey *key, DnsAnswer *answer, DnsPacket *p
return 0;
}
+static bool mdns_should_reply_using_unicast(DnsPacket *p) {
+ DnsQuestionItem *item;
+
+ /* Work out if we should respond using multicast or unicast. */
+
+ /* The query was a legacy "one-shot mDNS query", RFC 6762, sections 5.1 and 6.7 */
+ if (p->sender_port != MDNS_PORT)
+ return true;
+
+ /* The query was a "direct unicast query", RFC 6762, section 5.5 */
+ switch (p->family) {
+ case AF_INET:
+ if (!in4_addr_equal(&p->destination.in, &MDNS_MULTICAST_IPV4_ADDRESS))
+ return true;
+ break;
+ case AF_INET6:
+ if (!in6_addr_equal(&p->destination.in6, &MDNS_MULTICAST_IPV6_ADDRESS))
+ return true;
+ break;
+ }
+
+ /* All the questions in the query had a QU bit set, RFC 6762, section 5.4 */
+ DNS_QUESTION_FOREACH_ITEM(item, p->question) {
+ if (!FLAGS_SET(item->flags, DNS_QUESTION_WANTS_UNICAST_REPLY))
+ return false;
+ }
+ return true;
+}
+
+static bool sender_on_local_subnet(DnsScope *s, DnsPacket *p) {
+ LinkAddress *a;
+ int r;
+
+ /* Check whether the sender is on a local subnet. */
+
+ if (!s->link)
+ return false;
+
+ LIST_FOREACH(addresses, a, s->link->addresses) {
+ if (a->family != p->family)
+ continue;
+ if (a->prefixlen == UCHAR_MAX) /* don't know subnet mask */
+ continue;
+
+ r = in_addr_prefix_covers(a->family, &a->in_addr, a->prefixlen, &p->sender);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine whether link address covers sender address: %m");
+ if (r > 0)
+ return true;
+ }
+
+ return false;
+}
+
+
static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
_cleanup_(dns_answer_unrefp) DnsAnswer *full_answer = NULL;
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
DnsResourceKey *key = NULL;
DnsResourceRecord *rr;
bool tentative = false;
+ bool legacy_query = p->sender_port != MDNS_PORT;
+ bool unicast_reply;
int r;
assert(s);
@@ -190,8 +247,18 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
assert_return((dns_question_size(p->question) > 0), -EINVAL);
+ unicast_reply = mdns_should_reply_using_unicast(p);
+ if (unicast_reply && !sender_on_local_subnet(s, p)) {
+ /* RFC 6762, section 5.5 recommends silently ignoring unicast queries
+ * from senders outside the local network, so that we don't reveal our
+ * internal network structure to outsiders. */
+ log_debug("Sender wants a unicast reply, but is not on a local subnet. Ignoring.");
+ return 0;
+ }
+
DNS_QUESTION_FOREACH(key, p->question) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
+ DnsAnswerItem *item;
r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
if (r < 0)
@@ -222,21 +289,48 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
}
}
- r = dns_answer_extend(&full_answer, answer);
- if (r < 0)
- return log_debug_errno(r, "Failed to extend answer: %m");
+ if (dns_answer_isempty(answer))
+ continue;
+
+ /* Copy answer items from full_answer to answer, tweaking them if needed. */
+ if (full_answer) {
+ r = dns_answer_reserve(&full_answer, dns_answer_size(answer));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to reserve space in answer");
+ } else {
+ full_answer = dns_answer_new(dns_answer_size(answer));
+ if (!full_answer)
+ return log_oom();
+ }
+
+ DNS_ANSWER_FOREACH_ITEM(item, answer) {
+ DnsAnswerFlags flags = item->flags;
+ /* The cache-flush bit must not be set in legacy unicast responses.
+ * See section 6.7 of RFC 6762. */
+ if (legacy_query)
+ flags &= ~DNS_ANSWER_CACHE_FLUSH;
+ r = dns_answer_add(full_answer, item->rr, item->ifindex, flags, item->rrsig);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extend answer: %m");
+ }
}
if (dns_answer_isempty(full_answer))
return 0;
- r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, NULL, full_answer, NULL, false, &reply);
+ r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS,
+ legacy_query ? p->question : NULL, full_answer,
+ NULL, false, &reply);
if (r < 0)
return log_debug_errno(r, "Failed to build reply packet: %m");
if (!ratelimit_below(&s->ratelimit))
return 0;
+ if (unicast_reply) {
+ reply->destination = p->sender;
+ reply->destination_port = p->sender_port;
+ }
r = dns_scope_emit_udp(s, -1, AF_UNSPEC, reply);
if (r < 0)
return log_debug_errno(r, "Failed to send reply packet: %m");
diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in
index 874bd2b6ee..0ef77fc6e7 100644
--- a/src/resolve/resolved.conf.in
+++ b/src/resolve/resolved.conf.in
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the resolved.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# Use 'systemd-analyze cat-config systemd/resolved.conf' to display the full config.
diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c
index e2d1a1be5f..bff1a2886b 100644
--- a/src/rfkill/rfkill.c
+++ b/src/rfkill/rfkill.c
@@ -177,7 +177,7 @@ static int load_state(Context *c, const struct rfkill_event *event) {
ssize_t l = write(c->rfkill_fd, &we, sizeof we);
if (l < 0)
return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx);
- if (l < RFKILL_EVENT_SIZE_V1)
+ if ((size_t)l < RFKILL_EVENT_SIZE_V1) /* l cannot be < 0 here. Cast to fix -Werror=sign-compare */
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Couldn't write rfkill event structure, too short (wrote %zd of %zu bytes).",
l, sizeof we);
@@ -335,9 +335,9 @@ static int run(int argc, char *argv[]) {
break;
}
- if (l < RFKILL_EVENT_SIZE_V1)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read of struct rfkill_event: (%zd < %d)",
- l, RFKILL_EVENT_SIZE_V1);
+ if ((size_t)l < RFKILL_EVENT_SIZE_V1) /* l cannot be < 0 here. Cast to fix -Werror=sign-compare */
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read of struct rfkill_event: (%zd < %zu)",
+ l, (size_t) RFKILL_EVENT_SIZE_V1); /* Casting necessary to make compiling with different kernel versions happy */
log_debug("Reading struct rfkill_event: got %zd bytes.", l);
/* The event structure has more fields. We only care about the first few, so it's OK if we
diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c
index ef4b88361f..10e1857649 100644
--- a/src/shared/acl-util.c
+++ b/src/shared/acl-util.c
@@ -211,7 +211,7 @@ int acl_search_groups(const char *path, char ***ret_groups) {
int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
_cleanup_free_ char **a = NULL, **d = NULL; /* strings are not freed */
- _cleanup_strv_free_ char **split;
+ _cleanup_strv_free_ char **split = NULL;
char **entry;
int r = -EINVAL;
_cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL;
@@ -233,7 +233,7 @@ int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want
}
if (!strv_isempty(a)) {
- _cleanup_free_ char *join;
+ _cleanup_free_ char *join = NULL;
join = strv_join(a, ",");
if (!join)
@@ -251,7 +251,7 @@ int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want
}
if (!strv_isempty(d)) {
- _cleanup_free_ char *join;
+ _cleanup_free_ char *join = NULL;
join = strv_join(d, ",");
if (!join)
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index 1d05409086..016eb7b82a 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -46,14 +46,13 @@ static const BaseFilesystem table[] = {
int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
- size_t i;
int r;
fd = open(root, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (fd < 0)
return log_error_errno(errno, "Failed to open root file system: %m");
- for (i = 0; i < ELEMENTSOF(table); i ++) {
+ for (size_t i = 0; i < ELEMENTSOF(table); i++) {
if (faccessat(fd, table[i].dir, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
continue;
@@ -94,10 +93,9 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
return -errno;
}
- if (uid_is_valid(uid) || gid_is_valid(gid)) {
+ if (uid_is_valid(uid) || gid_is_valid(gid))
if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir);
- }
continue;
}
@@ -114,10 +112,9 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
return -errno;
}
- if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (uid != UID_INVALID || gid != UID_INVALID)
if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
return log_error_errno(errno, "Failed to chown directory at %s/%s: %m", root, table[i].dir);
- }
}
return 0;
diff --git a/src/shared/bpf-program.c b/src/shared/bpf-program.c
index 10239142af..a8a34521fd 100644
--- a/src/shared/bpf-program.c
+++ b/src/shared/bpf-program.c
@@ -11,6 +11,50 @@
#include "memory-util.h"
#include "missing_syscall.h"
#include "path-util.h"
+#include "string-table.h"
+
+static const char *const bpf_cgroup_attach_type_table[__MAX_BPF_ATTACH_TYPE] = {
+ [BPF_CGROUP_INET_INGRESS] = "ingress",
+ [BPF_CGROUP_INET_EGRESS] = "egress",
+ [BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
+ [BPF_CGROUP_SOCK_OPS] = "sock_ops",
+ [BPF_CGROUP_DEVICE] = "device",
+ [BPF_CGROUP_INET4_BIND] = "bind4",
+ [BPF_CGROUP_INET6_BIND] = "bind6",
+ [BPF_CGROUP_INET4_CONNECT] = "connect4",
+ [BPF_CGROUP_INET6_CONNECT] = "connect6",
+ [BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
+ [BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
+ [BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
+ [BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
+ [BPF_CGROUP_SYSCTL] = "sysctl",
+ [BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
+ [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
+ [BPF_CGROUP_GETSOCKOPT] = "getsockopt",
+ [BPF_CGROUP_SETSOCKOPT] = "setsockopt",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bpf_cgroup_attach_type, int);
+
+ /* struct bpf_prog_info info must be initialized since its value is both input and output
+ * for BPF_OBJ_GET_INFO_BY_FD syscall. */
+static int bpf_program_get_info_by_fd(int prog_fd, struct bpf_prog_info *info, uint32_t info_len) {
+ union bpf_attr attr;
+
+ /* Explicitly memset to zero since some compilers may produce non-zero-initialized padding when
+ * structured initialization is used.
+ * Refer to https://github.com/systemd/systemd/issues/18164
+ */
+ zero(attr);
+ attr.info.bpf_fd = prog_fd;
+ attr.info.info_len = info_len;
+ attr.info.info = PTR_TO_UINT64(info);
+
+ if (bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) < 0)
+ return -errno;
+
+ return 0;
+}
int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
_cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
@@ -28,6 +72,38 @@ int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
return 0;
}
+int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret) {
+ _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
+ struct bpf_prog_info info = {};
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ p = new(BPFProgram, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (BPFProgram) {
+ .prog_type = BPF_PROG_TYPE_UNSPEC,
+ .n_ref = 1,
+ .kernel_fd = -1,
+ };
+
+ r = bpf_program_load_from_bpf_fs(p, path);
+ if (r < 0)
+ return r;
+
+ r = bpf_program_get_info_by_fd(p->kernel_fd, &info, sizeof(info));
+ if (r < 0)
+ return r;
+
+ p->prog_type = info.type;
+ *ret = TAKE_PTR(p);
+
+ return 0;
+}
+
static BPFProgram *bpf_program_free(BPFProgram *p) {
assert(p);
@@ -254,3 +330,31 @@ int bpf_map_lookup_element(int fd, const void *key, void *value) {
return 0;
}
+
+int bpf_program_pin(int prog_fd, const char *bpffs_path) {
+ union bpf_attr attr;
+
+ zero(attr);
+ attr.pathname = PTR_TO_UINT64((void *) bpffs_path);
+ attr.bpf_fd = prog_fd;
+
+ if (bpf(BPF_OBJ_PIN, &attr, sizeof(attr)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int bpf_program_get_id_by_fd(int prog_fd, uint32_t *ret_id) {
+ struct bpf_prog_info info = {};
+ int r;
+
+ assert(ret_id);
+
+ r = bpf_program_get_info_by_fd(prog_fd, &info, sizeof(info));
+ if (r < 0)
+ return r;
+
+ *ret_id = info.id;
+
+ return 0;
+};
diff --git a/src/shared/bpf-program.h b/src/shared/bpf-program.h
index eef77f9d8e..86fd338c93 100644
--- a/src/shared/bpf-program.h
+++ b/src/shared/bpf-program.h
@@ -26,8 +26,9 @@ struct BPFProgram {
};
int bpf_program_new(uint32_t prog_type, BPFProgram **ret);
-BPFProgram *bpf_program_unref(BPFProgram *p);
+int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret);
BPFProgram *bpf_program_ref(BPFProgram *p);
+BPFProgram *bpf_program_unref(BPFProgram *p);
int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count);
int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size);
@@ -35,9 +36,14 @@ int bpf_program_load_from_bpf_fs(BPFProgram *p, const char *path);
int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags);
int bpf_program_cgroup_detach(BPFProgram *p);
+int bpf_program_pin(int prog_fd, const char *bpffs_path);
+int bpf_program_get_id_by_fd(int prog_fd, uint32_t *ret_id);
int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags);
int bpf_map_update_element(int fd, const void *key, void *value);
int bpf_map_lookup_element(int fd, const void *key, void *value);
+int bpf_cgroup_attach_type_from_string(const char *str) _pure_;
+const char *bpf_cgroup_attach_type_to_string(int attach_type) _const_;
+
DEFINE_TRIVIAL_CLEANUP_FUNC(BPFProgram*, bpf_program_unref);
diff --git a/src/shared/bus-message-util.c b/src/shared/bus-message-util.c
index 565560c6be..14a4a4cfd6 100644
--- a/src/shared/bus-message-util.c
+++ b/src/shared/bus-message-util.c
@@ -17,7 +17,7 @@ int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *
return r;
if (ifindex <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
*ret = ifindex;
@@ -62,7 +62,7 @@ int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error,
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
if (ret_family)
*ret_family = family;
@@ -99,7 +99,7 @@ static int bus_message_read_dns_one(
return r;
if (!dns_server_address_valid(family, &a)) {
- r = sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
+ r = sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
assert(r < 0);
return r;
}
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 84d4729334..eb28c35924 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -842,6 +842,26 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
return 1;
}
+ if (streq(field, "BPFProgram")) {
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
+ else {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&eq, &word, ":", 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s: %m", field);
+
+ r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, word, eq);
+ }
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
return 0;
}
diff --git a/src/shared/bus-wait-for-jobs.c b/src/shared/bus-wait-for-jobs.c
index 51b71ecc2c..8458fe8684 100644
--- a/src/shared/bus-wait-for-jobs.c
+++ b/src/shared/bus-wait-for-jobs.c
@@ -184,7 +184,7 @@ static void log_job_error_with_service_result(const char* service, const char *r
service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
if (!strv_isempty((char**) extra_args)) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
t = strv_join((char**) extra_args, " ");
systemctl = strjoina("systemctl ", t ? : "<args>");
@@ -306,7 +306,8 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_ar
if (q < 0 && r == 0)
r = q;
- log_debug_errno(q, "Got result %s/%m for job %s", d->result, d->name);
+ log_full_errno_zerook(LOG_DEBUG, q,
+ "Got result %s/%m for job %s", d->result, d->name);
}
d->name = mfree(d->name);
diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c
index 77fe227e36..497b0884d4 100644
--- a/src/shared/clean-ipc.c
+++ b/src/shared/clean-ipc.c
@@ -240,7 +240,7 @@ static int clean_posix_shm_internal(const char *dirname, DIR *dir, uid_t uid, gi
}
if (S_ISDIR(st.st_mode)) {
- _cleanup_closedir_ DIR *kid;
+ _cleanup_closedir_ DIR *kid = NULL;
kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
if (!kid) {
diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c
index ec67b054b4..b446daf581 100644
--- a/src/shared/clock-util.c
+++ b/src/shared/clock-util.c
@@ -55,7 +55,7 @@ int clock_set_hwclock(const struct tm *tm) {
}
int clock_is_localtime(const char* adjtime_path) {
- _cleanup_fclose_ FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
if (!adjtime_path)
diff --git a/src/shared/cryptsetup-util.h b/src/shared/cryptsetup-util.h
index 5ebb0ac576..855997f335 100644
--- a/src/shared/cryptsetup-util.h
+++ b/src/shared/cryptsetup-util.h
@@ -61,4 +61,12 @@ int cryptsetup_get_token_as_json(struct crypt_device *cd, int idx, const char *v
int cryptsetup_get_keyslot_from_token(JsonVariant *v);
int cryptsetup_add_token_json(struct crypt_device *cd, JsonVariant *v);
+#else
+
+/* If libcryptsetup is not available, let's at least define the basic type and NOP destructors for it, to
+ * make a little bit less #ifdeferry necessary in main programs. */
+struct crypt_device;
+static inline void sym_crypt_free(struct crypt_device* cd) {}
+static inline void sym_crypt_freep(struct crypt_device** cd) {}
+
#endif
diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c
index 43138e0a6f..8d04ba04fc 100644
--- a/src/shared/discover-image.c
+++ b/src/shared/discover-image.c
@@ -1201,10 +1201,13 @@ int image_read_metadata(Image *i) {
r = dissect_image(
d->fd,
NULL, NULL,
+ d->uevent_seqnum_not_before,
+ d->timestamp_not_before,
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
- DISSECT_IMAGE_USR_NO_ROOT, &m);
+ DISSECT_IMAGE_USR_NO_ROOT,
+ &m);
if (r < 0)
return r;
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index ce8a683bd6..b425014457 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -123,10 +123,6 @@ static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) {
if (r < 0)
return r;
- r = sd_device_enumerator_allow_uninitialized(e);
- if (r < 0)
- return r;
-
r = sd_device_enumerator_add_match_subsystem(e, "block", true);
if (r < 0)
return r;
@@ -143,7 +139,11 @@ static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) {
return 0;
}
-static int device_is_partition(sd_device *d, sd_device *expected_parent, blkid_partition pp) {
+static int device_is_partition(
+ sd_device *d,
+ sd_device *expected_parent,
+ blkid_partition pp) {
+
const char *v, *parent_syspath, *expected_parent_syspath;
blkid_loff_t bsize, bstart;
uint64_t size, start;
@@ -178,9 +178,16 @@ static int device_is_partition(sd_device *d, sd_device *expected_parent, blkid_p
if (!path_equal(parent_syspath, expected_parent_syspath))
return false; /* Has a different parent than what we need, not interesting to us */
- r = sd_device_get_sysattr_value(d, "partition", &v);
+ /* On kernel uevents we may find the partition number in the PARTN= field. Let's use that preferably,
+ * since it's cheaper and more importantly: the sysfs attribute "partition" appears to become
+ * available late, hence let's use the property instead, which is available at the moment we see the
+ * uevent. */
+ r = sd_device_get_property_value(d, "PARTN", &v);
+ if (r == -ENOENT)
+ r = sd_device_get_sysattr_value(d, "partition", &v);
if (r < 0)
return r;
+
r = safe_atoi(v, &partno);
if (r < 0)
return r;
@@ -229,6 +236,7 @@ static int device_is_partition(sd_device *d, sd_device *expected_parent, blkid_p
static int find_partition(
sd_device *parent,
blkid_partition pp,
+ usec_t timestamp_not_before,
sd_device **ret) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
@@ -244,6 +252,18 @@ static int find_partition(
return r;
FOREACH_DEVICE(e, q) {
+ uint64_t usec;
+
+ r = sd_device_get_usec_initialized(q, &usec);
+ if (r == -EBUSY) /* Not initialized yet */
+ continue;
+ if (r < 0)
+ return r;
+
+ if (timestamp_not_before != USEC_INFINITY &&
+ usec < timestamp_not_before) /* udev database entry older than our attachment? Then it's not ours */
+ continue;
+
r = device_is_partition(q, parent, pp);
if (r < 0)
return r;
@@ -260,6 +280,7 @@ struct wait_data {
sd_device *parent_device;
blkid_partition blkidp;
sd_device *found;
+ uint64_t uevent_seqnum_not_before;
};
static inline void wait_data_done(struct wait_data *d) {
@@ -275,6 +296,20 @@ static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device,
if (device_for_action(device, SD_DEVICE_REMOVE))
return 0;
+ if (w->uevent_seqnum_not_before != UINT64_MAX) {
+ uint64_t seqnum;
+
+ r = sd_device_get_seqnum(device, &seqnum);
+ if (r < 0)
+ goto finish;
+
+ if (seqnum <= w->uevent_seqnum_not_before) { /* From an older use of this loop device */
+ log_debug("Dropping event because seqnum too old (%" PRIu64 " <= %" PRIu64 ")",
+ seqnum, w->uevent_seqnum_not_before);
+ return 0;
+ }
+ }
+
r = device_is_partition(device, w->parent_device, w->blkidp);
if (r < 0)
goto finish;
@@ -294,6 +329,8 @@ static int wait_for_partition_device(
sd_device *parent,
blkid_partition pp,
usec_t deadline,
+ uint64_t uevent_seqnum_not_before,
+ usec_t timestamp_not_before,
sd_device **ret) {
_cleanup_(sd_event_source_unrefp) sd_event_source *timeout_source = NULL;
@@ -305,7 +342,7 @@ static int wait_for_partition_device(
assert(pp);
assert(ret);
- r = find_partition(parent, pp, ret);
+ r = find_partition(parent, pp, timestamp_not_before, ret);
if (r != -ENXIO)
return r;
@@ -336,6 +373,7 @@ static int wait_for_partition_device(
_cleanup_(wait_data_done) struct wait_data w = {
.parent_device = parent,
.blkidp = pp,
+ .uevent_seqnum_not_before = uevent_seqnum_not_before,
};
r = sd_device_monitor_start(monitor, device_monitor_handler, &w);
@@ -343,7 +381,7 @@ static int wait_for_partition_device(
return r;
/* Check again, the partition might have appeared in the meantime */
- r = find_partition(parent, pp, ret);
+ r = find_partition(parent, pp, timestamp_not_before, ret);
if (r != -ENXIO)
return r;
@@ -492,6 +530,8 @@ int dissect_image(
int fd,
const VeritySettings *verity,
const MountOptions *mount_options,
+ uint64_t uevent_seqnum_not_before,
+ usec_t timestamp_not_before,
DissectImageFlags flags,
DissectedImage **ret) {
@@ -744,7 +784,7 @@ int dissect_image(
if (!pp)
return errno_or_else(EIO);
- r = wait_for_partition_device(d, pp, deadline, &q);
+ r = wait_for_partition_device(d, pp, deadline, uevent_seqnum_not_before, timestamp_not_before, &q);
if (r < 0)
return r;
@@ -1408,7 +1448,7 @@ static int mount_partition(
if (streq(fstype, "crypto_LUKS"))
return -EUNATCH;
- rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
+ rw = m->rw && !(flags & DISSECT_IMAGE_MOUNT_READ_ONLY);
if (FLAGS_SET(flags, DISSECT_IMAGE_FSCK) && rw) {
r = run_fsck(node, fstype);
@@ -1463,6 +1503,27 @@ static int mount_partition(
if (!strextend_with_separator(&options, ",", m->mount_options))
return -ENOMEM;
+ /* So, when you request MS_RDONLY from ext4, then this means nothing. It happily still writes to the
+ * backing storage. What's worse, the BLKRO[GS]ET flag and (in case of loopback devices)
+ * LO_FLAGS_READ_ONLY don't mean anything, they affect userspace accesses only, and write accesses
+ * from the upper file system still get propagated through to the underlying file system,
+ * unrestricted. To actually get ext4/xfs/btrfs to stop writing to the device we need to specify
+ * "norecovery" as mount option, in addition to MS_RDONLY. Yes, this sucks, since it means we need to
+ * carry a per file system table here.
+ *
+ * Note that this means that we might not be able to mount corrupted file systems as read-only
+ * anymore (since in some cases the kernel implementations will refuse mounting when corrupted,
+ * read-only and "norecovery" is specified). But I think for the case of automatically determined
+ * mount options for loopback devices this is the right choice, since otherwise using the same
+ * loopback file twice even in read-only mode, is going to fail badly sooner or later. The usecase of
+ * making reuse of the immutable images "just work" is more relevant to us than having read-only
+ * access that actually modifies stuff work on such image files. Or to say this differently: if
+ * people want their file systems to be fixed up they should just open them in writable mode, where
+ * all these problems don't exist. */
+ if (!rw && STRPTR_IN_SET(fstype, "ext3", "ext4", "xfs", "btrfs"))
+ if (!strextend_with_separator(&options, ",", "norecovery"))
+ return -ENOMEM;
+
r = mount_nofollow_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
if (r < 0)
return r;
@@ -1756,7 +1817,7 @@ static int decrypt_partition(
return log_debug_errno(r, "Failed to load LUKS metadata: %m");
r = sym_crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
- ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
+ ((flags & DISSECT_IMAGE_DEVICE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
if (r < 0) {
log_debug_errno(r, "Failed to activate LUKS device: %m");
@@ -2558,6 +2619,8 @@ int dissect_image_and_warn(
const char *name,
const VeritySettings *verity,
const MountOptions *mount_options,
+ uint64_t uevent_seqnum_not_before,
+ usec_t timestamp_not_before,
DissectImageFlags flags,
DissectedImage **ret) {
@@ -2572,7 +2635,7 @@ int dissect_image_and_warn(
name = buffer;
}
- r = dissect_image(fd, verity, mount_options, flags, ret);
+ r = dissect_image(fd, verity, mount_options, uevent_seqnum_not_before, timestamp_not_before, flags, ret);
switch (r) {
case -EOPNOTSUPP:
@@ -2674,13 +2737,13 @@ int mount_image_privately_interactively(
r = loop_device_make_by_path(
image,
- FLAGS_SET(flags, DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR,
+ FLAGS_SET(flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
&d);
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device: %m");
- r = dissect_image_and_warn(d->fd, image, &verity, NULL, flags, &dissected_image);
+ r = dissect_image_and_warn(d->fd, image, &verity, NULL, d->uevent_seqnum_not_before, d->timestamp_not_before, flags, &dissected_image);
if (r < 0)
return r;
@@ -2771,6 +2834,8 @@ int verity_dissect_and_mount(
loop_device->fd,
&verity,
options,
+ loop_device->uevent_seqnum_not_before,
+ loop_device->timestamp_not_before,
dissect_image_flags,
&dissected_image);
/* No partition table? Might be a single-filesystem image, try again */
@@ -2779,7 +2844,9 @@ int verity_dissect_and_mount(
loop_device->fd,
&verity,
options,
- dissect_image_flags|DISSECT_IMAGE_NO_PARTITION_TABLE,
+ loop_device->uevent_seqnum_not_before,
+ loop_device->timestamp_not_before,
+ dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE,
&dissected_image);
if (r < 0)
return log_debug_errno(r, "Failed to dissect image: %m");
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index f07955230b..88106ee4f3 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -87,13 +87,13 @@ static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) {
}
typedef enum DissectImageFlags {
- DISSECT_IMAGE_READ_ONLY = 1 << 0,
+ DISSECT_IMAGE_DEVICE_READ_ONLY = 1 << 0, /* Make device read-only */
DISSECT_IMAGE_DISCARD_ON_LOOP = 1 << 1, /* Turn on "discard" if on a loop device and file system supports it */
DISSECT_IMAGE_DISCARD = 1 << 2, /* Turn on "discard" if file system supports it, on all block devices */
DISSECT_IMAGE_DISCARD_ON_CRYPTO = 1 << 3, /* Turn on "discard" also on crypto devices */
- DISSECT_IMAGE_DISCARD_ANY = DISSECT_IMAGE_DISCARD_ON_LOOP |
- DISSECT_IMAGE_DISCARD |
- DISSECT_IMAGE_DISCARD_ON_CRYPTO,
+ DISSECT_IMAGE_DISCARD_ANY = DISSECT_IMAGE_DISCARD_ON_LOOP |
+ DISSECT_IMAGE_DISCARD |
+ DISSECT_IMAGE_DISCARD_ON_CRYPTO,
DISSECT_IMAGE_GPT_ONLY = 1 << 4, /* Only recognize images with GPT partition tables */
DISSECT_IMAGE_GENERIC_ROOT = 1 << 5, /* If no partition table or only single generic partition, assume it's the root fs */
DISSECT_IMAGE_MOUNT_ROOT_ONLY = 1 << 6, /* Mount only the root and /usr partitions */
@@ -107,6 +107,9 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_MKDIR = 1 << 14, /* Make top-level directory to mount right before mounting, if missing */
DISSECT_IMAGE_USR_NO_ROOT = 1 << 15, /* If no root fs is in the image, but /usr is, then allow this (so that we can mount the rootfs as tmpfs or so */
DISSECT_IMAGE_REQUIRE_ROOT = 1 << 16, /* Don't accept disks without root partition (or at least /usr partition if DISSECT_IMAGE_USR_NO_ROOT is set) */
+ DISSECT_IMAGE_MOUNT_READ_ONLY = 1 << 17, /* Make mounts read-only */
+ DISSECT_IMAGE_READ_ONLY = DISSECT_IMAGE_DEVICE_READ_ONLY |
+ DISSECT_IMAGE_MOUNT_READ_ONLY,
} DissectImageFlags;
struct DissectedImage {
@@ -156,8 +159,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator);
int probe_filesystem(const char *node, char **ret_fstype);
-int dissect_image(int fd, const VeritySettings *verity, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
-int dissect_image_and_warn(int fd, const char *name, const VeritySettings *verity, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
+int dissect_image(int fd, const VeritySettings *verity, const MountOptions *mount_options, uint64_t uevent_seqnum_not_before, usec_t timestamp_not_before, DissectImageFlags flags, DissectedImage **ret);
+int dissect_image_and_warn(int fd, const char *name, const VeritySettings *verity, const MountOptions *mount_options, uint64_t uevent_seqnum_not_before, usec_t timestamp_not_before, DissectImageFlags flags, DissectedImage **ret);
DissectedImage* dissected_image_unref(DissectedImage *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
diff --git a/src/shared/firewall-util-iptables.c b/src/shared/firewall-util-iptables.c
index 982c61d8fb..d53a394895 100644
--- a/src/shared/firewall-util-iptables.c
+++ b/src/shared/firewall-util-iptables.c
@@ -102,9 +102,9 @@ int fw_iptables_add_masquerade(
if (!source || source_prefixlen == 0)
return -EINVAL;
- h = iptc_init("nat");
- if (!h)
- return -errno;
+ r = fw_iptables_init_nat(&h);
+ if (r < 0)
+ return r;
sz = XT_ALIGN(sizeof(struct ipt_entry)) +
XT_ALIGN(sizeof(struct ipt_entry_target)) +
@@ -192,9 +192,9 @@ int fw_iptables_add_local_dnat(
if (remote_port <= 0)
return -EINVAL;
- h = iptc_init("nat");
- if (!h)
- return -errno;
+ r = fw_iptables_init_nat(&h);
+ if (r < 0)
+ return r;
sz = XT_ALIGN(sizeof(struct ipt_entry)) +
XT_ALIGN(sizeof(struct ipt_entry_match)) +
@@ -348,3 +348,16 @@ int fw_iptables_add_local_dnat(
return 0;
}
+
+int fw_iptables_init_nat(struct xtc_handle **ret) {
+ _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
+
+ h = iptc_init("nat");
+ if (!h)
+ return log_debug_errno(errno, "Failed to init \"nat\" table: %s", iptc_strerror(errno));
+
+ if (ret)
+ *ret = TAKE_PTR(h);
+
+ return 0;
+}
diff --git a/src/shared/firewall-util-private.h b/src/shared/firewall-util-private.h
index 07e2d0bbd3..14f5a35a87 100644
--- a/src/shared/firewall-util-private.h
+++ b/src/shared/firewall-util-private.h
@@ -46,6 +46,7 @@ int fw_nftables_add_local_dnat(
const union in_addr_union *previous_remote);
#if HAVE_LIBIPTC
+struct xtc_handle;
int fw_iptables_add_masquerade(
bool add,
@@ -61,4 +62,6 @@ int fw_iptables_add_local_dnat(
const union in_addr_union *remote,
uint16_t remote_port,
const union in_addr_union *previous_remote);
+
+int fw_iptables_init_nat(struct xtc_handle **ret);
#endif
diff --git a/src/shared/format-table.c b/src/shared/format-table.c
index 6bbc8bd509..76cf3343db 100644
--- a/src/shared/format-table.c
+++ b/src/shared/format-table.c
@@ -783,19 +783,17 @@ int table_update(Table *t, TableCell *cell, TableDataType type, const void *data
}
int table_add_many_internal(Table *t, TableDataType first_type, ...) {
- TableDataType type;
- va_list ap;
TableCell *last_cell = NULL;
+ va_list ap;
int r;
assert(t);
assert(first_type >= 0);
assert(first_type < _TABLE_DATA_TYPE_MAX);
- type = first_type;
-
va_start(ap, first_type);
- for (;;) {
+
+ for (TableDataType type = first_type;; type = va_arg(ap, TableDataType)) {
const void *data;
union {
uint64_t size;
@@ -968,43 +966,43 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
size_t w = va_arg(ap, size_t);
r = table_set_minimum_width(t, last_cell, w);
- break;
+ goto check;
}
case TABLE_SET_MAXIMUM_WIDTH: {
size_t w = va_arg(ap, size_t);
r = table_set_maximum_width(t, last_cell, w);
- break;
+ goto check;
}
case TABLE_SET_WEIGHT: {
unsigned w = va_arg(ap, unsigned);
r = table_set_weight(t, last_cell, w);
- break;
+ goto check;
}
case TABLE_SET_ALIGN_PERCENT: {
unsigned p = va_arg(ap, unsigned);
r = table_set_align_percent(t, last_cell, p);
- break;
+ goto check;
}
case TABLE_SET_ELLIPSIZE_PERCENT: {
unsigned p = va_arg(ap, unsigned);
r = table_set_ellipsize_percent(t, last_cell, p);
- break;
+ goto check;
}
case TABLE_SET_COLOR: {
const char *c = va_arg(ap, const char*);
r = table_set_color(t, last_cell, c);
- break;
+ goto check;
}
case TABLE_SET_RGAP_COLOR: {
const char *c = va_arg(ap, const char*);
r = table_set_rgap_color(t, last_cell, c);
- break;
+ goto check;
}
case TABLE_SET_BOTH_COLORS: {
@@ -1017,19 +1015,19 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
}
r = table_set_rgap_color(t, last_cell, c);
- break;
+ goto check;
}
case TABLE_SET_URL: {
const char *u = va_arg(ap, const char*);
r = table_set_url(t, last_cell, u);
- break;
+ goto check;
}
case TABLE_SET_UPPERCASE: {
int u = va_arg(ap, int);
r = table_set_uppercase(t, last_cell, u);
- break;
+ goto check;
}
case _TABLE_DATA_TYPE_MAX:
@@ -1041,15 +1039,12 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
assert_not_reached("Uh? Unexpected data type.");
}
- if (type < _TABLE_DATA_TYPE_MAX)
- r = table_add_cell(t, &last_cell, type, data);
-
+ r = table_add_cell(t, &last_cell, type, data);
+ check:
if (r < 0) {
va_end(ap);
return r;
}
-
- type = va_arg(ap, TableDataType);
}
}
@@ -1414,7 +1409,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
case TABLE_TIMESTAMP:
case TABLE_TIMESTAMP_UTC:
case TABLE_TIMESTAMP_RELATIVE: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
char *ret;
p = new(char, FORMAT_TIMESTAMP_MAX);
@@ -1436,7 +1431,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
case TABLE_TIMESPAN:
case TABLE_TIMESPAN_MSEC: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, FORMAT_TIMESPAN_MAX);
if (!p)
@@ -1451,7 +1446,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_SIZE: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, FORMAT_BYTES_MAX);
if (!p)
@@ -1465,7 +1460,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_BPS: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
size_t n;
p = new(char, FORMAT_BYTES_MAX+2);
@@ -1483,7 +1478,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_INT: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->int_val) + 1);
if (!p)
@@ -1495,7 +1490,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_INT8: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->int8) + 1);
if (!p)
@@ -1507,7 +1502,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_INT16: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->int16) + 1);
if (!p)
@@ -1519,7 +1514,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_INT32: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->int32) + 1);
if (!p)
@@ -1531,7 +1526,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_INT64: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->int64) + 1);
if (!p)
@@ -1543,7 +1538,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_UINT: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->uint_val) + 1);
if (!p)
@@ -1555,7 +1550,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_UINT8: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->uint8) + 1);
if (!p)
@@ -1567,7 +1562,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_UINT16: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->uint16) + 1);
if (!p)
@@ -1579,7 +1574,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_UINT32: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->uint32) + 1);
if (!p)
@@ -1591,7 +1586,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_UINT64: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->uint64) + 1);
if (!p)
@@ -1603,7 +1598,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
}
case TABLE_PERCENT: {
- _cleanup_free_ char *p;
+ _cleanup_free_ char *p = NULL;
p = new(char, DECIMAL_STR_WIDTH(d->percent) + 2);
if (!p)
diff --git a/src/shared/generate-ip-protocol-list.sh b/src/shared/generate-ip-protocol-list.sh
index 749a1305c1..1144e8027f 100755
--- a/src/shared/generate-ip-protocol-list.sh
+++ b/src/shared/generate-ip-protocol-list.sh
@@ -1,8 +1,9 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
-$1 -dM -include netinet/in.h - </dev/null | \
- awk '/^#define[ \t]+IPPROTO_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \
- sed -e 's/IPPROTO_//'
+${1:?} -dM -include netinet/in.h - </dev/null | \
+ awk '/^#define[ \t]+IPPROTO_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \
+ sed -e 's/IPPROTO_//'
diff --git a/src/shared/generator.c b/src/shared/generator.c
index 5b9c432527..0bb3efa700 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -71,12 +71,22 @@ int generator_add_symlink(const char *dir, const char *dst, const char *dep_type
return 0;
}
-static int write_fsck_sysroot_service(const char *dir, const char *what) {
+static int write_fsck_sysroot_service(
+ const char *unit, /* Either SPECIAL_FSCK_ROOT_SERVICE or SPECIAL_FSCK_USR_SERVICE */
+ const char *dir,
+ const char *what,
+ const char *extra_after) {
+
_cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
_cleanup_fclose_ FILE *f = NULL;
- const char *unit;
+ const char *fn;
int r;
+ /* Writes out special versions of systemd-root-fsck.service and systemd-usr-fsck.service for use in
+ * the initrd. The regular statically shipped versions of these unit files use / and /usr for as
+ * paths, which doesn't match what we need for the initrd (where the dirs are /sysroot +
+ * /sysusr/usr), hence we overwrite those versions here. */
+
escaped = specifier_escape(what);
if (!escaped)
return log_oom();
@@ -85,41 +95,44 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) {
if (!escaped2)
return log_oom();
- unit = strjoina(dir, "/"SPECIAL_FSCK_ROOT_SERVICE);
- log_debug("Creating %s", unit);
+ fn = strjoina(dir, "/", unit);
+ log_debug("Creating %s", fn);
r = unit_name_from_path(what, ".device", &device);
if (r < 0)
return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
- f = fopen(unit, "wxe");
+ f = fopen(fn, "wxe");
if (!f)
- return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
+ return log_error_errno(errno, "Failed to create unit file %s: %m", fn);
fprintf(f,
"# Automatically generated by %1$s\n\n"
"[Unit]\n"
"Description=File System Check on %2$s\n"
- "Documentation=man:systemd-fsck-root.service(8)\n"
+ "Documentation=man:%3$s(8)\n"
"DefaultDependencies=no\n"
- "BindsTo=%3$s\n"
+ "BindsTo=%4$s\n"
"Conflicts=shutdown.target\n"
- "After=initrd-root-device.target local-fs-pre.target %3$s\n"
+ "After=%5$s%6$slocal-fs-pre.target %4$s\n"
"Before=shutdown.target\n"
"\n"
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
- "ExecStart=" SYSTEMD_FSCK_PATH " %4$s\n"
+ "ExecStart=" SYSTEMD_FSCK_PATH " %7$s\n"
"TimeoutSec=0\n",
program_invocation_short_name,
escaped,
+ unit,
device,
+ strempty(extra_after),
+ isempty(extra_after) ? "" : " ",
escaped2);
r = fflush_and_check(f);
if (r < 0)
- return log_error_errno(r, "Failed to write unit file %s: %m", unit);
+ return log_error_errno(r, "Failed to write unit file %s: %m", fn);
return 0;
}
@@ -138,6 +151,13 @@ int generator_write_fsck_deps(
assert(what);
assert(where);
+ /* Let's do an early exit if we are invoked for the root and /usr/ trees in the initrd, to avoid
+ * generating confusing log messages */
+ if (in_initrd() && PATH_IN_SET(where, "/", "/usr")) {
+ log_debug("Skipping fsck for %s in initrd.", where);
+ return 0;
+ }
+
if (!is_device_path(what)) {
log_warning("Checking was requested for \"%s\", but it is not a device.", what);
return 0;
@@ -157,6 +177,11 @@ int generator_write_fsck_deps(
if (path_equal(where, "/")) {
const char *lnk;
+ /* We support running the fsck instance for the root fs while it is already mounted, for
+ * compatibility with non-initrd boots. It's ugly, but it is how it is. Since – unlike for
+ * regular file systems – this means the ordering is reversed (i.e. mount *before* fsck) we
+ * have a separate fsck unit for this, independent of systemd-fsck@.service. */
+
lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
(void) mkdir_parents(lnk, 0755);
@@ -168,19 +193,27 @@ int generator_write_fsck_deps(
const char *fsck, *dep;
if (in_initrd() && path_equal(where, "/sysroot")) {
- r = write_fsck_sysroot_service(dir, what);
+ r = write_fsck_sysroot_service(SPECIAL_FSCK_ROOT_SERVICE, dir, what, SPECIAL_INITRD_ROOT_DEVICE_TARGET);
if (r < 0)
return r;
fsck = SPECIAL_FSCK_ROOT_SERVICE;
dep = "Requires";
+
+ } else if (in_initrd() && path_equal(where, "/sysusr/usr")) {
+ r = write_fsck_sysroot_service(SPECIAL_FSCK_USR_SERVICE, dir, what, NULL);
+ if (r < 0)
+ return r;
+
+ fsck = SPECIAL_FSCK_USR_SERVICE;
+ dep = "Requires";
} else {
/* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
* dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
* Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
* we'd have to unmount /usr too. */
- dep = !in_initrd() && path_equal(where, "/usr") ? "Wants" : "Requires";
+ dep = path_equal(where, "/usr") ? "Wants" : "Requires";
r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
if (r < 0)
diff --git a/src/shared/gpt.c b/src/shared/gpt.c
index a96f5ee02d..69da6c7280 100644
--- a/src/shared/gpt.c
+++ b/src/shared/gpt.c
@@ -106,3 +106,60 @@ int gpt_partition_label_valid(const char *s) {
return char16_strlen(recoded) <= 36;
}
+
+bool gpt_partition_type_is_root(sd_id128_t id) {
+ return sd_id128_in_set(id,
+ GPT_ROOT_X86,
+ GPT_ROOT_X86_64,
+ GPT_ROOT_ARM,
+ GPT_ROOT_ARM_64,
+ GPT_ROOT_IA64,
+ GPT_ROOT_RISCV32,
+ GPT_ROOT_RISCV64);
+}
+
+bool gpt_partition_type_is_root_verity(sd_id128_t id) {
+ return sd_id128_in_set(id,
+ GPT_ROOT_X86_VERITY,
+ GPT_ROOT_X86_64_VERITY,
+ GPT_ROOT_ARM_VERITY,
+ GPT_ROOT_ARM_64_VERITY,
+ GPT_ROOT_IA64_VERITY,
+ GPT_ROOT_RISCV32_VERITY,
+ GPT_ROOT_RISCV64_VERITY);
+}
+
+bool gpt_partition_type_is_usr(sd_id128_t id) {
+ return sd_id128_in_set(id,
+ GPT_USR_X86,
+ GPT_USR_X86_64,
+ GPT_USR_ARM,
+ GPT_USR_ARM_64,
+ GPT_USR_IA64,
+ GPT_USR_RISCV32,
+ GPT_USR_RISCV64);
+}
+
+bool gpt_partition_type_is_usr_verity(sd_id128_t id) {
+ return sd_id128_in_set(id,
+ GPT_USR_X86_VERITY,
+ GPT_USR_X86_64_VERITY,
+ GPT_USR_ARM_VERITY,
+ GPT_USR_ARM_64_VERITY,
+ GPT_USR_IA64_VERITY,
+ GPT_USR_RISCV32_VERITY,
+ GPT_USR_RISCV64_VERITY);
+}
+
+bool gpt_partition_type_knows_read_only(sd_id128_t id) {
+ return gpt_partition_type_is_root(id) ||
+ gpt_partition_type_is_usr(id) ||
+ sd_id128_in_set(id,
+ GPT_HOME,
+ GPT_SRV,
+ GPT_VAR,
+ GPT_TMP,
+ GPT_XBOOTLDR) ||
+ gpt_partition_type_is_root_verity(id) || /* pretty much implied, but let's set the bit to make things really clear */
+ gpt_partition_type_is_usr_verity(id); /* ditto */
+}
diff --git a/src/shared/gpt.h b/src/shared/gpt.h
index 2e0f50c3c6..f3a74813f0 100644
--- a/src/shared/gpt.h
+++ b/src/shared/gpt.h
@@ -128,3 +128,10 @@ typedef struct GptPartitionType {
extern const GptPartitionType gpt_partition_type_table[];
int gpt_partition_label_valid(const char *s);
+
+bool gpt_partition_type_is_root(sd_id128_t id);
+bool gpt_partition_type_is_root_verity(sd_id128_t id);
+bool gpt_partition_type_is_usr(sd_id128_t id);
+bool gpt_partition_type_is_usr_verity(sd_id128_t id);
+
+bool gpt_partition_type_knows_read_only(sd_id128_t id);
diff --git a/src/shared/install.c b/src/shared/install.c
index c6cea43126..eb8d7c1c45 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -2950,7 +2950,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, UnitFilePrese
return r;
STRV_FOREACH(p, files) {
- _cleanup_fclose_ FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
int n = 0;
f = fopen(*p, "re");
diff --git a/src/shared/log-link.h b/src/shared/log-link.h
index 3a4dcaa267..51eaa0c06e 100644
--- a/src/shared/log-link.h
+++ b/src/shared/log-link.h
@@ -3,13 +3,38 @@
#include "log.h"
-#define log_interface_full_errno(ifname, level, error, ...) \
+#define log_interface_full_errno_zerook(ifname, level, error, ...) \
({ \
const char *_ifname = (ifname); \
_ifname ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "INTERFACE=", _ifname, NULL, NULL, ##__VA_ARGS__) : \
log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
})
+#define log_interface_full_errno(ifname, level, error, ...) \
+ ({ \
+ int _error = (error); \
+ ASSERT_NON_ZERO(_error); \
+ log_interface_full_errno_zerook(ifname, level, _error, __VA_ARGS__); \
+ })
+
+#define log_interface_prefix_full_errno_zerook(prefix, ifname_expr, error, fmt, ...) \
+ ({ \
+ int _e = (error); \
+ if (DEBUG_LOGGING) \
+ log_interface_full_errno_zerook( \
+ ifname_expr, \
+ LOG_DEBUG, _e, prefix fmt, \
+ ##__VA_ARGS__); \
+ -ERRNO_VALUE(_e); \
+ })
+
+#define log_interface_prefix_full_errno(prefix, ifname_expr, error, fmt, ...) \
+ ({ \
+ int _error = (error); \
+ ASSERT_NON_ZERO(_error); \
+ log_interface_prefix_full_errno_zerook(prefix, ifname_expr, _error, fmt, ##__VA_ARGS__); \
+ })
+
/*
* The following macros append INTERFACE= to the message.
* The macros require a struct named 'Link' which contains 'char *ifname':
@@ -21,15 +46,22 @@
* See, network/networkd-link.h for example.
*/
-#define log_link_full_errno(link, level, error, ...) \
+#define log_link_full_errno_zerook(link, level, error, ...) \
({ \
const Link *_l = (link); \
- log_interface_full_errno(_l ? _l->ifname : NULL, level, error, ##__VA_ARGS__); \
+ log_interface_full_errno_zerook(_l ? _l->ifname : NULL, level, error, __VA_ARGS__); \
+ })
+
+#define log_link_full_errno(link, level, error, ...) \
+ ({ \
+ int _error = (error); \
+ ASSERT_NON_ZERO(_error); \
+ log_link_full_errno_zerook(link, level, _error, __VA_ARGS__); \
})
-#define log_link_full(link, level, ...) (void) log_link_full_errno(link, level, 0, __VA_ARGS__)
+#define log_link_full(link, level, ...) (void) log_link_full_errno_zerook(link, level, 0, __VA_ARGS__)
-#define log_link_debug(link, ...) log_link_full_errno(link, LOG_DEBUG, 0, __VA_ARGS__)
+#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, __VA_ARGS__)
#define log_link_info(link, ...) log_link_full(link, LOG_INFO, __VA_ARGS__)
#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, __VA_ARGS__)
#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, __VA_ARGS__)
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 706a00c7f0..e63c59bd94 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -294,7 +294,7 @@ static bool print_multiline(
continuation * prefix, "",
color_on, len, pos, color_off);
else {
- _cleanup_free_ char *e;
+ _cleanup_free_ char *e = NULL;
e = ellipsize_mem(pos, len, n_columns - prefix,
tail_line ? 100 : 90);
@@ -1651,7 +1651,7 @@ int show_journal_by_unit(
return r;
if (DEBUG_LOGGING) {
- _cleanup_free_ char *filter;
+ _cleanup_free_ char *filter = NULL;
filter = journal_make_match_string(j);
if (!filter)
diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c
index 84f415aa61..c038e7aae6 100644
--- a/src/shared/loop-util.c
+++ b/src/shared/loop-util.c
@@ -53,6 +53,23 @@ static int loop_is_bound(int fd) {
return true; /* bound! */
}
+static int get_current_uevent_seqnum(uint64_t *ret) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ r = read_full_virtual_file("/sys/kernel/uevent_seqnum", &p, NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read current uevent sequence number: %m");
+
+ truncate_nl(p);
+
+ r = safe_atou64(p, ret);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse current uevent sequence number: %s", p);
+
+ return 0;
+}
+
static int device_has_block_children(sd_device *d) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
const char *main_sn, *main_ss;
@@ -114,11 +131,15 @@ static int loop_configure(
int fd,
int nr,
const struct loop_config *c,
- bool *try_loop_configure) {
+ bool *try_loop_configure,
+ uint64_t *ret_seqnum_not_before,
+ usec_t *ret_timestamp_not_before) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_free_ char *sysname = NULL;
_cleanup_close_ int lock_fd = -1;
+ uint64_t seqnum;
+ usec_t timestamp;
int r;
assert(fd >= 0);
@@ -167,6 +188,17 @@ static int loop_configure(
}
if (*try_loop_configure) {
+ /* Acquire uevent seqnum immediately before attaching the loopback device. This allows
+ * callers to ignore all uevents with a seqnum before this one, if they need to associate
+ * uevent with this attachment. Doing so isn't race-free though, as uevents that happen in
+ * the window between this reading of the seqnum, and the LOOP_CONFIGURE call might still be
+ * mistaken as originating from our attachment, even though might be caused by an earlier
+ * use. But doing this at least shortens the race window a bit. */
+ r = get_current_uevent_seqnum(&seqnum);
+ if (r < 0)
+ return r;
+ timestamp = now(CLOCK_MONOTONIC);
+
if (ioctl(fd, LOOP_CONFIGURE, c) < 0) {
/* Do fallback only if LOOP_CONFIGURE is not supported, propagate all other
* errors. Note that the kernel is weird: non-existing ioctls currently return EINVAL
@@ -224,10 +256,21 @@ static int loop_configure(
goto fail;
}
+ if (ret_seqnum_not_before)
+ *ret_seqnum_not_before = seqnum;
+ if (ret_timestamp_not_before)
+ *ret_timestamp_not_before = timestamp;
+
return 0;
}
}
+ /* Let's read the seqnum again, to shorten the window. */
+ r = get_current_uevent_seqnum(&seqnum);
+ if (r < 0)
+ return r;
+ timestamp = now(CLOCK_MONOTONIC);
+
/* Since kernel commit 5db470e229e22b7eda6e23b5566e532c96fb5bc3 (kernel v5.0) the LOOP_SET_STATUS64
* ioctl can return EAGAIN in case we change the lo_offset field, if someone else is accessing the
* block device while we try to reconfigure it. This is a pretty common case, since udev might
@@ -252,9 +295,14 @@ static int loop_configure(
/* Sleep some random time, but at least 10ms, at most 250ms. Increase the delay the more
* failed attempts we see */
(void) usleep(UINT64_C(10) * USEC_PER_MSEC +
- random_u64() % (UINT64_C(240) * USEC_PER_MSEC * n_attempts/64));
+ random_u64_range(UINT64_C(240) * USEC_PER_MSEC * n_attempts/64));
}
+ if (ret_seqnum_not_before)
+ *ret_seqnum_not_before = seqnum;
+ if (ret_timestamp_not_before)
+ *ret_timestamp_not_before = timestamp;
+
return 0;
fail:
@@ -312,6 +360,8 @@ int loop_device_make(
bool try_loop_configure = true;
struct loop_config config;
LoopDevice *d = NULL;
+ uint64_t seqnum = UINT64_MAX;
+ usec_t timestamp = USEC_INFINITY;
struct stat st;
int nr = -1, r;
@@ -353,6 +403,9 @@ int loop_device_make(
.nr = nr,
.node = TAKE_PTR(loopdev),
.relinquished = true, /* It's not allocated by us, don't destroy it when this object is freed */
+ .devno = st.st_rdev,
+ .uevent_seqnum_not_before = UINT64_MAX,
+ .timestamp_not_before = USEC_INFINITY,
};
*ret = d;
@@ -400,7 +453,7 @@ int loop_device_make(
if (!IN_SET(errno, ENOENT, ENXIO))
return -errno;
} else {
- r = loop_configure(loop, nr, &config, &try_loop_configure);
+ r = loop_configure(loop, nr, &config, &try_loop_configure, &seqnum, &timestamp);
if (r >= 0) {
loop_with_fd = TAKE_FD(loop);
break;
@@ -421,10 +474,14 @@ int loop_device_make(
/* Wait some random time, to make collision less likely. Let's pick a random time in the
* range 0ms…250ms, linearly scaled by the number of failed attempts. */
- (void) usleep(random_u64() % (UINT64_C(10) * USEC_PER_MSEC +
- UINT64_C(240) * USEC_PER_MSEC * n_attempts/64));
+ (void) usleep(random_u64_range(UINT64_C(10) * USEC_PER_MSEC +
+ UINT64_C(240) * USEC_PER_MSEC * n_attempts/64));
}
+ if (fstat(loop_with_fd, &st) < 0)
+ return -errno;
+ assert(S_ISBLK(st.st_mode));
+
d = new(LoopDevice, 1);
if (!d)
return -ENOMEM;
@@ -432,13 +489,21 @@ int loop_device_make(
.fd = TAKE_FD(loop_with_fd),
.node = TAKE_PTR(loopdev),
.nr = nr,
+ .devno = st.st_rdev,
+ .uevent_seqnum_not_before = seqnum,
+ .timestamp_not_before = timestamp,
};
*ret = d;
- return 0;
+ return d->fd;
}
-int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, LoopDevice **ret) {
+int loop_device_make_by_path(
+ const char *path,
+ int open_flags,
+ uint32_t loop_flags,
+ LoopDevice **ret) {
+
_cleanup_close_ int fd = -1;
int r;
@@ -561,6 +626,9 @@ int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret) {
.nr = nr,
.node = TAKE_PTR(p),
.relinquished = true, /* It's not ours, don't try to destroy it when this object is freed */
+ .devno = st.st_dev,
+ .uevent_seqnum_not_before = UINT64_MAX,
+ .timestamp_not_before = USEC_INFINITY,
};
*ret = d;
diff --git a/src/shared/loop-util.h b/src/shared/loop-util.h
index 9538daea31..e06dfebb7c 100644
--- a/src/shared/loop-util.h
+++ b/src/shared/loop-util.h
@@ -2,6 +2,7 @@
#pragma once
#include "macro.h"
+#include "time-util.h"
typedef struct LoopDevice LoopDevice;
@@ -10,8 +11,11 @@ typedef struct LoopDevice LoopDevice;
struct LoopDevice {
int fd;
int nr;
+ dev_t devno;
char *node;
bool relinquished;
+ uint64_t uevent_seqnum_not_before; /* uevent sequm right before we attached the loopback device, or UINT64_MAX if we don't know */
+ usec_t timestamp_not_before; /* CLOCK_MONOTONIC timestamp taken immediately before attaching the loopback device, or USEC_INFINITY if we don't know */
};
int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t loop_flags, LoopDevice **ret);
diff --git a/src/shared/module-util.c b/src/shared/module-util.c
index 587e6369fb..1526f59b0a 100644
--- a/src/shared/module-util.c
+++ b/src/shared/module-util.c
@@ -20,11 +20,10 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
"Failed to look up module alias '%s': %m", module);
- if (!modlist) {
- log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
- "Failed to find module '%s'", module);
- return -ENOENT;
- }
+ if (!modlist)
+ return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG,
+ SYNTHETIC_ERRNO(ENOENT),
+ "Failed to find module '%s'", module);
kmod_list_foreach(itr, modlist) {
_cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h
index 7183897686..6469181d1c 100644
--- a/src/shared/mount-util.h
+++ b/src/shared/mount-util.h
@@ -93,8 +93,10 @@ int mode_to_inaccessible_node(const char *runtime_dir, mode_t mode, char **dest)
/* Useful for usage with _cleanup_(), unmounts, removes a directory and frees the pointer */
static inline char* umount_and_rmdir_and_free(char *p) {
PROTECT_ERRNO;
- (void) umount_recursive(p, 0);
- (void) rmdir(p);
+ if (p) {
+ (void) umount_recursive(p, 0);
+ (void) rmdir(p);
+ }
return mfree(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_rmdir_and_free);
diff --git a/src/shared/net-condition.c b/src/shared/net-condition.c
index 174bb2a7ea..2479a5672c 100644
--- a/src/shared/net-condition.c
+++ b/src/shared/net-condition.c
@@ -147,7 +147,7 @@ bool net_match_config(
const char *ssid,
const struct ether_addr *bssid) {
- _cleanup_free_ char *iftype_str;
+ _cleanup_free_ char *iftype_str = NULL;
const char *path = NULL;
assert(match);
diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c
index 4ea72a8b2b..bb47ae5e87 100644
--- a/src/shared/openssl-util.c
+++ b/src/shared/openssl-util.c
@@ -46,7 +46,7 @@ int rsa_pkey_to_suitable_key_size(
size_t *ret_suitable_key_size) {
size_t suitable_key_size;
- RSA *rsa;
+ const RSA *rsa;
int bits;
assert_se(pkey);
diff --git a/src/shared/pkcs11-util.c b/src/shared/pkcs11-util.c
index 4fa1effb2d..aff45ed868 100644
--- a/src/shared/pkcs11-util.c
+++ b/src/shared/pkcs11-util.c
@@ -31,7 +31,7 @@ bool pkcs11_uri_valid(const char *uri) {
if (isempty(p))
return false;
- if (!in_charset(p, ALPHANUMERICAL "-_?;&%="))
+ if (!in_charset(p, ALPHANUMERICAL ".~/-_?;&%="))
return false;
return true;
diff --git a/src/shared/qrcode-util.c b/src/shared/qrcode-util.c
index 6b9ff8531b..79ac640672 100644
--- a/src/shared/qrcode-util.c
+++ b/src/shared/qrcode-util.c
@@ -110,7 +110,7 @@ int print_qrcode(FILE *out, const char *header, const char *string) {
if (r < 0)
return r;
- qr = sym_QRcode_encodeString(string, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
+ qr = sym_QRcode_encodeString(string, 0, QR_ECLEVEL_L, QR_MODE_8, 1);
if (!qr)
return -ENOMEM;
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
index 37f83306db..53280cf40a 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -34,7 +34,7 @@
#include "time-util.h"
int parse_sleep_config(SleepConfig **ret_sleep_config) {
- _cleanup_(free_sleep_configp) SleepConfig *sc;
+ _cleanup_(free_sleep_configp) SleepConfig *sc = NULL;
int allow_suspend = -1, allow_hibernate = -1,
allow_s2h = -1, allow_hybrid_sleep = -1;
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 8a0f45c2db..4d17f3c96a 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -686,6 +686,10 @@ int tpm2_unseal(
assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
+ r = dlopen_tpm2();
+ if (r < 0)
+ return log_error_errno(r, "TPM2 support is not installed.");
+
/* So here's what we do here: We connect to the TPM2 chip. As we do when sealing we generate a
* "primary" key on the TPM2 chip, with the same parameters as well as a PCR-bound policy
* session. Given we pass the same parameters, this will result in the same "primary" key, and same
diff --git a/src/shared/wifi-util.c b/src/shared/wifi-util.c
index 35940ad02e..b05e1aa0df 100644
--- a/src/shared/wifi-util.c
+++ b/src/shared/wifi-util.c
@@ -30,7 +30,7 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp
if (r < 0)
return log_debug_errno(r, "Failed to request information about wifi interface %d: %m", ifindex);
if (!reply) {
- log_debug_errno(r, "No reply received to request for information about wifi interface %d, ignoring.", ifindex);
+ log_debug("No reply received to request for information about wifi interface %d, ignoring.", ifindex);
goto nodata;
}
@@ -98,7 +98,7 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
if (r < 0)
return log_debug_errno(r, "Failed to request information about wifi station: %m");
if (!reply) {
- log_debug_errno(r, "No reply received to request for information about wifi station, ignoring.");
+ log_debug("No reply received to request for information about wifi station, ignoring.");
goto nodata;
}
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index 262d4cea66..8aeaa1a543 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -163,10 +163,10 @@ static int lock_all_homes(void) {
if (!bus_error_is_unknown_service(&error))
return log_error_errno(r, "Failed to lock home directories: %s", bus_error_message(&error, r));
- return log_debug("systemd-homed is not running, locking of home directories skipped.");
- }
-
- return log_debug("Successfully requested locking of all home directories.");
+ log_debug("systemd-homed is not running, locking of home directories skipped.");
+ } else
+ log_debug("Successfully requested locking of all home directories.");
+ return 0;
}
static int execute(char **modes, char **states, const char *action) {
diff --git a/src/sleep/sleep.conf b/src/sleep/sleep.conf
index 7c04072d42..174f5ea3e8 100644
--- a/src/sleep/sleep.conf
+++ b/src/sleep/sleep.conf
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the sleep.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# See systemd-sleep.conf(5) for details.
diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c
index 217bd97ea5..b45f7912cb 100644
--- a/src/stdio-bridge/stdio-bridge.c
+++ b/src/stdio-bridge/stdio-bridge.c
@@ -10,7 +10,6 @@
#include "sd-daemon.h"
#include "alloc-util.h"
-#include "build.h"
#include "bus-internal.h"
#include "bus-util.h"
#include "errno-util.h"
@@ -18,6 +17,7 @@
#include "log.h"
#include "main-func.h"
#include "util.h"
+#include "version.h"
#define DEFAULT_BUS_PATH "unix:path=/run/dbus/system_bus_socket"
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
index 49ec23d934..c5fdf99aa7 100644
--- a/src/sysext/sysext.c
+++ b/src/sysext/sysext.c
@@ -532,6 +532,8 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
img->path,
&verity_settings,
NULL,
+ d->uevent_seqnum_not_before,
+ d->timestamp_not_before,
flags,
&m);
if (r < 0)
diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c
index 314962ac69..6e7c67ef2f 100644
--- a/src/systemctl/systemctl-edit.c
+++ b/src/systemctl/systemctl-edit.c
@@ -576,7 +576,7 @@ end:
/* Removing empty dropin dirs */
if (!arg_full) {
- _cleanup_free_ char *dir;
+ _cleanup_free_ char *dir = NULL;
dir = dirname_malloc(*original);
if (!dir)
diff --git a/src/systemctl/systemctl-list-units.c b/src/systemctl/systemctl-list-units.c
index e02a7608fe..135d8388a3 100644
--- a/src/systemctl/systemctl-list-units.c
+++ b/src/systemctl/systemctl-list-units.c
@@ -24,7 +24,7 @@ static int get_unit_list_recursive(
char ***ret_machines) {
_cleanup_free_ UnitInfo *unit_infos = NULL;
- _cleanup_(message_set_freep) Set *replies;
+ _cleanup_(message_set_freep) Set *replies = NULL;
sd_bus_message *reply;
int c, r;
diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c
index c3c81f03fb..1a0bd35617 100644
--- a/src/systemctl/systemctl-show.c
+++ b/src/systemctl/systemctl-show.c
@@ -1282,7 +1282,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
while ((r = exec_status_info_deserialize(m, &info, is_ex_prop)) > 0) {
char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
_cleanup_strv_free_ char **optv = NULL;
- _cleanup_free_ char *tt, *o = NULL;
+ _cleanup_free_ char *tt = NULL, *o = NULL;
tt = strv_join(info.argv, " ");
@@ -1694,6 +1694,23 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
return 1;
+ } else if (streq(name, "BPFProgram")) {
+ const char *a, *p;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(ss)", &a, &p)) > 0)
+ bus_print_property_valuef(name, expected_value, value, "%s:%s", a, p);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 1;
}
break;
@@ -2112,7 +2129,7 @@ int show(int argc, char *argv[], void *userdata) {
return r;
STRV_FOREACH(name, names) {
- _cleanup_free_ char *path;
+ _cleanup_free_ char *path = NULL;
path = unit_dbus_path_from_name(*name);
if (!path)
diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h
index e760e03f35..8b7b361295 100644
--- a/src/systemd/sd-device.h
+++ b/src/systemd/sd-device.h
@@ -79,6 +79,7 @@ int sd_device_get_action(sd_device *device, sd_device_action_t *ret);
int sd_device_get_seqnum(sd_device *device, uint64_t *ret);
int sd_device_get_is_initialized(sd_device *device);
+int sd_device_get_usec_initialized(sd_device *device, uint64_t *usec);
int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec);
const char *sd_device_get_tag_first(sd_device *device);
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index c784cbcb9a..da0a5a7ac4 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -199,6 +199,7 @@ int sd_dhcp_client_set_fallback_lease_lifetime(
int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);
+int sd_dhcp_client_is_running(const sd_dhcp_client *client);
int sd_dhcp_client_stop(sd_dhcp_client *client);
int sd_dhcp_client_start(sd_dhcp_client *client);
int sd_dhcp_client_send_release(sd_dhcp_client *client);
diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h
index 5abf9a406c..9cf36d896b 100644
--- a/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/sd-dhcp-lease.h
@@ -46,36 +46,36 @@ typedef enum sd_dhcp_lease_server_type_t {
_SD_ENUM_FORCE_S64(DHCP_LEASE_SERVER_TYPE),
} sd_dhcp_lease_server_type_t;
-int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
-int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1);
-int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2);
-int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type_t what, const struct in_addr **addr);
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
-int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
-int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
-int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
-int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
-int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
+int sd_dhcp_lease_get_address(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_lifetime(const sd_dhcp_lease *lease, uint32_t *lifetime);
+int sd_dhcp_lease_get_t1(const sd_dhcp_lease *lease, uint32_t *t1);
+int sd_dhcp_lease_get_t2(const sd_dhcp_lease *lease, uint32_t *t2);
+int sd_dhcp_lease_get_broadcast(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_netmask(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_router(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_next_server(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_server_identifier(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_servers(const sd_dhcp_lease *lease, sd_dhcp_lease_server_type_t what, const struct in_addr **addr);
+int sd_dhcp_lease_get_dns(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_ntp(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_sip(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_pop3(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_smtp(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_lpr(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_mtu(const sd_dhcp_lease *lease, uint16_t *mtu);
+int sd_dhcp_lease_get_domainname(const sd_dhcp_lease *lease, const char **domainname);
+int sd_dhcp_lease_get_search_domains(const sd_dhcp_lease *lease, char ***domains);
+int sd_dhcp_lease_get_hostname(const sd_dhcp_lease *lease, const char **hostname);
+int sd_dhcp_lease_get_root_path(const sd_dhcp_lease *lease, const char **root_path);
+int sd_dhcp_lease_get_routes(const sd_dhcp_lease *lease, sd_dhcp_route ***routes);
+int sd_dhcp_lease_get_vendor_specific(const sd_dhcp_lease *lease, const void **data, size_t *data_len);
+int sd_dhcp_lease_get_client_id(const sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
+int sd_dhcp_lease_get_timezone(const sd_dhcp_lease *lease, const char **timezone);
-int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
-int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);
-int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway);
-int sd_dhcp_route_get_option(sd_dhcp_route *route);
+int sd_dhcp_route_get_destination(const sd_dhcp_route *route, struct in_addr *destination);
+int sd_dhcp_route_get_destination_prefix_length(const sd_dhcp_route *route, uint8_t *length);
+int sd_dhcp_route_get_gateway(const sd_dhcp_route *route, struct in_addr *gateway);
+int sd_dhcp_route_get_option(const sd_dhcp_route *route);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref);
diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h
index e3097ebb31..3cee2c2b06 100644
--- a/src/systemd/sd-dhcp-server.h
+++ b/src/systemd/sd-dhcp-server.h
@@ -58,6 +58,7 @@ int sd_dhcp_server_stop(sd_dhcp_server *server);
int sd_dhcp_server_configure_pool(sd_dhcp_server *server, const struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size);
+int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled);
int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone);
int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled);
@@ -82,6 +83,8 @@ int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t);
int sd_dhcp_server_forcerenew(sd_dhcp_server *server);
+int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server);
+int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr* address);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server, sd_dhcp_server_unref);
_SD_END_DECLARATIONS;
diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h
index 02aa318a06..ab209c8c7d 100644
--- a/src/systemd/sd-id128.h
+++ b/src/systemd/sd-id128.h
@@ -18,6 +18,7 @@
***/
#include <inttypes.h>
+#include <stdarg.h>
#include <string.h>
#include "_sd-common.h"
@@ -119,6 +120,32 @@ _sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
+_sd_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
+ for (;;) {
+ sd_id128_t b = va_arg(ap, sd_id128_t);
+
+ if (sd_id128_is_null(b))
+ return 0;
+
+ if (sd_id128_equal(a, b))
+ return 1;
+ }
+}
+
+_sd_pure_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) {
+ va_list ap;
+ int r;
+
+ va_start(ap, a);
+ r = sd_id128_in_setv(a, ap);
+ va_end(ap);
+
+ return r;
+}
+
+#define sd_id128_in_set(a, ...) \
+ sd_id128_in_set_sentinel(a, ##__VA_ARGS__, SD_ID128_NULL)
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h
index 6958d99a1e..d3ad6abef1 100644
--- a/src/systemd/sd-login.h
+++ b/src/systemd/sd-login.h
@@ -110,7 +110,7 @@ int sd_peer_get_machine_name(int fd, char **machine);
/* Similar to sd_pid_get_cgroup(), but retrieves data about the peer
* of a connected AF_UNIX socket. */
-int sd_peer_get_cgroup(pid_t pid, char **cgroup);
+int sd_peer_get_cgroup(int fd, char **cgroup);
/* Get state from UID. Possible states: offline, lingering, online, active, closing */
int sd_uid_get_state(uid_t uid, char **state);
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index 884dba81b9..3f4ccf1908 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -51,6 +51,8 @@ _SD_BEGIN_DECLARATIONS;
int sd_network_get_operational_state(char **state);
int sd_network_get_carrier_state(char **state);
int sd_network_get_address_state(char **state);
+int sd_network_get_ipv4_address_state(char **state);
+int sd_network_get_ipv6_address_state(char **state);
/* Get DNS entries for all links. These are string representations of
* IP addresses */
@@ -92,8 +94,11 @@ int sd_network_link_get_setup_state(int ifindex, char **state);
*/
int sd_network_link_get_operational_state(int ifindex, char **state);
int sd_network_link_get_required_operstate_for_online(int ifindex, char **state);
+int sd_network_link_get_required_family_for_online(int ifindex, char **state);
int sd_network_link_get_carrier_state(int ifindex, char **state);
int sd_network_link_get_address_state(int ifindex, char **state);
+int sd_network_link_get_ipv4_address_state(int ifindex, char **state);
+int sd_network_link_get_ipv6_address_state(int ifindex, char **state);
/* Indicates whether the network is relevant to being online.
* Possible return codes:
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 8c7aef23c3..cef141fbac 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -137,7 +137,7 @@ static int generate_unit_file(SysvStub *s) {
path_escaped);
if (s->description) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
t = specifier_escape(s->description);
if (!t)
@@ -165,7 +165,7 @@ static int generate_unit_file(SysvStub *s) {
yes_no(!s->pid_file));
if (s->pid_file) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
t = specifier_escape(s->pid_file);
if (!t)
@@ -419,7 +419,7 @@ static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text
}
static int load_sysv(SysvStub *s) {
- _cleanup_fclose_ FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
unsigned line = 0;
int r;
enum {
diff --git a/src/test/meson.build b/src/test/meson.build
index 45f1fd6e3f..e077c8e03f 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -160,7 +160,8 @@ tests += [
[['src/test/test-random-util.c'],
[],
- [libm]],
+ [libm],
+ [], '', 'timeout=120'],
[['src/test/test-format-table.c']],
@@ -323,6 +324,12 @@ tests += [
libblkid],
core_includes],
+ [['src/test/test-bpf-foreign-programs.c'],
+ [libcore,
+ libshared],
+ [],
+ core_includes],
+
[['src/test/test-watch-pid.c'],
[libcore,
libshared],
diff --git a/src/test/test-bpf-foreign-programs.c b/src/test/test-bpf-foreign-programs.c
new file mode 100644
index 0000000000..666317e520
--- /dev/null
+++ b/src/test/test-bpf-foreign-programs.c
@@ -0,0 +1,332 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <fcntl.h>
+#include <linux/bpf_insn.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "bpf-foreign.h"
+#include "load-fragment.h"
+#include "manager.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "rm-rf.h"
+#include "service.h"
+#include "tests.h"
+#include "unit.h"
+#include "virt.h"
+
+struct Test {
+ const char *option_name;
+ enum bpf_prog_type prog_type;
+ enum bpf_attach_type attach_type;
+ const char *bpffs_path;
+};
+
+typedef struct Test Test;
+
+#define BPFFS_PATH(prog_suffix) ("/sys/fs/bpf/test-bpf-foreing-" # prog_suffix)
+static const Test single_prog[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ },
+};
+static const Test path_split_test[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("path:split:test"),
+ },
+};
+
+static const Test same_prog_same_hook[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
+ .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
+ .bpffs_path = BPFFS_PATH("trivial-sock"),
+ },
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
+ .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
+ .bpffs_path = BPFFS_PATH("trivial-sock"),
+ }
+};
+
+static const Test multi_prog_same_hook[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
+ .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
+ .bpffs_path = BPFFS_PATH("trivial-sock-0"),
+ },
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
+ .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
+ .bpffs_path = BPFFS_PATH("trivial-sock-1"),
+ }
+};
+
+static const Test same_prog_multi_hook[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ },
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_EGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ }
+};
+
+static const Test same_prog_multi_option_0[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ },
+ {
+ .option_name = "IPIngressFilterPath",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ }
+};
+
+static const Test same_prog_multi_option_1[] = {
+ {
+ .option_name = "IPEgressFilterPath",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_EGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ },
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_EGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ }
+};
+#undef BPFFS_PATH
+
+static int bpf_foreign_test_to_string(enum bpf_attach_type attach_type, const char *bpffs_path, char **ret_str) {
+ const char *s = NULL;
+
+ assert_se(bpffs_path);
+ assert_se(ret_str);
+
+ assert_se(s = bpf_cgroup_attach_type_to_string(attach_type));
+ assert_se(*ret_str = strjoin(s, ":", bpffs_path));
+
+ return 0;
+}
+
+static char **unlink_paths_and_free(char **paths) {
+ char **i;
+
+ STRV_FOREACH(i, paths)
+ (void) unlink(*i);
+
+ return strv_free(paths);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(char **, unlink_paths_and_free);
+
+static int pin_programs(Unit *u, CGroupContext *cc, const Test *test_suite, size_t test_suite_size, char ***paths_ret) {
+ _cleanup_(unlink_paths_and_freep) char **bpffs_paths = NULL;
+ static const struct bpf_insn trivial[] = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN()
+ };
+ char log_buf[0xffff];
+ int r;
+
+ assert_se(paths_ret);
+
+ for (size_t i = 0; i < test_suite_size; i++) {
+ _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_free_ char *str = NULL;
+
+ r = bpf_foreign_test_to_string(test_suite[i].attach_type, test_suite[i].bpffs_path, &str);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert program to string");
+
+ r = bpf_program_new(test_suite[i].prog_type, &prog);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create program '%s'", str);
+
+ r = bpf_program_add_instructions(prog, trivial, ELEMENTSOF(trivial));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add trivial instructions for '%s'", str);
+
+ r = bpf_program_load_kernel(prog, log_buf, ELEMENTSOF(log_buf));
+ if (r < 0)
+ return log_error_errno(r, "Failed to load BPF program '%s'", str);
+
+ if (strv_contains(bpffs_paths, test_suite[i].bpffs_path))
+ continue;
+
+ r = strv_extend(&bpffs_paths, test_suite[i].bpffs_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to put path into a vector: %m");
+
+ r = bpf_program_pin(prog->kernel_fd, test_suite[i].bpffs_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pin BPF program '%s'", str);
+ }
+
+ *paths_ret = TAKE_PTR(bpffs_paths);
+ return 0;
+}
+
+static int test_bpf_cgroup_programs(Manager *m, const char *unit_name, const Test *test_suite, size_t test_suite_size) {
+ _cleanup_(unlink_paths_and_freep) char **bpffs_paths = NULL;
+ _cleanup_(unit_freep) Unit *u = NULL;
+ CGroupContext *cc = NULL;
+ int cld_code, r;
+
+ assert_se(u = unit_new(m, sizeof(Service)));
+ assert_se(unit_add_name(u, unit_name) == 0);
+ assert_se(cc = unit_get_cgroup_context(u));
+
+ r = pin_programs(u, cc, test_suite, test_suite_size, &bpffs_paths);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pin programs: %m");
+
+ for (size_t i = 0; i < test_suite_size; i++) {
+ if (streq(test_suite[i].option_name, "BPFProgram")) {
+ _cleanup_free_ char *option = NULL;
+ r = bpf_foreign_test_to_string(test_suite[i].attach_type, test_suite[i].bpffs_path, &option);
+ if (r < 0)
+ return log_error_errno(r, "Failed to compose option string: %m");
+ r = config_parse_bpf_foreign_program(
+ u->id, "filename", 1, "Service", 1, test_suite[i].option_name, 0, option, cc, u);
+
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse option string '%s': %m", option);
+ } else if (STR_IN_SET(test_suite[i].option_name, "IPIngressFilterPath", "IPEgressFilterPath")) {
+ const char *option = test_suite[i].bpffs_path;
+ void *paths = NULL;
+
+ if (streq(test_suite[i].option_name, "IPIngressFilterPath"))
+ paths = &cc->ip_filters_ingress;
+ else
+ paths = &cc->ip_filters_egress;
+
+ r = config_parse_ip_filter_bpf_progs(
+ u->id, "filename", 1, "Service", 1, test_suite[i].option_name, 0, option, paths, u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse option string '%s': %m", option);
+ }
+ }
+
+ r = config_parse_exec(
+ u->id,
+ "filename",
+ 1,
+ "Service",
+ 1,
+ "ExecStart",
+ SERVICE_EXEC_START,
+ "-/bin/ping -c 5 127.0.0.1 -W 1",
+ SERVICE(u)->exec_command,
+ u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse ExecStart");
+
+ SERVICE(u)->type = SERVICE_ONESHOT;
+ u->load_state = UNIT_LOADED;
+
+ r = unit_start(u);
+ if (r < 0)
+ return log_error_errno(r, "Unit start failed %m");
+
+ while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED)) {
+ r = sd_event_run(m->event, UINT64_MAX);
+ if (r < 0)
+ return log_error_errno(errno, "Event run failed %m");
+ }
+
+ cld_code = SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code;
+ if (cld_code != CLD_EXITED)
+ return log_error_errno(SYNTHETIC_ERRNO(EBUSY),
+ "ExecStart didn't exited, code='%s'", sigchld_code_to_string(cld_code));
+
+ if (SERVICE(u)->state != SERVICE_DEAD)
+ return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Service is not dead");
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
+ _cleanup_(manager_freep) Manager *m = NULL;
+ _cleanup_free_ char *unit_dir = NULL;
+ struct rlimit rl;
+ int r;
+
+ test_setup_logging(LOG_DEBUG);
+
+ if (detect_container() > 0)
+ return log_tests_skipped("test-bpf fails inside LXC and Docker containers: https://github.com/systemd/systemd/issues/9666");
+
+ if (getuid() != 0)
+ return log_tests_skipped("not running as root");
+
+ assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+ rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
+ (void) setrlimit_closest(RLIMIT_MEMLOCK, &rl);
+
+ if (!can_memlock())
+ return log_tests_skipped("Can't use mlock(), skipping.");
+
+ r = cg_all_unified();
+ if (r <= 0)
+ return log_tests_skipped("Unified hierarchy is required, skipping.");
+
+ r = enter_cgroup_subroot(NULL);
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
+
+ assert_se(get_testdata_dir("units", &unit_dir) >= 0);
+ assert_se(set_unit_path(unit_dir) >= 0);
+ assert_se(runtime_dir = setup_fake_runtime_dir());
+
+ assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
+ assert_se(manager_startup(m, NULL, NULL) >= 0);
+
+ assert_se(test_bpf_cgroup_programs(m,
+ "single_prog.service", single_prog, ELEMENTSOF(single_prog)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "multi_prog_same_hook.service",
+ multi_prog_same_hook, ELEMENTSOF(multi_prog_same_hook)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "same_prog_multi_hook.service",
+ same_prog_multi_hook, ELEMENTSOF(same_prog_multi_hook)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "same_prog_multi_option_0.service",
+ same_prog_multi_option_0, ELEMENTSOF(same_prog_multi_option_0)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "same_prog_multi_option_1.service",
+ same_prog_multi_option_1, ELEMENTSOF(same_prog_multi_option_1)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "same_prog_same_hook.service",
+ same_prog_same_hook,
+ ELEMENTSOF(same_prog_same_hook)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "path_split_test.service",
+ path_split_test,
+ ELEMENTSOF(path_split_test)) >= 0);
+ return 0;
+}
diff --git a/src/test/test-capability.c b/src/test/test-capability.c
index bd17dc0e79..5fa8fe242e 100644
--- a/src/test/test-capability.c
+++ b/src/test/test-capability.c
@@ -242,7 +242,7 @@ static void test_ensure_cap_64bit(void) {
}
int main(int argc, char *argv[]) {
- bool run_ambient = false; /* unnecessary initialization to silence gcc warning */
+ bool run_ambient = false; /* avoid false maybe-uninitialized warning */
test_setup_logging(LOG_INFO);
diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index b53e327c63..d721946f71 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -140,7 +140,7 @@ static void test_cg_mask_to_string_one(CGroupMask mask, const char *t) {
static void test_cg_mask_to_string(void) {
test_cg_mask_to_string_one(0, NULL);
- test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices");
+ test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign");
test_cg_mask_to_string_one(CGROUP_MASK_CPU, "cpu");
test_cg_mask_to_string_one(CGROUP_MASK_CPUACCT, "cpuacct");
test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset");
diff --git a/src/test/test-cgroup-setup.c b/src/test/test-cgroup-setup.c
index 4978a92e46..37ef66b0fd 100644
--- a/src/test/test-cgroup-setup.c
+++ b/src/test/test-cgroup-setup.c
@@ -3,13 +3,13 @@
#include <unistd.h>
#include "alloc-util.h"
-#include "build.h"
#include "cgroup-setup.h"
#include "errno-util.h"
#include "log.h"
#include "proc-cmdline.h"
#include "string-util.h"
#include "tests.h"
+#include "version.h"
static void test_is_wanted_print(bool header) {
_cleanup_free_ char *cmdline = NULL;
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index f95832acf6..c2adfa07ce 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
-#include "build.h"
#include "cgroup-util.h"
#include "dirent-util.h"
#include "errno-util.h"
@@ -17,6 +16,7 @@
#include "tests.h"
#include "user-util.h"
#include "util.h"
+#include "version.h"
static void check_p_d_u(const char *path, int code, const char *result) {
_cleanup_free_ char *unit = NULL;
diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c
index f1085266df..391968463f 100644
--- a/src/test/test-extract-word.c
+++ b/src/test/test-extract-word.c
@@ -428,6 +428,20 @@ static void test_extract_first_word(void) {
assert_se(streq(t, "c"));
free(t);
assert_se(p == NULL);
+
+ p = original = "foobar=\"waldo\"maldo, baldo";
+ assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
+ assert_se(streq(t, "foobar"));
+ free(t);
+ assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
+ assert_se(streq(t, "maldo"));
+ free(t);
+ assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
+ assert_se(streq(t, "baldo"));
+ free(t);
}
static void test_extract_first_word_and_warn(void) {
diff --git a/src/test/test-firewall-util.c b/src/test/test-firewall-util.c
index dfde01a678..d2843cfab7 100644
--- a/src/test/test-firewall-util.c
+++ b/src/test/test-firewall-util.c
@@ -61,6 +61,13 @@ static bool test_v4(FirewallContext *ctx) {
log_info("/* %s(backend=%s) */", __func__, firewall_backend_to_string(ctx->backend));
+#if HAVE_LIBIPTC
+ if (ctx->backend == FW_BACKEND_IPTABLES && fw_iptables_init_nat(NULL) < 0) {
+ log_debug("iptables backend is used, but nat table is not enabled, skipping tests");
+ return false;
+ }
+#endif
+
assert_se(fw_add_masquerade(&ctx, true, AF_INET, NULL, 0) == -EINVAL);
assert_se(fw_add_masquerade(&ctx, true, AF_INET, parse_addr("10.1.2.0", &u), 0) == -EINVAL);
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index a0649b9deb..a61b35b9a3 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -31,6 +31,13 @@ int main(int argc, char *argv[]) {
assert_se(sd_id128_from_string(t, &id2) == 0);
assert_se(sd_id128_equal(id, id2));
+ assert_se(sd_id128_in_set(id, id));
+ assert_se(sd_id128_in_set(id, id2));
+ assert_se(sd_id128_in_set(id, id2, id));
+ assert_se(sd_id128_in_set(id, ID128_WALDI, id));
+ assert_se(!sd_id128_in_set(id));
+ assert_se(!sd_id128_in_set(id, ID128_WALDI));
+ assert_se(!sd_id128_in_set(id, ID128_WALDI, ID128_WALDI));
if (sd_booted() > 0) {
assert_se(sd_id128_get_machine(&id) == 0);
diff --git a/src/test/test-loop-block.c b/src/test/test-loop-block.c
index 93f2da70e7..ba44b5f3f9 100644
--- a/src/test/test-loop-block.c
+++ b/src/test/test-loop-block.c
@@ -51,7 +51,7 @@ static void* thread_func(void *ptr) {
log_notice("Acquired loop device %s, will mount on %s", loop->node, mounted);
- r = dissect_image(loop->fd, NULL, NULL, DISSECT_IMAGE_READ_ONLY, &dissected);
+ r = dissect_image(loop->fd, NULL, NULL, loop->uevent_seqnum_not_before, loop->timestamp_not_before, DISSECT_IMAGE_READ_ONLY, &dissected);
if (r < 0)
log_error_errno(r, "Failed dissect loopback device %s: %m", loop->node);
assert_se(r >= 0);
@@ -188,7 +188,7 @@ int main(int argc, char *argv[]) {
sfdisk = NULL;
assert_se(loop_device_make(fd, O_RDWR, 0, UINT64_MAX, LO_FLAGS_PARTSCAN, &loop) >= 0);
- assert_se(dissect_image(loop->fd, NULL, NULL, 0, &dissected) >= 0);
+ assert_se(dissect_image(loop->fd, NULL, NULL, loop->uevent_seqnum_not_before, loop->timestamp_not_before, 0, &dissected) >= 0);
assert_se(dissected->partitions[PARTITION_ESP].found);
assert_se(dissected->partitions[PARTITION_ESP].node);
@@ -212,7 +212,7 @@ int main(int argc, char *argv[]) {
assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", id, true) >= 0);
dissected = dissected_image_unref(dissected);
- assert_se(dissect_image(loop->fd, NULL, NULL, 0, &dissected) >= 0);
+ assert_se(dissect_image(loop->fd, NULL, NULL, loop->uevent_seqnum_not_before, loop->timestamp_not_before, 0, &dissected) >= 0);
assert_se(mkdtemp_malloc(NULL, &mounted) >= 0);
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index b49b0ae908..1572483602 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -113,6 +113,11 @@ static void test_path(void) {
assert_se(!path_equal_ptr("/a", "/b"));
assert_se(!path_equal_ptr("/a", NULL));
assert_se(!path_equal_ptr(NULL, "/a"));
+
+ assert_se(path_equal_filename("/a/c", "/b/c"));
+ assert_se(path_equal_filename("/a", "/a"));
+ assert_se(!path_equal_filename("/a/b", "/a/c"));
+ assert_se(!path_equal_filename("/b", "/c"));
}
static void test_path_equal_root(void) {
diff --git a/src/test/test-sigbus.c b/src/test/test-sigbus.c
index d141735320..5262947c08 100644
--- a/src/test/test-sigbus.c
+++ b/src/test/test-sigbus.c
@@ -9,6 +9,7 @@
#endif
#include "fd-util.h"
+#include "fs-util.h"
#include "memory-util.h"
#include "sigbus.h"
#include "tests.h"
@@ -35,7 +36,7 @@ int main(int argc, char *argv[]) {
assert_se((fd = mkostemp(template, O_RDWR|O_CREAT|O_EXCL)) >= 0);
assert_se(unlink(template) >= 0);
- assert_se(posix_fallocate(fd, 0, page_size() * 8) >= 0);
+ assert_se(posix_fallocate_loop(fd, 0, page_size() * 8) >= 0);
p = mmap(NULL, page_size() * 16, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
assert_se(p != MAP_FAILED);
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index 488b965c82..6bb8a9e4fc 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -11,7 +11,6 @@
#include <sys/signalfd.h>
#include <unistd.h>
-#include "build.h"
#include "device-private.h"
#include "fs-util.h"
#include "log.h"
@@ -24,6 +23,7 @@
#include "string-util.h"
#include "tests.h"
#include "udev-event.h"
+#include "version.h"
static int fake_filesystems(void) {
static const struct fakefs {
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index fb08f9ad26..4cab8acbd9 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -96,21 +96,17 @@ static int print_status_info(const StatusInfo *i) {
} else
log_warning("Could not get time from timedated and not operating locally, ignoring.");
- if (have_time)
- n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm));
-
+ n = have_time ? strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) : 0;
r = table_add_many(table,
TABLE_STRING, "Local time:",
- TABLE_STRING, have_time && n > 0 ? a : "n/a");
+ TABLE_STRING, n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
- if (have_time)
- n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm));
-
+ n = have_time ? strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) : 0;
r = table_add_many(table,
TABLE_STRING, "Universal time:",
- TABLE_STRING, have_time && n > 0 ? a : "n/a");
+ TABLE_STRING, n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
@@ -119,26 +115,23 @@ static int print_status_info(const StatusInfo *i) {
rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC);
n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm));
- }
-
+ } else
+ n = 0;
r = table_add_many(table,
TABLE_STRING, "RTC time:",
- TABLE_STRING, i->rtc_time > 0 && n > 0 ? a : "n/a");
+ TABLE_STRING, n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
- if (have_time)
- n = strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm));
-
r = table_add_cell(table, NULL, TABLE_STRING, "Time zone:");
if (r < 0)
return table_log_add_error(r);
- r = table_add_cell_stringf(table, NULL, "%s (%s)", strna(i->timezone), have_time && n > 0 ? a : "n/a");
+ n = have_time ? strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm)) : 0;
+ r = table_add_cell_stringf(table, NULL, "%s (%s)", strna(i->timezone), n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
-
/* Restore the $TZ */
r = set_unset_env("TZ", old_tz, true);
if (r < 0)
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 567244dc24..6e0f91dec4 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -65,24 +65,33 @@ typedef struct Context {
LIST_HEAD(UnitStatusInfo, units);
} Context;
-#define log_unit_full(unit, level, error, ...) \
+#define log_unit_full_errno_zerook(unit, level, error, ...) \
({ \
const UnitStatusInfo *_u = (unit); \
- log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, \
- "UNIT=", _u->name, NULL, NULL, ##__VA_ARGS__); \
+ _u ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "UNIT=", _u->name, NULL, NULL, ##__VA_ARGS__) : \
+ log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
})
-#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, 0, ##__VA_ARGS__)
-#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, 0, ##__VA_ARGS__)
-#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, 0, ##__VA_ARGS__)
-#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, 0, ##__VA_ARGS__)
-#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, 0, ##__VA_ARGS__)
+#define log_unit_full_errno(unit, level, error, ...) \
+ ({ \
+ int _error = (error); \
+ ASSERT_NON_ZERO(_error); \
+ log_unit_full_errno_zerook(unit, level, _error, ##__VA_ARGS__); \
+ })
+
+#define log_unit_full(unit, level, ...) (void) log_unit_full_errno_zerook(unit, level, 0, ##__VA_ARGS__)
+
+#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, ##__VA_ARGS__)
+#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, ##__VA_ARGS__)
+#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, ##__VA_ARGS__)
+#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, ##__VA_ARGS__)
+#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, ##__VA_ARGS__)
-#define log_unit_debug_errno(unit, error, ...) log_unit_full(unit, LOG_DEBUG, error, ##__VA_ARGS__)
-#define log_unit_info_errno(unit, error, ...) log_unit_full(unit, LOG_INFO, error, ##__VA_ARGS__)
-#define log_unit_notice_errno(unit, error, ...) log_unit_full(unit, LOG_NOTICE, error, ##__VA_ARGS__)
-#define log_unit_warning_errno(unit, error, ...) log_unit_full(unit, LOG_WARNING, error, ##__VA_ARGS__)
-#define log_unit_error_errno(unit, error, ...) log_unit_full(unit, LOG_ERR, error, ##__VA_ARGS__)
+#define log_unit_debug_errno(unit, error, ...) log_unit_full_errno(unit, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_unit_info_errno(unit, error, ...) log_unit_full_errno(unit, LOG_INFO, error, ##__VA_ARGS__)
+#define log_unit_notice_errno(unit, error, ...) log_unit_full_errno(unit, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_unit_warning_errno(unit, error, ...) log_unit_full_errno(unit, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_unit_error_errno(unit, error, ...) log_unit_full_errno(unit, LOG_ERR, error, ##__VA_ARGS__)
static void unit_status_info_clear(UnitStatusInfo *p) {
assert(p);
@@ -485,8 +494,8 @@ static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *erro
"ss",
u->name,
"replace");
- log_unit_full(u, r < 0 ? LOG_WARNING : LOG_DEBUG, r,
- "%s unit: %m", start ? "Starting" : "Stopping");
+ log_unit_full_errno_zerook(u, r < 0 ? LOG_WARNING : LOG_DEBUG, r,
+ "%s unit: %m", start ? "Starting" : "Stopping");
if (r < 0)
return r;
@@ -801,6 +810,7 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
sd_bus *bus = sd_bus_message_get_bus(m);
+ char buf[FORMAT_TIMESTAMP_MAX];
int relative, interactive, r;
Context *c = userdata;
int64_t utc;
@@ -886,7 +896,7 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
"REALTIME="USEC_FMT, timespec_load(&ts),
- LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)));
+ LOG_MESSAGE("Changed local time to %s", strnull(format_timestamp(buf, sizeof(buf), timespec_load(&ts)))));
return sd_bus_reply_method_return(m, NULL);
}
diff --git a/src/timesync/timesyncd.conf.in b/src/timesync/timesyncd.conf.in
index 8a9a14cf0c..8a2c33e17c 100644
--- a/src/timesync/timesyncd.conf.in
+++ b/src/timesync/timesyncd.conf.in
@@ -7,7 +7,7 @@
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file, or by creating "drop-ins" in
-# the system.conf.d/ subdirectory. The latter is generally recommended.
+# the timesyncd.conf.d/ subdirectory. The latter is generally recommended.
# Defaults can be restored by simply deleting this file and all drop-ins.
#
# See timesyncd.conf(5) for details.
diff --git a/src/timesync/wait-sync.c b/src/timesync/wait-sync.c
index df34541bf7..2a9b113ff4 100644
--- a/src/timesync/wait-sync.c
+++ b/src/timesync/wait-sync.c
@@ -179,7 +179,7 @@ static int clock_state_update(
}
static int run(int argc, char * argv[]) {
- _cleanup_(sd_event_unrefp) sd_event *event;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(clock_state_release) ClockState state = {
.timerfd_fd = -1,
.inotify_fd = -1,
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index f11b4eed7c..b6de1e74b2 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -1656,10 +1656,9 @@ static int create_directory_or_subvolume(const char *path, mode_t mode, bool sub
return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", path);
if (k < 0)
return log_error_errno(k, "Failed to check if %s exists: %m", path);
- if (!k) {
- log_warning("\"%s\" already exists and is not a directory.", path);
- return -EEXIST;
- }
+ if (!k)
+ return log_warning_errno(SYNTHETIC_ERRNO(EEXIST),
+ "\"%s\" already exists and is not a directory.", path);
*creation = CREATION_EXISTING;
} else
@@ -1742,10 +1741,10 @@ static int empty_directory(Item *i, const char *path) {
}
if (r < 0)
return log_error_errno(r, "is_dir() failed on path %s: %m", path);
- if (r == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "'%s' already exists and is not a directory.",
- path);
+ if (r == 0) {
+ log_warning("\"%s\" already exists and is not a directory.", path);
+ return 0;
+ }
return path_set_perms(i, path);
}
@@ -1804,7 +1803,7 @@ static int create_device(Item *i, mode_t file_type) {
return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
creation = CREATION_FORCE;
} else {
- log_debug("%s is not a device node.", i->path);
+ log_warning("\"%s\" already exists is not a device node.", i->path);
return 0;
}
} else
@@ -2575,7 +2574,9 @@ static int patch_var_run(const char *fname, unsigned line, char **path) {
/* Also log about this briefly. We do so at LOG_NOTICE level, as we fixed up the situation automatically, hence
* there's no immediate need for action by the user. However, in the interest of making things less confusing
* to the user, let's still inform the user that these snippets should really be updated. */
- log_syntax(NULL, LOG_NOTICE, fname, line, 0, "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", *path, n);
+ log_syntax(NULL, LOG_NOTICE, fname, line, 0,
+ "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.",
+ *path, n);
free_and_replace(*path, n);
@@ -3186,7 +3187,7 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe
_cleanup_fclose_ FILE *_f = NULL;
unsigned v = 0;
FILE *f;
- Item *i;
+ ItemArray *ia;
int r = 0;
assert(fn);
@@ -3239,31 +3240,41 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe
}
/* we have to determine age parameter for each entry of type X */
- ORDERED_HASHMAP_FOREACH(i, globs) {
- Item *j, *candidate_item = NULL;
-
- if (i->type != IGNORE_DIRECTORY_PATH)
- continue;
+ ORDERED_HASHMAP_FOREACH(ia, globs)
+ for (size_t ni = 0; ni < ia->n_items; ni++) {
+ ItemArray *ja;
+ Item *i = ia->items + ni, *candidate_item = NULL;
- ORDERED_HASHMAP_FOREACH(j, items) {
- if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA))
+ if (i->type != IGNORE_DIRECTORY_PATH)
continue;
- if (path_equal(j->path, i->path)) {
- candidate_item = j;
- break;
- }
+ ORDERED_HASHMAP_FOREACH(ja, items)
+ for (size_t nj = 0; nj < ja->n_items; nj++) {
+ Item *j = ja->items + nj;
- if ((!candidate_item && path_startswith(i->path, j->path)) ||
- (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
- candidate_item = j;
- }
+ if (!IN_SET(j->type, CREATE_DIRECTORY,
+ TRUNCATE_DIRECTORY,
+ CREATE_SUBVOLUME,
+ CREATE_SUBVOLUME_INHERIT_QUOTA,
+ CREATE_SUBVOLUME_NEW_QUOTA))
+ continue;
+
+ if (path_equal(j->path, i->path)) {
+ candidate_item = j;
+ break;
+ }
- if (candidate_item && candidate_item->age_set) {
- i->age = candidate_item->age;
- i->age_set = true;
+ if (candidate_item
+ ? (path_startswith(j->path, candidate_item->path) && fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)
+ : path_startswith(i->path, j->path) != NULL)
+ candidate_item = j;
+ }
+
+ if (candidate_item && candidate_item->age_set) {
+ i->age = candidate_item->age;
+ i->age_set = true;
+ }
}
- }
if (ferror(f)) {
log_error_errno(errno, "Failed to read from file %s: %m", fn);
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 5ee82c708b..ceacb61bf1 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -289,7 +289,7 @@ static int wall_tty_block(void) {
}
static int process_password_files(void) {
- _cleanup_closedir_ DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c
index 60c7b4922c..f2d54b297d 100644
--- a/src/udev/cdrom_id/cdrom_id.c
+++ b/src/udev/cdrom_id/cdrom_id.c
@@ -302,10 +302,22 @@ static int cd_capability_compat(Context *c) {
}
static int cd_media_compat(Context *c) {
+ int r;
+
assert(c);
- if (ioctl(c->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK)
- return log_debug_errno(errno, "CDROM_DRIVE_STATUS != CDS_DISC_OK");
+ r = ioctl(c->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+ if (r < 0)
+ return log_debug_errno(errno, "ioctl(CDROM_DRIVE_STATUS) failed: %m");
+ if (r != CDS_DISC_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
+ "ioctl(CDROM_DRIVE_STATUS) → %d (%s), ignoring.",
+ r,
+ r == CDS_NO_INFO ? "no info" :
+ r == CDS_NO_DISC ? "no disc" :
+ r == CDS_TRAY_OPEN ? "tray open" :
+ r == CDS_DRIVE_NOT_READY ? "drive not ready" :
+ "unknown status");
c->has_media = true;
return 0;
@@ -327,7 +339,7 @@ static int cd_inquiry(Context *c) {
return r;
if ((inq[0] & 0x1F) != 5)
- return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Not an MMC unit");
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Not an MMC unit.");
log_debug("INQUIRY: [%.8s][%.16s][%.4s]", inq + 8, inq + 16, inq + 32);
return 0;
@@ -437,7 +449,7 @@ static int cd_profiles(Context *c) {
log_debug("GET CONFIGURATION: size of features buffer %zu", len);
if (len > sizeof(features)) {
- log_debug("cannot get features in a single query, truncating");
+ log_debug("Cannot get features in a single query, truncating.");
len = sizeof(features);
} else if (len <= 8)
len = sizeof(features);
@@ -457,7 +469,7 @@ static int cd_profiles(Context *c) {
log_debug("GET CONFIGURATION: size of features buffer %zu", len);
if (len > sizeof(features)) {
- log_debug("cannot get features in a single query, truncating");
+ log_debug("Cannot get features in a single query, truncating.");
len = sizeof(features);
}
@@ -516,7 +528,7 @@ static int dvd_ram_media_update_state(Context *c) {
if (dvdstruct[4] & 0x02) {
c->media_state = MEDIA_STATE_COMPLETE;
- log_debug("write-protected DVD-RAM media inserted");
+ log_debug("Write-protected DVD-RAM media inserted");
return 1;
}
@@ -532,24 +544,24 @@ static int dvd_ram_media_update_state(Context *c) {
len = format[3];
if (len & 7 || len < 16)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
- "invalid format capacities length");
+ "Invalid format capacities length.");
switch(format[8] & 3) {
case 1:
/* This means that last format was interrupted or failed, blank dvd-ram discs are
* factory formatted. Take no action here as it takes quite a while to reformat a
* dvd-ram and it's not automatically started. */
- log_debug("unformatted DVD-RAM media inserted");
+ log_debug("Unformatted DVD-RAM media inserted.");
return 1;
case 2:
- log_debug("formatted DVD-RAM media inserted");
+ log_debug("Formatted DVD-RAM media inserted.");
return 0;
case 3:
c->has_media = false;
return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
- "format capacities returned no media");
+ "Format capacities returned no media.");
}
return 0;
@@ -586,17 +598,17 @@ static int dvd_media_update_state(Context *c) {
for (size_t offset = 32768; offset < 32768 + 2048; offset++)
if (buffer[offset] != 0) {
- log_debug("data in block 16, assuming complete");
+ log_debug("Data in block 16, assuming complete.");
return 0;
}
for (size_t offset = 0; offset < 2048; offset++)
if (buffer[offset] != 0) {
- log_debug("data in block 0, assuming complete");
+ log_debug("Data in block 0, assuming complete.");
return 0;
}
- log_debug("no data in blocks 0 or 16, assuming blank");
+ log_debug("No data in blocks 0 or 16, assuming blank.");
c->media_state = MEDIA_STATE_BLANK;
return 0;
}
@@ -730,25 +742,23 @@ static int cd_media_toc(Context *c) {
}
static int open_drive(Context *c) {
- _cleanup_close_ int fd = -1;
+ int fd;
assert(c);
assert(c->fd < 0);
- for (int cnt = 0; cnt < 20; cnt++) {
- if (cnt != 0)
- (void) usleep(100 * USEC_PER_MSEC + random_u64() % (100 * USEC_PER_MSEC));
-
+ for (int cnt = 0;; cnt++) {
fd = open(arg_node, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
- if (fd >= 0 || errno != EBUSY)
+ if (fd >= 0)
break;
+ if (++cnt >= 20 || errno != EBUSY)
+ return log_debug_errno(errno, "Unable to open '%s': %m", arg_node);
+
+ (void) usleep(100 * USEC_PER_MSEC + random_u64_range(100 * USEC_PER_MSEC));
}
- if (fd < 0)
- return log_debug_errno(errno, "Unable to open '%s'", arg_node);
log_debug("probing: '%s'", arg_node);
-
- c->fd = TAKE_FD(fd);
+ c->fd = fd;
return 0;
}
@@ -820,7 +830,7 @@ static void print_feature(Feature feature, const char *prefix) {
found = typesafe_bsearch(&in, feature_to_string, ELEMENTSOF(feature_to_string), feature_to_string_compare_func);
if (!found)
- return (void) log_debug("Unknown feature 0x%02x, ignoring", (unsigned) feature);
+ return (void) log_debug("Unknown feature 0x%02x, ignoring.", (unsigned) feature);
printf("%s_%s=1\n", prefix, found->str);
}
@@ -939,7 +949,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_node = argv[optind];
if (!arg_node)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No device is specified.");
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No device specified.");
return 1;
}
diff --git a/src/udev/dmi_memory_id/dmi_memory_id.c b/src/udev/dmi_memory_id/dmi_memory_id.c
index c5bea8c9a8..64eba0d314 100644
--- a/src/udev/dmi_memory_id/dmi_memory_id.c
+++ b/src/udev/dmi_memory_id/dmi_memory_id.c
@@ -45,12 +45,12 @@
#include <getopt.h>
#include "alloc-util.h"
-#include "build.h"
#include "fileio.h"
#include "main-func.h"
#include "string-util.h"
#include "udev-util.h"
#include "unaligned.h"
+#include "version.h"
#define SUPPORTED_SMBIOS_VER 0x030300
diff --git a/src/udev/generate-keyboard-keys-gperf.sh b/src/udev/generate-keyboard-keys-gperf.sh
index d417da22fd..1db4cbe47d 100755
--- a/src/udev/generate-keyboard-keys-gperf.sh
+++ b/src/udev/generate-keyboard-keys-gperf.sh
@@ -2,6 +2,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+# shellcheck disable=SC1004
awk '
BEGIN {
print "%{\n\
@@ -16,4 +17,4 @@ _Pragma(\"GCC diagnostic ignored \\\"-Wimplicit-fallthrough\\\"\")\n\
/^KEY_/ { print tolower(substr($1 ,5)) ", " $1 }
{ print tolower($1) ", " $1 }
-' < "$1"
+' < "${1:?}"
diff --git a/src/udev/generate-keyboard-keys-list.sh b/src/udev/generate-keyboard-keys-list.sh
index b40368467b..d4f9a7d0e1 100755
--- a/src/udev/generate-keyboard-keys-list.sh
+++ b/src/udev/generate-keyboard-keys-list.sh
@@ -1,8 +1,9 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
-$1 -dM -include linux/input.h - </dev/null | awk '
+${1:?} -dM -include linux/input.h - </dev/null | awk '
/\<(KEY_(MAX|MIN_INTERESTING))|(BTN_(MISC|MOUSE|JOYSTICK|GAMEPAD|DIGI|WHEEL|TRIGGER_HAPPY))\>/ { next }
/^#define[ \t]+(KEY|BTN)_[^ ]+[ \t]+[0-9BK]/ { print $2 }
'
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 31e5d0cd67..87afe8383a 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -364,9 +364,11 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr
return r;
switch (addr_type) {
case NET_ADDR_SET:
- return log_device_debug(device, "MAC on the device already set by userspace");
+ log_device_debug(device, "MAC on the device already set by userspace");
+ return 0;
case NET_ADDR_STOLEN:
- return log_device_debug(device, "MAC on the device already set based on another device");
+ log_device_debug(device, "MAC on the device already set based on another device");
+ return 0;
case NET_ADDR_RANDOM:
case NET_ADDR_PERM:
break;
@@ -375,9 +377,11 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr
return 0;
}
- if (want_random == (addr_type == NET_ADDR_RANDOM))
- return log_device_debug(device, "MAC on the device already matches policy *%s*",
- mac_address_policy_to_string(policy));
+ if (want_random == (addr_type == NET_ADDR_RANDOM)) {
+ log_device_debug(device, "MAC on the device already matches policy *%s*",
+ mac_address_policy_to_string(policy));
+ return 0;
+ }
if (want_random) {
log_device_debug(device, "Using random bytes to generate MAC");
diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c
index 0c4a17c456..41f92b68be 100644
--- a/src/udev/scsi_id/scsi_id.c
+++ b/src/udev/scsi_id/scsi_id.c
@@ -17,13 +17,16 @@
#include <unistd.h>
#include "alloc-util.h"
-#include "build.h"
#include "device-nodes.h"
+#include "extract-word.h"
#include "fd-util.h"
+#include "fileio.h"
#include "scsi_id.h"
#include "string-util.h"
+#include "strv.h"
#include "strxcpyx.h"
#include "udev-util.h"
+#include "version.h"
static const struct option options[] = {
{ "device", required_argument, NULL, 'd' },
@@ -91,50 +94,6 @@ static void set_type(const char *from, char *to, size_t len) {
}
/*
- * get_value:
- *
- * buf points to an '=' followed by a quoted string ("foo") or a string ending
- * with a space or ','.
- *
- * Return a pointer to the NUL terminated string, returns NULL if no
- * matches.
- */
-static char *get_value(char **buffer) {
- static const char *quote_string = "\"\n";
- static const char *comma_string = ",\n";
- char *val;
- const char *end;
-
- if (**buffer == '"') {
- /*
- * skip leading quote, terminate when quote seen
- */
- (*buffer)++;
- end = quote_string;
- } else
- end = comma_string;
- val = strsep(buffer, end);
- if (val && end == quote_string)
- /*
- * skip trailing quote
- */
- (*buffer)++;
-
- while (isspace(**buffer))
- (*buffer)++;
-
- return val;
-}
-
-static int argc_count(char *opts) {
- int i = 0;
- while (*opts != '\0')
- if (*opts++ == ' ')
- i++;
- return i;
-}
-
-/*
* get_file_options:
*
* If vendor == NULL, find a line in the config file with only "OPTIONS=";
@@ -145,14 +104,10 @@ static int argc_count(char *opts) {
*/
static int get_file_options(const char *vendor, const char *model,
int *argc, char ***newargv) {
- _cleanup_free_ char *buffer = NULL;
- _cleanup_fclose_ FILE *f;
- char *buf;
- char *str1;
- char *vendor_in, *model_in, *options_in; /* read in from file */
- int lineno;
- int c;
- int retval = 0;
+ _cleanup_free_ char *vendor_in = NULL, *model_in = NULL, *options_in = NULL; /* read in from file */
+ _cleanup_strv_free_ char **options_argv = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int lineno, r;
f = fopen(config_file, "re");
if (!f) {
@@ -164,28 +119,21 @@ static int get_file_options(const char *vendor, const char *model,
}
}
- /*
- * Allocate a buffer rather than put it on the stack so we can
- * keep it around to parse any options (any allocated newargv
- * points into this buffer for its strings).
- */
- buffer = malloc(MAX_BUFFER_LEN);
- if (!buffer)
- return log_oom();
-
*newargv = NULL;
lineno = 0;
for (;;) {
+ _cleanup_free_ char *buffer = NULL, *key = NULL, *value = NULL;
+ const char *buf;
+
vendor_in = model_in = options_in = NULL;
- buf = fgets(buffer, MAX_BUFFER_LEN, f);
- if (!buf)
+ r = read_line(f, MAX_BUFFER_LEN, &buffer);
+ if (r < 0)
+ return log_error_errno(r, "read_line() on line %d of %s failed: %m", lineno, config_file);
+ if (r == 0)
break;
+ buf = buffer;
lineno++;
- if (buf[strlen(buffer) - 1] != '\n') {
- log_error("Config file line %d too long", lineno);
- break;
- }
while (isspace(*buf))
buf++;
@@ -198,44 +146,36 @@ static int get_file_options(const char *vendor, const char *model,
if (*buf == '#')
continue;
- str1 = strsep(&buf, "=");
- if (str1 && strcaseeq(str1, "VENDOR")) {
- str1 = get_value(&buf);
- if (!str1) {
- retval = log_oom();
- break;
- }
- vendor_in = str1;
-
- str1 = strsep(&buf, "=");
- if (str1 && strcaseeq(str1, "MODEL")) {
- str1 = get_value(&buf);
- if (!str1) {
- retval = log_oom();
- break;
- }
- model_in = str1;
- str1 = strsep(&buf, "=");
- }
- }
+ r = extract_many_words(&buf, "=\",\n", 0, &key, &value, NULL);
+ if (r < 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error parsing config file line %d '%s'", lineno, buffer);
- if (str1 && strcaseeq(str1, "OPTIONS")) {
- str1 = get_value(&buf);
- if (!str1) {
- retval = log_oom();
- break;
+ if (strcaseeq(key, "VENDOR")) {
+ vendor_in = TAKE_PTR(value);
+
+ key = mfree(key);
+ r = extract_many_words(&buf, "=\",\n", 0, &key, &value, NULL);
+ if (r < 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error parsing config file line %d '%s'", lineno, buffer);
+
+ if (strcaseeq(key, "MODEL")) {
+ model_in = TAKE_PTR(value);
+
+ key = mfree(key);
+ r = extract_many_words(&buf, "=\",\n", 0, &key, &value, NULL);
+ if (r < 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error parsing config file line %d '%s'", lineno, buffer);
}
- options_in = str1;
}
+ if (strcaseeq(key, "OPTIONS"))
+ options_in = TAKE_PTR(value);
+
/*
* Only allow: [vendor=foo[,model=bar]]options=stuff
*/
- if (!options_in || (!vendor_in && model_in)) {
- log_error("Error parsing config file line %d '%s'", lineno, buffer);
- retval = -1;
- break;
- }
+ if (!options_in || (!vendor_in && model_in))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error parsing config file line %d '%s'", lineno, buffer);
if (!vendor) {
if (!vendor_in)
break;
@@ -251,39 +191,30 @@ static int get_file_options(const char *vendor, const char *model,
*/
break;
}
- }
- if (retval == 0) {
- if (vendor_in != NULL || model_in != NULL ||
- options_in != NULL) {
- /*
- * Something matched. Allocate newargv, and store
- * values found in options_in.
- */
- strcpy(buffer, options_in);
- c = argc_count(buffer) + 2;
- *newargv = calloc(c, sizeof(**newargv));
- if (!*newargv)
- retval = log_oom();
- else {
- *argc = c;
- c = 0;
- /*
- * argv[0] at 0 is skipped by getopt, but
- * store the buffer address there for
- * later freeing
- */
- (*newargv)[c] = buffer;
- for (c = 1; c < *argc; c++)
- (*newargv)[c] = strsep(&buffer, " \t");
- buffer = NULL;
- }
- } else {
- /* No matches */
- retval = 1;
- }
+ vendor_in = mfree(vendor_in);
+ model_in = mfree(model_in);
+ options_in = mfree(options_in);
+
}
- return retval;
+
+ if (vendor_in == NULL && model_in == NULL && options_in == NULL)
+ return 1; /* No matches */
+
+ /*
+ * Something matched. Allocate newargv, and store
+ * values found in options_in.
+ */
+ options_argv = strv_split(options_in, " \t");
+ if (!options_argv)
+ return log_oom();
+ r = strv_prepend(&options_argv, ""); /* getopt skips over argv[0] */
+ if (r < 0)
+ return r;
+ *newargv = TAKE_PTR(options_argv);
+ *argc = strv_length(*newargv);
+
+ return 0;
}
static void help(void) {
@@ -391,9 +322,9 @@ static int set_options(int argc, char **argv,
}
static int per_dev_options(struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) {
+ _cleanup_strv_free_ char **newargv = NULL;
int retval;
int newargc;
- char **newargv = NULL;
int option;
*good_bad = all_good;
@@ -436,10 +367,6 @@ static int per_dev_options(struct scsi_id_device *dev_scsi, int *good_bad, int *
}
}
- if (newargv) {
- free(newargv[0]);
- free(newargv);
- }
return retval;
}
@@ -543,10 +470,10 @@ out:
}
int main(int argc, char **argv) {
+ _cleanup_strv_free_ char **newargv = NULL;
int retval = 0;
char maj_min_dev[MAX_PATH_LEN];
int newargc;
- char **newargv = NULL;
log_set_target(LOG_TARGET_AUTO);
udev_parse_config();
@@ -585,10 +512,6 @@ int main(int argc, char **argv) {
retval = scsi_id(maj_min_dev);
exit:
- if (newargv) {
- free(newargv[0]);
- free(newargv);
- }
log_close();
return retval;
}
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 66c52e624c..088bfe38d9 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -23,6 +23,7 @@
#include <linux/pci_regs.h>
#include "alloc-util.h"
+#include "device-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -262,18 +263,60 @@ static bool is_pci_bridge(sd_device *dev) {
return strneq(p + 2, "04", 2);
}
+static int parse_hotplug_slot_from_function_id(sd_device *dev, const char *slots, uint32_t *ret) {
+ uint64_t function_id;
+ char path[PATH_MAX];
+ const char *attr;
+ int r;
+
+ /* The <sysname>/function_id attribute is unique to the s390 PCI driver. If present, we know
+ * that the slot's directory name for this device is /sys/bus/pci/XXXXXXXX/ where XXXXXXXX is
+ * the fixed length 8 hexadecimal character string representation of function_id. Therefore we
+ * can short cut here and just check for the existence of the slot directory. As this directory
+ * has to exist, we're emitting a debug message for the unlikely case it's not found. Note that
+ * the domain part doesn't belong to the slot name here because there's a 1-to-1 relationship
+ * between PCI function and its hotplug slot. */
+
+ assert(dev);
+ assert(slots);
+ assert(ret);
+
+ if (!naming_scheme_has(NAMING_SLOT_FUNCTION_ID))
+ return 0;
+
+ if (sd_device_get_sysattr_value(dev, "function_id", &attr) < 0)
+ return 0;
+
+ r = safe_atou64(attr, &function_id);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to parse function_id, ignoring: %s", attr);
+
+ if (function_id <= 0 || function_id > UINT32_MAX)
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL),
+ "Invalid function id (0x%"PRIx64"), ignoring.",
+ function_id);
+
+ if (!snprintf_ok(path, sizeof path, "%s/%08"PRIx64, slots, function_id))
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENAMETOOLONG),
+ "PCI slot path is too long, ignoring.");
+
+ if (access(path, F_OK) < 0)
+ return log_device_debug_errno(dev, errno, "Cannot access %s, ignoring: %m", path);
+
+ *ret = (uint32_t) function_id;
+ return 1;
+}
+
static int dev_pci_slot(sd_device *dev, struct netnames *names) {
- unsigned long dev_port = 0;
- unsigned domain, bus, slot, func;
- int hotplug_slot = -1;
- size_t l;
- char *s;
const char *sysname, *attr, *port_name = NULL, *syspath;
_cleanup_(sd_device_unrefp) sd_device *pci = NULL;
- sd_device *hotplug_slot_dev;
- char slots[PATH_MAX];
_cleanup_closedir_ DIR *dir = NULL;
- struct dirent *dent;
+ unsigned domain, bus, slot, func;
+ sd_device *hotplug_slot_dev;
+ unsigned long dev_port = 0;
+ uint32_t hotplug_slot = 0;
+ char slots[PATH_MAX], *s;
+ size_t l;
int r;
r = sd_device_get_sysname(names->pcidev, &sysname);
@@ -342,44 +385,29 @@ static int dev_pci_slot(sd_device *dev, struct netnames *names) {
hotplug_slot_dev = names->pcidev;
while (hotplug_slot_dev) {
- if (sd_device_get_sysname(hotplug_slot_dev, &sysname) < 0)
- continue;
+ struct dirent *dent;
- /* The <sysname>/function_id attribute is unique to the s390 PCI driver.
- If present, we know that the slot's directory name for this device is
- /sys/bus/pci/XXXXXXXX/ where XXXXXXXX is the fixed length 8 hexadecimal
- character string representation of function_id.
- Therefore we can short cut here and just check for the existence of
- the slot directory. As this directory has to exist, we're emitting a
- debug message for the unlikely case it's not found.
- Note that the domain part of doesn't belong to the slot name here
- because there's a 1-to-1 relationship between PCI function and its hotplug
- slot.
- */
- if (naming_scheme_has(NAMING_SLOT_FUNCTION_ID) &&
- sd_device_get_sysattr_value(hotplug_slot_dev, "function_id", &attr) >= 0) {
- int function_id;
- _cleanup_free_ char *str;
-
- if (safe_atoi(attr, &function_id) >= 0 &&
- asprintf(&str, "%s/%08x/", slots, function_id) >= 0 &&
- access(str, R_OK) == 0) {
- hotplug_slot = function_id;
- domain = 0;
- } else
- log_debug("No matching slot for function_id (%s).", attr);
+ r = parse_hotplug_slot_from_function_id(hotplug_slot_dev, slots, &hotplug_slot);
+ if (r < 0)
+ return 0;
+ if (r > 0) {
+ domain = 0; /* See comments in parse_hotplug_slot_from_function_id(). */
break;
}
+ r = sd_device_get_sysname(hotplug_slot_dev, &sysname);
+ if (r < 0)
+ return log_device_debug_errno(hotplug_slot_dev, r, "Failed to get sysname: %m");
+
FOREACH_DIRENT_ALL(dent, dir, break) {
- int i;
- char str[PATH_MAX];
_cleanup_free_ char *address = NULL;
+ char str[PATH_MAX];
+ uint32_t i;
if (dot_or_dot_dot(dent->d_name))
continue;
- r = safe_atoi(dent->d_name, &i);
+ r = safe_atou32(dent->d_name, &i);
if (r < 0 || i <= 0)
continue;
@@ -394,12 +422,12 @@ static int dev_pci_slot(sd_device *dev, struct netnames *names) {
* devices that will try to claim the same index and that would create name
* collision. */
if (naming_scheme_has(NAMING_BRIDGE_NO_SLOT) && is_pci_bridge(hotplug_slot_dev))
- hotplug_slot = 0;
+ return 0;
break;
}
}
- if (hotplug_slot >= 0)
+ if (hotplug_slot > 0)
break;
if (sd_device_get_parent_with_subsystem_devtype(hotplug_slot_dev, "pci", NULL, &hotplug_slot_dev) < 0)
break;
@@ -411,7 +439,7 @@ static int dev_pci_slot(sd_device *dev, struct netnames *names) {
l = sizeof(names->pci_slot);
if (domain > 0)
l = strpcpyf(&s, l, "P%d", domain);
- l = strpcpyf(&s, l, "s%d", hotplug_slot);
+ l = strpcpyf(&s, l, "s%"PRIu32, hotplug_slot);
if (func > 0 || is_pci_multifunction(names->pcidev))
l = strpcpyf(&s, l, "f%d", func);
if (port_name)
@@ -971,7 +999,7 @@ static int builtin_net_id(sd_device *dev, int argc, char *argv[], bool test) {
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
if (names.pci_slot[0] &&
- snprintf(str, sizeof str, "%s%s%s", prefix, names.pci_slot, names.bcma_core))
+ snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_slot, names.bcma_core))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
return 0;
}
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index 57ede6a197..edf2c385bd 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -184,21 +184,30 @@ struct UdevRules {
/*** Logging helpers ***/
-#define log_rule_full_errno(device, rules, level, error, fmt, ...) \
+#define log_rule_full_errno_zerook(device, rules, level, error, fmt, ...) \
({ \
UdevRules *_r = (rules); \
UdevRuleFile *_f = _r ? _r->current_file : NULL; \
UdevRuleLine *_l = _f ? _f->current_line : NULL; \
const char *_n = _f ? _f->filename : NULL; \
\
- log_device_full_errno(device, level, error, "%s:%u " fmt, \
- strna(_n), _l ? _l->line_number : 0, \
- ##__VA_ARGS__); \
+ log_device_full_errno_zerook( \
+ device, level, error, "%s:%u " fmt, \
+ strna(_n), _l ? _l->line_number : 0, \
+ ##__VA_ARGS__); \
+ })
+
+#define log_rule_full_errno(device, rules, level, error, fmt, ...) \
+ ({ \
+ int _error = (error); \
+ ASSERT_NON_ZERO(_error); \
+ log_rule_full_errno_zerook( \
+ device, rules, level, _error, fmt, ##__VA_ARGS__); \
})
-#define log_rule_full(device, rules, level, ...) (void) log_rule_full_errno(device, rules, level, 0, __VA_ARGS__)
+#define log_rule_full(device, rules, level, ...) (void) log_rule_full_errno_zerook(device, rules, level, 0, __VA_ARGS__)
-#define log_rule_debug(device, rules, ...) log_rule_full_errno(device, rules, LOG_DEBUG, 0, __VA_ARGS__)
+#define log_rule_debug(device, rules, ...) log_rule_full(device, rules, LOG_DEBUG, __VA_ARGS__)
#define log_rule_info(device, rules, ...) log_rule_full(device, rules, LOG_INFO, __VA_ARGS__)
#define log_rule_notice(device, rules, ...) log_rule_full(device, rules, LOG_NOTICE, __VA_ARGS__)
#define log_rule_warning(device, rules, ...) log_rule_full(device, rules, LOG_WARNING, __VA_ARGS__)
@@ -210,10 +219,11 @@ struct UdevRules {
#define log_rule_warning_errno(device, rules, error, ...) log_rule_full_errno(device, rules, LOG_WARNING, error, __VA_ARGS__)
#define log_rule_error_errno(device, rules, error, ...) log_rule_full_errno(device, rules, LOG_ERR, error, __VA_ARGS__)
+#define log_token_full_errno_zerook(rules, level, error, ...) log_rule_full_errno_zerook(NULL, rules, level, error, __VA_ARGS__)
#define log_token_full_errno(rules, level, error, ...) log_rule_full_errno(NULL, rules, level, error, __VA_ARGS__)
-#define log_token_full(rules, level, ...) (void) log_token_full_errno(rules, level, 0, __VA_ARGS__)
+#define log_token_full(rules, level, ...) (void) log_token_full_errno_zerook(rules, level, 0, __VA_ARGS__)
-#define log_token_debug(rules, ...) log_token_full_errno(rules, LOG_DEBUG, 0, __VA_ARGS__)
+#define log_token_debug(rules, ...) log_token_full(rules, LOG_DEBUG, __VA_ARGS__)
#define log_token_info(rules, ...) log_token_full(rules, LOG_INFO, __VA_ARGS__)
#define log_token_notice(rules, ...) log_token_full(rules, LOG_NOTICE, __VA_ARGS__)
#define log_token_warning(rules, ...) log_token_full(rules, LOG_WARNING, __VA_ARGS__)
@@ -2123,7 +2133,11 @@ static int udev_rule_apply_token_to_event(
(void) udev_event_apply_format(event, token->value, value, sizeof(value), false);
log_rule_debug(dev, rules, "ATTR '%s' writing '%s'", buf, value);
- r = write_string_file(buf, value, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_AVOID_NEWLINE);
+ r = write_string_file(buf, value,
+ WRITE_STRING_FILE_VERIFY_ON_FAILURE |
+ WRITE_STRING_FILE_DISABLE_BUFFER |
+ WRITE_STRING_FILE_AVOID_NEWLINE |
+ WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
if (r < 0)
log_rule_error_errno(dev, rules, r, "Failed to write ATTR{%s}, ignoring: %m", buf);
break;
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 71cc0d2d9b..33fdecca50 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -249,8 +249,6 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
static void cleanup_db(void) {
_cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
- (void) unlink("/run/udev/queue.bin");
-
dir1 = opendir("/run/udev/data");
if (dir1)
cleanup_dir(dir1, S_ISVTX, 1);
diff --git a/src/udev/udevadm.h b/src/udev/udevadm.h
index 162bbb9a43..75ce633632 100644
--- a/src/udev/udevadm.h
+++ b/src/udev/udevadm.h
@@ -3,7 +3,6 @@
#include <stdio.h>
-#include "build.h"
#include "macro.h"
int info_main(int argc, char *argv[], void *userdata);
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 0a44b40a32..2c702d0388 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -28,7 +28,6 @@
#include "sd-event.h"
#include "alloc-util.h"
-#include "build.h"
#include "cgroup-util.h"
#include "cpu-set-util.h"
#include "dev-setup.h"
@@ -65,6 +64,7 @@
#include "udev-util.h"
#include "udev-watch.h"
#include "user-util.h"
+#include "version.h"
#define WORKER_NUM_MAX 2048U
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index 45915ef853..d1c3febdd5 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -146,7 +146,7 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m
args[i++] = NULL;
if (DEBUG_LOGGING) {
- _cleanup_free_ char *cmd;
+ _cleanup_free_ char *cmd = NULL;
cmd = strv_join((char**) args, " ");
log_debug("Executing \"%s\"...", strnull(cmd));
@@ -189,7 +189,7 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map,
args[i++] = NULL;
if (DEBUG_LOGGING) {
- _cleanup_free_ char *cmd;
+ _cleanup_free_ char *cmd = NULL;
cmd = strv_join((char**) args, " ");
log_debug("Executing \"%s\"...", strnull(cmd));
diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh
index 66c35fe2b7..3de0e51d5a 100755
--- a/test/TEST-01-BASIC/test.sh
+++ b/test/TEST-01-BASIC/test.sh
@@ -1,17 +1,19 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Basic systemd setup"
IMAGE_NAME="basic"
RUN_IN_UNPRIVILEGED_CONTAINER=${RUN_IN_UNPRIVILEGED_CONTAINER:-yes}
TEST_REQUIRE_INSTALL_TESTS=0
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
test_append_files() {
# install tests manually so the test is functional even when -Dinstall-tests=false
- local dst="$1/usr/lib/systemd/tests/testdata/units/"
+ local dst="${1:?}/usr/lib/systemd/tests/testdata/units/"
mkdir -p "$dst"
- cp -v $TEST_UNITS_DIR/{testsuite-01,end}.service $TEST_UNITS_DIR/testsuite.target "$dst"
+ cp -v "$TEST_UNITS_DIR"/{testsuite-01,end}.service "$TEST_UNITS_DIR/testsuite.target" "$dst"
}
do_test "$@" 01
diff --git a/test/TEST-02-UNITTESTS/test.sh b/test/TEST-02-UNITTESTS/test.sh
index 6143d3352b..2bfe41a42b 100755
--- a/test/TEST-02-UNITTESTS/test.sh
+++ b/test/TEST-02-UNITTESTS/test.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Run unit tests under containers"
RUN_IN_UNPRIVILEGED_CONTAINER=yes
@@ -11,53 +12,63 @@ frobnicate!
$KERNEL_APPEND
"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
check_result_nspawn() {
- local _ret=1
- [[ -e $1/testok ]] && _ret=0
- if [[ -s $1/failed ]]; then
- _ret=$(($_ret+1))
+ local workspace="${1:?}"
+ local ret=1
+
+ [[ -e "$workspace/testok" ]] && ret=0
+
+ if [[ -s "$workspace/failed" ]]; then
+ ret=$((ret + 1))
echo "=== Failed test log ==="
- cat $1/failed
+ cat "$workspace/failed"
else
- if [[ -s $1/skipped ]]; then
+ if [[ -s "$workspace/skipped" ]]; then
echo "=== Skipped test log =="
- cat $1/skipped
+ cat "$workspace/skipped"
fi
- if [[ -s $1/testok ]]; then
+ if [[ -s "$workspace/testok" ]]; then
echo "=== Passed tests ==="
- cat $1/testok
+ cat "$workspace/testok"
fi
fi
- save_journal $1/var/log/journal
- _umount_dir $initdir
- [[ -n "$TIMED_OUT" ]] && _ret=$(($_ret+1))
- return $_ret
+
+ save_journal "$workspace/var/log/journal"
+ _umount_dir "${initdir:?}"
+
+ [[ -n "${TIMED_OUT:=}" ]] && ret=$((ret + 1))
+ return $ret
}
check_result_qemu() {
- local _ret=1
+ local ret=1
+
mount_initdir
- [[ -e $initdir/testok ]] && _ret=0
- if [[ -s $initdir/failed ]]; then
- _ret=$(($_ret+1))
+ [[ -e "${initdir:?}/testok" ]] && ret=0
+
+ if [[ -s "$initdir/failed" ]]; then
+ ret=$((ret + 1))
echo "=== Failed test log ==="
- cat $initdir/failed
+ cat "$initdir/failed"
else
- if [[ -s $initdir/skipped ]]; then
+ if [[ -s "$initdir/skipped" ]]; then
echo "=== Skipped test log =="
- cat $initdir/skipped
+ cat "$initdir/skipped"
fi
- if [[ -s $initdir/testok ]]; then
+ if [[ -s "$initdir/testok" ]]; then
echo "=== Passed tests ==="
- cat $initdir/testok
+ cat "$initdir/testok"
fi
fi
- save_journal $initdir/var/log/journal
- _umount_dir $initdir
- [[ -n "$TIMED_OUT" ]] && _ret=$(($_ret+1))
- return $_ret
+
+ save_journal "$initdir/var/log/journal"
+ _umount_dir "$initdir"
+
+ [[ -n "${TIMED_OUT:=}" ]] && ret=$((ret + 1))
+ return $ret
}
do_test "$@" 02
diff --git a/test/TEST-03-JOBS/test.sh b/test/TEST-03-JOBS/test.sh
index 221a18682a..d719238d1a 100755
--- a/test/TEST-03-JOBS/test.sh
+++ b/test/TEST-03-JOBS/test.sh
@@ -1,9 +1,11 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Job-related tests"
TEST_NO_QEMU=1
IMAGE_NAME="default"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 03
diff --git a/test/TEST-04-JOURNAL/test.sh b/test/TEST-04-JOURNAL/test.sh
index f16543c2b4..66182435da 100755
--- a/test/TEST-04-JOURNAL/test.sh
+++ b/test/TEST-04-JOURNAL/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Journal-related tests"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 04
diff --git a/test/TEST-05-RLIMITS/test.sh b/test/TEST-05-RLIMITS/test.sh
index 463fe42a7c..f95c198f1a 100755
--- a/test/TEST-05-RLIMITS/test.sh
+++ b/test/TEST-05-RLIMITS/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Resource limits-related tests"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 05
diff --git a/test/TEST-06-SELINUX/test.sh b/test/TEST-06-SELINUX/test.sh
index f05cd35593..984caf4b4f 100755
--- a/test/TEST-06-SELINUX/test.sh
+++ b/test/TEST-06-SELINUX/test.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="SELinux tests"
IMAGE_NAME="selinux"
TEST_NO_NSPAWN=1
@@ -12,32 +13,39 @@ TEST_NO_NSPAWN=1
# Check if selinux-policy-devel is installed, and if it isn't bail out early instead of failing
test -f /usr/share/selinux/devel/include/system/systemd.if || exit 0
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
SETUP_SELINUX=yes
-KERNEL_APPEND="$KERNEL_APPEND selinux=1 security=selinux"
+KERNEL_APPEND="${KERNEL_APPEND:=} selinux=1 security=selinux"
test_append_files() {
(
+ local workspace="${1:?}"
+ local policy_headers_dir=/usr/share/selinux/devel
+ local modules_dir=/var/lib/selinux
+
setup_selinux
- local _modules_dir=/var/lib/selinux
- rm -rf $1/$_modules_dir
- if ! cp -ar $_modules_dir $1/$_modules_dir; then
- dfatal "Failed to copy $_modules_dir"
+ # Make sure we never expand this to "/..."
+ rm -rf "${workspace:?}/$modules_dir"
+
+ if ! cp -ar "$modules_dir" "$workspace/$modules_dir"; then
+ dfatal "Failed to copy $modules_dir"
exit 1
fi
- local _policy_headers_dir=/usr/share/selinux/devel
- rm -rf $1/$_policy_headers_dir
+ rm -rf "${workspace:?}/$policy_headers_dir"
inst_dir /usr/share/selinux
- if ! cp -ar $_policy_headers_dir $1/$_policy_headers_dir; then
- dfatal "Failed to copy $_policy_headers_dir"
+
+ if ! cp -ar "$policy_headers_dir" "$workspace/$policy_headers_dir"; then
+ dfatal "Failed to copy $policy_headers_dir"
exit 1
fi
- mkdir $1/systemd-test-module
- cp systemd_test.te $1/systemd-test-module
- cp systemd_test.if $1/systemd-test-module
- cp systemd_test.fc $1/systemd-test-module
+ mkdir "$workspace/systemd-test-module"
+ cp systemd_test.te "$workspace/systemd-test-module"
+ cp systemd_test.if "$workspace/systemd-test-module"
+ cp systemd_test.fc "$workspace/systemd-test-module"
dracut_install -o sesearch
dracut_install runcon
dracut_install checkmodule semodule semodule_package m4 make load_policy sefcontext_compile
diff --git a/test/TEST-07-ISSUE-1981/test.sh b/test/TEST-07-ISSUE-1981/test.sh
index 5da24a987c..bcb7584cb1 100755
--- a/test/TEST-07-ISSUE-1981/test.sh
+++ b/test/TEST-07-ISSUE-1981/test.sh
@@ -1,9 +1,11 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/1981"
TEST_NO_QEMU=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
NSPAWN_TIMEOUT=30
diff --git a/test/TEST-08-ISSUE-2730/test.sh b/test/TEST-08-ISSUE-2730/test.sh
index 34d68835d8..0e66aa88de 100755
--- a/test/TEST-08-ISSUE-2730/test.sh
+++ b/test/TEST-08-ISSUE-2730/test.sh
@@ -1,10 +1,13 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2730"
IMAGE_NAME="test08"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
QEMU_TIMEOUT=300
FSTYPE=ext4
TEST_FORCE_NEWIMAGE=1
diff --git a/test/TEST-09-ISSUE-2691/test.sh b/test/TEST-09-ISSUE-2691/test.sh
index a4d155be1d..19eb3ac6b3 100755
--- a/test/TEST-09-ISSUE-2691/test.sh
+++ b/test/TEST-09-ISSUE-2691/test.sh
@@ -1,9 +1,12 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2691"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
QEMU_TIMEOUT=300
do_test "$@" 09
diff --git a/test/TEST-10-ISSUE-2467/test.sh b/test/TEST-10-ISSUE-2467/test.sh
index 14ded56ba1..c0b8e27164 100755
--- a/test/TEST-10-ISSUE-2467/test.sh
+++ b/test/TEST-10-ISSUE-2467/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 10
diff --git a/test/TEST-11-ISSUE-3166/test.sh b/test/TEST-11-ISSUE-3166/test.sh
index da003c90d5..e8189f5276 100755
--- a/test/TEST-11-ISSUE-3166/test.sh
+++ b/test/TEST-11-ISSUE-3166/test.sh
@@ -1,8 +1,10 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3166"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 11
diff --git a/test/TEST-12-ISSUE-3171/test.sh b/test/TEST-12-ISSUE-3171/test.sh
index c8abefbd86..52e33cdfdb 100755
--- a/test/TEST-12-ISSUE-3171/test.sh
+++ b/test/TEST-12-ISSUE-3171/test.sh
@@ -1,8 +1,10 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3171"
TEST_NO_QEMU=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 12
diff --git a/test/TEST-13-NSPAWN-SMOKE/test.sh b/test/TEST-13-NSPAWN-SMOKE/test.sh
index 0f6bf587a6..05a33e0716 100755
--- a/test/TEST-13-NSPAWN-SMOKE/test.sh
+++ b/test/TEST-13-NSPAWN-SMOKE/test.sh
@@ -1,15 +1,19 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="systemd-nspawn smoke test"
IMAGE_NAME="nspawn"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
test_append_files() {
(
- ../create-busybox-container $1/testsuite-13.nc-container
- initdir="$1/testsuite-13.nc-container" dracut_install nc ip md5sum
+ local workspace="${1:?}"
+
+ "$TEST_BASE_DIR/create-busybox-container" "$workspace/testsuite-13.nc-container"
+ initdir="$workspace/testsuite-13.nc-container" dracut_install nc ip md5sum
)
}
diff --git a/test/TEST-14-MACHINE-ID/test.sh b/test/TEST-14-MACHINE-ID/test.sh
index 87abe2e439..3507915bb9 100755
--- a/test/TEST-14-MACHINE-ID/test.sh
+++ b/test/TEST-14-MACHINE-ID/test.sh
@@ -1,13 +1,15 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="/etc/machine-id testing"
IMAGE_NAME="badid"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
test_append_files() {
- printf "556f48e837bc4424a710fa2e2c9d3e3c\ne3d\n" >$1/etc/machine-id
+ printf "556f48e837bc4424a710fa2e2c9d3e3c\ne3d\n" >"${1:?}/etc/machine-id"
}
do_test "$@" 14
diff --git a/test/TEST-15-DROPIN/test.sh b/test/TEST-15-DROPIN/test.sh
index 1540e2e1f1..134ed7bba3 100755
--- a/test/TEST-15-DROPIN/test.sh
+++ b/test/TEST-15-DROPIN/test.sh
@@ -1,8 +1,10 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Dropin tests"
TEST_NO_QEMU=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 15
diff --git a/test/TEST-16-EXTEND-TIMEOUT/test.sh b/test/TEST-16-EXTEND-TIMEOUT/test.sh
index e1e2a68fa9..60c0dd1ee2 100755
--- a/test/TEST-16-EXTEND-TIMEOUT/test.sh
+++ b/test/TEST-16-EXTEND-TIMEOUT/test.sh
@@ -1,9 +1,11 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="EXTEND_TIMEOUT_USEC=usec start/runtime/stop tests"
SKIP_INITRD=yes
TEST_NO_QEMU=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 16
diff --git a/test/TEST-17-UDEV/test.sh b/test/TEST-17-UDEV/test.sh
index b13ae0aa78..2a4cef2fa5 100755
--- a/test/TEST-17-UDEV/test.sh
+++ b/test/TEST-17-UDEV/test.sh
@@ -1,10 +1,13 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="UDEV"
IMAGE_NAME="udev"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
QEMU_TIMEOUT=500
test_append_files() {
diff --git a/test/TEST-18-FAILUREACTION/test.sh b/test/TEST-18-FAILUREACTION/test.sh
index 5c386b8ea2..913c254f5d 100755
--- a/test/TEST-18-FAILUREACTION/test.sh
+++ b/test/TEST-18-FAILUREACTION/test.sh
@@ -1,8 +1,11 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="FailureAction= operation"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
QEMU_TIMEOUT=600
do_test "$@" 18
diff --git a/test/TEST-19-DELEGATE/test.sh b/test/TEST-19-DELEGATE/test.sh
index 03c7760bfd..a25c28ef4e 100755
--- a/test/TEST-19-DELEGATE/test.sh
+++ b/test/TEST-19-DELEGATE/test.sh
@@ -1,9 +1,12 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test cgroup delegation in the unified hierarchy"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
QEMU_TIMEOUT=600
UNIFIED_CGROUP_HIERARCHY=yes
diff --git a/test/TEST-20-MAINPIDGAMES/test.sh b/test/TEST-20-MAINPIDGAMES/test.sh
index 50724b3f3c..96c85db909 100755
--- a/test/TEST-20-MAINPIDGAMES/test.sh
+++ b/test/TEST-20-MAINPIDGAMES/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test changing main PID"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 20
diff --git a/test/TEST-22-TMPFILES/test.sh b/test/TEST-22-TMPFILES/test.sh
index e4874b4d23..7038864deb 100755
--- a/test/TEST-22-TMPFILES/test.sh
+++ b/test/TEST-22-TMPFILES/test.sh
@@ -1,12 +1,15 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Tmpfiles related tests"
TEST_NO_QEMU=1
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
test_append_files() {
- if [[ "$IS_BUILT_WITH_ASAN" == "yes" ]]; then
- if [[ -z "$initdir" ]]; then
+ if [[ "${IS_BUILT_WITH_ASAN:=}" == "yes" ]]; then
+ if [[ -z "${initdir:=}" ]]; then
echo >&2 "\$initdir is not defined, can't continue"
exit 1
fi
diff --git a/test/TEST-23-TYPE-EXEC/test.sh b/test/TEST-23-TYPE-EXEC/test.sh
index 1b0d25a721..2d1b938071 100755
--- a/test/TEST-23-TYPE-EXEC/test.sh
+++ b/test/TEST-23-TYPE-EXEC/test.sh
@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test Type=exec"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 23
diff --git a/test/TEST-24-CRYPTSETUP/test.sh b/test/TEST-24-CRYPTSETUP/test.sh
index d7b338c7f0..e4d99d10b9 100755
--- a/test/TEST-24-CRYPTSETUP/test.sh
+++ b/test/TEST-24-CRYPTSETUP/test.sh
@@ -1,68 +1,77 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="cryptsetup systemd setup"
IMAGE_NAME="cryptsetup"
TEST_NO_NSPAWN=1
TEST_FORCE_NEWIMAGE=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
check_result_qemu() {
- ret=1
+ local ret=1
+
mount_initdir
- [[ -e $initdir/testok ]] && ret=0
- [[ -f $initdir/failed ]] && cp -a $initdir/failed $TESTDIR
- cryptsetup luksOpen ${LOOPDEV}p2 varcrypt <$TESTDIR/keyfile
- mount /dev/mapper/varcrypt $initdir/var
- save_journal $initdir/var/log/journal
- _umount_dir $initdir/var
- _umount_dir $initdir
+ [[ -e "${initdir:?}/testok" ]] && ret=0
+ [[ -f "$initdir/failed" ]] && cp -a "$initdir/failed" "${TESTDIR:?}"
+
+ cryptsetup luksOpen "${LOOPDEV:?}p2" varcrypt <"$TESTDIR/keyfile"
+ mount /dev/mapper/varcrypt "$initdir/var"
+ save_journal "$initdir/var/log/journal"
+ _umount_dir "$initdir/var"
+ _umount_dir "$initdir"
cryptsetup luksClose /dev/mapper/varcrypt
- [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
- echo $JOURNAL_LIST
- test -s $TESTDIR/failed && ret=$(($ret+1))
+
+ [[ -f "$TESTDIR/failed" ]] && cat "$TESTDIR/failed"
+ echo "${JOURNAL_LIST:-No journals were saved}"
+
+ test -s "$TESTDIR/failed" && ret=$((ret + 1))
return $ret
}
test_create_image() {
create_empty_image_rootdir
- echo -n test >$TESTDIR/keyfile
- cryptsetup -q luksFormat --pbkdf pbkdf2 --pbkdf-force-iterations 1000 ${LOOPDEV}p2 $TESTDIR/keyfile
- cryptsetup luksOpen ${LOOPDEV}p2 varcrypt <$TESTDIR/keyfile
+
+ echo -n test >"${TESTDIR:?}/keyfile"
+ cryptsetup -q luksFormat --pbkdf pbkdf2 --pbkdf-force-iterations 1000 "${LOOPDEV:?}p2" "$TESTDIR/keyfile"
+ cryptsetup luksOpen "${LOOPDEV}p2" varcrypt <"$TESTDIR/keyfile"
mkfs.ext4 -L var /dev/mapper/varcrypt
- mkdir -p $initdir/var
- mount /dev/mapper/varcrypt $initdir/var
+ mkdir -p "${initdir:?}/var"
+ mount /dev/mapper/varcrypt "$initdir/var"
# Create what will eventually be our root filesystem onto an overlay
(
LOG_LEVEL=5
- eval $(udevadm info --export --query=env --name=/dev/mapper/varcrypt)
- eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+ # shellcheck source=/dev/null
+ source <(udevadm info --export --query=env --name=/dev/mapper/varcrypt)
+ # shellcheck source=/dev/null
+ source <(udevadm info --export --query=env --name="${LOOPDEV}p2")
setup_basic_environment
mask_supporting_services
install_dmevent
generate_module_dependencies
- cat >$initdir/etc/crypttab <<EOF
+ cat >"$initdir/etc/crypttab" <<EOF
$DM_NAME UUID=$ID_FS_UUID /etc/varkey
EOF
- echo -n test >$initdir/etc/varkey
- cat $initdir/etc/crypttab | ddebug
+ echo -n test >"$initdir/etc/varkey"
+ ddebug <"$initdir/etc/crypttab"
- cat >>$initdir/etc/fstab <<EOF
+ cat >>"$initdir/etc/fstab" <<EOF
/dev/mapper/varcrypt /var ext4 defaults 0 1
EOF
# Forward journal messages to the console, so we have something
# to investigate even if we fail to mount the encrypted /var
- echo ForwardToConsole=yes >> $initdir/etc/systemd/journald.conf
+ echo ForwardToConsole=yes >> "$initdir/etc/systemd/journald.conf"
)
}
cleanup_root_var() {
- ddebug "umount $initdir/var"
- mountpoint $initdir/var && umount $initdir/var
+ ddebug "umount ${initdir:?}/var"
+ mountpoint "$initdir/var" && umount "$initdir/var"
[[ -b /dev/mapper/varcrypt ]] && cryptsetup luksClose /dev/mapper/varcrypt
}
diff --git a/test/TEST-25-IMPORT/test.sh b/test/TEST-25-IMPORT/test.sh
index 034b94ca79..51dae2d887 100755
--- a/test/TEST-25-IMPORT/test.sh
+++ b/test/TEST-25-IMPORT/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test importd"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 25
diff --git a/test/TEST-26-SETENV/test.sh b/test/TEST-26-SETENV/test.sh
index 158fa6fdc0..2523e20929 100755
--- a/test/TEST-26-SETENV/test.sh
+++ b/test/TEST-26-SETENV/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test setenv"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 26
diff --git a/test/TEST-27-STDOUTFILE/test.sh b/test/TEST-27-STDOUTFILE/test.sh
index 23aadf314e..f19ac4ac8c 100755
--- a/test/TEST-27-STDOUTFILE/test.sh
+++ b/test/TEST-27-STDOUTFILE/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test StandardOutput=file:"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 27
diff --git a/test/TEST-28-PERCENTJ-WANTEDBY/test.sh b/test/TEST-28-PERCENTJ-WANTEDBY/test.sh
index 09baf22776..5525538463 100755
--- a/test/TEST-28-PERCENTJ-WANTEDBY/test.sh
+++ b/test/TEST-28-PERCENTJ-WANTEDBY/test.sh
@@ -1,8 +1,10 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Ensure %j Wants directives work"
RUN_IN_UNPRIVILEGED_CONTAINER=yes
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 28
diff --git a/test/TEST-29-PORTABLE/test.sh b/test/TEST-29-PORTABLE/test.sh
index cd421efdae..9eff1d797e 100755
--- a/test/TEST-29-PORTABLE/test.sh
+++ b/test/TEST-29-PORTABLE/test.sh
@@ -2,12 +2,14 @@
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
+
TEST_DESCRIPTION="test systemd-portabled"
IMAGE_NAME="portabled"
TEST_NO_NSPAWN=1
TEST_INSTALL_VERITY_MINIMAL=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
# Need loop devices for mounting images
test_append_files() {
diff --git a/test/TEST-30-ONCLOCKCHANGE/test.sh b/test/TEST-30-ONCLOCKCHANGE/test.sh
index 4723e7b0be..6a201514dc 100755
--- a/test/TEST-30-ONCLOCKCHANGE/test.sh
+++ b/test/TEST-30-ONCLOCKCHANGE/test.sh
@@ -1,7 +1,10 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test OnClockChange= + OnTimezoneChange="
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 30
diff --git a/test/TEST-31-DEVICE-ENUMERATION/test.sh b/test/TEST-31-DEVICE-ENUMERATION/test.sh
index 4fbd38d2d0..a474ec84ed 100755
--- a/test/TEST-31-DEVICE-ENUMERATION/test.sh
+++ b/test/TEST-31-DEVICE-ENUMERATION/test.sh
@@ -1,9 +1,12 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="plugged -> dead -> plugged issue #11997"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
QEMU_TIMEOUT=300
do_test "$@" 31
diff --git a/test/TEST-32-OOMPOLICY/test.sh b/test/TEST-32-OOMPOLICY/test.sh
index 6f2955cf62..11290fd8e4 100755
--- a/test/TEST-32-OOMPOLICY/test.sh
+++ b/test/TEST-32-OOMPOLICY/test.sh
@@ -1,8 +1,11 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test OOM killer logic"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
UNIFIED_CGROUP_HIERARCHY=yes
diff --git a/test/TEST-33-CLEAN-UNIT/test.sh b/test/TEST-33-CLEAN-UNIT/test.sh
index 64cb6aee59..0fcaae69e3 100755
--- a/test/TEST-33-CLEAN-UNIT/test.sh
+++ b/test/TEST-33-CLEAN-UNIT/test.sh
@@ -2,7 +2,10 @@
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
+
TEST_DESCRIPTION="test CleanUnit"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 33
diff --git a/test/TEST-34-DYNAMICUSERMIGRATE/test.sh b/test/TEST-34-DYNAMICUSERMIGRATE/test.sh
index 0b2174a894..d30113c757 100755
--- a/test/TEST-34-DYNAMICUSERMIGRATE/test.sh
+++ b/test/TEST-34-DYNAMICUSERMIGRATE/test.sh
@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test migrating state directory from DynamicUser=1 to DynamicUser=0 and back"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 34
diff --git a/test/TEST-36-NUMAPOLICY/test.sh b/test/TEST-36-NUMAPOLICY/test.sh
index cec8b69920..9422a16083 100755
--- a/test/TEST-36-NUMAPOLICY/test.sh
+++ b/test/TEST-36-NUMAPOLICY/test.sh
@@ -3,7 +3,10 @@ set -e
TEST_DESCRIPTION="test NUMAPolicy= and NUMAMask= options"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
if qemu_min_version "5.2.0"; then
QEMU_OPTIONS="-object memory-backend-ram,id=mem0,size=512M -numa node,memdev=mem0,nodeid=0"
else
diff --git a/test/TEST-37-RUNTIMEDIRECTORYPRESERVE/test.sh b/test/TEST-37-RUNTIMEDIRECTORYPRESERVE/test.sh
index b5806c429f..72e312fca0 100755
--- a/test/TEST-37-RUNTIMEDIRECTORYPRESERVE/test.sh
+++ b/test/TEST-37-RUNTIMEDIRECTORYPRESERVE/test.sh
@@ -2,7 +2,10 @@
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
+
TEST_DESCRIPTION="test RuntimeDirectoryPreserve=yes"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 37
diff --git a/test/TEST-38-FREEZER/test.sh b/test/TEST-38-FREEZER/test.sh
index 3821db9f00..524180b1ad 100755
--- a/test/TEST-38-FREEZER/test.sh
+++ b/test/TEST-38-FREEZER/test.sh
@@ -1,7 +1,10 @@
#!/bin/bash
set -e
+
TEST_DESCRIPTION="test unit freezing and thawing via DBus and systemctl"
TEST_NO_NSPAWN=1
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 38
diff --git a/test/TEST-39-EXECRELOAD/test.sh b/test/TEST-39-EXECRELOAD/test.sh
index e38a9902ce..8f09ff1ac8 100755
--- a/test/TEST-39-EXECRELOAD/test.sh
+++ b/test/TEST-39-EXECRELOAD/test.sh
@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Test ExecReload= (PR #13098)"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 39
diff --git a/test/TEST-40-EXEC-COMMAND-EX/test.sh b/test/TEST-40-EXEC-COMMAND-EX/test.sh
index 4ee84d4d35..4e998d31f8 100755
--- a/test/TEST-40-EXEC-COMMAND-EX/test.sh
+++ b/test/TEST-40-EXEC-COMMAND-EX/test.sh
@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test ExecXYZEx= service unit dbus hookups"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 40
diff --git a/test/TEST-41-ONESHOT-RESTART/test.sh b/test/TEST-41-ONESHOT-RESTART/test.sh
index d3f96ae107..43ed4648c0 100755
--- a/test/TEST-41-ONESHOT-RESTART/test.sh
+++ b/test/TEST-41-ONESHOT-RESTART/test.sh
@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Test oneshot unit restart on failure"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 41
diff --git a/test/TEST-42-EXECSTOPPOST/test.sh b/test/TEST-42-EXECSTOPPOST/test.sh
index 53e6fa3dd0..8e25dd8f18 100755
--- a/test/TEST-42-EXECSTOPPOST/test.sh
+++ b/test/TEST-42-EXECSTOPPOST/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test that ExecStopPost= is always run"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 42
diff --git a/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh b/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh
index 4749150ff1..bdd18290e8 100755
--- a/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh
+++ b/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh
@@ -1,7 +1,10 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Test PrivateUsers=yes on user manager"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
has_user_dbus_socket || exit 0
diff --git a/test/TEST-44-LOG-NAMESPACE/test.sh b/test/TEST-44-LOG-NAMESPACE/test.sh
index 26d863708e..2562b325cb 100755
--- a/test/TEST-44-LOG-NAMESPACE/test.sh
+++ b/test/TEST-44-LOG-NAMESPACE/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test log namespaces"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 44
diff --git a/test/TEST-46-HOMED/test.sh b/test/TEST-46-HOMED/test.sh
index 877cbfefd0..dedc660b44 100755
--- a/test/TEST-46-HOMED/test.sh
+++ b/test/TEST-46-HOMED/test.sh
@@ -1,8 +1,10 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="testing homed"
TEST_NO_QEMU=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 46
diff --git a/test/TEST-47-ISSUE-14566/test.sh b/test/TEST-47-ISSUE-14566/test.sh
index 4e80ec76ff..f9ce149865 100755
--- a/test/TEST-47-ISSUE-14566/test.sh
+++ b/test/TEST-47-ISSUE-14566/test.sh
@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Test that KillMode=mixed does not leave left over processes with ExecStopPost="
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 47
diff --git a/test/TEST-48-START-STOP-NO-RELOAD/test.sh b/test/TEST-48-START-STOP-NO-RELOAD/test.sh
index f6638b3241..68d584fe77 100755
--- a/test/TEST-48-START-STOP-NO-RELOAD/test.sh
+++ b/test/TEST-48-START-STOP-NO-RELOAD/test.sh
@@ -2,7 +2,10 @@
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
+
TEST_DESCRIPTION="test StartStopNoReload"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 48
diff --git a/test/TEST-49-RUNTIME-BIND-PATHS/test.sh b/test/TEST-49-RUNTIME-BIND-PATHS/test.sh
index ff24a4f254..d20dd9ca1b 100755
--- a/test/TEST-49-RUNTIME-BIND-PATHS/test.sh
+++ b/test/TEST-49-RUNTIME-BIND-PATHS/test.sh
@@ -2,6 +2,8 @@
set -e
TEST_DESCRIPTION="test adding new BindPaths while unit is already running"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 49
diff --git a/test/TEST-50-DISSECT/deny-list-ubuntu-ci b/test/TEST-50-DISSECT/deny-list-ubuntu-ci
deleted file mode 100644
index e7585d747e..0000000000
--- a/test/TEST-50-DISSECT/deny-list-ubuntu-ci
+++ /dev/null
@@ -1,2 +0,0 @@
-Skip this test due to issue #17469
-https://github.com/systemd/systemd/issues/17469
diff --git a/test/TEST-50-DISSECT/test.sh b/test/TEST-50-DISSECT/test.sh
index 9d42e4891c..8765b11cb8 100755
--- a/test/TEST-50-DISSECT/test.sh
+++ b/test/TEST-50-DISSECT/test.sh
@@ -2,12 +2,14 @@
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
+
TEST_DESCRIPTION="test systemd-dissect"
IMAGE_NAME="dissect"
TEST_NO_NSPAWN=1
TEST_INSTALL_VERITY_MINIMAL=1
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
command -v mksquashfs >/dev/null 2>&1 || exit 0
command -v veritysetup >/dev/null 2>&1 || exit 0
@@ -23,6 +25,7 @@ test_append_files() {
install_dmevent
generate_module_dependencies
inst_binary losetup
+ inst_binary wc
install_verity_minimal
)
}
diff --git a/test/TEST-51-ISSUE-16115/test.sh b/test/TEST-51-ISSUE-16115/test.sh
index eca235c0a6..11dd85e460 100755
--- a/test/TEST-51-ISSUE-16115/test.sh
+++ b/test/TEST-51-ISSUE-16115/test.sh
@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="Test ExecCondition= does not restart on abnormal or failure"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 51
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh
index a0848ef672..3bcf1b8c9e 100755
--- a/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh
+++ b/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh
@@ -1,19 +1,21 @@
#!/bin/bash
set -e
-. $TEST_BASE_DIR/test-functions
+
TEST_REQUIRE_INSTALL_TESTS=0
TEST_DESCRIPTION="testing honor first shutdown"
-#INTERACTIVE_DEBUG=1
TEST_NO_QEMU=1
-#Using timeout because if the test fails it can loop.
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+# Using timeout because if the test fails it can loop.
# The reason is because the poweroff executed by end.service
# could turn into a reboot if the test fails.
NSPAWN_TIMEOUT=20
-#Remove this file if it exists. this is used along with
-# the make target "finish". Since concrete confirmaion is
+# Remove this file if it exists. This is used along with
+# the make target "finish". Since concrete confirmation is
# only found from the console during the poweroff.
rm -f /tmp/honorfirstshutdown.log >/dev/null
-do_test "$@" 52 > /tmp/honorfirstshutdown.log
+do_test "$@" 52 >/tmp/honorfirstshutdown.log
diff --git a/test/TEST-53-ISSUE-16347/test.sh b/test/TEST-53-ISSUE-16347/test.sh
index 089768e8dd..492d517932 100755
--- a/test/TEST-53-ISSUE-16347/test.sh
+++ b/test/TEST-53-ISSUE-16347/test.sh
@@ -4,8 +4,9 @@ set -e
TEST_DESCRIPTION="test timer units when initial clock is ahead"
TEST_NO_NSPAWN=1
-future_date=$(date -u +%Y-%m-%dT%H:%M:%S -d '+3 days')
-QEMU_OPTIONS="-rtc base=${future_date}"
-. $TEST_BASE_DIR/test-functions
+QEMU_OPTIONS="-rtc base=$(date -u +%Y-%m-%dT%H:%M:%S -d '+3 days')"
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 53
diff --git a/test/TEST-54-CREDS/test.sh b/test/TEST-54-CREDS/test.sh
index 5feb15e7f1..4562c6095e 100755
--- a/test/TEST-54-CREDS/test.sh
+++ b/test/TEST-54-CREDS/test.sh
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test credentials"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 54
diff --git a/test/TEST-55-OOMD/test.sh b/test/TEST-55-OOMD/test.sh
index 121aa8d56e..afc6706e32 100755
--- a/test/TEST-55-OOMD/test.sh
+++ b/test/TEST-55-OOMD/test.sh
@@ -1,47 +1,56 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="systemd-oomd Memory Pressure Test"
-. $TEST_BASE_DIR/test-functions
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
check_result_nspawn() {
+ local workspace="${1:?}"
local ret=1
local journald_report=""
local pids=""
- [[ -e $1/testok ]] && ret=0
- if [[ -e $1/skipped ]]; then
+
+ [[ -e "$workspace/testok" ]] && ret=0
+ if [[ -e "$workspace/skipped" ]]; then
echo "TEST-56-OOMD was skipped:"
- cat $1/skipped
+ cat "$workspace/skipped"
ret=0
fi
- [[ -f $1/failed ]] && cp -a $1/failed $TESTDIR
- save_journal $1/var/log/journal
- [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
- echo $JOURNAL_LIST
- test -s $TESTDIR/failed && ret=$(($ret+1))
- [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
- check_asan_reports "$1" || ret=$(($ret+1))
- _umount_dir $initdir
+
+ [[ -f "$workspace/failed" ]] && cp -a "$workspace/failed" "${TESTDIR:?}"
+ save_journal "$workspace/var/log/journal"
+ [[ -f "$TESTDIR/failed" ]] && cat "$TESTDIR/failed"
+ echo "${JOURNAL_LIST:-No journals were saved}"
+
+ test -s "$TESTDIR/failed" && ret=$((ret + 1))
+ [ -n "${TIMED_OUT:=}" ] && ret=$((ret + 1))
+ check_asan_reports "$workspace" || ret=$((ret + 1))
+ _umount_dir "${initdir:?}"
return $ret
}
check_result_qemu() {
local ret=1
+
mount_initdir
- [[ -e $initdir/testok ]] && ret=0
- if [[ -e $initdir/skipped ]]; then
+ [[ -e "${initdir:?}/testok" ]] && ret=0
+ if [[ -e "$initdir/skipped" ]]; then
echo "TEST-56-OOMD was skipped:"
- cat $initdir/skipped
+ cat "$initdir/skipped"
ret=0
fi
- [[ -f $initdir/failed ]] && cp -a $initdir/failed $TESTDIR
- save_journal $initdir/var/log/journal
- check_asan_reports "$initdir" || ret=$(($ret+1))
- _umount_dir $initdir
- [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
- echo $JOURNAL_LIST
- test -s $TESTDIR/failed && ret=$(($ret+1))
- [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
+
+ [[ -f "$initdir/failed" ]] && cp -a "$initdir/failed" "${TESTDIR:?}"
+ save_journal "$initdir/var/log/journal"
+ check_asan_reports "$initdir" || ret=$((ret + 1))
+ _umount_dir "$initdir"
+ [[ -f "$TESTDIR/failed" ]] && cat "$TESTDIR/failed"
+ echo "${JOURNAL_LIST:-No journals were saved}"
+
+ test -s "$TESTDIR/failed" && ret=$((ret + 1))
+ [ -n "${TIMED_OUT:=}" ] && ret=$((ret + 1))
return $ret
}
diff --git a/test/TEST-56-EXIT-TYPE/test.sh b/test/TEST-56-EXIT-TYPE/test.sh
index fc321e4141..e654accb0e 100755
--- a/test/TEST-56-EXIT-TYPE/test.sh
+++ b/test/TEST-56-EXIT-TYPE/test.sh
@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -e
+
TEST_DESCRIPTION="test ExitType=cgroup"
-. $TEST_BASE_DIR/test-functions
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@" 56
diff --git a/test/TEST-58-REPART/Makefile b/test/TEST-58-REPART/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-58-REPART/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-58-REPART/test.sh b/test/TEST-58-REPART/test.sh
new file mode 100755
index 0000000000..d94a9cbfdc
--- /dev/null
+++ b/test/TEST-58-REPART/test.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -e
+TEST_DESCRIPTION="test systemd-repart"
+. $TEST_BASE_DIR/test-functions
+
+do_test "$@" 56
diff --git a/test/TEST-59-RELOADING-RESTART/Makefile b/test/TEST-59-RELOADING-RESTART/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-59-RELOADING-RESTART/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-59-RELOADING-RESTART/test.sh b/test/TEST-59-RELOADING-RESTART/test.sh
new file mode 100755
index 0000000000..ad990963b8
--- /dev/null
+++ b/test/TEST-59-RELOADING-RESTART/test.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+set -e
+TEST_DESCRIPTION="Test auto restart of exited services which are stuck in reloading state"
+
+TEST_NO_QEMU=1
+
+. $TEST_BASE_DIR/test-functions
+
+do_test "$@" 59
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index ef32cbcad0..d6c1cc7f92 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -32,6 +32,7 @@ PermanentMACAddress=
[Link]
ActivationPolicy=
RequiredForOnline=
+RequiredFamilyForOnline=
ARP=
AllMulticast=
Unmanaged=
@@ -140,12 +141,16 @@ UserClass=
VendorClass=
SendVendorOption=
RouteMetric=
+IAID=
+DUIDType=
+DUIDRawData=
[DHCPv6PrefixDelegation]
SubnetId=
Announce=
Assign=
ManageTemporaryAddress=
Token=
+RouteMetric=
[Route]
Destination=
Protocol=
@@ -235,6 +240,7 @@ PreferredLifetimeSec=
AddressAutoconfiguration=
ValidLifetimeSec=
Assign=
+RouteMetric=
[IPv6RoutePrefix]
Route=
LifetimeSec=
@@ -269,6 +275,7 @@ ManageTemporaryAddress=
Broadcast=
Peer=
Label=
+RouteMetric=
[RoutingPolicyRule]
Table=
IncomingInterface=
@@ -316,6 +323,7 @@ Prefix=
[IPv6AcceptRA]
UseDomains=
RouteTable=
+RouteMetric=
UseDNS=
DHCPv6Client=
UseAutonomousPrefix=
@@ -350,6 +358,8 @@ EmitTimezone=
DNS=
SendOption=
SendVendorOption=
+BindToInterface=
+RelayTarget=
[NextHop]
Id=
Gateway=
diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service
index 310dbd6add..a152bebd73 100644
--- a/test/fuzz/fuzz-unit-file/directives-all.service
+++ b/test/fuzz/fuzz-unit-file/directives-all.service
@@ -545,6 +545,7 @@ RemoteChecksumRx=
RemoteChecksumTx=
ReorderHeader=
RequestBroadcast=
+RequiredFamilyForOnline=
RequiredForOnline=
ResendIGMP=
RootDistanceMaxSec=
diff --git a/test/fuzz/fuzz-unit-file/directives.mount b/test/fuzz/fuzz-unit-file/directives.mount
index 4cc64a96b6..f1a8d19d18 100644
--- a/test/fuzz/fuzz-unit-file/directives.mount
+++ b/test/fuzz/fuzz-unit-file/directives.mount
@@ -4,6 +4,7 @@ AllowedCPUs=
AllowedMemoryNodes=
AmbientCapabilities=
AppArmorProfile=
+BPFProgram=
BindPaths=
BindReadOnlyPaths=
BlockIOAccounting=
diff --git a/test/fuzz/fuzz-unit-file/directives.scope b/test/fuzz/fuzz-unit-file/directives.scope
index 0af372f7ca..97ca9b47aa 100644
--- a/test/fuzz/fuzz-unit-file/directives.scope
+++ b/test/fuzz/fuzz-unit-file/directives.scope
@@ -2,6 +2,7 @@ scope
[Scope]
AllowedCPUs=
AllowedMemoryNodes=
+BPFProgram=
BlockIOAccounting=
BlockIODeviceWeight=
BlockIOReadBandwidth=
diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
index ea3c93d93d..c2069c1a78 100644
--- a/test/fuzz/fuzz-unit-file/directives.service
+++ b/test/fuzz/fuzz-unit-file/directives.service
@@ -28,6 +28,7 @@ AssertPathIsSymbolicLink=
AssertSecurity=
AssertUser=
AssertVirtualization=
+BPFProgram=
Before=
BindTo=
BindsTo=
diff --git a/test/fuzz/fuzz-unit-file/directives.slice b/test/fuzz/fuzz-unit-file/directives.slice
index 2a35b3e518..b96d0628ad 100644
--- a/test/fuzz/fuzz-unit-file/directives.slice
+++ b/test/fuzz/fuzz-unit-file/directives.slice
@@ -2,6 +2,7 @@ slice
[Slice]
AllowedCPUs=
AllowedMemoryNodes=
+BPFProgram=
BlockIOAccounting=
BlockIODeviceWeight=
BlockIOReadBandwidth=
diff --git a/test/fuzz/fuzz-unit-file/directives.socket b/test/fuzz/fuzz-unit-file/directives.socket
index 3931601ead..79e04a2e84 100644
--- a/test/fuzz/fuzz-unit-file/directives.socket
+++ b/test/fuzz/fuzz-unit-file/directives.socket
@@ -5,6 +5,7 @@ AllowedCPUs=
AllowedMemoryNodes=
AmbientCapabilities=
AppArmorProfile=
+BPFProgram=
Backlog=
BindIPv6Only=
BindPaths=
diff --git a/test/fuzz/fuzz-unit-file/directives.swap b/test/fuzz/fuzz-unit-file/directives.swap
index 3998f8bd33..c3b63aac56 100644
--- a/test/fuzz/fuzz-unit-file/directives.swap
+++ b/test/fuzz/fuzz-unit-file/directives.swap
@@ -4,6 +4,7 @@ AllowedCPUs=
AllowedMemoryNodes=
AmbientCapabilities=
AppArmorProfile=
+BPFProgram=
BindPaths=
BindReadOnlyPaths=
BlockIOAccounting=
diff --git a/test/fuzz/fuzz-unit-file/oss-fuzz-32991 b/test/fuzz/fuzz-unit-file/oss-fuzz-32991
new file mode 100644
index 0000000000..6305b2aa22
--- /dev/null
+++ b/test/fuzz/fuzz-unit-file/oss-fuzz-32991
Binary files differ
diff --git a/test/test-execute/exec-standardoutput-append.service b/test/test-execute/exec-standardoutput-append.service
index 8983bb056b..2118bfc2a6 100644
--- a/test/test-execute/exec-standardoutput-append.service
+++ b/test/test-execute/exec-standardoutput-append.service
@@ -2,8 +2,8 @@
Description=Test for StandardOutput=append:
[Service]
-ExecStartPre=sh -c 'printf "hello\n" > /tmp/test-exec-standardoutput-output'
-ExecStartPre=sh -c 'printf "hello\nhello\n" > /tmp/test-exec-standardoutput-expected'
+ExecStartPre=sh -c 'printf "hello\n" >/tmp/test-exec-standardoutput-output'
+ExecStartPre=sh -c 'printf "hello\nhello\n" >/tmp/test-exec-standardoutput-expected'
StandardInput=data
StandardInputText=hello
StandardOutput=append:/tmp/test-exec-standardoutput-output
diff --git a/test/test-execute/exec-standardoutput-file.service b/test/test-execute/exec-standardoutput-file.service
index 71e2604b94..8d484a456e 100644
--- a/test/test-execute/exec-standardoutput-file.service
+++ b/test/test-execute/exec-standardoutput-file.service
@@ -2,8 +2,8 @@
Description=Test for StandardOutput=file:
[Service]
-ExecStartPre=sh -c 'printf "nooo\nhello\n" > /tmp/test-exec-standardoutput-output'
-ExecStartPre=sh -c 'printf "hello\nello\n" > /tmp/test-exec-standardoutput-expected'
+ExecStartPre=sh -c 'printf "nooo\nhello\n" >/tmp/test-exec-standardoutput-output'
+ExecStartPre=sh -c 'printf "hello\nello\n" >/tmp/test-exec-standardoutput-expected'
StandardInput=data
StandardInputText=hello
StandardOutput=file:/tmp/test-exec-standardoutput-output
diff --git a/test/test-execute/exec-standardoutput-truncate.service b/test/test-execute/exec-standardoutput-truncate.service
index 4b4bb87b75..8d6ea2769f 100644
--- a/test/test-execute/exec-standardoutput-truncate.service
+++ b/test/test-execute/exec-standardoutput-truncate.service
@@ -2,8 +2,8 @@
Description=Test for StandardOutput=truncate:
[Service]
-ExecStartPre=sh -c 'printf "hello\n" > /tmp/test-exec-standardoutput-output'
-ExecStartPre=sh -c 'printf "hi\n" > /tmp/test-exec-standardoutput-expected'
+ExecStartPre=sh -c 'printf "hello\n" >/tmp/test-exec-standardoutput-output'
+ExecStartPre=sh -c 'printf "hi\n" >/tmp/test-exec-standardoutput-expected'
StandardInput=data
StandardInputText=hi
StandardOutput=truncate:/tmp/test-exec-standardoutput-output
diff --git a/test/test-functions b/test/test-functions
index 6b94058fd3..8f2ffb1323 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -1,15 +1,27 @@
#!/usr/bin/env bash
+# shellcheck disable=SC2031
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
+# ex: ts=8 sw=4 sts=4 et filetype=sh tw=180
+# Note: the shellcheck line above disables warning for variables which were
+# modified in a subshell. In our case this behavior is expected, but
+# `shellcheck` can't distinguish this because of poor variable tracking,
+# which results in warning for every instance of such variable used
+# throughout this file.
+# See:
+# * comment in function install_verity_minimal()
+# * koalaman/shellcheck#280
+set -o pipefail
+
PATH=/sbin:/bin:/usr/sbin:/usr/bin
export PATH
os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)
-LOOKS_LIKE_DEBIAN=$(source $os_release && [[ "$ID" = "debian" || " $ID_LIKE " = *" debian "* ]] && echo yes || :)
-LOOKS_LIKE_ARCH=$(source $os_release && [[ "$ID" = "arch" || " $ID_LIKE " = *" arch "* ]] && echo yes || :)
-LOOKS_LIKE_SUSE=$(source $os_release && [[ " $ID_LIKE " = *" suse "* ]] && echo yes || :)
-KERNEL_VER=${KERNEL_VER-$(uname -r)}
-KERNEL_MODS="/lib/modules/$KERNEL_VER/"
+# shellcheck source=/dev/null
+source "$os_release"
+[[ "$ID" = "debian" || " $ID_LIKE " = *" debian "* ]] && LOOKS_LIKE_DEBIAN=yes || LOOKS_LIKE_DEBIAN=""
+[[ "$ID" = "arch" || " $ID_LIKE " = *" arch "* ]] && LOOKS_LIKE_ARCH=yes || LOOKS_LIKE_ARCH=""
+[[ " $ID_LIKE " = *" suse "* ]] && LOOKS_LIKE_SUSE=yes || LOOKS_LIKE_SUSE=""
+KERNEL_VER="${KERNEL_VER-$(uname -r)}"
QEMU_TIMEOUT="${QEMU_TIMEOUT:-infinity}"
NSPAWN_TIMEOUT="${NSPAWN_TIMEOUT:-infinity}"
TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out
@@ -32,7 +44,7 @@ LOOPDEV=
# check if it's not explicitly disabled (TEST_NO_KVM) and we're not already
# running under KVM. If these conditions are met, enable KVM (and possibly
# nested KVM), otherwise disable it.
-if [[ -n "$TEST_NESTED_KVM" || ( -z "$TEST_NO_KVM" && $(systemd-detect-virt -v) != kvm ) ]]; then
+if [[ -n "${TEST_NESTED_KVM:=}" || ( -z "${TEST_NO_KVM:=}" && $(systemd-detect-virt -v) != kvm ) ]]; then
QEMU_KVM=yes
else
QEMU_KVM=no
@@ -44,34 +56,36 @@ if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
fi
# The calling test.sh scripts have TEST_BASE_DIR set via their Makefile, but we don't need them to provide it
-TEST_BASE_DIR=${TEST_BASE_DIR:-$(realpath $(dirname "$BASH_SOURCE"))}
+TEST_BASE_DIR=${TEST_BASE_DIR:-$(realpath "$(dirname "${BASH_SOURCE[0]}")")}
TEST_UNITS_DIR="$TEST_BASE_DIR/units"
SOURCE_DIR=$(realpath "$TEST_BASE_DIR/..")
TOOLS_DIR="$SOURCE_DIR/tools"
+# These variables are used by test scripts
+export TEST_BASE_DIR TEST_UNITS_DIR SOURCE_DIR TOOLS_DIR
# note that find-build-dir.sh will return $BUILD_DIR if provided, else it will try to find it
-if ! BUILD_DIR=$($TOOLS_DIR/find-build-dir.sh); then
+if ! BUILD_DIR="$("$TOOLS_DIR"/find-build-dir.sh)"; then
if [ "$NO_BUILD" ]; then
- BUILD_DIR=$SOURCE_DIR
+ BUILD_DIR="$SOURCE_DIR"
else
echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2
exit 1
fi
fi
-PATH_TO_INIT=$ROOTLIBDIR/systemd
-[ "$SYSTEMD_JOURNALD" ] || SYSTEMD_JOURNALD=$(which -a $BUILD_DIR/systemd-journald $ROOTLIBDIR/systemd-journald 2>/dev/null | grep '^/' -m1)
-[ "$SYSTEMD_JOURNAL_REMOTE" ] || SYSTEMD_JOURNAL_REMOTE=$(which -a $BUILD_DIR/systemd-journal-remote $ROOTLIBDIR/systemd-journal-remote 2>/dev/null | grep '^/' -m1)
-[ "$SYSTEMD" ] || SYSTEMD=$(which -a $BUILD_DIR/systemd $ROOTLIBDIR/systemd 2>/dev/null | grep '^/' -m1)
-[ "$SYSTEMD_NSPAWN" ] || SYSTEMD_NSPAWN=$(which -a $BUILD_DIR/systemd-nspawn systemd-nspawn 2>/dev/null | grep '^/' -m1)
-[ "$JOURNALCTL" ] || JOURNALCTL=$(which -a $BUILD_DIR/journalctl journalctl 2>/dev/null | grep '^/' -m1)
+PATH_TO_INIT="$ROOTLIBDIR/systemd"
+SYSTEMD_JOURNALD="${SYSTEMD_JOURNALD:-$(command -v "$BUILD_DIR/systemd-journald" || command -v "$ROOTLIBDIR/systemd-journald")}"
+SYSTEMD_JOURNAL_REMOTE="${SYSTEMD_JOURNAL_REMOTE:-$(command -v "$BUILD_DIR/systemd-journal-remote" || command -v "$ROOTLIBDIR/systemd-journal-remote")}"
+SYSTEMD="${SYSTEMD:-$(command -v "$BUILD_DIR/systemd" || command -v "$ROOTLIBDIR/systemd")}"
+SYSTEMD_NSPAWN="${SYSTEMD_NSPAWN:-$(command -v "$BUILD_DIR/systemd-nspawn" || command -v systemd-nspawn)}"
+JOURNALCTL="${JOURNALCTL:-$(command -v "$BUILD_DIR/journalctl" || command -v journalctl)}"
-TESTFILE=${BASH_SOURCE[1]}
+TESTFILE="${BASH_SOURCE[1]}"
if [ -z "$TESTFILE" ]; then
echo "ERROR: test-functions must be sourced from one of the TEST-*/test.sh scripts" >&2
exit 1
fi
-TESTNAME=$(basename $(dirname $(realpath $TESTFILE)))
+TESTNAME="$(basename "$(dirname "$(realpath "$TESTFILE")")")"
STATEDIR="$BUILD_DIR/test/$TESTNAME"
STATEFILE="$STATEDIR/.testdir"
IMAGESTATEDIR="$STATEDIR/.."
@@ -183,8 +197,9 @@ is_built_with_asan() {
fi
# Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
- local _asan_calls=$(objdump -dC $SYSTEMD_JOURNALD | egrep "callq?\s+[0-9a-f]+\s+<__asan" -c)
- if (( $_asan_calls < 1000 )); then
+ local _asan_calls
+ _asan_calls="$(objdump -dC "$SYSTEMD_JOURNALD" | grep -E "callq?\s+[0-9a-f]+\s+<__asan" -c)"
+ if ((_asan_calls < 1000)); then
return 1
else
return 0
@@ -227,31 +242,32 @@ if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
echo "Detected ASan RT '$ASAN_RT_NAME' located at '$ASAN_RT_PATH'"
fi
-function find_qemu_bin() {
+find_qemu_bin() {
+ QEMU_BIN="${QEMU_BIN:-""}"
# SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
if [[ $QEMU_KVM == "yes" ]]; then
- [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
+ [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v kvm qemu-kvm 2>/dev/null | grep '^/' -m1)"
fi
- [ "$ARCH" ] || ARCH=$(uname -m)
+ [[ -n "$ARCH" ]] || ARCH="$(uname -m)"
case $ARCH in
x86_64)
# QEMU's own build system calls it qemu-system-x86_64
- [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-x86_64 2>/dev/null | grep '^/' -m1)
+ [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu-system-x86_64 2>/dev/null | grep '^/' -m1)"
;;
i*86)
# new i386 version of QEMU
- [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-i386 2>/dev/null | grep '^/' -m1)
+ [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu-system-i386 2>/dev/null | grep '^/' -m1)"
# i386 version of QEMU
- [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu 2>/dev/null | grep '^/' -m1)
+ [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu 2>/dev/null | grep '^/' -m1)"
;;
ppc64*)
- [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a qemu-system-ppc64 2>/dev/null | grep '^/' -m1)
+ [[ -n "$QEMU_BIN" ]] || QEMU_BIN="$(command -v qemu-system-ppc64 2>/dev/null | grep '^/' -m1)"
;;
esac
- if [ ! -e "$QEMU_BIN" ]; then
+ if [[ ! -e "$QEMU_BIN" ]]; then
echo "Could not find a suitable QEMU binary" >&2
return 1
fi
@@ -261,11 +277,12 @@ function find_qemu_bin() {
# returns 0 if newer or equal
# returns 1 if older
# returns 2 if failing
-function qemu_min_version() {
+qemu_min_version() {
find_qemu_bin || return 2
# get version from binary
- qemu_ver=$($QEMU_BIN --version | awk '/^QEMU emulator version ([0-9]*\.[0-9]*\.[0-9]*)/ {print $4}')
+ local qemu_ver
+ qemu_ver="$("$QEMU_BIN" --version | awk '/^QEMU emulator version ([0-9]*\.[0-9]*\.[0-9]*)/ {print $4}')"
# Check version string format
echo "$qemu_ver" | grep -q '^[0-9]*\.[0-9]*\.[0-9]*$' || return 2
@@ -279,14 +296,14 @@ function qemu_min_version() {
# success), or 1 if QEMU is not available.
run_qemu() {
if [ -f /etc/machine-id ]; then
- read MACHINE_ID < /etc/machine-id
+ read -r MACHINE_ID </etc/machine-id
[ -z "$INITRD" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd" ] \
&& INITRD="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/initrd"
[ -z "$KERNEL_BIN" ] && [ -e "$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux" ] \
&& KERNEL_BIN="$EFI_MOUNT/$MACHINE_ID/$KERNEL_VER/linux"
fi
- CONSOLE=ttyS0
+ local CONSOLE=ttyS0
rm -f "$initdir"/{testok,failed,skipped}
# make sure the initdir is not mounted to avoid concurrent access
@@ -300,20 +317,20 @@ run_qemu() {
[ "$ARCH" ] || ARCH=$(uname -m)
case $ARCH in
ppc64*)
- KERNEL_BIN=/boot/vmlinux-$KERNEL_VER
+ KERNEL_BIN="/boot/vmlinux-$KERNEL_VER"
CONSOLE=hvc0
;;
*)
- KERNEL_BIN=/boot/vmlinuz-$KERNEL_VER
+ KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER"
;;
esac
fi
fi
- default_fedora_initrd=/boot/initramfs-${KERNEL_VER}.img
- default_debian_initrd=/boot/initrd.img-${KERNEL_VER}
- default_arch_initrd=/boot/initramfs-linux-fallback.img
- default_suse_initrd=/boot/initrd-${KERNEL_VER}
+ local default_fedora_initrd="/boot/initramfs-${KERNEL_VER}.img"
+ local default_debian_initrd="/boot/initrd.img-${KERNEL_VER}"
+ local default_arch_initrd="/boot/initramfs-linux-fallback.img"
+ local default_suse_initrd="/boot/initrd-${KERNEL_VER}"
if [[ ! "$INITRD" ]]; then
if [[ -e "$default_fedora_initrd" ]]; then
INITRD="$default_fedora_initrd"
@@ -330,7 +347,7 @@ run_qemu() {
# i.e. use the number of online CPUs on the host machine. If the nproc utility
# is not installed or there's some other error when calling it, fall back
# to the original value (QEMU_SMP=1).
- if ! [ "$QEMU_SMP" ]; then
+ if [[ -z "${QEMU_SMP:=}" ]]; then
if ! QEMU_SMP=$(nproc); then
dwarn "nproc utility is not installed, falling back to QEMU_SMP=1"
QEMU_SMP=1
@@ -340,70 +357,81 @@ run_qemu() {
find_qemu_bin || return 1
# Umount initdir to avoid concurrent access to the filesystem
- _umount_dir $initdir
+ _umount_dir "$initdir"
+
+ local kernel_params=()
+ local qemu_options=()
+ local qemu_cmd=("$QEMU_BIN")
- local _cgroup_args
if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then
- _cgroup_args="systemd.unified_cgroup_hierarchy=yes"
+ kernel_params+=("systemd.unified_cgroup_hierarchy=yes")
elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
- _cgroup_args="systemd.unified_cgroup_hierarchy=no systemd.legacy_systemd_cgroup_controller=yes"
+ kernel_params+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=yes")
elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
- _cgroup_args="systemd.unified_cgroup_hierarchy=no systemd.legacy_systemd_cgroup_controller=no"
+ kernel_params+=("systemd.unified_cgroup_hierarchy=no" "systemd.legacy_systemd_cgroup_controller=no")
elif [[ "$UNIFIED_CGROUP_HIERARCHY" != "default" ]]; then
dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
exit 1
fi
if [[ "$LOOKS_LIKE_SUSE" ]]; then
- PARAMS+="rd.hostonly=0"
- fi
+ kernel_params+=("rd.hostonly=0")
+ fi
+
+ kernel_params+=(
+ "root=/dev/sda1"
+ "rw"
+ "raid=noautodetect"
+ "rd.luks=0"
+ "loglevel=2"
+ "init=$PATH_TO_INIT"
+ "console=$CONSOLE"
+ "selinux=0"
+ "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units:"
+ "systemd.unit=testsuite.target"
+ "systemd.wants=testsuite-$1.service"
+ )
- local _end
if [[ ! "$INTERACTIVE_DEBUG" ]]; then
- _end="systemd.wants=end.service"
- else
- _end=""
- fi
-
- KERNEL_APPEND="$PARAMS \
-root=/dev/sda1 \
-rw \
-raid=noautodetect \
-rd.luks=0 \
-loglevel=2 \
-init=$PATH_TO_INIT \
-console=$CONSOLE \
-selinux=0 \
-$_cgroup_args \
-SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units: \
-systemd.unit=testsuite.target \
-systemd.wants=testsuite-$1.service ${_end} \
-$KERNEL_APPEND \
-"
+ kernel_params+=("systemd.wants=end.service")
+ fi
[ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC"
- QEMU_OPTIONS="-smp $QEMU_SMP \
--net none \
--m $QEMU_MEM \
--nographic \
--kernel $KERNEL_BIN \
--drive format=raw,cache=unsafe,file=$image \
-$QEMU_OPTIONS \
-"
+ qemu_options+=(
+ -smp "$QEMU_SMP"
+ -net none
+ -m "$QEMU_MEM"
+ -nographic
+ -kernel "$KERNEL_BIN"
+ -drive "format=raw,cache=unsafe,file=$image"
+ )
+
+ if [[ -n "${QEMU_OPTIONS:=}" ]]; then
+ local user_qemu_options
+ read -ra user_qemu_options <<< "$QEMU_OPTIONS"
+ qemu_options+=("${user_qemu_options[@]}")
+ fi
+
+ if [[ -n "${KERNEL_APPEND:=}" ]]; then
+ local user_kernel_append
+ read -ra user_kernel_append <<< "$KERNEL_APPEND"
+ kernel_params+=("${user_kernel_append[@]}")
+ fi
if [[ "$INITRD" && "$SKIP_INITRD" != "yes" ]]; then
- QEMU_OPTIONS="$QEMU_OPTIONS -initrd $INITRD"
+ qemu_options+=(-initrd "$INITRD")
fi
# Let's use KVM if possible
if [[ -c /dev/kvm && $QEMU_KVM == "yes" ]]; then
- QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
+ qemu_options+=(-machine "accel=kvm" -enable-kvm -cpu host)
fi
if [[ "$QEMU_TIMEOUT" != "infinity" ]]; then
- QEMU_BIN="timeout --foreground $QEMU_TIMEOUT $QEMU_BIN"
+ qemu_cmd=(timeout --foreground "$QEMU_TIMEOUT" "$QEMU_BIN")
fi
- (set -x; $QEMU_BIN $QEMU_OPTIONS -append "$KERNEL_APPEND")
+
+ (set -x; "${qemu_cmd[@]}" "${qemu_options[@]}" -append "${kernel_params[*]}")
rc=$?
if [ "$rc" = 124 ] && [ "$QEMU_TIMEOUT" != "infinity" ]; then
derror "test timed out after $QEMU_TIMEOUT s"
@@ -418,43 +446,56 @@ $QEMU_OPTIONS \
# success), or 1 if nspawn is not available.
run_nspawn() {
[[ -d /run/systemd/system ]] || return 1
- rm -f "$initdir"/{testok,failed,skipped}
-
- local _nspawn_cmd=(
- --register=no
- --kill-signal=SIGKILL
- --directory=$1
- --setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:
- $PATH_TO_INIT
- $KERNEL_APPEND
- systemd.unit=testsuite.target
- systemd.wants=testsuite-$2.service
+ rm -f "${initdir:?}"/{testok,failed,skipped}
+
+ local nspawn_cmd=()
+ local nspawn_options=(
+ "--register=no"
+ "--kill-signal=SIGKILL"
+ "--directory=${1:?}"
+ "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:"
+ )
+ local kernel_params=(
+ "$PATH_TO_INIT"
+ "systemd.unit=testsuite.target"
+ "systemd.wants=testsuite-$2.service"
)
if [[ ! "$INTERACTIVE_DEBUG" ]]; then
- _nspawn_cmd+=( systemd.wants=end.service )
+ kernel_params+=("systemd.wants=end.service")
fi
- local _nspawn_pre
- if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
- _nspawn_pre=(timeout --foreground $NSPAWN_TIMEOUT)
- else
- _nspawn_pre=()
+ if [[ -n "${NSPAWN_ARGUMENTS:=}" ]]; then
+ local user_nspawn_arguments
+ read -ra user_nspawn_arguments <<< "$NSPAWN_ARGUMENTS"
+ nspawn_options+=("${user_nspawn_arguments[@]}")
+ fi
+
+ if [[ -n "${KERNEL_APPEND:=}" ]]; then
+ local user_kernel_append
+ read -ra user_kernel_append <<< "$KERNEL_APPEND"
+ kernel_params+=("${user_kernel_append[@]}")
fi
if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
dwarn "nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping"
exit
elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
- _nspawn_pre=("${_nspawn_pre[@]}" env SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY)
+ nspawn_cmd+=(env "SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY")
elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
- _nspawn_pre=("${_nspawn_pre[@]}" env --unset=UNIFIED_CGROUP_HIERARCHY --unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY)
+ nspawn_cmd+=(env "--unset=UNIFIED_CGROUP_HIERARCHY" "--unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY")
else
dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
exit 1
fi
- (set -x; "${_nspawn_pre[@]}" "$SYSTEMD_NSPAWN" $NSPAWN_ARGUMENTS "${_nspawn_cmd[@]}")
+ if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
+ nspawn_cmd+=(timeout --foreground "$NSPAWN_TIMEOUT" "$SYSTEMD_NSPAWN")
+ else
+ nspawn_cmd+=("$SYSTEMD_NSPAWN")
+ fi
+
+ (set -x; "${nspawn_cmd[@]}" "${nspawn_options[@]}" "${kernel_params[@]}")
rc=$?
if [ "$rc" = 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then
derror "test timed out after $NSPAWN_TIMEOUT s"
@@ -467,7 +508,8 @@ run_nspawn() {
# Build two very minimal root images, with two units, one is the same and one is different across them
install_verity_minimal() {
- if [ -e $initdir/usr/share/minimal.raw ]; then
+ dinfo "Set up a set of minimal images for verity verification"
+ if [ -e "$initdir/usr/share/minimal.raw" ]; then
return
fi
if ! command -v mksquashfs >/dev/null 2>&1; then
@@ -478,6 +520,9 @@ install_verity_minimal() {
dfatal "veritysetup not found"
exit 1
fi
+ # Local modifications of some global variables is intentional in this
+ # subshell (SC2030)
+ # shellcheck disable=SC2030
(
BASICTOOLS=(
bash
@@ -486,12 +531,15 @@ install_verity_minimal() {
mount
sleep
)
- oldinitdir=$initdir
- rm -rfv $TESTDIR/minimal
- export initdir=$TESTDIR/minimal
- mkdir -p $initdir/usr/lib/systemd/system $initdir/usr/lib/extension-release.d $initdir/etc $initdir/var/tmp $initdir/opt
+ oldinitdir="$initdir"
+ rm -rfv "$TESTDIR/minimal"
+ export initdir="$TESTDIR/minimal"
+ mkdir -p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt"
setup_basic_dirs
install_basic_tools
+ # Shellcheck treats [[ -v VAR ]] as an assignment to avoid a different
+ # issue, thus falsely triggering SC2030 in this case
+ # See: koalaman/shellcheck#1409
if [[ -v ASAN_RT_PATH ]]; then
# If we're compiled with ASan, install the ASan RT (and its dependencies)
# into the verity images to get rid of the annoying errors about
@@ -499,71 +547,71 @@ install_verity_minimal() {
inst_libs "$ASAN_RT_PATH"
inst_library "$ASAN_RT_PATH"
fi
- cp $os_release $initdir/usr/lib/os-release
- ln -s ../usr/lib/os-release $initdir/etc/os-release
- touch $initdir/etc/machine-id $initdir/etc/resolv.conf
- touch $initdir/opt/some_file
- echo MARKER=1 >> $initdir/usr/lib/os-release
- echo -e "[Service]\nExecStartPre=cat /usr/lib/os-release\nExecStart=sleep 120" > $initdir/usr/lib/systemd/system/app0.service
- cp $initdir/usr/lib/systemd/system/app0.service $initdir/usr/lib/systemd/system/app0-foo.service
-
- mksquashfs $initdir $oldinitdir/usr/share/minimal_0.raw
- veritysetup format $oldinitdir/usr/share/minimal_0.raw $oldinitdir/usr/share/minimal_0.verity | \
- grep '^Root hash:' | cut -f2 | tr -d '\n' > $oldinitdir/usr/share/minimal_0.roothash
-
- sed -i "s/MARKER=1/MARKER=2/g" $initdir/usr/lib/os-release
- rm $initdir/usr/lib/systemd/system/app0-foo.service
- cp $initdir/usr/lib/systemd/system/app0.service $initdir/usr/lib/systemd/system/app0-bar.service
-
- mksquashfs $initdir $oldinitdir/usr/share/minimal_1.raw
- veritysetup format $oldinitdir/usr/share/minimal_1.raw $oldinitdir/usr/share/minimal_1.verity | \
- grep '^Root hash:' | cut -f2 | tr -d '\n' > $oldinitdir/usr/share/minimal_1.roothash
+ cp "$os_release" "$initdir/usr/lib/os-release"
+ ln -s ../usr/lib/os-release "$initdir/etc/os-release"
+ touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf"
+ touch "$initdir/opt/some_file"
+ echo MARKER=1 >>"$initdir/usr/lib/os-release"
+ echo -e "[Service]\nExecStartPre=cat /usr/lib/os-release\nExecStart=sleep 120" >"$initdir/usr/lib/systemd/system/app0.service"
+ cp "$initdir/usr/lib/systemd/system/app0.service" "$initdir/usr/lib/systemd/system/app0-foo.service"
+
+ mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_0.raw"
+ veritysetup format "$oldinitdir/usr/share/minimal_0.raw" "$oldinitdir/usr/share/minimal_0.verity" | \
+ grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_0.roothash"
+
+ sed -i "s/MARKER=1/MARKER=2/g" "$initdir/usr/lib/os-release"
+ rm "$initdir/usr/lib/systemd/system/app0-foo.service"
+ cp "$initdir/usr/lib/systemd/system/app0.service" "$initdir/usr/lib/systemd/system/app0-bar.service"
+
+ mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw"
+ veritysetup format "$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \
+ grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash"
# Rolling distros like Arch do not set VERSION_ID
local version_id=""
- if grep -q "^VERSION_ID=" $os_release; then
- version_id="$(grep "^VERSION_ID=" $os_release)"
+ if grep -q "^VERSION_ID=" "$os_release"; then
+ version_id="$(grep "^VERSION_ID=" "$os_release")"
fi
- export initdir=$TESTDIR/app0
- mkdir -p $initdir/usr/lib/extension-release.d $initdir/usr/lib/systemd/system $initdir/opt
- grep "^ID=" $os_release > $initdir/usr/lib/extension-release.d/extension-release.app0
- echo "${version_id}" >> $initdir/usr/lib/extension-release.d/extension-release.app0
- cat <<EOF > $initdir/usr/lib/systemd/system/app0.service
+ export initdir="$TESTDIR/app0"
+ mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
+ grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0"
+ echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app0"
+ cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/script0.sh
EOF
- cat <<EOF > $initdir/opt/script0.sh
+ cat >"$initdir/opt/script0.sh" <<EOF
#!/bin/bash
set -e
test -e /usr/lib/os-release
cat /usr/lib/extension-release.d/extension-release.app0
EOF
- chmod +x $initdir/opt/script0.sh
- echo MARKER=1 > $initdir/usr/lib/systemd/system/some_file
- mksquashfs $initdir $oldinitdir/usr/share/app0.raw
-
- export initdir=$TESTDIR/app1
- mkdir -p $initdir/usr/lib/extension-release.d $initdir/usr/lib/systemd/system $initdir/opt
- grep "^ID=" $os_release > $initdir/usr/lib/extension-release.d/extension-release.app1
- echo "${version_id}" >> $initdir/usr/lib/extension-release.d/extension-release.app1
- cat <<EOF > $initdir/usr/lib/systemd/system/app1.service
+ chmod +x "$initdir/opt/script0.sh"
+ echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw"
+
+ export initdir="$TESTDIR/app1"
+ mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
+ grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app1"
+ echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app1"
+ cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/script1.sh
EOF
- cat <<EOF > $initdir/opt/script1.sh
+ cat >"$initdir/opt/script1.sh" <<EOF
#!/bin/bash
set -e
test -e /usr/lib/os-release
cat /usr/lib/extension-release.d/extension-release.app1
EOF
- chmod +x $initdir/opt/script1.sh
- echo MARKER=1 > $initdir/usr/lib/systemd/system/other_file
- mksquashfs $initdir $oldinitdir/usr/share/app1.raw
+ chmod +x "$initdir/opt/script1.sh"
+ echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw"
)
}
@@ -603,26 +651,29 @@ setup_basic_environment() {
}
setup_selinux() {
+ dinfo "Setup SELinux"
# don't forget KERNEL_APPEND='... selinux=1 ...'
if [[ "$SETUP_SELINUX" != "yes" ]]; then
- ddebug "Don't setup SELinux"
+ dinfo "SETUP_SELINUX != yes, skipping SELinux configuration"
return 0
fi
- ddebug "Setup SELinux"
- local _conf_dir=/etc/selinux
- local _fixfiles_tools="bash uname cat sort uniq awk grep egrep head expr find rm secon setfiles"
- rm -rf $initdir/$_conf_dir
- if ! cp -ar $_conf_dir $initdir/$_conf_dir; then
- dfatal "Failed to copy $_conf_dir"
+ local conf_dir=/etc/selinux
+ local fixfiles_tools=(bash uname cat sort uniq awk grep egrep head expr find rm secon setfiles)
+
+ # Make sure the following statement can't expand to "/" to prevent
+ # a potential where-are-my-backups situation
+ rm -rf "${initdir:?}/$conf_dir"
+ if ! cp -ar "$conf_dir" "$initdir/$conf_dir"; then
+ dfatal "Failed to copy $conf_dir"
exit 1
fi
- touch $initdir/.autorelabel
- mkdir -p $initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants
- ln -sf ../autorelabel.service $initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/
+ touch "$initdir/.autorelabel"
+ mkdir -p "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants"
+ ln -sf ../autorelabel.service "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/"
- dracut_install $_fixfiles_tools
+ dracut_install "${fixfiles_tools[@]}"
dracut_install fixfiles
dracut_install sestatus
}
@@ -633,35 +684,36 @@ install_valgrind() {
exit 1
fi
- local _valgrind_bins=$(strace -e execve valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if /^execve\("([^"]+)"/')
- dracut_install $_valgrind_bins
+ local valgrind_bins valgrind_libs valgrind_dbg_and_supp
- local _valgrind_libs=$(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if m{calling init: (/.*vgpreload_.*)}')
- dracut_install $_valgrind_libs
+ valgrind_bins="$(strace -e execve valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if /^execve\("([^"]+)"/')"
+ dracut_install "$valgrind_bins"
- local _valgrind_dbg_and_supp=$(
+ valgrind_libs="$(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if m{calling init: (/.*vgpreload_.*)}')"
+ dracut_install "$valgrind_libs"
+
+ valgrind_dbg_and_supp="$(
strace -e open valgrind /bin/true 2>&1 >/dev/null |
perl -lne 'if (my ($fname) = /^open\("([^"]+).*= (?!-)\d+/) { print $fname if $fname =~ /debug|\.supp$/ }'
- )
- dracut_install $_valgrind_dbg_and_supp
+ )"
+ dracut_install "$valgrind_dbg_and_supp"
}
create_valgrind_wrapper() {
- local _valgrind_wrapper=$initdir/$ROOTLIBDIR/systemd-under-valgrind
- ddebug "Create $_valgrind_wrapper"
- cat >$_valgrind_wrapper <<EOF
+ local valgrind_wrapper="$initdir/$ROOTLIBDIR/systemd-under-valgrind"
+ ddebug "Create $valgrind_wrapper"
+ cat >"$valgrind_wrapper" <<EOF
#!/usr/bin/env bash
mount -t proc proc /proc
exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
EOF
- chmod 0755 $_valgrind_wrapper
+ chmod 0755 "$valgrind_wrapper"
}
create_asan_wrapper() {
- local _asan_wrapper=$initdir/$ROOTLIBDIR/systemd-under-asan
- local _asan_rt_pattern
- ddebug "Create $_asan_wrapper"
+ local asan_wrapper="$initdir/$ROOTLIBDIR/systemd-under-asan"
+ dinfo "Create ASan wrapper as '$asan_wrapper'"
[[ -z "$ASAN_RT_PATH" ]] && dfatal "ASAN_RT_PATH is empty, but it shouldn't be"
@@ -669,7 +721,7 @@ create_asan_wrapper() {
# See: https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
[[ "$ASAN_COMPILER" == "clang" ]] && dracut_install "llvm-symbolizer"
- cat >$_asan_wrapper <<EOF
+ cat >"$asan_wrapper" <<EOF
#!/usr/bin/env bash
set -x
@@ -700,7 +752,7 @@ if [[ "$ASAN_COMPILER" == "clang" ]]; then
# Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
# unnecessary for gcc & libasan, however, for clang this is crucial, as its
# runtime ASan DSO is in a non-standard (library) path.
- echo "${ASAN_RT_PATH%/*}" > /etc/ld.so.conf.d/asan-path-override.conf
+ echo "${ASAN_RT_PATH%/*}" >/etc/ld.so.conf.d/asan-path-override.conf
ldconfig
fi
echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
@@ -741,7 +793,7 @@ printf "[Service]\nEnvironment=ASAN_OPTIONS=leak_check_at_exit=false\n" >/etc/sy
# they're uninstrumented (like dmsetup). Let's add a simple rule which sets
# LD_PRELOAD to the ASan RT library to fix this.
mkdir -p /etc/udev/rules.d
-cat > /etc/udev/rules.d/00-set-LD_PRELOAD.rules << INNER_EOF
+cat >/etc/udev/rules.d/00-set-LD_PRELOAD.rules <<INNER_EOF
SUBSYSTEM=="block", ENV{LD_PRELOAD}="$ASAN_RT_PATH"
INNER_EOF
chmod 0644 /etc/udev/rules.d/00-set-LD_PRELOAD.rules
@@ -764,24 +816,25 @@ unset_ld_preload systemd-remount-fs
unset_ld_preload testsuite-
export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
-exec $ROOTLIBDIR/systemd "\$@"
+exec "$ROOTLIBDIR/systemd" "\$@"
EOF
- chmod 0755 $_asan_wrapper
+ chmod 0755 "$asan_wrapper"
}
create_strace_wrapper() {
- local _strace_wrapper=$initdir/$ROOTLIBDIR/systemd-under-strace
- ddebug "Create $_strace_wrapper"
- cat >$_strace_wrapper <<EOF
+ local strace_wrapper="$initdir/$ROOTLIBDIR/systemd-under-strace"
+ ddebug "Create $strace_wrapper"
+ cat >"$strace_wrapper" <<EOF
#!/usr/bin/env bash
-exec strace -f -D -o /strace.out $ROOTLIBDIR/systemd "\$@"
+exec strace -f -D -o /strace.out "$ROOTLIBDIR/systemd" "\$@"
EOF
- chmod 0755 $_strace_wrapper
+ chmod 0755 "$strace_wrapper"
}
install_fsck() {
+ dinfo "Install fsck"
dracut_install /sbin/fsck*
dracut_install -o /bin/fsck*
@@ -806,34 +859,35 @@ install_dmevent() {
}
install_compiled_systemd() {
- ddebug "Install compiled systemd"
+ dinfo "Install compiled systemd"
- local _ninja_bin=$(type -P ninja || type -P ninja-build)
- if [[ -z "$_ninja_bin" ]]; then
+ local ninja_bin
+ ninja_bin="$(type -P ninja || type -P ninja-build)"
+ if [[ -z "$ninja_bin" ]]; then
dfatal "ninja was not found"
exit 1
fi
- (set -x; DESTDIR=$initdir "$_ninja_bin" -C $BUILD_DIR install)
+ (set -x; DESTDIR="$initdir" "$ninja_bin" -C "$BUILD_DIR" install)
}
install_debian_systemd() {
- ddebug "Install debian systemd"
+ dinfo "Install debian systemd"
- local _systemd_pkgs=$(grep -E '^Package:' ${SOURCE_DIR}/debian/control | cut -d ':' -f 2)
- local _files=""
- for deb in $_systemd_pkgs; do
- _files=$(dpkg-query -L $deb 2>/dev/null) || continue
+ local files
+
+ while read -r deb; do
+ files="$(dpkg-query -L "$deb" 2>/dev/null)" || continue
ddebug "Install debian files from package $deb"
- for file in $_files; do
+ for file in $files; do
[ -e "$file" ] || continue
[ -d "$file" ] && continue
- inst $file
+ inst "$file"
done
- done
+ done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2)
}
install_distro_systemd() {
- ddebug "Install distro systemd"
+ dinfo "Install distro systemd"
if [ "$LOOKS_LIKE_DEBIAN" ]; then
install_debian_systemd
@@ -844,6 +898,7 @@ install_distro_systemd() {
}
install_systemd() {
+ dinfo "Install systemd"
if [ "$NO_BUILD" ]; then
install_distro_systemd
else
@@ -851,39 +906,41 @@ install_systemd() {
fi
# remove unneeded documentation
- rm -fr $initdir/usr/share/{man,doc}
+ rm -fr "$initdir"/usr/share/{man,doc}
[[ "$LOOKS_LIKE_SUSE" ]] && setup_suse
# enable debug logging in PID1
- echo LogLevel=debug >> $initdir/etc/systemd/system.conf
+ echo LogLevel=debug >>"$initdir/etc/systemd/system.conf"
# store coredumps in journal
- echo Storage=journal >> $initdir/etc/systemd/coredump.conf
+ echo Storage=journal >>"$initdir/etc/systemd/coredump.conf"
}
get_ldpath() {
- local _bin="$1"
- local rpath=$(objdump -p "$_bin" 2>/dev/null | awk "/R(UN)?PATH/ { print \"$initdir\" \$2 }" | paste -sd :)
+ local rpath
+ rpath="$(objdump -p "${1:?}" 2>/dev/null | awk "/R(UN)?PATH/ { print \"$initdir\" \$2 }" | paste -sd :)"
if [ -z "$rpath" ] ; then
- echo $BUILD_DIR
+ echo "$BUILD_DIR"
else
- echo $rpath
+ echo "$rpath"
fi
}
install_missing_libraries() {
+ dinfo "Install missing libraries"
# install possible missing libraries
- for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
- LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath $i)" inst_libs $i
+ for i in "${initdir:?}"{,/usr}/{sbin,bin}/* "$initdir"{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do
+ LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath "$i")" inst_libs "$i"
done
+ local lib path
# A number of dependencies is now optional via dlopen, so the install
# script will not pick them up, since it looks at linkage.
for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu libfido2; do
ddebug "Searching for $lib via pkg-config"
- if pkg-config --exists ${lib}; then
- path=$(pkg-config --variable=libdir ${lib})
+ if pkg-config --exists "$lib"; then
+ path="$(pkg-config --variable=libdir "$lib")"
if [ -z "${path}" ]; then
ddebug "$lib.pc does not contain a libdir variable, skipping"
continue
@@ -904,7 +961,7 @@ install_missing_libraries() {
}
cleanup_loopdev() {
- if [ -n "${LOOPDEV}" ]; then
+ if [ -n "${LOOPDEV:=}" ]; then
ddebug "losetup -d $LOOPDEV"
losetup -d "${LOOPDEV}"
unset LOOPDEV
@@ -914,66 +971,65 @@ cleanup_loopdev() {
trap cleanup_loopdev EXIT INT QUIT PIPE
create_empty_image() {
- if [ -z "$IMAGE_NAME" ]; then
+ if [ -z "${IMAGE_NAME:=}" ]; then
echo "create_empty_image: \$IMAGE_NAME not set"
exit 1
fi
- local _size=500
+ local size=500
if [[ "$STRIP_BINARIES" = "no" ]]; then
- _size=$((4*_size))
+ size=$((4 * size))
fi
- echo "Setting up $IMAGE_PUBLIC (${_size} MB)"
- rm -f "$IMAGE_PRIVATE" "$IMAGE_PUBLIC"
+ echo "Setting up ${IMAGE_PUBLIC:?} (${size} MB)"
+ rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC"
# Create the blank file to use as a root filesystem
- truncate -s "${_size}M" "$IMAGE_PUBLIC"
+ truncate -s "${size}M" "$IMAGE_PUBLIC"
LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC")
[ -b "$LOOPDEV" ] || return 1
sfdisk "$LOOPDEV" <<EOF
-,$((_size-50))M
+,$((size - 50))M
,
EOF
udevadm settle
- local _label="-L systemd.${name}"
+ local label=(-L systemd)
# mkfs.reiserfs doesn't know -L. so, use --label instead
- [[ "$FSTYPE" == "reiserfs" ]] && _label="--label systemd.${name}"
- mkfs -t "${FSTYPE}" ${_label} "${LOOPDEV}p1" -q; ret=$?
- if [ $ret -ne 0 ] ; then
+ [[ "$FSTYPE" == "reiserfs" ]] && label=(--label systemd)
+ if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p1" -q; then
dfatal "Failed to mkfs -t ${FSTYPE}"
exit 1
fi
}
mount_initdir() {
- if [ -z "${LOOPDEV}" ]; then
- [ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC"
- LOOPDEV=$(losetup --show -P -f "$image")
+ if [ -z "${LOOPDEV:=}" ]; then
+ [ -e "${IMAGE_PRIVATE:?}" ] && image="$IMAGE_PRIVATE" || image="${IMAGE_PUBLIC:?}"
+ LOOPDEV="$(losetup --show -P -f "$image")"
[ -b "$LOOPDEV" ] || return 1
udevadm settle
fi
- if ! mountpoint -q $initdir; then
- mkdir -p $initdir
- mount ${LOOPDEV}p1 $initdir
+ if ! mountpoint -q "${initdir:?}"; then
+ mkdir -p "$initdir"
+ mount "${LOOPDEV}p1" "$initdir"
TEST_SETUP_CLEANUP_ROOTDIR=1
fi
}
cleanup_initdir() {
# only umount if create_empty_image_rootdir() was called to mount it
- [[ -z $TEST_SETUP_CLEANUP_ROOTDIR ]] || _umount_dir $initdir
+ [[ -z $TEST_SETUP_CLEANUP_ROOTDIR ]] || _umount_dir "${initdir:?}"
}
umount_loopback() {
# unmount the loopback device from all places. Otherwise we risk file
# system corruption.
- for device in $(losetup -l | awk '$6=="'"$IMAGE_PUBLIC"'" {print $1}'); do
+ for device in $(losetup -l | awk '$6=="'"${IMAGE_PUBLIC:?}"'" {print $1}'); do
ddebug "Unmounting all uses of $device"
mount | awk '/^'"${device}"'p/{print $1}' | xargs --no-run-if-empty umount -v
done
@@ -986,23 +1042,23 @@ create_empty_image_rootdir() {
check_asan_reports() {
local ret=0
- local root="$1"
+ local root="${1:?}"
if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then
ls -l "$root"
if [[ -e "$root/systemd.asan.log.1" ]]; then
cat "$root/systemd.asan.log.1"
- ret=$(($ret+1))
+ ret=$((ret+1))
fi
- journald_report=$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;)
- if [[ ! -z "$journald_report" ]]; then
+ journald_report="$(find "$root" -name "systemd-journald.*san.log*" -exec cat {} \;)"
+ if [[ -n "$journald_report" ]]; then
printf "%s\n" "$journald_report"
cat "$root/systemd-journald.out" || :
- ret=$(($ret+1))
+ ret=$((ret+1))
fi
- pids=$(
+ pids="$(
"$JOURNALCTL" -D "$root/var/log/journal" | perl -alne '
BEGIN {
%services_to_ignore = (
@@ -1010,11 +1066,11 @@ check_asan_reports() {
);
}
print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}'
- )
- if [[ ! -z "$pids" ]]; then
- ret=$(($ret+1))
+ )"
+ if [[ -n "$pids" ]]; then
+ ret=$((ret+1))
for pid in $pids; do
- "$JOURNALCTL" -D "$root/var/log/journal" _PID=$pid --no-pager
+ "$JOURNALCTL" -D "$root/var/log/journal" _PID="$pid" --no-pager
done
fi
fi
@@ -1024,44 +1080,45 @@ check_asan_reports() {
save_journal() {
if [ -n "${ARTIFACT_DIRECTORY}" ]; then
- dest="${ARTIFACT_DIRECTORY}/${testname}.journal"
+ dest="${ARTIFACT_DIRECTORY}/${testname:?}.journal"
else
- dest="$TESTDIR/system.journal"
+ dest="${TESTDIR:?}/system.journal"
fi
- for j in $1/*; do
- $SYSTEMD_JOURNAL_REMOTE \
- -o $dest \
+ for j in "${1:?}"/*; do
+ "$SYSTEMD_JOURNAL_REMOTE" \
+ -o "$dest" \
--getter="$JOURNALCTL -o export -D $j"
if [ -n "${TEST_SHOW_JOURNAL}" ]; then
echo "---- $j ----"
- $JOURNALCTL --no-pager -o short-monotonic --no-hostname --priority=${TEST_SHOW_JOURNAL} -D $j
+ "$JOURNALCTL" --no-pager -o short-monotonic --no-hostname --priority="${TEST_SHOW_JOURNAL}" -D "$j"
fi
- rm -r $j
+ rm -r "$j"
done
# we want to print this sometime later, so save this in a variable
- JOURNAL_LIST="$(ls -l $dest*)"
+ JOURNAL_LIST="$(ls -l "$dest"*)"
}
check_result_nspawn() {
+ local workspace="${1:?}"
local ret=1
local journald_report=""
local pids=""
- [[ -e $1/testok ]] && ret=0
- [[ -f $1/failed ]] && cp -a $1/failed $TESTDIR
- save_journal $1/var/log/journal
- [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
- echo $JOURNAL_LIST
- test -s $TESTDIR/failed && ret=$(($ret+1))
- [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
- check_asan_reports "$1" || ret=$(($ret+1))
- if [ -d "${ARTIFACT_DIRECTORY}" ] && [ -f $1/strace.out ]; then
- cp $1/strace.out "${ARTIFACT_DIRECTORY}/"
- fi
- _umount_dir $initdir
+ [[ -e "$workspace/testok" ]] && ret=0
+ [[ -f "$workspace/failed" ]] && cp -a "$workspace/failed" "${TESTDIR:?}"
+ save_journal "$workspace/var/log/journal"
+ [[ -f "$TESTDIR/failed" ]] && cat "$TESTDIR/failed"
+ echo "${JOURNAL_LIST:-"No journals were saved"}"
+ test -s "$TESTDIR/failed" && ret=$((ret+1))
+ [ -n "$TIMED_OUT" ] && ret=$((ret+1))
+ check_asan_reports "$workspace" || ret=$((ret+1))
+ if [ -d "${ARTIFACT_DIRECTORY}" ] && [ -f "$workspace/strace.out" ]; then
+ cp "$workspace/strace.out" "${ARTIFACT_DIRECTORY}/"
+ fi
+ _umount_dir "${initdir:?}"
return $ret
}
@@ -1069,100 +1126,106 @@ check_result_nspawn() {
check_result_qemu() {
local ret=1
mount_initdir
- [[ -e $initdir/testok ]] && ret=0
- [[ -f $initdir/failed ]] && cp -a $initdir/failed $TESTDIR
- save_journal $initdir/var/log/journal
- check_asan_reports "$initdir" || ret=$(($ret+1))
- if [ -d "${ARTIFACT_DIRECTORY}" ] && [ -f $initdir/strace.out ]; then
- cp $initdir/strace.out "${ARTIFACT_DIRECTORY}/"
- fi
- _umount_dir $initdir
- [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
- echo $JOURNAL_LIST
- test -s $TESTDIR/failed && ret=$(($ret+1))
- [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
+ [[ -e "${initdir:?}/testok" ]] && ret=0
+ [[ -f "$initdir/failed" ]] && cp -a "$initdir/failed" "${TESTDIR:?}"
+ save_journal "$initdir/var/log/journal"
+ check_asan_reports "$initdir" || ret=$((ret+1))
+ if [ -d "${ARTIFACT_DIRECTORY}" ] && [ -f "$initdir/strace.out" ]; then
+ cp "$initdir/strace.out" "${ARTIFACT_DIRECTORY}/"
+ fi
+ _umount_dir "$initdir"
+ [[ -f "$TESTDIR/failed" ]] && cat "$TESTDIR/failed"
+ echo "${JOURNAL_LIST:-"No journals were saved"}"
+ test -s "$TESTDIR/failed" && ret=$((ret+1))
+ [ -n "$TIMED_OUT" ] && ret=$((ret+1))
return $ret
}
strip_binaries() {
+ dinfo "Strip binaries"
if [[ "$STRIP_BINARIES" = "no" ]]; then
- ddebug "Don't strip binaries"
+ dinfo "STRIP_BINARIES == no, keeping binaries unstripped"
return 0
fi
- ddebug "Strip binaries"
- find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | \
- xargs strip --strip-unneeded |& \
- grep -vi 'file format not recognized' | \
- ddebug
+ while read -r bin; do
+ strip --strip-unneeded "$bin" |& grep -vi 'file format not recognized' | ddebug || :
+ done < <(find "${initdir:?}" -executable -not -path '*/lib/modules/*.ko' -type f)
}
create_rc_local() {
- mkdir -p $initdir/etc/rc.d
- cat >$initdir/etc/rc.d/rc.local <<EOF
+ dinfo "Create rc.local"
+ mkdir -p "${initdir:?}/etc/rc.d"
+ cat >"$initdir/etc/rc.d/rc.local" <<EOF
#!/usr/bin/env bash
exit 0
EOF
- chmod 0755 $initdir/etc/rc.d/rc.local
+ chmod 0755 "$initdir/etc/rc.d/rc.local"
}
install_execs() {
- ddebug "install any Execs from the service files"
- (
- export PKG_CONFIG_PATH=$BUILD_DIR/src/core/
- systemdsystemunitdir=$(pkg-config --variable=systemdsystemunitdir systemd)
- systemduserunitdir=$(pkg-config --variable=systemduserunitdir systemd)
- sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' $initdir/{$systemdsystemunitdir,$systemduserunitdir}/*.service \
- | sort -u | while read i; do
- # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
- # also, plymouth is pulled in by rescue.service, but even there the exit code
- # is ignored; as it's not present on some distros, don't fail if it doesn't exist
- dinfo "Attempting to install $i (based on unit file reference)"
- inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "${i##*/}" == "plymouth" ]
- done
- )
+ ddebug "Install executables from the service files"
+
+ local pkg_config_path="${BUILD_DIR:?}/src/core/"
+ local systemunitdir userunitdir exe
+ systemunitdir="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemdsystemunitdir systemd)"
+ userunitdir="$(PKG_CONFIG_PATH="$pkg_config_path" pkg-config --variable=systemduserunitdir systemd)"
+ while read -r exe; do
+ # some {rc,halt}.local scripts and programs are okay to not exist, the rest should
+ # also, plymouth is pulled in by rescue.service, but even there the exit code
+ # is ignored; as it's not present on some distros, don't fail if it doesn't exist
+ dinfo "Attempting to install $exe (based on unit file reference)"
+ inst "$exe" || [ "${exe%.local}" != "$exe" ] || [ "${exe%systemd-update-done}" != "$exe" ] || [ "${exe##*/}" == "plymouth" ]
+ done < <(sed -r -n 's|^Exec[a-zA-Z]*=[@+!-]*([^ ]+).*|\1|gp' "${initdir:?}"/{"$systemunitdir","$userunitdir"}/*.service | sort -u)
}
generate_module_dependencies() {
- if [[ -d $initdir/lib/modules/$KERNEL_VER ]] && \
- ! depmod -a -b "$initdir" $KERNEL_VER; then
+ dinfo "Generate modules dependencies"
+ if [[ -d "${initdir:?}/lib/modules/${KERNEL_VER:?}" ]] && \
+ ! depmod -a -b "$initdir" "$KERNEL_VER"; then
dfatal "\"depmod -a $KERNEL_VER\" failed."
exit 1
fi
}
install_depmod_files() {
- inst /lib/modules/$KERNEL_VER/modules.order
- inst /lib/modules/$KERNEL_VER/modules.builtin
+ dinfo "Install depmod files"
+ inst "/lib/modules/${KERNEL_VER:?}/modules.order"
+ inst "/lib/modules/$KERNEL_VER/modules.builtin"
}
install_plymouth() {
+ dinfo "Install plymouth"
# install plymouth, if found... else remove plymouth service files
# if [ -x /usr/libexec/plymouth/plymouth-populate-initrd ]; then
# PLYMOUTH_POPULATE_SOURCE_FUNCTIONS="$TEST_BASE_DIR/test-functions" \
# /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
# dracut_install plymouth plymouthd
# else
- rm -f $initdir/{usr/lib,lib,etc}/systemd/system/plymouth* $initdir/{usr/lib,lib,etc}/systemd/system/*/plymouth*
+ rm -f "${initdir:?}"/{usr/lib,lib,etc}/systemd/system/plymouth* "$initdir"/{usr/lib,lib,etc}/systemd/system/*/plymouth*
# fi
}
install_ld_so_conf() {
- cp -a /etc/ld.so.conf* $initdir/etc
+ dinfo "Install /etc/ld.so.conf*"
+ cp -a /etc/ld.so.conf* "${initdir:?}/etc"
ldconfig -r "$initdir"
}
install_testuser() {
+ dinfo "Set up a test user"
# create unprivileged user for user manager tests
- mkdir -p $initdir/etc/sysusers.d
- cat >$initdir/etc/sysusers.d/testuser.conf <<EOF
+ mkdir -p "${initdir:?}/etc/sysusers.d"
+ cat >"$initdir/etc/sysusers.d/testuser.conf" <<EOF
u testuser 4711 "Test User" /home/testuser
EOF
- mkdir -p $initdir/home/testuser -m 0700
- chown 4711:4711 $initdir/home/testuser
+ mkdir -p "$initdir/home/testuser"
+ chmod 0700 "$initdir/home/testuser"
+ chown 4711:4711 "$initdir/home/testuser"
}
install_config_files() {
+ dinfo "Install config files"
inst /etc/sysconfig/init || :
inst /etc/passwd
inst /etc/shadow
@@ -1174,21 +1237,22 @@ install_config_files() {
inst_any /etc/os-release /usr/lib/os-release
inst /etc/localtime
# we want an empty environment
- > $initdir/etc/environment
- > $initdir/etc/machine-id
- > $initdir/etc/resolv.conf
+ : >"${initdir:?}/etc/environment"
+ : >"$initdir/etc/machine-id"
+ : >"$initdir/etc/resolv.conf"
# set the hostname
- echo systemd-testsuite > $initdir/etc/hostname
+ echo systemd-testsuite >"$initdir/etc/hostname"
# let's set up just one image with the traditional verbose output
- if [ ${IMAGE_NAME} != "basic" ]; then
- mkdir -p $initdir/etc/systemd/system.conf.d
- echo -e '[Manager]\nStatusUnitFormat=name' >$initdir/etc/systemd/system.conf.d/status.conf
+ if [ "${IMAGE_NAME:?}" != "basic" ]; then
+ mkdir -p "$initdir/etc/systemd/system.conf.d"
+ echo -e '[Manager]\nStatusUnitFormat=name' >"$initdir/etc/systemd/system.conf.d/status.conf"
fi
}
install_basic_tools() {
+ dinfo "Install basic tools"
dracut_install "${BASICTOOLS[@]}"
dracut_install -o sushell
# in Debian ldconfig is just a shell script wrapper around ldconfig.real
@@ -1196,15 +1260,16 @@ install_basic_tools() {
}
install_debug_tools() {
+ dinfo "Install debug tools"
dracut_install "${DEBUGTOOLS[@]}"
if [[ $INTERACTIVE_DEBUG ]]; then
# Set default TERM from vt220 to linux, so at least basic key shortcuts work
- local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d"
- mkdir -p "$_getty_override"
- echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf"
+ local getty_override="${initdir:?}/etc/systemd/system/serial-getty@.service.d"
+ mkdir -p "$getty_override"
+ echo -e "[Service]\nEnvironment=TERM=linux" >"$getty_override/default-TERM.conf"
- cat > "$initdir/etc/motd" << EOF
+ cat >"$initdir/etc/motd" <<EOF
To adjust the terminal size use:
export COLUMNS=xx
export LINES=yy
@@ -1215,38 +1280,39 @@ EOF
}
install_libnss() {
+ dinfo "Install libnss"
# install libnss_files for login
- NSS_LIBS=$(LD_DEBUG=files getent passwd 2>&1 >/dev/null |sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
- dracut_install $NSS_LIBS
+ local NSS_LIBS
+ mapfile -t NSS_LIBS < <(LD_DEBUG=files getent passwd 2>&1 >/dev/null | sed -n '/calling init: .*libnss_/ {s!^.* /!/!; p}')
+ dracut_install "${NSS_LIBS[@]}"
}
install_dbus() {
- inst $ROOTLIBDIR/system/dbus.socket
+ dinfo "Install dbus"
+ inst "${ROOTLIBDIR:?}/system/dbus.socket"
# Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
- if [ -f $ROOTLIBDIR/system/dbus-broker.service ]; then
- inst $ROOTLIBDIR/system/dbus-broker.service
+ if [ -f "$ROOTLIBDIR/system/dbus-broker.service" ]; then
+ inst "$ROOTLIBDIR/system/dbus-broker.service"
inst_symlink /etc/systemd/system/dbus.service
inst /usr/bin/dbus-broker
inst /usr/bin/dbus-broker-launch
- elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
+ elif [ -f "$ROOTLIBDIR/system/dbus-daemon.service" ]; then
# Fedora rawhide replaced dbus.service with dbus-daemon.service
- inst $ROOTLIBDIR/system/dbus-daemon.service
+ inst "$ROOTLIBDIR/system/dbus-daemon.service"
# Alias symlink
inst_symlink /etc/systemd/system/dbus.service
else
- inst $ROOTLIBDIR/system/dbus.service
+ inst "$ROOTLIBDIR/system/dbus.service"
fi
- find \
- /etc/dbus-1 /usr/share/dbus-1 -xtype f \
- | while read file; do
- inst $file
- done
+ while read -r file; do
+ inst "$file"
+ done < <(find /etc/dbus-1 /usr/share/dbus-1 -xtype f 2>/dev/null)
# setup policy for Type=dbus test
- mkdir -p $initdir/etc/dbus-1/system.d
- cat > $initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf <<EOF
+ mkdir -p "${initdir:?}/etc/dbus-1/system.d"
+ cat >"$initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf" <<EOF
<?xml version="1.0"?>
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
@@ -1259,59 +1325,65 @@ EOF
}
install_user_dbus() {
+ dinfo "Install user dbus"
local userunitdir
- if ! userunitdir=$(pkg-config --variable=systemduserunitdir systemd); then
- echo "WARNING! Cannot determine userunitdir from pkg-config, assuming /usr/lib/systemd/user" >&2
- local userunitdir=/usr/lib/systemd/user
+ if ! userunitdir="$(pkg-config --variable=systemduserunitdir systemd)"; then
+ dwarn "WARNING! Cannot determine userunitdir from pkg-config, assuming /usr/lib/systemd/user"
+ userunitdir=/usr/lib/systemd/user
fi
- inst $userunitdir/dbus.socket
- inst_symlink $userunitdir/sockets.target.wants/dbus.socket || inst_symlink /etc/systemd/user/sockets.target.wants/dbus.socket
+ inst "$userunitdir/dbus.socket"
+ inst_symlink "$userunitdir/sockets.target.wants/dbus.socket" || inst_symlink /etc/systemd/user/sockets.target.wants/dbus.socket
# Append the After= dependency on dbus in case it isn't already set up
- mkdir -p "$initdir/etc/systemd/system/user@.service.d/"
- cat <<EOF >"$initdir/etc/systemd/system/user@.service.d/dbus.conf"
+ mkdir -p "${initdir:?}/etc/systemd/system/user@.service.d/"
+ cat >"$initdir/etc/systemd/system/user@.service.d/dbus.conf" <<EOF
[Unit]
After=dbus.service
EOF
# Newer Fedora versions use dbus-broker by default. Let's install it if it's available.
- if [ -f $userunitdir/dbus-broker.service ]; then
- inst $userunitdir/dbus-broker.service
+ if [ -f "$userunitdir/dbus-broker.service" ]; then
+ inst "$userunitdir/dbus-broker.service"
inst_symlink /etc/systemd/user/dbus.service
- elif [ -f $ROOTLIBDIR/system/dbus-daemon.service ]; then
+ elif [ -f "${ROOTLIBDIR:?}/system/dbus-daemon.service" ]; then
# Fedora rawhide replaced dbus.service with dbus-daemon.service
- inst $userunitdir/dbus-daemon.service
+ inst "$userunitdir/dbus-daemon.service"
# Alias symlink
inst_symlink /etc/systemd/user/dbus.service
else
- inst $userunitdir/dbus.service
+ inst "$userunitdir/dbus.service"
fi
}
install_pam() {
- (
+ dinfo "Install PAM"
+ local paths=()
+
if [[ "$LOOKS_LIKE_DEBIAN" ]] && type -p dpkg-architecture &>/dev/null; then
- find "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security" -xtype f
+ paths+=("/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/security")
else
- find /lib*/security -xtype f
+ paths+=(/lib*/security)
fi
+
for d in /etc/pam.d /etc/security /usr/lib/pam.d; do
- [ -d "$d" ] && find $d -xtype f
- done
- ) | while read file; do
- inst $file
+ [ -d "$d" ] && paths+=("$d")
done
+ while read -r file; do
+ inst "$file"
+ done < <(find "${paths[@]}" -xtype f)
+
# pam_unix depends on unix_chkpwd.
# see http://www.linux-pam.org/Linux-PAM-html/sag-pam_unix.html
dracut_install -o unix_chkpwd
# set empty root password for easy debugging
- sed -i 's/^root:x:/root::/' $initdir/etc/passwd
+ sed -i 's/^root:x:/root::/' "${initdir:?}/etc/passwd"
}
install_keymaps() {
+ dinfo "Install keymaps"
# The first three paths may be deprecated.
# It seems now the last two paths are used by many distributions.
for i in \
@@ -1320,22 +1392,23 @@ install_keymaps() {
/usr/lib/kbd/keymaps/i386/qwerty/us.* \
/usr/lib/kbd/keymaps/legacy/include/* \
/usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do
- [[ -f $i ]] || continue
- inst $i
+ [[ -f "$i" ]] || continue
+ inst "$i"
done
# When it takes any argument, then install more keymaps.
- if [[ -n $1 ]]; then
+ if [[ $# -gt 1 ]]; then
for i in \
/usr/lib/kbd/keymaps/i386/*/* \
/usr/lib/kbd/keymaps/legacy/i386/*/*; do
- [[ -f $i ]] || continue
- inst $i
+ [[ -f "$i" ]] || continue
+ inst "$i"
done
fi
}
install_zoneinfo() {
+ dinfo "Install time zones"
inst_any /usr/share/zoneinfo/Asia/Seoul
inst_any /usr/share/zoneinfo/Asia/Vladivostok
inst_any /usr/share/zoneinfo/Australia/Sydney
@@ -1350,19 +1423,22 @@ install_zoneinfo() {
}
install_fonts() {
+ dinfo "Install system fonts"
for i in \
/usr/lib/kbd/consolefonts/eurlatgr* \
/usr/lib/kbd/consolefonts/latarcyrheb-sun16*; do
- [[ -f $i ]] || continue
- inst $i
+ [[ -f "$i" ]] || continue
+ inst "$i"
done
}
install_terminfo() {
- for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
- [ -f ${_terminfodir}/l/linux ] && break
+ dinfo "Install terminfo files"
+ local terminfodir
+ for terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f "${terminfodir}/l/linux" ] && break
done
- dracut_install -o ${_terminfodir}/l/linux
+ dracut_install -o "${terminfodir}/l/linux"
}
has_user_dbus_socket() {
@@ -1380,20 +1456,21 @@ setup_nspawn_root() {
exit 1
fi
- rm -rf "$TESTDIR/unprivileged-nspawn-root"
+ rm -rf "${TESTDIR:?}/unprivileged-nspawn-root"
if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
- cp -ar $initdir $TESTDIR/unprivileged-nspawn-root
+ cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root"
fi
}
setup_basic_dirs() {
- mkdir -p $initdir/run
- mkdir -p $initdir/etc/systemd/system
- mkdir -p $initdir/var/log/journal
+ mkdir -p "${initdir:?}/run"
+ mkdir -p "$initdir/etc/systemd/system"
+ mkdir -p "$initdir/var/log/journal"
+
- for d in usr/bin usr/sbin bin etc lib "$libdir" sbin tmp usr var var/log var/tmp dev proc sys sysroot root run run/lock run/initramfs; do
+ for d in usr/bin usr/sbin bin etc lib "${libdir:?}" sbin tmp usr var var/log var/tmp dev proc sys sysroot root run run/lock run/initramfs; do
if [ -L "/$d" ]; then
inst_symlink "/$d"
else
@@ -1407,63 +1484,65 @@ setup_basic_dirs() {
mask_supporting_services() {
# mask some services that we do not want to run in these tests
- ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
- ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
- ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
- ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
- ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null "${initdir:?}/etc/systemd/system/systemd-hwdb-update.service"
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-journal-catalog-update.service"
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-networkd.service"
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-networkd.socket"
+ ln -fs /dev/null "$initdir/etc/systemd/system/systemd-resolved.service"
}
inst_libs() {
- local _bin=$1
- local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
- local _file _line
+ local bin="${1:?}"
+ local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
+ local file line
- LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
- [[ $_line = 'not a dynamic executable' ]] && break
+ while read -r line; do
+ [[ "$line" = 'not a dynamic executable' ]] && break
- if [[ $_line =~ $_so_regex ]]; then
- _file=${BASH_REMATCH[1]}
- [[ -e ${initdir}/$_file ]] && continue
- inst_library "$_file"
+ if [[ "$line" =~ $so_regex ]]; then
+ file="${BASH_REMATCH[1]}"
+ [[ -e "${initdir:?}/$file" ]] && continue
+ inst_library "$file"
continue
fi
- if [[ $_line =~ not\ found ]]; then
- dfatal "Missing a shared library required by $_bin."
- dfatal "Run \"ldd $_bin\" to find out what it is."
- dfatal "$_line"
+ if [[ "$line" =~ not\ found ]]; then
+ dfatal "Missing a shared library required by $bin."
+ dfatal "Run \"ldd $bin\" to find out what it is."
+ dfatal "$line"
dfatal "dracut cannot create an initrd."
exit 1
fi
- done
+ done < <(LC_ALL=C ldd "$bin" 2>/dev/null)
}
import_testdir() {
# make sure we don't get a stale LOOPDEV value from old times
- __LOOPDEV=$LOOPDEV
- [[ -e $STATEFILE ]] && . $STATEFILE
- LOOPDEV=$__LOOPDEV
+ local _LOOPDEV="${LOOPDEV:=}"
+ # We don't want shellcheck to follow & check the $STATEFILE
+ # shellcheck source=/dev/null
+ [[ -e "$STATEFILE" ]] && . "$STATEFILE"
+ LOOPDEV="$_LOOPDEV"
if [[ ! -d "$TESTDIR" ]]; then
if [[ -z "$TESTDIR" ]]; then
- TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
+ TESTDIR="$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)"
else
mkdir -p "$TESTDIR"
fi
- cat >$STATEFILE<<EOF
+ cat >"$STATEFILE" <<EOF
TESTDIR="$TESTDIR"
EOF
export TESTDIR
fi
- IMAGE_PRIVATE="${TESTDIR}/${IMAGE_NAME}.img"
- IMAGE_PUBLIC="${IMAGESTATEDIR}/${IMAGE_NAME}.img"
+ IMAGE_PRIVATE="${TESTDIR}/${IMAGE_NAME:?}.img"
+ IMAGE_PUBLIC="${IMAGESTATEDIR:?}/${IMAGE_NAME}.img"
}
import_initdir() {
- initdir=$TESTDIR/root
- mkdir -p $initdir
+ initdir="${TESTDIR:?}/root"
+ mkdir -p "$initdir"
export initdir
}
@@ -1501,18 +1580,20 @@ _lvl2char() {
# This enables:
# dwarn "This is a warning"
# echo "This is a warning" | dwarn
-LOG_LEVEL=${LOG_LEVEL:-4}
+LOG_LEVEL="${LOG_LEVEL:-4}"
dlog() {
+ local lvl lvlc
+
[ -z "$LOG_LEVEL" ] && return 0
- [ $1 -le $LOG_LEVEL ] || return 0
- local lvl="$1"; shift
- local lvlc=$(_lvl2char "$lvl") || return 0
+ lvl="${1:?}"; shift
+ [ "$lvl" -le "$LOG_LEVEL" ] || return 0
+ lvlc="$(_lvl2char "$lvl")" || return 0
if [ $# -ge 1 ]; then
echo "$lvlc: $*"
else
- while read line; do
+ while read -r line; do
echo "$lvlc: " "$line"
done
fi
@@ -1525,7 +1606,9 @@ dlog() {
dtrace() {
set +x
dlog 6 "$@"
- [ -n "$debug" ] && set -x || :
+ if [[ "${debug:=}" ]]; then
+ set -x
+ fi
}
## @brief Logs message at DEBUG level (5)
@@ -1533,9 +1616,7 @@ dtrace() {
# @param msg Message.
# @retval 0 It's always returned, even if logging failed.
ddebug() {
-# set +x
dlog 5 "$@"
-# [ -n "$debug" ] && set -x || :
}
## @brief Logs message at INFO level (4)
@@ -1545,7 +1626,9 @@ ddebug() {
dinfo() {
set +x
dlog 4 "$@"
- [ -n "$debug" ] && set -x || :
+ if [[ "${debug:=}" ]]; then
+ set -x
+ fi
}
## @brief Logs message at WARN level (3)
@@ -1555,7 +1638,9 @@ dinfo() {
dwarn() {
set +x
dlog 3 "$@"
- [ -n "$debug" ] && set -x || :
+ if [[ "${debug:=}" ]]; then
+ set -x
+ fi
}
## @brief Logs message at ERROR level (2)
@@ -1563,9 +1648,7 @@ dwarn() {
# @param msg Message.
# @retval 0 It's always returned, even if logging failed.
derror() {
-# set +x
dlog 2 "$@"
-# [ -n "$debug" ] && set -x || :
}
## @brief Logs message at FATAL level (1)
@@ -1575,7 +1658,9 @@ derror() {
dfatal() {
set +x
dlog 1 "$@"
- [ -n "$debug" ] && set -x || :
+ if [[ "${debug:=}" ]]; then
+ set -x
+ fi
}
@@ -1605,7 +1690,7 @@ convert_abs_rel() {
local __current __absolute __abssize __cursize __newpath
local -i __i __level
- set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
+ set -- "$(normalize_path "${1:?}")" "$(normalize_path "${2:?}")"
# corner case #1 - self looping link
[[ "$1" == "$2" ]] && { echo "${1##*/}"; return; }
@@ -1613,13 +1698,13 @@ convert_abs_rel() {
# corner case #2 - own dir link
[[ "${1%/*}" == "$2" ]] && { echo "."; return; }
- IFS="/" __current=($1)
- IFS="/" __absolute=($2)
+ IFS="/" read -ra __current <<< "$1"
+ IFS="/" read -ra __absolute <<< "$2"
__abssize=${#__absolute[@]}
__cursize=${#__current[@]}
- while [[ ${__absolute[__level]} == ${__current[__level]} ]]
+ while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]
do
(( __level++ ))
if (( __level > __abssize || __level > __cursize ))
@@ -1655,24 +1740,27 @@ convert_abs_rel() {
# will create ${initdir}/lib64, ${initdir}/lib64/file,
# and a symlink ${initdir}/lib -> lib64.
inst_dir() {
- [[ -e ${initdir}/"$1" ]] && return 0 # already there
+ local dir="${1:?}"
+ local part="${dir%/*}"
+ local file
+
+ [[ -e "${initdir:?}/${dir}" ]] && return 0 # already there
- local _dir="$1" _part="${1%/*}" _file
- while [[ "$_part" != "${_part%/*}" ]] && ! [[ -e "${initdir}/${_part}" ]]; do
- _dir="$_part $_dir"
- _part=${_part%/*}
+ while [[ "$part" != "${part%/*}" ]] && ! [[ -e "${initdir}/${part}" ]]; do
+ dir="$part $dir"
+ part="${part%/*}"
done
# iterate over parent directories
- for _file in $_dir; do
- [[ -e "${initdir}/$_file" ]] && continue
- if [[ -L $_file ]]; then
- inst_symlink "$_file"
+ for file in $dir; do
+ [[ -e "${initdir}/$file" ]] && continue
+ if [[ -L $file ]]; then
+ inst_symlink "$file"
else
# create directory
- mkdir -m 0755 -p "${initdir}/$_file" || return 1
- [[ -e "$_file" ]] && chmod --reference="$_file" "${initdir}/$_file"
- chmod u+w "${initdir}/$_file"
+ mkdir -m 0755 "${initdir}/$file" || return 1
+ [[ -e "$file" ]] && chmod --reference="$file" "${initdir}/$file"
+ chmod u+w "${initdir}/$file"
fi
done
}
@@ -1682,21 +1770,22 @@ inst_dir() {
# Location of the image dir is assumed to be $initdir
# We never overwrite the target if it exists.
inst_simple() {
- [[ -f "$1" ]] || return 1
+ [[ -f "${1:?}" ]] || return 1
strstr "$1" "/" || return 1
- local _src=$1 target="${2:-$1}"
- if ! [[ -d ${initdir}/$target ]]; then
+ local src="$1"
+ local target="${2:-$1}"
+ if ! [[ -d ${initdir:?}/$target ]]; then
[[ -e ${initdir}/$target ]] && return 0
[[ -L ${initdir}/$target ]] && return 0
[[ -d "${initdir}/${target%/*}" ]] || inst_dir "${target%/*}"
fi
# install checksum files also
- if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
- inst "${_src%/*}/.${_src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
+ if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then
+ inst "${src%/*}/.${src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
fi
- ddebug "Installing $_src"
- cp --sparse=always -pfL "$_src" "${initdir}/$target"
+ ddebug "Installing $src"
+ cp --sparse=always -pfL "$src" "${initdir}/$target"
}
# find symlinks linked to given library file
@@ -1710,15 +1799,16 @@ inst_simple() {
# output: libfoo.so.8 libfoo.so
# (Only if libfoo.so.8 and libfoo.so exists on host system.)
rev_lib_symlinks() {
- [[ ! $1 ]] && return 0
+ local fn="${1:?}"
+ local links=""
+ local orig
+ orig="$(readlink -f "$1")"
- local fn="$1" orig="$(readlink -f "$1")" links=''
+ [[ "${fn}" =~ .*\.so\..* ]] || return 1
- [[ ${fn} =~ .*\.so\..* ]] || return 1
-
- until [[ ${fn##*.} == so ]]; do
+ until [[ "${fn##*.}" == so ]]; do
fn="${fn%.*}"
- [[ -L ${fn} && $(readlink -f "${fn}") == ${orig} ]] && links+=" ${fn}"
+ [[ -L "${fn}" && "$(readlink -f "${fn}")" == "${orig}" ]] && links+=" ${fn}"
done
echo "${links}"
@@ -1728,185 +1818,204 @@ rev_lib_symlinks() {
# It handles making symlinks according to how the original library
# is referenced.
inst_library() {
- local _src="$1" _dest=${2:-$1} _lib _reallib _symlink
+ local src="${1:?}"
+ local dest="${2:-$1}"
+ local reallib symlink
+
strstr "$1" "/" || return 1
- [[ -e $initdir/$_dest ]] && return 0
- if [[ -L $_src ]]; then
+ [[ -e ${initdir:?}/$dest ]] && return 0
+ if [[ -L $src ]]; then
# install checksum files also
- if [[ -e "${_src%/*}/.${_src##*/}.hmac" ]]; then
- inst "${_src%/*}/.${_src##*/}.hmac" "${_dest%/*}/.${_dest##*/}.hmac"
+ if [[ -e "${src%/*}/.${src##*/}.hmac" ]]; then
+ inst "${src%/*}/.${src##*/}.hmac" "${dest%/*}/.${dest##*/}.hmac"
fi
- _reallib=$(readlink -f "$_src")
- inst_simple "$_reallib" "$_reallib"
- inst_dir "${_dest%/*}"
- [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
- ln -sfn $(convert_abs_rel "${_dest}" "${_reallib}") "${initdir}/${_dest}"
+ reallib="$(readlink -f "$src")"
+ inst_simple "$reallib" "$reallib"
+ inst_dir "${dest%/*}"
+ [[ -d "${dest%/*}" ]] && dest="$(readlink -f "${dest%/*}")/${dest##*/}"
+ ln -sfn -- "$(convert_abs_rel "${dest}" "${reallib}")" "${initdir}/${dest}"
else
- inst_simple "$_src" "$_dest"
+ inst_simple "$src" "$dest"
fi
# Create additional symlinks. See rev_symlinks description.
- for _symlink in $(rev_lib_symlinks $_src) $(rev_lib_symlinks $_reallib); do
- [[ -e $initdir/$_symlink ]] || {
- ddebug "Creating extra symlink: $_symlink"
- inst_symlink $_symlink
- }
+ for symlink in $(rev_lib_symlinks "$src") ${reallib:+$(rev_lib_symlinks "$reallib")}; do
+ if [[ ! -e "$initdir/$symlink" ]]; then
+ ddebug "Creating extra symlink: $symlink"
+ inst_symlink "$symlink"
+ fi
done
}
# find a binary. If we were not passed the full path directly,
# search in the usual places to find the binary.
find_binary() {
- if [[ -z ${1##/*} ]]; then
- if [[ -x $1 ]] || { strstr "$1" ".so" && ldd $1 &>/dev/null; }; then
- echo $1
+ local bin="${1:?}"
+ if [[ -z ${bin##/*} ]]; then
+ if [[ -x "$bin" ]] || { strstr "$bin" ".so" && ldd "$bin" &>/dev/null; }; then
+ echo "$bin"
return 0
fi
fi
- type -P $1
+ type -P "$bin"
}
# Same as above, but specialized to install binary executables.
# Install binary executable, and all shared library dependencies, if any.
inst_binary() {
- local _bin _target
+ local bin="${1:?}"
+ local path target
# In certain cases we might attempt to install a binary which is already
# present in the test image, yet it's missing from the host system.
# In such cases, let's check if the binary indeed exists in the image
# before doing any other chcecks. If it does, immediately return with
# success.
- [[ $# -eq 1 && -e $initdir/$1 || -e $initdir/bin/$1 || -e $initdir/sbin/$1 || -e $initdir/usr/bin/$1 || -e $initdir/usr/sbin/$1 ]] && return 0
-
- _bin=$(find_binary "$1") || return 1
- _target=${2:-$_bin}
- [[ -e $initdir/$_target ]] && return 0
- [[ -L $_bin ]] && inst_symlink $_bin $_target && return 0
- local _file _line
- local _so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
+ if [[ $# -eq 1 ]]; then
+ for path in "" bin sbin usr/bin usr/sbin; do
+ [[ -e "${initdir:?}${path:+/$path}/${bin}" ]] && return 0
+ done
+ fi
+
+ bin="$(find_binary "$bin")" || return 1
+ target="${2:-$bin}"
+ [[ -e "${initdir:?}/$target" ]] && return 0
+ [[ -L "$bin" ]] && inst_symlink "$bin" "$target" && return 0
+
+ local file line
+ local so_regex='([^ ]*/lib[^/]*/[^ ]*\.so[^ ]*)'
# I love bash!
- LC_ALL=C ldd "$_bin" 2>/dev/null | while read _line; do
- [[ $_line = 'not a dynamic executable' ]] && break
+ while read -r line; do
+ [[ "$line" = 'not a dynamic executable' ]] && break
- if [[ $_line =~ $_so_regex ]]; then
- _file=${BASH_REMATCH[1]}
- [[ -e ${initdir}/$_file ]] && continue
- inst_library "$_file"
+ if [[ "$line" =~ $so_regex ]]; then
+ file="${BASH_REMATCH[1]}"
+ [[ -e "${initdir}/$file" ]] && continue
+ inst_library "$file"
continue
fi
- if [[ $_line =~ not\ found ]]; then
- dfatal "Missing a shared library required by $_bin."
- dfatal "Run \"ldd $_bin\" to find out what it is."
- dfatal "$_line"
+ if [[ "$line" =~ not\ found ]]; then
+ dfatal "Missing a shared library required by $bin."
+ dfatal "Run \"ldd $bin\" to find out what it is."
+ dfatal "$line"
dfatal "dracut cannot create an initrd."
exit 1
fi
- done
- inst_simple "$_bin" "$_target"
+ done < <(LC_ALL=C ldd "$bin" 2>/dev/null)
+ inst_simple "$bin" "$target"
}
# same as above, except for shell scripts.
# If your shell script does not start with shebang, it is not a shell script.
inst_script() {
- local _bin
- _bin=$(find_binary "$1") || return 1
+ local bin line shebang_regex
+ bin="$(find_binary "${1:?}")" || return 1
shift
- local _line _shebang_regex
- read -r -n 80 _line <"$_bin"
+
+ read -r -n 80 line <"$bin"
# If debug is set, clean unprintable chars to prevent messing up the term
- [[ $debug ]] && _line=$(echo -n "$_line" | tr -c -d '[:print:][:space:]')
- _shebang_regex='(#! *)(/[^ ]+).*'
- [[ $_line =~ $_shebang_regex ]] || return 1
- inst "${BASH_REMATCH[2]}" && inst_simple "$_bin" "$@"
+ [[ "${debug:=}" ]] && line="$(echo -n "$line" | tr -c -d '[:print:][:space:]')"
+ shebang_regex='(#! *)(/[^ ]+).*'
+ [[ "$line" =~ $shebang_regex ]] || return 1
+ inst "${BASH_REMATCH[2]}" && inst_simple "$bin" "$@"
}
# same as above, but specialized for symlinks
inst_symlink() {
- local _src=$1 _target=${2:-$1} _realsrc
- strstr "$1" "/" || return 1
- [[ -L $1 ]] || return 1
- [[ -L $initdir/$_target ]] && return 0
- _realsrc=$(readlink -f "$_src")
- if ! [[ -e $initdir/$_realsrc ]]; then
- if [[ -d $_realsrc ]]; then
- inst_dir "$_realsrc"
+ local src="${1:?}"
+ local target="${2:-$src}"
+ local realsrc
+
+ strstr "$src" "/" || return 1
+ [[ -L "$src" ]] || return 1
+ [[ -L "${initdir:?}/$target" ]] && return 0
+ realsrc="$(readlink -f "$src")"
+ if ! [[ -e "$initdir/$realsrc" ]]; then
+ if [[ -d "$realsrc" ]]; then
+ inst_dir "$realsrc"
else
- inst "$_realsrc"
+ inst "$realsrc"
fi
fi
- [[ ! -e $initdir/${_target%/*} ]] && inst_dir "${_target%/*}"
- [[ -d ${_target%/*} ]] && _target=$(readlink -f ${_target%/*})/${_target##*/}
- ln -sfn $(convert_abs_rel "${_target}" "${_realsrc}") "$initdir/$_target"
+ [[ ! -e "$initdir/${target%/*}" ]] && inst_dir "${target%/*}"
+ [[ -d "${target%/*}" ]] && target="$(readlink -f "${target%/*}")/${target##*/}"
+ ln -sfn -- "$(convert_abs_rel "${target}" "${realsrc}")" "$initdir/$target"
}
# attempt to install any programs specified in a udev rule
inst_rule_programs() {
- local _prog _bin
+ local rule="${1:?}"
+ local prog bin
- if grep -qE 'PROGRAM==?"[^ "]+' "$1"; then
- for _prog in $(grep -E 'PROGRAM==?"[^ "]+' "$1" | sed -r 's/.*PROGRAM==?"([^ "]+).*/\1/'); do
- if [ -x /lib/udev/$_prog ]; then
- _bin=/lib/udev/$_prog
- else
- _bin=$(find_binary "$_prog") || {
- dinfo "Skipping program $_prog using in udev rule $(basename $1) as it cannot be found"
- continue;
- }
+ awk 'match($0, /PROGRAM==?"([^ "]+)/, m) { print m[1]; }' "$rule" | while read -r prog; do
+ if [ -x "/lib/udev/$prog" ]; then
+ bin="/lib/udev/$prog"
+ else
+ if ! bin="$(find_binary "$prog")"; then
+ dinfo "Skipping program $prog used in udev rule $(basename "$rule") as it cannot be found"
+ continue
fi
+ fi
- #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
- dracut_install "$_bin"
- done
- fi
+ #dinfo "Installing $_bin due to it's use in the udev rule $(basename $1)"
+ dracut_install "$bin"
+ done
}
# udev rules always get installed in the same place, so
# create a function to install them to make life simpler.
inst_rules() {
- local _target=/etc/udev/rules.d _rule _found
+ local target=/etc/udev/rules.d
+ local found rule
inst_dir "/lib/udev/rules.d"
- inst_dir "$_target"
- for _rule in "$@"; do
+ inst_dir "$target"
+ for rule in "$@"; do
if [ "${rule#/}" = "$rule" ]; then
for r in /lib/udev/rules.d /etc/udev/rules.d; do
- if [[ -f $r/$_rule ]]; then
- _found="$r/$_rule"
- inst_simple "$_found"
- inst_rule_programs "$_found"
+ if [[ -f "$r/$rule" ]]; then
+ found="$r/$rule"
+ inst_simple "$found"
+ inst_rule_programs "$found"
fi
done
fi
- for r in '' ./ $dracutbasedir/rules.d/; do
- if [[ -f ${r}$_rule ]]; then
- _found="${r}$_rule"
- inst_simple "$_found" "$_target/${_found##*/}"
- inst_rule_programs "$_found"
+ for r in '' ./; do
+ if [[ -f "${r}${rule}" ]]; then
+ found="${r}${rule}"
+ inst_simple "$found" "$target/${found##*/}"
+ inst_rule_programs "$found"
fi
done
- [[ $_found ]] || dinfo "Skipping udev rule: $_rule"
- _found=
+ [[ $found ]] || dinfo "Skipping udev rule: $rule"
+ found=
done
}
# general purpose installation function
# Same args as above.
inst() {
- local _x
-
case $# in
1) ;;
- 2) [[ ! $initdir && -d $2 ]] && export initdir=$2
- [[ $initdir = $2 ]] && set $1;;
- 3) [[ -z $initdir ]] && export initdir=$2
- set $1 $3;;
- *) dfatal "inst only takes 1 or 2 or 3 arguments"
- exit 1;;
+ 2)
+ [[ ! "$initdir" && -d "$2" ]] && export initdir="$2"
+ [[ "$initdir" = "$2" ]] && set "$1"
+ ;;
+ 3)
+ [[ -z "$initdir" ]] && export initdir="$2"
+ set "$1" "$3"
+ ;;
+ *)
+ dfatal "inst only takes 1 or 2 or 3 arguments"
+ exit 1
+ ;;
esac
- for _x in inst_symlink inst_script inst_binary inst_simple; do
- $_x "$@" && return 0
+
+ local fun
+ for fun in inst_symlink inst_script inst_binary inst_simple; do
+ "$fun" "$@" && return 0
done
return 1
}
@@ -1925,14 +2034,14 @@ inst() {
# Lets assume that /bin/baz exists, so it will be installed as /bin/foo in
# initramfs.
inst_any() {
- local to f
+ local dest file
- [[ $1 = '-d' ]] && to="$2" && shift 2
+ [[ "${1:?}" = '-d' ]] && dest="${2:?}" && shift 2
- for f in "$@"; do
- if [[ -e $f ]]; then
- [[ $to ]] && inst "$f" "$to" && return 0
- inst "$f" && return 0
+ for file in "$@"; do
+ if [[ -e "$file" ]]; then
+ [[ -n "$dest" ]] && inst "$file" "$dest" && return 0
+ inst "$file" && return 0
fi
done
@@ -1943,75 +2052,61 @@ inst_any() {
# Install <file> to the initramfs image
# -o optionally install the <file> and don't fail, if it is not there
dracut_install() {
- local _optional=no
- if [[ $1 = '-o' ]]; then
- _optional=yes
+ local optional=no
+ local prog="${1:?}"
+
+ if [[ "$prog" = '-o' ]]; then
+ optional=yes
shift
fi
- while (($# > 0)); do
- if ! inst "$1" ; then
- if [[ $_optional = yes ]]; then
- dinfo "Skipping program $1 as it cannot be found and is" \
+
+ for prog in "$@"; do
+ if ! inst "$prog" ; then
+ if [[ "$optional" = yes ]]; then
+ dinfo "Skipping program $prog as it cannot be found and is" \
"flagged to be optional"
else
- dfatal "Failed to install $1"
+ dfatal "Failed to install $prog"
exit 1
fi
fi
- shift
done
}
# Install a single kernel module along with any firmware it may require.
# $1 = full path to kernel module to install
install_kmod_with_fw() {
+ local module="${1:?}"
# no need to go further if the module is already installed
+ [[ -e "${initdir:?}/lib/modules/${KERNEL_VER:?}/${module##*/lib/modules/$KERNEL_VER/}" ]] && return 0
+ [[ -e "$initdir/.kernelmodseen/${module##*/}" ]] && return 0
- [[ -e "${initdir}/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" ]] \
- && return 0
-
- [[ -e "$initdir/.kernelmodseen/${1##*/}" ]] && return 0
+ [ -d "$initdir/.kernelmodseen" ] && : >"$initdir/.kernelmodseen/${module##*/}"
- if [[ $omit_drivers ]]; then
- local _kmod=${1##*/}
- _kmod=${_kmod%.ko}
- _kmod=${_kmod/-/_}
- if [[ "$_kmod" =~ $omit_drivers ]]; then
- dinfo "Omitting driver $_kmod"
- return 1
- fi
- if [[ "${1##*/lib/modules/$KERNEL_VER/}" =~ $omit_drivers ]]; then
- dinfo "Omitting driver $_kmod"
- return 1
- fi
- fi
-
- [ -d "$initdir/.kernelmodseen" ] && \
- > "$initdir/.kernelmodseen/${1##*/}"
+ inst_simple "$module" "/lib/modules/$KERNEL_VER/${module##*/lib/modules/$KERNEL_VER/}" || return $?
- inst_simple "$1" "/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" \
- || return $?
+ local modname="${module##*/}"
+ local fwdir found fw
+ modname="${modname%.ko*}"
- local _modname=${1##*/} _fwdir _found _fw
- _modname=${_modname%.ko*}
- for _fw in $(modinfo -k $KERNEL_VER -F firmware $1 2>/dev/null); do
- _found=''
- for _fwdir in $fw_dir; do
- if [[ -d $_fwdir && -f $_fwdir/$_fw ]]; then
- inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw"
- _found=yes
+ while read -r fw; do
+ found=
+ for fwdir in /lib/firmware/updates /lib/firmware; do
+ if [[ -d "$fwdir" && -f "$fwdir/$fw" ]]; then
+ inst_simple "$fwdir/$fw" "/lib/firmware/$fw"
+ found=yes
fi
done
- if [[ $_found != yes ]]; then
- if ! grep -qe "\<${_modname//-/_}\>" /proc/modules; then
- dinfo "Possible missing firmware \"${_fw}\" for kernel module" \
- "\"${_modname}.ko\""
+ if [[ $found != yes ]]; then
+ if ! grep -qe "\<${modname//-/_}\>" /proc/modules; then
+ dinfo "Possible missing firmware \"${fw}\" for kernel module" \
+ "\"${modname}.ko\""
else
- dwarn "Possible missing firmware \"${_fw}\" for kernel module" \
- "\"${_modname}.ko\""
+ dwarn "Possible missing firmware \"${fw}\" for kernel module" \
+ "\"${modname}.ko\""
fi
fi
- done
+ done < <(modinfo -k "$KERNEL_VER" -F firmware "$module" 2>/dev/null)
return 0
}
@@ -2021,181 +2116,118 @@ install_kmod_with_fw() {
# It will be passed the full path to the found kernel module
# $2 = module to get dependencies for
# rest of args = arguments to modprobe
-# _fderr specifies FD passed from surrounding scope
for_each_kmod_dep() {
- local _func=$1 _kmod=$2 _cmd _modpath _options _found=0
+ local func="${1:?}"
+ local kmod="${2:?}"
+ local found=0
+ local cmd modpath
shift 2
- modprobe "$@" --ignore-install --show-depends $_kmod 2>&${_fderr} | (
- while read _cmd _modpath _options; do
- [[ $_cmd = insmod ]] || continue
- $_func ${_modpath} || exit $?
- _found=1
- done
- [[ $_found -eq 0 ]] && exit 1
- exit 0
- )
-}
-
-# filter kernel modules to install certain modules that meet specific
-# requirements.
-# $1 = search only in subdirectory of /kernel/$1
-# $2 = function to call with module name to filter.
-# This function will be passed the full path to the module to test.
-# The behavior of this function can vary depending on whether $hostonly is set.
-# If it is, we will only look at modules that are already in memory.
-# If it is not, we will look at all kernel modules
-# This function returns the full filenames of modules that match $1
-filter_kernel_modules_by_path () (
- local _modname _filtercmd
- if ! [[ $hostonly ]]; then
- _filtercmd='find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra"'
- _filtercmd+=' "$KERNEL_MODS/weak-updates" -name "*.ko" -o -name "*.ko.gz"'
- _filtercmd+=' -o -name "*.ko.xz"'
- _filtercmd+=' 2>/dev/null'
- else
- _filtercmd='cut -d " " -f 1 </proc/modules|xargs modinfo -F filename '
- _filtercmd+='-k $KERNEL_VER 2>/dev/null'
- fi
- for _modname in $(eval $_filtercmd); do
- case $_modname in
- *.ko) "$2" "$_modname" && echo "$_modname";;
- *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko
- $2 $initdir/$$.ko && echo "$_modname"
- rm -f $initdir/$$.ko
- ;;
- *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko
- $2 $initdir/$$.ko && echo "$_modname"
- rm -f $initdir/$$.ko
- ;;
- esac
- done
-)
-find_kernel_modules_by_path () (
- if ! [[ $hostonly ]]; then
- find "$KERNEL_MODS/kernel/$1" "$KERNEL_MODS/extra" "$KERNEL_MODS/weak-updates" \
- -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" 2>/dev/null
- else
- cut -d " " -f 1 </proc/modules \
- | xargs modinfo -F filename -k $KERNEL_VER 2>/dev/null
- fi
-)
-filter_kernel_modules () {
- filter_kernel_modules_by_path drivers "$1"
-}
+ while read -r cmd modpath _; do
+ [[ "$cmd" = insmod ]] || continue
+ "$func" "$modpath" || return $?
+ found=1
+ done < <(modprobe "$@" --ignore-install --show-depends "$kmod")
-find_kernel_modules () {
- find_kernel_modules_by_path drivers
+ [[ $found -eq 0 ]] && return 1
+ return 0
}
# instmods [-c] <kernel module> [<kernel module> ... ]
# instmods [-c] <kernel subsystem>
# install kernel modules along with all their dependencies.
# <kernel subsystem> can be e.g. "=block" or "=drivers/usb/storage"
+# FIXME(?): dracutdevs/dracut@f4e38c0da8d6bf3764c1ad753d9d52aef63050e5
instmods() {
- [[ $no_kernel = yes ]] && return
- # called [sub]functions inherit _fderr
- local _fderr=9
- local _check=no
- if [[ $1 = '-c' ]]; then
- _check=yes
+ local check=no
+ if [[ $# -ge 0 && "$1" = '-c' ]]; then
+ check=yes
shift
fi
- function inst1mod() {
- local _ret=0 _mod="$1"
- case $_mod in
+ inst1mod() {
+ local mod="${1:?}"
+ local ret=0
+ local mod_dir="/lib/modules/${KERNEL_VER:?}/"
+
+ case "$mod" in
=*)
- if [ -f $KERNEL_MODS/modules.${_mod#=} ]; then
- ( [[ "$_mpargs" ]] && echo $_mpargs
- cat "${KERNEL_MODS}/modules.${_mod#=}" ) \
- | instmods
+ if [ -f "${mod_dir}/modules.${mod#=}" ]; then
+ (
+ [[ "$mpargs" ]] && echo "$mpargs"
+ cat "${mod_dir}/modules.${mod#=}"
+ ) | instmods
else
- ( [[ "$_mpargs" ]] && echo $_mpargs
- find "$KERNEL_MODS" -path "*/${_mod#=}/*" -type f -printf '%f\n' ) \
- | instmods
+ (
+ [[ "$mpargs" ]] && echo "$mpargs"
+ find "$mod_dir" -path "*/${mod#=}/*" -type f -printf '%f\n'
+ ) | instmods
fi
;;
- --*) _mpargs+=" $_mod" ;;
- i2o_scsi) return ;; # Do not load this diagnostic-only module
+ --*)
+ mpargs+=" $mod"
+ ;;
+ i2o_scsi)
+ # Do not load this diagnostic-only module
+ return
+ ;;
*)
- _mod=${_mod##*/}
+ mod=${mod##*/}
# if we are already installed, skip this module and go on
# to the next one.
- [[ -f "$initdir/.kernelmodseen/${_mod%.ko}.ko" ]] && return
-
- if [[ $omit_drivers ]] && [[ "$1" =~ $omit_drivers ]]; then
- dinfo "Omitting driver ${_mod##$KERNEL_MODS}"
- return
- fi
- # If we are building a host-specific initramfs and this
- # module is not already loaded, move on to the next one.
- [[ $hostonly ]] && ! grep -qe "\<${_mod//-/_}\>" /proc/modules \
- && ! echo $add_drivers | grep -qe "\<${_mod}\>" \
- && return
+ [[ -f "${initdir:?}/.kernelmodseen/${mod%.ko}.ko" ]] && return
# We use '-d' option in modprobe only if modules prefix path
# differs from default '/'. This allows us to use Dracut with
# old version of modprobe which doesn't have '-d' option.
- local _moddirname=${KERNEL_MODS%%/lib/modules/*}
- [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/"
+ local mod_dirname=${mod_dir%%/lib/modules/*}
+ [[ -n ${mod_dirname} ]] && mod_dirname="-d ${mod_dirname}/"
# ok, load the module, all its dependencies, and any firmware
# it may require
- for_each_kmod_dep install_kmod_with_fw $_mod \
- --set-version $KERNEL_VER ${_moddirname} $_mpargs
- ((_ret+=$?))
+ for_each_kmod_dep install_kmod_with_fw "$mod" \
+ --set-version "$KERNEL_VER" \
+ ${mod_dirname:+"$mod_dirname"} \
+ ${mpargs:+"$mpargs"}
+ ((ret+=$?))
;;
esac
- return $_ret
+ return $ret
}
- function instmods_1() {
- local _mod _mpargs
- if (($# == 0)); then # filenames from stdin
- while read _mod; do
- inst1mod "${_mod%.ko*}" || {
- if [ "$_check" = "yes" ]; then
- dfatal "Failed to install $_mod"
- return 1
- fi
- }
- done
- fi
- while (($# > 0)); do # filenames as arguments
- inst1mod ${1%.ko*} || {
- if [ "$_check" = "yes" ]; then
- dfatal "Failed to install $1"
- return 1
- fi
- }
- shift
+ local mod mpargs
+
+ if [[ $# -eq 0 ]]; then # filenames from stdin
+ while read -r mod; do
+ if ! inst1mod "${mod%.ko*}" && [ "$check" = "yes" ]; then
+ dfatal "Failed to install $mod"
+ return 1
+ fi
done
- return 0
- }
+ fi
- local _ret _filter_not_found='FATAL: Module .* not found.'
- set -o pipefail
- # Capture all stderr from modprobe to _fderr. We could use {var}>...
- # redirections, but that would make dracut require bash4 at least.
- eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \
- | while read line; do [[ "$line" =~ $_filter_not_found ]] && echo $line || echo $line >&2 ;done | derror
- _ret=$?
- set +o pipefail
- return $_ret
+ for mod in "$@"; do # filenames as arguments
+ if ! inst1mod "${mod%.ko*}" && [ "$check" = "yes" ]; then
+ dfatal "Failed to install $mod"
+ return 1
+ fi
+ done
+
+ return 0
}
setup_suse() {
- ln -fs ../usr/bin/systemctl $initdir/bin/
- ln -fs ../usr/lib/systemd $initdir/lib/
+ ln -fs ../usr/bin/systemctl "${initdir:?}/bin/"
+ ln -fs ../usr/lib/systemd "$initdir/lib/"
inst_simple "/usr/lib/systemd/system/haveged.service"
instmods ext4
}
_umount_dir() {
- if mountpoint -q $1; then
- ddebug "umount $1"
- umount $1
+ local mountpoint="${1:?}"
+ if mountpoint -q "$mountpoint"; then
+ ddebug "umount $mountpoint"
+ umount "$mountpoint"
fi
}
@@ -2208,14 +2240,14 @@ _test_cleanup() {
# (post-test) cleanup should always ignore failure and cleanup as much as possible
(
set +e
- _umount_dir $initdir
- rm -vf "$IMAGE_PUBLIC"
+ [[ -n "$initdir" ]] && _umount_dir "$initdir"
+ [[ -n "$IMAGE_PUBLIC" ]] && rm -vf "$IMAGE_PUBLIC"
# If multiple setups/cleans are ran in parallel, this can cause a race
- if [ ${TEST_PARALLELIZE} -ne 1 ]; then
+ if [[ -n "$IMAGESTATEDIR" && $TEST_PARALLELIZE -ne 1 ]]; then
rm -vf "${IMAGESTATEDIR}/default.img"
fi
- rm -vfr "$TESTDIR"
- rm -vf "$STATEFILE"
+ [[ -n "$TESTDIR" ]] && rm -vfr "$TESTDIR"
+ [[ -n "$STATEFILE" ]] && rm -vf "$STATEFILE"
) || :
}
@@ -2227,7 +2259,7 @@ test_cleanup() {
test_cleanup_again() {
[ -n "$TESTDIR" ] || return
rm -rf "$TESTDIR/unprivileged-nspawn-root"
- _umount_dir $initdir
+ [[ -n "$initdir" ]] && _umount_dir "$initdir"
}
test_create_image() {
@@ -2242,25 +2274,25 @@ test_create_image() {
}
test_setup() {
- if [ ${TEST_REQUIRE_INSTALL_TESTS} -ne 0 ] && \
- type -P meson >/dev/null && \
- [[ "$(meson configure $BUILD_DIR | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
+ if [ "${TEST_REQUIRE_INSTALL_TESTS:?}" -ne 0 ] && \
+ command -v meson >/dev/null && \
+ [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
dfatal "$BUILD_DIR needs to be built with -Dinstall-tests=true"
exit 1
fi
- if [ -e "$IMAGE_PRIVATE" ]; then
- echo "Reusing existing image $IMAGE_PRIVATE → $(realpath $IMAGE_PRIVATE)"
+ if [ -e "${IMAGE_PRIVATE:?}" ]; then
+ echo "Reusing existing image $IMAGE_PRIVATE → $(realpath "$IMAGE_PRIVATE")"
mount_initdir
else
- if [ ! -e "$IMAGE_PUBLIC" ]; then
+ if [ ! -e "${IMAGE_PUBLIC:?}" ]; then
# default.img is the base that every test uses and optionally appends to
- if [ ! -e "${IMAGESTATEDIR}/default.img" ] || [ -n "${TEST_FORCE_NEWIMAGE}" ]; then
+ if [ ! -e "${IMAGESTATEDIR:?}/default.img" ] || [ -n "${TEST_FORCE_NEWIMAGE:=}" ]; then
# Create the backing public image, but then completely unmount
# it and drop the loopback device responsible for it, since we're
# going to symlink/copy the image and mount it again from
# elsewhere.
- local image_old=${IMAGE_PUBLIC}
+ local image_old="${IMAGE_PUBLIC}"
if [ -z "${TEST_FORCE_NEWIMAGE}" ]; then
IMAGE_PUBLIC="${IMAGESTATEDIR}/default.img"
fi
@@ -2270,26 +2302,26 @@ test_setup() {
cleanup_loopdev
IMAGE_PUBLIC="${image_old}"
fi
- if [ "${IMAGE_NAME}" != "default" ] && [ -z "${TEST_FORCE_NEWIMAGE}" ]; then
+ if [ "${IMAGE_NAME:?}" != "default" ] && [ -z "${TEST_FORCE_NEWIMAGE}" ]; then
cp -v "$(realpath "${IMAGESTATEDIR}/default.img")" "$IMAGE_PUBLIC"
fi
fi
local hook_defined=1
- if declare -f -F test_append_files > /dev/null; then
+ if declare -f -F test_append_files >/dev/null; then
hook_defined=$?
fi
- echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath $IMAGE_PUBLIC)"
- if [ ${TEST_PARALLELIZE} -ne 0 ] || [ ${hook_defined} -eq 0 ]; then
- cp -v "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
+ echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath "$IMAGE_PUBLIC")"
+ if [ "$TEST_PARALLELIZE" -ne 0 ] || [ "$hook_defined" -eq 0 ]; then
+ cp -v -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
else
- ln -sv "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
+ ln -sv -- "$(realpath "$IMAGE_PUBLIC")" "$IMAGE_PRIVATE"
fi
mount_initdir
- if [ ${hook_defined} -eq 0 ]; then
- test_append_files "$initdir"
+ if [ "$hook_defined" -eq 0 ]; then
+ test_append_files "${initdir:?}"
fi
fi
@@ -2297,26 +2329,27 @@ test_setup() {
}
test_run() {
+ local test_id="${1:?}"
mount_initdir
- if [ -z "$TEST_NO_QEMU" ]; then
- if run_qemu "$1"; then
+ if [ -z "${TEST_NO_QEMU:=}" ]; then
+ if run_qemu "$test_id"; then
check_result_qemu || { echo "QEMU test failed"; return 1; }
else
dwarn "can't run QEMU, skipping"
fi
fi
- if [ -z "$TEST_NO_NSPAWN" ]; then
+ if [ -z "${TEST_NO_NSPAWN:=}" ]; then
mount_initdir
- if run_nspawn "$initdir" "$1"; then
+ if run_nspawn "${initdir:?}" "$test_id"; then
check_result_nspawn "$initdir" || { echo "nspawn-root test failed"; return 1; }
else
dwarn "can't run systemd-nspawn, skipping"
fi
- if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
+ if [[ "${RUN_IN_UNPRIVILEGED_CONTAINER:=}" = "yes" ]]; then
dir="$TESTDIR/unprivileged-nspawn-root"
- if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "$dir" "$1"; then
+ if NSPAWN_ARGUMENTS="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn "$dir" "$test_id"; then
check_result_nspawn "$dir" || { echo "unprivileged-nspawn-root test failed"; return 1; }
else
dwarn "can't run systemd-nspawn, skipping"
@@ -2332,26 +2365,26 @@ do_test() {
exit 0
fi
- if [ -n "$TEST_NO_QEMU" ] && [ -n "$TEST_NO_NSPAWN" ]; then
+ if [ -n "${TEST_NO_QEMU:=}" ] && [ -n "${TEST_NO_NSPAWN:=}" ]; then
echo "TEST: $TEST_DESCRIPTION [SKIPPED]: both QEMU and nspawn disabled" >&2
exit 0
fi
- if [ -n "$TEST_QEMU_ONLY" ] && [ -z "$TEST_NO_NSPAWN" ]; then
+ if [ -n "${TEST_QEMU_ONLY:=}" ] && [ -z "$TEST_NO_NSPAWN" ]; then
echo "TEST: $TEST_DESCRIPTION [SKIPPED]: QEMU-only tests requested" >&2
exit 0
fi
- if [ -n "$TEST_PREFER_NSPAWN" ] && [ -z "$TEST_NO_NSPAWN" ]; then
+ if [ -n "${TEST_PREFER_NSPAWN:=}" ] && [ -z "$TEST_NO_NSPAWN" ]; then
TEST_NO_QEMU=1
fi
# Detect lib paths
- [[ $libdir ]] || for libdir in /lib64 /lib; do
+ [[ "$libdir" ]] || for libdir in /lib64 /lib; do
[[ -d $libdir ]] && libdirs+=" $libdir" && break
done
- [[ $usrlibdir ]] || for usrlibdir in /usr/lib64 /usr/lib; do
+ [[ "$usrlibdir" ]] || for usrlibdir in /usr/lib64 /usr/lib; do
[[ -d $usrlibdir ]] && libdirs+=" $usrlibdir" && break
done
@@ -2360,7 +2393,7 @@ do_test() {
import_testdir
import_initdir
- testname="$(basename $PWD)"
+ testname="$(basename "$PWD")"
while (($# > 0)); do
case $1 in
@@ -2368,7 +2401,7 @@ do_test() {
echo "${testname} RUN: $TEST_DESCRIPTION"
test_run "$2"
ret=$?
- if (( $ret == 0 )); then
+ if [ $ret -eq 0 ]; then
echo "${testname} RUN: $TEST_DESCRIPTION [OK]"
else
echo "${testname} RUN: $TEST_DESCRIPTION [FAILED]"
diff --git a/test/test-network/conf/agent-client-peer.network b/test/test-network/conf/agent-client-peer.network
new file mode 100644
index 0000000000..0db83dc9d2
--- /dev/null
+++ b/test/test-network/conf/agent-client-peer.network
@@ -0,0 +1,9 @@
+[Match]
+Name=client-peer
+[Network]
+Address=192.168.6.2/24
+DHCPServer=yes
+IPForward=ipv4
+[DHCPServer]
+RelayTarget=192.168.5.1
+BindToInterface=no
diff --git a/test/test-network/conf/agent-client.network b/test/test-network/conf/agent-client.network
new file mode 100644
index 0000000000..773dc8a4fb
--- /dev/null
+++ b/test/test-network/conf/agent-client.network
@@ -0,0 +1,5 @@
+[Match]
+Name=client
+[Network]
+DHCP=yes
+IPForward=ipv4
diff --git a/test/test-network/conf/agent-server-peer.network b/test/test-network/conf/agent-server-peer.network
new file mode 100644
index 0000000000..fdb235e1ff
--- /dev/null
+++ b/test/test-network/conf/agent-server-peer.network
@@ -0,0 +1,5 @@
+[Match]
+Name=server-peer
+[Network]
+Address=192.168.5.2/24
+IPForward=ipv4
diff --git a/test/test-network/conf/agent-server.network b/test/test-network/conf/agent-server.network
new file mode 100644
index 0000000000..d58abdf271
--- /dev/null
+++ b/test/test-network/conf/agent-server.network
@@ -0,0 +1,10 @@
+[Match]
+Name=server
+[Network]
+Address=192.168.5.1/24
+IPForward=ipv4
+DHCPServer=yes
+[DHCPServer]
+BindToInterface=no
+PoolOffset=150
+PoolSize=1
diff --git a/test/test-network/conf/agent-veth-client.netdev b/test/test-network/conf/agent-veth-client.netdev
new file mode 100644
index 0000000000..ace785f5ca
--- /dev/null
+++ b/test/test-network/conf/agent-veth-client.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=client
+Kind=veth
+MACAddress=12:34:56:78:9a:bc
+
+[Peer]
+Name=client-peer
+MACAddress=12:34:56:78:9a:bd
diff --git a/test/test-network/conf/agent-veth-server.netdev b/test/test-network/conf/agent-veth-server.netdev
new file mode 100644
index 0000000000..3a3d3931ba
--- /dev/null
+++ b/test/test-network/conf/agent-veth-server.netdev
@@ -0,0 +1,8 @@
+[NetDev]
+Name=server
+Kind=veth
+MACAddress=12:34:56:78:9b:bc
+
+[Peer]
+Name=server-peer
+MACAddress=12:34:56:78:9b:bd
diff --git a/test/test-network/conf/dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network b/test/test-network/conf/dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network
new file mode 100644
index 0000000000..448cfb0e59
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network
@@ -0,0 +1,9 @@
+[Match]
+Name=veth99
+
+[NetworkEmulator]
+DelaySec=9
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=true
diff --git a/test/test-network/conf/dhcp-server-with-ipv6-prefix.network b/test/test-network/conf/dhcp-server-with-ipv6-prefix.network
new file mode 100644
index 0000000000..05ebe3e7e2
--- /dev/null
+++ b/test/test-network/conf/dhcp-server-with-ipv6-prefix.network
@@ -0,0 +1,11 @@
+[Match]
+Name=veth-peer
+
+[Network]
+Address=192.168.5.1/24
+IPv6AcceptRA=no
+DHCPServer=yes
+IPv6SendRA=yes
+
+[IPv6Prefix]
+Prefix=2002:da8:1:0::/64
diff --git a/test/test-network/conf/ipv6-prefix-with-delay.network b/test/test-network/conf/ipv6-prefix-with-delay.network
new file mode 100644
index 0000000000..4fe3c9a04b
--- /dev/null
+++ b/test/test-network/conf/ipv6-prefix-with-delay.network
@@ -0,0 +1,12 @@
+[Match]
+Name=veth-peer
+
+[NetworkEmulator]
+DelaySec=15
+
+[Network]
+IPv6AcceptRA=no
+IPv6SendRA=yes
+
+[IPv6Prefix]
+Prefix=2002:da8:1:0::/64
diff --git a/test/test-network/conf/ipv6ra-prefix-client-with-static-ipv4-address.network b/test/test-network/conf/ipv6ra-prefix-client-with-static-ipv4-address.network
new file mode 100644
index 0000000000..13fd0adeff
--- /dev/null
+++ b/test/test-network/conf/ipv6ra-prefix-client-with-static-ipv4-address.network
@@ -0,0 +1,6 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
+Address=192.168.5.1/24
diff --git a/test/test-network/conf/state-file-tests.network b/test/test-network/conf/state-file-tests.network
index 1f7e7d16f0..d9db9a9ab9 100644
--- a/test/test-network/conf/state-file-tests.network
+++ b/test/test-network/conf/state-file-tests.network
@@ -3,6 +3,7 @@ Name=dummy98
[Link]
RequiredForOnline=routable
+RequiredFamilyForOnline=both
[Network]
IPv6AcceptRA=no
@@ -14,3 +15,4 @@ MulticastDNS=yes
DNSSEC=no
Address=192.168.10.10/24
Address=192.168.12.12/24
+Address=2002:da8:1:0:1034:56ff:fe78:9abc/64
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index fc88fefd0f..148ff6e8c0 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -555,7 +555,7 @@ class Utilities():
self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
return False
- def wait_online(self, links_with_operstate, timeout='20s', bool_any=False, setup_state='configured', setup_timeout=5):
+ def wait_online(self, links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5):
"""Wait for the link(s) to reach the specified operstate and/or setup state.
This is similar to wait_operstate() but can be used for multiple links,
@@ -569,6 +569,9 @@ class Utilities():
Set 'bool_any' to True to wait for any (instead of all) of the given links.
If this is set, no setup_state checks are done.
+ Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
+ This is applied only for the operational state 'degraded' or above.
+
Note that this function waits for the link(s) to reach *or exceed* the given operstate.
However, the setup_state, if specified, must be matched *exactly*.
@@ -578,6 +581,10 @@ class Utilities():
args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate]
if bool_any:
args += ['--any']
+ if ipv4:
+ args += ['--ipv4']
+ if ipv6:
+ args += ['--ipv6']
try:
check_output(*args, env=env)
except subprocess.CalledProcessError:
@@ -1781,6 +1788,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'gretun97',
'ip6gretun97',
'test1',
+ 'veth-peer',
'veth99',
'vrf99',
]
@@ -1842,6 +1850,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-vrf.netdev',
'25-vrf.network',
'26-link-local-addressing-ipv6.network',
+ 'dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network',
+ 'dhcp-server-with-ipv6-prefix.network',
+ 'ipv6ra-prefix-client-with-static-ipv4-address.network',
+ 'ipv6-prefix-with-delay.network',
'routing-policy-rule-dummy98.network',
'routing-policy-rule-test1.network',
'routing-policy-rule-reconfigure1.network',
@@ -3099,6 +3111,22 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+ def test_wait_online_ipv4(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-with-ipv6-prefix.network', 'dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network')
+ start_networkd()
+
+ self.wait_online(['veth99:routable'], ipv4=True)
+
+ self.wait_address('veth99', r'192.168.5.[0-9]+', ipv='-4', timeout_sec=1)
+
+ def test_wait_online_ipv6(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix-with-delay.network', 'ipv6ra-prefix-client-with-static-ipv4-address.network')
+ start_networkd()
+
+ self.wait_online(['veth99:routable'], ipv6=True)
+
+ self.wait_address('veth99', r'2002:da8:1:0:1034:56ff:fe78:9abc', ipv='-6', timeout_sec=1)
+
class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [
'dummy98',
@@ -3135,10 +3163,13 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
with open(path) as f:
data = f.read()
+ self.assertRegex(data, r'IPV4_ADDRESS_STATE=routable')
+ self.assertRegex(data, r'IPV6_ADDRESS_STATE=routable')
self.assertRegex(data, r'ADMIN_STATE=configured')
self.assertRegex(data, r'OPER_STATE=routable')
self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes')
self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable')
+ self.assertRegex(data, r'REQUIRED_FAMILY_FOR_ONLINE=both')
self.assertRegex(data, r'ACTIVATION_POLICY=up')
self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/state-file-tests.network')
self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
@@ -3658,6 +3689,43 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
self.assertRegex(output, '192.168.5.*')
self.assertRegex(output, 'Europe/Berlin')
+class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
+ links = [
+ 'client',
+ 'server',
+ 'client-peer',
+ 'server-peer',
+ ]
+
+ units = [
+ 'agent-veth-client.netdev',
+ 'agent-veth-server.netdev',
+ 'agent-client.network',
+ 'agent-server.network',
+ 'agent-client-peer.network',
+ 'agent-server-peer.network',
+ ]
+
+ def setUp(self):
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
+
+ def tearDown(self):
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
+
+ def test_relay_agent(self):
+ copy_unit_to_networkd_unit_path(*self.units)
+ start_networkd()
+
+ #Test is disabled until BindToInterface DHCP server configuration option is supported
+ self.wait_online(['client:routable'])
+
+ output = check_output(*networkctl_cmd, '-n', '0', 'status', 'client', env=env)
+ print(output)
+ self.assertRegex(output, 'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
+
class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
links = [
'veth99',
@@ -3843,7 +3911,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.wait_online(['veth99:routable', 'veth-peer:routable'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
@@ -3946,7 +4014,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip address show dev veth99 scope global')
print(output)
self.assertRegex(output, r'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99')
- self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global secondary dynamic veth99')
+ self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global secondary dynamic veth99')
output = check_output('ip route show dev veth99')
print(output)
@@ -3976,7 +4044,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip route show dev veth99')
print(output)
- self.assertRegex(output, 'metric 24')
+ self.assertIn('default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 24', output)
+ self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.181 metric 24', output)
+ self.assertIn('192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 24', output)
def test_dhcp_client_reassign_static_routes_ipv4(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
@@ -3988,7 +4058,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip address show dev veth99 scope global')
print(output)
- self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
+ self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
output = check_output('ip route show dev veth99')
print(output)
@@ -4133,7 +4203,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.wait_online(['veth99:routable', 'veth-peer:routable'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
output = check_output('ip address show dev veth99 scope global')
@@ -4176,7 +4246,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
print('## ip -d link show dev vrf99')
@@ -4187,14 +4257,14 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
print('## ip address show vrf vrf99')
output = check_output('ip address show vrf vrf99')
print(output)
- self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
+ self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
self.assertRegex(output, 'inet6 .* scope link')
print('## ip address show dev veth99')
output = check_output('ip address show dev veth99')
print(output)
- self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
+ self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
self.assertRegex(output, 'inet6 .* scope link')
@@ -4270,9 +4340,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip -6 address show dev veth99 scope link')
self.assertRegex(output, r'inet6 .* scope link')
output = check_output('ip -4 address show dev veth99 scope global dynamic')
- self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 brd 192\.168\.5\.255 scope global dynamic veth99')
+ self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
output = check_output('ip -4 address show dev veth99 scope link')
- self.assertNotRegex(output, r'inet 169\.254\.\d+\.\d+/16 brd 169\.254\.255\.255 scope link')
+ self.assertNotRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
print('Wait for the dynamic address to be expired')
time.sleep(130)
@@ -4285,9 +4355,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip -6 address show dev veth99 scope link')
self.assertRegex(output, r'inet6 .* scope link')
output = check_output('ip -4 address show dev veth99 scope global dynamic')
- self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 brd 192\.168\.5\.255 scope global dynamic veth99')
+ self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
output = check_output('ip -4 address show dev veth99 scope link')
- self.assertNotRegex(output, r'inet 169\.254\.\d+\.\d+/16 brd 169\.254\.255\.255 scope link')
+ self.assertNotRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
search_words_in_dnsmasq_log('DHCPOFFER', show_all=True)
@@ -4307,13 +4377,13 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip -6 address show dev veth99 scope link')
self.assertRegex(output, r'inet6 .* scope link')
output = check_output('ip -4 address show dev veth99 scope global dynamic')
- self.assertNotRegex(output, r'inet 192\.168\.5\.\d+/24 brd 192\.168\.5\.255 scope global dynamic veth99')
+ self.assertNotRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
output = check_output('ip -4 address show dev veth99 scope link')
- self.assertRegex(output, r'inet 169\.254\.\d+\.\d+/16 brd 169\.254\.255\.255 scope link')
+ self.assertRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
start_dnsmasq(lease_time='2m')
- self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
- self.wait_address_dropped('veth99', r'inet 169\.254\.\d+\.\d+/16 brd 169\.255\.255\.255 scope link', scope='link', ipv='-4')
+ self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
+ self.wait_address_dropped('veth99', r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.255\.255\.255 scope link', scope='link', ipv='-4')
def test_dhcp_client_route_remove_on_renew(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
@@ -4327,7 +4397,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip -4 address show dev veth99 scope global dynamic')
print(output)
- self.assertRegex(output, 'inet 192.168.5.1[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
+ self.assertRegex(output, 'inet 192.168.5.1[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
address1=None
for line in output.splitlines():
if 'brd 192.168.5.255 scope global dynamic veth99' in line:
@@ -4347,10 +4417,10 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip -4 address show dev veth99 scope global dynamic')
print(output)
- self.assertRegex(output, 'inet 192.168.5.2[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
+ self.assertRegex(output, 'inet 192.168.5.2[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
address2=None
for line in output.splitlines():
- if 'brd 192.168.5.255 scope global dynamic veth99' in line:
+ if 'metric 1024 brd 192.168.5.255 scope global dynamic veth99' in line:
address2 = line.split()[1].split('/')[0]
break
@@ -4372,7 +4442,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.wait_online(['veth99:routable', 'veth-peer:routable'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
time.sleep(3)
@@ -4390,7 +4460,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.wait_online(['veth99:routable', 'veth-peer:routable'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
time.sleep(3)
@@ -4408,7 +4478,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.wait_online(['veth99:routable', 'veth-peer:routable'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
time.sleep(3)
@@ -4426,7 +4496,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.wait_online(['veth99:routable', 'veth-peer:routable'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
time.sleep(3)
diff --git a/test/testsuite-28.units/specifier-j-wants.service b/test/testsuite-28.units/specifier-j-wants.service
index facf5577be..766a454d6a 100644
--- a/test/testsuite-28.units/specifier-j-wants.service
+++ b/test/testsuite-28.units/specifier-j-wants.service
@@ -7,4 +7,4 @@ After=testsuite-28-pre.service
[Service]
Type=oneshot
ExecStart=test -f /tmp/test-specifier-j-%j
-ExecStart=sh -c 'echo OK > /testok'
+ExecStart=sh -c 'echo OK >/testok'
diff --git a/test/testsuite-52.units/testsuite-52.sh b/test/testsuite-52.units/testsuite-52.sh
index 9cccf1b6c1..e10bf56110 100755
--- a/test/testsuite-52.units/testsuite-52.sh
+++ b/test/testsuite-52.units/testsuite-52.sh
@@ -3,7 +3,7 @@ set -ex
set -o pipefail
if ! test -x /usr/lib/systemd/tests/testdata/units/test-honor-first-shutdown.sh ; then
- echo "honor-first-shutdown script not found - FAIL" > /testok
+ echo "honor-first-shutdown script not found - FAIL" >/testok
exit 0
fi
@@ -13,6 +13,6 @@ systemd-analyze log-target console
systemctl enable test-honor-first-shutdown.service
systemctl start test-honor-first-shutdown.service
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-02.sh b/test/units/testsuite-02.sh
index 1ff1c3347b..da02304a32 100755
--- a/test/units/testsuite-02.sh
+++ b/test/units/testsuite-02.sh
@@ -1,13 +1,13 @@
#!/usr/bin/env bash
-#set -ex
-#set -o pipefail
+set -eux
+set -o pipefail
NPROC=$(nproc)
MAX_QUEUE_SIZE=${NPROC:-2}
-IFS=$'\n' TEST_LIST=($(ls /usr/lib/systemd/tests/test-*))
+mapfile -t TEST_LIST < <(find /usr/lib/systemd/tests/ -maxdepth 1 -type f -name "test-*")
# reset state
-rm /failed-tests /skipped-tests /skipped
+rm -fv /failed-tests /skipped-tests /skipped
# Check & report test results
# Arguments:
@@ -54,10 +54,9 @@ for task in "${TEST_LIST[@]}"; do
# 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
+ if ! kill -0 "${running[$key]}" &>/dev/null; then
# Task has finished, report its result and drop it from the queue
- wait ${running[$key]}
- ec=$?
+ wait "${running[$key]}" && ec=0 || ec=$?
report_result "$key" $ec
unset running["$key"]
# Break from inner for loop and outer while loop to skip
diff --git a/test/units/testsuite-03.sh b/test/units/testsuite-03.sh
index 85efeeb741..8a8ba870e8 100755
--- a/test/units/testsuite-03.sh
+++ b/test/units/testsuite-03.sh
@@ -1,14 +1,15 @@
#!/usr/bin/env bash
-set -ex
+set -eux
+set -o pipefail
# 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
+systemctl list-jobs >/root/list-jobs.txt
while ! grep 'sleep\.service.*running' /root/list-jobs.txt; do
- systemctl list-jobs > /root/list-jobs.txt
+ systemctl list-jobs >/root/list-jobs.txt
done
grep 'hello\.service.*waiting' /root/list-jobs.txt
@@ -17,22 +18,22 @@ grep 'hello\.service.*waiting' /root/list-jobs.txt
START_SEC=$(date -u '+%s')
systemctl start --job-mode=ignore-dependencies hello
END_SEC=$(date -u '+%s')
-ELAPSED=$(($END_SEC-$START_SEC))
+ELAPSED=$((END_SEC-START_SEC))
-[ "$ELAPSED" -lt 3 ]
+test "$ELAPSED" -lt 3
# sleep should still be running, hello not.
-systemctl list-jobs > /root/list-jobs.txt
+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 is-active systemd-importd && { echo 'unexpected success'; exit 1; }
systemctl -T start systemd-importd
systemctl is-active systemd-importd
systemctl --show-transaction stop systemd-importd
-! systemctl is-active systemd-importd
+systemctl is-active systemd-importd && { echo 'unexpected success'; exit 1; }
# Test for a crash when enqueuing a JOB_NOP when other job already exists
systemctl start --no-block hello-after-sleep.target
@@ -58,13 +59,13 @@ systemctl stop --job-mode=replace-irreversibly unstoppable.service
systemctl start unstoppable.service
# Test waiting for a started unit(s) to terminate again
-cat <<EOF > /run/systemd/system/wait2.service
+cat <<EOF >/run/systemd/system/wait2.service
[Unit]
Description=Wait for 2 seconds
[Service]
ExecStart=/bin/sh -ec 'sleep 2'
EOF
-cat <<EOF > /run/systemd/system/wait5fail.service
+cat <<EOF >/run/systemd/system/wait5fail.service
[Unit]
Description=Wait for 5 seconds and fail
[Service]
@@ -75,14 +76,14 @@ EOF
START_SEC=$(date -u '+%s')
systemctl start --wait wait2.service
END_SEC=$(date -u '+%s')
-ELAPSED=$(($END_SEC-$START_SEC))
+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 || exit 1
+systemctl start --wait wait2.service wait5fail.service && { echo 'unexpected success'; exit 1; }
END_SEC=$(date -u '+%s')
-ELAPSED=$(($END_SEC-$START_SEC))
+ELAPSED=$((END_SEC-START_SEC))
[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
# Test time-limited scopes
@@ -91,7 +92,7 @@ set +e
systemd-run --scope --property=RuntimeMaxSec=3s sleep 10
RESULT=$?
END_SEC=$(date -u '+%s')
-ELAPSED=$(($END_SEC-$START_SEC))
+ELAPSED=$((END_SEC-START_SEC))
[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
[[ "$RESULT" -ne 0 ]] || exit 1
diff --git a/test/units/testsuite-04.sh b/test/units/testsuite-04.sh
index 3dce73b778..da5f9ee2af 100755
--- a/test/units/testsuite-04.sh
+++ b/test/units/testsuite-04.sh
@@ -1,20 +1,19 @@
#!/usr/bin/env bash
-set -x
-set -e
+set -eux
set -o pipefail
# Test stdout stream
# Skip empty lines
ID=$(journalctl --new-id128 | sed -n 2p)
->/expected
+: >/expected
printf $'\n\n\n' | systemd-cat -t "$ID" --level-prefix false
journalctl --sync
journalctl -b -o cat -t "$ID" >/output
cmp /expected /output
ID=$(journalctl --new-id128 | sed -n 2p)
->/expected
+: >/expected
printf $'<5>\n<6>\n<7>\n' | systemd-cat -t "$ID" --level-prefix true
journalctl --sync
journalctl -b -o cat -t "$ID" >/output
@@ -55,23 +54,23 @@ ID=$(journalctl --new-id128 | sed -n 2p)
printf $'foo' | systemd-cat -t "$ID" --level-prefix false
journalctl --sync
journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/output
-[[ `grep -c . /output` -eq 6 ]]
+[[ $(grep -c . /output) -eq 6 ]]
grep -q '^__CURSOR=' /output
grep -q '^MESSAGE=foo$' /output
grep -q '^PRIORITY=6$' /output
-! grep -q '^FOO=' /output
-! grep -q '^SYSLOG_FACILITY=' /output
+grep '^FOO=' /output && { echo 'unexpected success'; exit 1; }
+grep '^SYSLOG_FACILITY=' /output && { echo 'unexpected success'; exit 1; }
# `-b all` negates earlier use of -b (-b and -m are otherwise exclusive)
-journalctl -b -1 -b all -m > /dev/null
+journalctl -b -1 -b all -m >/dev/null
# -b always behaves like -b0
-journalctl -q -b-1 -b0 | head -1 > /expected
-journalctl -q -b-1 -b | head -1 > /output
+journalctl -q -b-1 -b0 | head -1 >/expected
+journalctl -q -b-1 -b | head -1 >/output
cmp /expected /output
# ... even when another option follows (both of these should fail due to -m)
-{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 > /expected
-{ journalctl -ball -b -m 2>&1 || :; } | head -1 > /output
+{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 >/expected
+{ journalctl -ball -b -m 2>&1 || :; } | head -1 >/output
cmp /expected /output
# https://github.com/systemd/systemd/issues/13708
@@ -83,7 +82,7 @@ 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=' >/output
-[[ `grep -c . /output` -eq 2 ]]
+[[ $(grep -c . /output) -eq 2 ]]
grep -q "^_PID=$PID" /output
grep -vq "^_PID=$PID" /output
diff --git a/test/units/testsuite-05.sh b/test/units/testsuite-05.sh
index 9168e72799..c98d849b08 100755
--- a/test/units/testsuite-05.sh
+++ b/test/units/testsuite-05.sh
@@ -1,6 +1,5 @@
#!/usr/bin/env bash
-set -x
-set -e
+set -eux
set -o pipefail
P=/run/systemd/system.conf.d
@@ -19,7 +18,9 @@ systemctl daemon-reload
[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
+# shellcheck disable=SC2016
systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
+# shellcheck disable=SC2016
systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
touch /testok
diff --git a/test/units/testsuite-06.sh b/test/units/testsuite-06.sh
index f9b106da7b..f89698ded4 100755
--- a/test/units/testsuite-06.sh
+++ b/test/units/testsuite-06.sh
@@ -1,6 +1,5 @@
#!/usr/bin/env bash
-set -x
-set -e
+set -eux
set -o pipefail
echo 1 >/sys/fs/selinux/enforce || {
diff --git a/test/units/testsuite-07.sh b/test/units/testsuite-07.sh
index fbb2d1d30a..bd1da341ac 100755
--- a/test/units/testsuite-07.sh
+++ b/test/units/testsuite-07.sh
@@ -1,31 +1,31 @@
#!/usr/bin/env bash
-set -x
-set -e
+set -eux
+set -o pipefail
->/failed
+: >/failed
-cat <<'EOL' >/lib/systemd/system/my.service
+cat >/lib/systemd/system/my.service <<EOF
[Service]
Type=oneshot
ExecStart=/bin/echo Timer runs me
-EOL
+EOF
-cat <<'EOL' >/lib/systemd/system/my.timer
+cat >/lib/systemd/system/my.timer <<EOF
[Timer]
OnBootSec=10s
OnUnitInactiveSec=1h
-EOL
+EOF
systemctl unmask my.timer
systemctl start my.timer
mkdir -p /etc/systemd/system/my.timer.d/
-cat <<'EOL' >/etc/systemd/system/my.timer.d/override.conf
+cat >/etc/systemd/system/my.timer.d/override.conf <<EOF
[Timer]
OnBootSec=10s
OnUnitInactiveSec=1h
-EOL
+EOF
systemctl daemon-reload
diff --git a/test/units/testsuite-11.sh b/test/units/testsuite-11.sh
index 708c7cebb7..97ab8be757 100755
--- a/test/units/testsuite-11.sh
+++ b/test/units/testsuite-11.sh
@@ -1,7 +1,8 @@
#!/usr/bin/env bash
-set -x
+set -eux
+set -o pipefail
-systemctl start fail-on-restart.service
+systemctl --no-block start fail-on-restart.service
active_state=$(systemctl show --value --property ActiveState fail-on-restart.service)
while [[ "$active_state" == "activating" || "$active_state" == "active" ]]; do
sleep 1
diff --git a/test/units/testsuite-12.sh b/test/units/testsuite-12.sh
index b5888a255b..c4ee600abe 100755
--- a/test/units/testsuite-12.sh
+++ b/test/units/testsuite-12.sh
@@ -1,10 +1,9 @@
#!/usr/bin/env bash
-set -x
-set -e
+set -eux
set -o pipefail
U=/run/systemd/system/test12.socket
-cat <<'EOF' >$U
+cat >$U <<EOF
[Unit]
Description=Test 12 socket
[Socket]
@@ -14,7 +13,7 @@ SocketGroup=adm
SocketMode=0660
EOF
-cat <<'EOF' > /run/systemd/system/test12@.service
+cat >/run/systemd/system/test12@.service <<EOF
[Unit]
Description=Test service
[Service]
diff --git a/test/units/testsuite-13.sh b/test/units/testsuite-13.sh
index 1844323d2f..0d468b2db1 100755
--- a/test/units/testsuite-13.sh
+++ b/test/units/testsuite-13.sh
@@ -1,7 +1,6 @@
#!/usr/bin/env bash
-set -x
-set -e
-set -u
+# shellcheck disable=SC2016
+set -eux
set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
@@ -31,10 +30,10 @@ if unshare -U sh -c :; then
is_user_ns_supported=yes
fi
-SUSE_OPTS=""
+SUSE_OPTS=()
ID_LIKE=$(awk -F= '$1=="ID_LIKE" { print $2 ;}' /etc/os-release)
if [[ "$ID_LIKE" = *"suse"* ]]; then
- SUSE_OPTS="--bind /lib64 --bind /usr/lib64 "
+ SUSE_OPTS+=(--bind /lib64 --bind /usr/lib64)
fi
function check_bind_tmp_path {
@@ -42,8 +41,8 @@ function check_bind_tmp_path {
local _root="/var/lib/machines/testsuite-13.bind-tmp-path"
rm -rf "$_root"
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
- >/tmp/bind
- systemd-nspawn $SUSE_OPTS--register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
+ : >/tmp/bind
+ systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
}
function check_norbind {
@@ -51,19 +50,19 @@ function check_norbind {
local _root="/var/lib/machines/testsuite-13.norbind-path"
rm -rf "$_root"
mkdir -p /tmp/binddir/subdir
- echo -n "outer" > /tmp/binddir/subdir/file
+ echo -n "outer" >/tmp/binddir/subdir/file
mount -t tmpfs tmpfs /tmp/binddir/subdir
- echo -n "inner" > /tmp/binddir/subdir/file
+ echo -n "inner" >/tmp/binddir/subdir/file
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
- systemd-nspawn $SUSE_OPTS--register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
+ systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
}
function check_notification_socket {
# https://github.com/systemd/systemd/issues/4944
local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/host/notify'
# /testsuite-13.nc-container is prepared by test.sh
- systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd"
- systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd"
+ systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd"
+ systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd"
}
function check_os_release {
@@ -73,19 +72,19 @@ if [ -n "${VERSION_ID:+set}" ] && [ "${VERSION_ID}" != "${container_host_version
if [ -n "${BUILD_ID:+set}" ] && [ "${BUILD_ID}" != "${container_host_build_id}" ]; then exit 1; fi
if [ -n "${VARIANT_ID:+set}" ] && [ "${VARIANT_ID}" != "${container_host_variant_id}" ]; then exit 1; fi
cd /tmp; (cd /run/host; md5sum os-release) | md5sum -c
-if echo test >> /run/host/os-release; then exit 1; fi
+if echo test >>/run/host/os-release; then exit 1; fi
'
local _os_release_source="/etc/os-release"
- if [ ! -r "${_os_release_source}" ]; then
+ if [[ ! -r "${_os_release_source}" ]]; then
_os_release_source="/usr/lib/os-release"
- elif [ -L "${_os_release_source}" ] && rm /etc/os-release; then
+ elif [[ -L "${_os_release_source}" ]] && rm /etc/os-release; then
# Ensure that /etc always wins if available
cp /usr/lib/os-release /etc
- echo MARKER=1 >> /etc/os-release
+ echo MARKER=1 >>/etc/os-release
fi
- systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
+ systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
if grep -q MARKER /etc/os-release; then
rm /etc/os-release
@@ -96,10 +95,10 @@ if echo test >> /run/host/os-release; then exit 1; fi
function check_machinectl_bind {
local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep 0.5; done; exit 1;'
- cat <<EOF > /run/systemd/system/nspawn_machinectl_bind.service
+ cat >/run/systemd/system/nspawn_machinectl_bind.service <<EOF
[Service]
Type=notify
-ExecStart=systemd-nspawn $SUSE_OPTS -D /testsuite-13.nc-container --notify-ready=no /bin/sh -x -e -c "$_cmd"
+ExecStart=systemd-nspawn ${SUSE_OPTS[@]} -D /testsuite-13.nc-container --notify-ready=no /bin/sh -x -e -c "$_cmd"
EOF
systemctl start nspawn_machinectl_bind.service
@@ -113,7 +112,7 @@ EOF
sleep 0.1
done
- return $(systemctl show -P ExecMainStatus nspawn_machinectl_bind.service)
+ return "$(systemctl show -P ExecMainStatus nspawn_machinectl_bind.service)"
}
function run {
@@ -129,65 +128,54 @@ function run {
local _root="/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3"
rm -rf "$_root"
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
- SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" -b
- SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" --private-network -b
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --private-network -b
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" -U -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -U -b; then
[[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1
else
[[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1
fi
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" --private-network -U -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --private-network -U -b; then
[[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1
else
[[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1
fi
local _netns_opt="--network-namespace-path=/proc/self/ns/net"
+ 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
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-interface=lo -b; then
- return 1
- fi
-
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-macvlan=lo -b; then
- return 1
- fi
-
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-ipvlan=lo -b; then
- return 1
- fi
-
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-veth -b; then
- return 1
- fi
-
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-veth-extra=lo -b; then
- return 1
- fi
-
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-bridge=lo -b; then
- return 1
- fi
-
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --network-zone=zone -b; then
- return 1
- fi
+ for netopt in "${_net_opts[@]}"; do
+ echo "$_netns_opt in combination with $netopt should fail"
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b "$_netns_opt" "$netopt"; then
+ echo >&2 "unexpected pass"
+ return 1
+ fi
+ done
# allow combination of --network-namespace-path and --private-network
- if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" --private-network -b; then
+ if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b "$_netns_opt" --private-network; then
return 1
fi
# 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="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn $SUSE_OPTS--register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
local r=$?
ip netns del nspawn_test
- if [ $r -ne 0 ]; then
+ if [[ $r -ne 0 ]]; then
return 1
fi
diff --git a/test/units/testsuite-14.sh b/test/units/testsuite-14.sh
index 95ac9b65ae..7a64d93736 100755
--- a/test/units/testsuite-14.sh
+++ b/test/units/testsuite-14.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-set -e
-set -x
+set -eux
+set -o pipefail
function setup_root {
local _root="$1"
@@ -21,18 +21,18 @@ r="$(pwd)/overwrite-broken-machine-id"
setup_root "$r"
systemd-machine-id-setup --print --root "$r"
echo abc >>"$r/etc/machine-id"
-id=$(systemd-machine-id-setup --print --root "$r")
-echo $id >expected
+id="$(systemd-machine-id-setup --print --root "$r")"
+echo "$id" >expected
check expected "$r/etc/machine-id"
-r="$(pwd)/transient-machine-id"
+r="$PWD/transient-machine-id"
setup_root "$r"
systemd-machine-id-setup --print --root "$r"
echo abc >>"$r/etc/machine-id"
mount -o remount,ro "$r"
mount -t tmpfs tmpfs "$r/run"
-transient_id=$(systemd-machine-id-setup --print --root "$r")
+transient_id="$(systemd-machine-id-setup --print --root "$r")"
mount -o remount,rw "$r"
-commited_id=$(systemd-machine-id-setup --print --commit --root "$r")
+commited_id="$(systemd-machine-id-setup --print --commit --root "$r")"
[[ "$transient_id" = "$commited_id" ]]
check "$r/etc/machine-id" "$r/run/machine-id"
diff --git a/test/units/testsuite-15.sh b/test/units/testsuite-15.sh
index 23a39bf090..23c2156501 100755
--- a/test/units/testsuite-15.sh
+++ b/test/units/testsuite-15.sh
@@ -1,53 +1,55 @@
-#! /bin/bash
-set -e
-set -x
+#!/bin/bash
+set -eux
+set -o pipefail
_clear_service () {
- systemctl stop $1.service 2>/dev/null || :
- rm -f /{etc,run,usr/lib}/systemd/system/$1.service
- rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.d
- rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.{wants,requires}
- if [[ $1 == *@ ]]; then
- systemctl stop $1*.service 2>/dev/null || :
- rm -f /{etc,run,usr/lib}/systemd/system/$1*.service
- rm -fr /{etc,run,usr/lib}/systemd/system/$1*.service.d
- rm -fr /{etc,run,usr/lib}/systemd/system/$1*.service.{wants,requires}
+ local SERVICE_NAME="${1:?_clear_service: missing argument}"
+ systemctl stop "$SERVICE_NAME.service" 2>/dev/null || :
+ rm -f /{etc,run,usr/lib}/systemd/system/"$SERVICE_NAME".service
+ rm -fr /{etc,run,usr/lib}/systemd/system/"$SERVICE_NAME".service.d
+ rm -fr /{etc,run,usr/lib}/systemd/system/"$SERVICE_NAME".service.{wants,requires}
+ if [[ $SERVICE_NAME == *@ ]]; then
+ systemctl stop "$SERVICE_NAME"*.service 2>/dev/null || :
+ rm -f /{etc,run,usr/lib}/systemd/system/"$SERVICE_NAME"*.service
+ rm -fr /{etc,run,usr/lib}/systemd/system/"$SERVICE_NAME"*.service.d
+ rm -fr /{etc,run,usr/lib}/systemd/system/"$SERVICE_NAME"*.service.{wants,requires}
fi
}
clear_services () {
- for u in $*; do
- _clear_service $u
+ for u in "$@"; do
+ _clear_service "$u"
done
systemctl daemon-reload
}
create_service () {
- clear_services $1
+ local SERVICE_NAME="${1:?create_service: missing argument}"
+ clear_services "$SERVICE_NAME"
- cat >/etc/systemd/system/$1.service<<EOF
+ cat >/etc/systemd/system/"$SERVICE_NAME".service <<EOF
[Unit]
-Description=$1 unit
+Description=$SERVICE_NAME unit
[Service]
ExecStart=/bin/sleep 100000
EOF
- mkdir -p /{etc,run,usr/lib}/systemd/system/$1.service.d
- mkdir -p /etc/systemd/system/$1.service.{wants,requires}
- mkdir -p /run/systemd/system/$1.service.{wants,requires}
- mkdir -p /usr/lib/systemd/system/$1.service.{wants,requires}
+ mkdir -p /{etc,run,usr/lib}/systemd/system/"$SERVICE_NAME".service.d
+ mkdir -p /etc/systemd/system/"$SERVICE_NAME".service.{wants,requires}
+ mkdir -p /run/systemd/system/"$SERVICE_NAME".service.{wants,requires}
+ mkdir -p /usr/lib/systemd/system/"$SERVICE_NAME".service.{wants,requires}
}
create_services () {
- for u in $*; do
- create_service $u
+ for u in "$@"; do
+ create_service "$u"
done
}
check_ok () {
[ $# -eq 3 ] || return
- x="$(systemctl show --value -p $2 $1)"
+ x="$(systemctl show --value -p "$2" "$1")"
case "$x" in
*$3*) return 0 ;;
*) return 1 ;;
@@ -165,7 +167,7 @@ test_hierarchical_dropins () {
echo "
[Service]
ExecCondition=/bin/echo $dropin
- " > /usr/lib/systemd/system/$dropin/override.conf
+ " >/usr/lib/systemd/system/$dropin/override.conf
systemctl daemon-reload
check_ok a-b-c ExecCondition "/bin/echo $dropin"
done
diff --git a/test/units/testsuite-16.sh b/test/units/testsuite-16.sh
index 8388ef7a5d..9f3a843da6 100755
--- a/test/units/testsuite-16.sh
+++ b/test/units/testsuite-16.sh
@@ -1,25 +1,24 @@
#!/usr/bin/env bash
-set -v -x
+set -eux
+set -o pipefail
rm -f /test.log
-TL=/test.log.XXXXXXXX
+TESTLOG=/test.log.XXXXXXXX
function wait_for()
{
- service=${1}
- result=${2:-success}
- time=${3:-45}
+ local service="${1:-wait_for: missing service argument}"
+ local result="${2:-success}"
+ local time="${3:-45}"
- while [[ ! -f /${service}.terminated && ! -f /${service}.success && $time -gt 0 ]]
- do
+ while [[ ! -f /${service}.terminated && ! -f /${service}.success && $time -gt 0 ]]; do
sleep 1
- time=$(( $time - 1 ))
+ time=$((time - 1))
done
- if [[ ! -f /${service}.${result} ]]
- then
- journalctl -u ${service/_/-}.service >> "${TL}"
+ if [[ ! -f /${service}.${result} ]]; then
+ journalctl -u "${service/_/-}.service" >>"$TESTLOG"
fi
}
@@ -44,12 +43,11 @@ wait_for fail_start startfail
wait_for fail_stop stopfail
wait_for fail_runtime runtimefail
-if [[ -f "${TL}" ]]
-then
+if [[ -f "$TESTLOG" ]]; then
# no mv
- cp "${TL}" /test.log
+ cp "$TESTLOG" /test.log
exit 1
-else
- touch /testok
- exit 0
fi
+
+touch /testok
+exit 0
diff --git a/test/units/testsuite-17.01.sh b/test/units/testsuite-17.01.sh
index 573ad41079..8be27d9507 100755
--- a/test/units/testsuite-17.01.sh
+++ b/test/units/testsuite-17.01.sh
@@ -19,7 +19,7 @@ while : ; do
sleep .5
done
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="foobar.service"
EOF
udevadm control --reload
@@ -36,7 +36,7 @@ while : ; do
sleep .5
done
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="waldo.service"
EOF
udevadm control --reload
diff --git a/test/units/testsuite-17.02.sh b/test/units/testsuite-17.02.sh
index c21fcc781b..5c77ab4b6a 100755
--- a/test/units/testsuite-17.02.sh
+++ b/test/units/testsuite-17.02.sh
@@ -4,7 +4,7 @@ set -o pipefail
mkdir -p /run/udev/rules.d/
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
ACTION=="remove", GOTO="lo_end"
SUBSYSTEM=="net", KERNEL=="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/lo"
diff --git a/test/units/testsuite-17.03.sh b/test/units/testsuite-17.03.sh
index 02ac2176b5..0452a06126 100755
--- a/test/units/testsuite-17.03.sh
+++ b/test/units/testsuite-17.03.sh
@@ -7,9 +7,9 @@ test_rule="/run/udev/rules.d/49-test.rules"
setup() {
mkdir -p "${test_rule%/*}"
cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bckp
- echo 'KERNEL=="lo", SUBSYSTEM=="net", PROGRAM=="/bin/sleep 60"' > "${test_rule}"
- echo "event_timeout=30" >> /etc/udev/udev.conf
- echo "timeout_signal=SIGABRT" >> /etc/udev/udev.conf
+ echo 'KERNEL=="lo", SUBSYSTEM=="net", PROGRAM=="/bin/sleep 60"' >"${test_rule}"
+ echo "event_timeout=30" >>/etc/udev/udev.conf
+ echo "timeout_signal=SIGABRT" >>/etc/udev/udev.conf
systemctl restart systemd-udevd.service
}
@@ -25,9 +25,9 @@ teardown() {
run_test() {
since="$(date +%T)"
- echo add > /sys/class/net/lo/uevent
+ echo add >/sys/class/net/lo/uevent
- for n in {1..20}; do
+ for _ in {1..20}; do
sleep 5
if coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then
return 0
diff --git a/test/units/testsuite-17.04.sh b/test/units/testsuite-17.04.sh
index c799936c0a..b33d47ef6d 100755
--- a/test/units/testsuite-17.04.sh
+++ b/test/units/testsuite-17.04.sh
@@ -4,14 +4,11 @@ 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 -q -v 'E: TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q -v 'E: TAGS=.*:changed:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:changed:.*'
+test ! -f /run/udev/tags/added/c1:3
+test ! -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -E 'E: (TAGS|CURRENT_TAGS)=.*:(added|changed):' && exit 1
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", TAG+="added"
ACTION=="change", SUBSYSTEM=="mem", KERNEL=="null", TAG+="changed"
EOF
@@ -19,45 +16,39 @@ EOF
udevadm control --reload
udevadm trigger -c add /dev/null
-while : ; do
- test -f /run/udev/tags/added/c1:3 &&
- ! test -f /run/udev/tags/changed/c1:3 &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q -v 'E: TAGS=.*:changed:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:changed:.*' &&
- break
-
+while test ! -f /run/udev/tags/added/c1:3 ||
+ test -f /run/udev/tags/changed/c1:3 ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' ||
+ udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' ||
+ udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
+do
sleep .5
done
udevadm control --reload
udevadm trigger -c change /dev/null
-while : ; do
- test -f /run/udev/tags/added/c1:3 &&
- test -f /run/udev/tags/changed/c1:3 &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' &&
- udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' &&
- break
-
+while test ! -f /run/udev/tags/added/c1:3 ||
+ test ! -f /run/udev/tags/changed/c1:3 ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' ||
+ udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
+do
sleep .5
done
udevadm control --reload
udevadm trigger -c add /dev/null
-while : ; do
- test -f /run/udev/tags/added/c1:3 &&
- test -f /run/udev/tags/changed/c1:3 &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:changed:.*' &&
- break
-
+while test ! -f /run/udev/tags/added/c1:3 ||
+ test ! -f /run/udev/tags/changed/c1:3 ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' ||
+ udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
+do
sleep .5
done
diff --git a/test/units/testsuite-17.05.sh b/test/units/testsuite-17.05.sh
index 0991ec9d59..1e7f7f41b0 100755
--- a/test/units/testsuite-17.05.sh
+++ b/test/units/testsuite-17.05.sh
@@ -4,7 +4,7 @@ set -o pipefail
mkdir -p /run/udev/rules.d/
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", IMPORT{program}="/bin/echo -e HOGE=aa\\\\x20\\\\x20\\\\x20bb\nFOO=\\\\x20aaa\\\\x20\n\n\n"
EOF
diff --git a/test/units/testsuite-17.sh b/test/units/testsuite-17.sh
index afce85aceb..65d0802969 100755
--- a/test/units/testsuite-17.sh
+++ b/test/units/testsuite-17.sh
@@ -1,12 +1,12 @@
#!/usr/bin/env bash
-set -x
-set -e
+set -eux
+set -o pipefail
->/failed
+: >/failed
-for t in ${0%.sh}.*.sh; do
- echo "Running $t"; ./$t
+for t in "${0%.sh}".*.sh; do
+ echo "Running $t"; ./"$t"
done
touch /testok
diff --git a/test/units/testsuite-18.sh b/test/units/testsuite-18.sh
index e471cda51b..fe4f9ce5e0 100755
--- a/test/units/testsuite-18.sh
+++ b/test/units/testsuite-18.sh
@@ -1,15 +1,15 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-run --wait -p FailureAction=poweroff true
-! systemd-run --wait -p SuccessAction=poweroff false
+systemd-run --wait -p SuccessAction=poweroff false && { echo 'unexpected success'; exit 1; }
if ! test -f /firstphase ; then
- echo OK > /firstphase
+ echo OK >/firstphase
systemd-run --wait -p SuccessAction=reboot true
else
- echo OK > /testok
+ echo OK >/testok
systemd-run --wait -p FailureAction=poweroff false
fi
diff --git a/test/units/testsuite-19.sh b/test/units/testsuite-19.sh
index 57831c267f..8ea89e6cfe 100755
--- a/test/units/testsuite-19.sh
+++ b/test/units/testsuite-19.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
if grep -q cgroup2 /proc/filesystems ; then
@@ -34,6 +34,6 @@ else
echo "Skipping TEST-19-DELEGATE, as the kernel doesn't actually support cgroup v2" >&2
fi
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-20.sh b/test/units/testsuite-20.sh
index d94f6b2afb..7876a9d10c 100755
--- a/test/units/testsuite-20.sh
+++ b/test/units/testsuite-20.sh
@@ -1,11 +1,11 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
systemd-analyze log-target console
-test `systemctl show -P MainPID testsuite-20.service` -eq $$
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
# Start a test process inside of our own cgroup
sleep infinity &
@@ -14,45 +14,45 @@ disown
# Start a test process outside of our own cgroup
systemd-run -p DynamicUser=1 --unit=test20-sleep.service /bin/sleep infinity
-EXTERNALPID=`systemctl show -P MainPID test20-sleep.service`
+EXTERNALPID="$(systemctl show -P MainPID test20-sleep.service)"
# Update our own main PID to the external test PID, this should work
-systemd-notify MAINPID=$EXTERNALPID
-test `systemctl show -P MainPID testsuite-20.service` -eq $EXTERNALPID
+systemd-notify MAINPID="$EXTERNALPID"
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq "$EXTERNALPID"
# Update our own main PID to the internal test PID, this should work, too
systemd-notify MAINPID=$INTERNALPID
-test `systemctl show -P MainPID testsuite-20.service` -eq $INTERNALPID
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq "$INTERNALPID"
# Update it back to our own PID, this should also work
systemd-notify MAINPID=$$
-test `systemctl show -P MainPID testsuite-20.service` -eq $$
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
# Try to set it to PID 1, which it should ignore, because that's the manager
systemd-notify MAINPID=1
-test `systemctl show -P MainPID testsuite-20.service` -eq $$
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
# Try to set it to PID 0, which is invalid and should be ignored
systemd-notify MAINPID=0
-test `systemctl show -P MainPID testsuite-20.service` -eq $$
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
# Try to set it to a valid but non-existing PID, which should be ignored. (Note
# that we set the PID to a value well above any known /proc/sys/kernel/pid_max,
# which means we can be pretty sure it doesn't exist by coincidence)
systemd-notify MAINPID=1073741824
-test `systemctl show -P MainPID testsuite-20.service` -eq $$
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
# Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges.
-systemd-notify --uid=1000 MAINPID=$EXTERNALPID
-test `systemctl show -P MainPID testsuite-20.service` -eq $$
+systemd-notify --uid=1000 MAINPID="$EXTERNALPID"
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
# Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges.
-systemd-notify --uid=1000 MAINPID=$INTERNALPID
-test `systemctl show -P MainPID testsuite-20.service` -eq $INTERNALPID
+systemd-notify --uid=1000 MAINPID="$INTERNALPID"
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq "$INTERNALPID"
# Update it back to our own PID, this should also work
systemd-notify --uid=1000 MAINPID=$$
-test `systemctl show -P MainPID testsuite-20.service` -eq $$
+test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
cat >/tmp/test20-mainpid.sh <<EOF
#!/usr/bin/env bash
@@ -71,12 +71,12 @@ disown
sleep infinity &
disown
-echo \$MAINPID > /run/mainpidsh/pid
+echo \$MAINPID >/run/mainpidsh/pid
EOF
chmod +x /tmp/test20-mainpid.sh
systemd-run --unit=test20-mainpidsh.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh -p PIDFile=/run/mainpidsh/pid /tmp/test20-mainpid.sh
-test `systemctl show -P MainPID test20-mainpidsh.service` -eq `cat /run/mainpidsh/pid`
+test "$(systemctl show -P MainPID test20-mainpidsh.service)" -eq "$(cat /run/mainpidsh/pid)"
cat >/tmp/test20-mainpid2.sh <<EOF
#!/usr/bin/env bash
@@ -95,13 +95,13 @@ disown
sleep infinity &
disown
-echo \$MAINPID > /run/mainpidsh2/pid
+echo \$MAINPID >/run/mainpidsh2/pid
chown 1001:1001 /run/mainpidsh2/pid
EOF
chmod +x /tmp/test20-mainpid2.sh
systemd-run --unit=test20-mainpidsh2.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh2 -p PIDFile=/run/mainpidsh2/pid /tmp/test20-mainpid2.sh
-test `systemctl show -P MainPID test20-mainpidsh2.service` -eq `cat /run/mainpidsh2/pid`
+test "$(systemctl show -P MainPID test20-mainpidsh2.service)" -eq "$(cat /run/mainpidsh2/pid)"
cat >/dev/shm/test20-mainpid3.sh <<EOF
#!/usr/bin/env bash
@@ -126,14 +126,24 @@ test -f /run/mainpidsh3/pid
EOF
chmod 755 /dev/shm/test20-mainpid3.sh
-# This has to fail, as we shouldn't accept the dangerous PID file, and then inotify-wait on it to be corrected which we never do
-! systemd-run --unit=test20-mainpidsh3.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh3 -p PIDFile=/run/mainpidsh3/pid -p DynamicUser=1 -p TimeoutStartSec=2s /dev/shm/test20-mainpid3.sh
+# This has to fail, as we shouldn't accept the dangerous PID file, and then
+# inotify-wait on it to be corrected which we never do.
+systemd-run --unit=test20-mainpidsh3.service \
+ -p StandardOutput=tty \
+ -p StandardError=tty \
+ -p Type=forking \
+ -p RuntimeDirectory=mainpidsh3 \
+ -p PIDFile=/run/mainpidsh3/pid \
+ -p DynamicUser=1 \
+ -p TimeoutStartSec=2s \
+ /dev/shm/test20-mainpid3.sh \
+ && { echo 'unexpected success'; exit 1; }
# Test that this failed due to timeout, and not some other error
-test `systemctl show -P Result test20-mainpidsh3.service` = timeout
+test "$(systemctl show -P Result test20-mainpidsh3.service)" = timeout
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-22.01.sh b/test/units/testsuite-22.01.sh
index d233e37fb2..7975980799 100755
--- a/test/units/testsuite-22.01.sh
+++ b/test/units/testsuite-22.01.sh
@@ -1,13 +1,14 @@
-#! /bin/bash
+#!/bin/bash
#
# With "e" don't attempt to set permissions when file doesn't exist, see
# https://github.com/systemd/systemd/pull/6682.
#
-set -e
+set -eux
+set -o pipefail
rm -fr /tmp/test
echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create -
-! test -e /tmp/test
+test ! -e /tmp/test
diff --git a/test/units/testsuite-22.02.sh b/test/units/testsuite-22.02.sh
index d1bf1ea04b..94fa11ba88 100755
--- a/test/units/testsuite-22.02.sh
+++ b/test/units/testsuite-22.02.sh
@@ -1,10 +1,10 @@
-#! /bin/bash
+#!/bin/bash
#
# Basic tests for types creating directories
#
-set -e
-set -x
+set -eux
+set -o pipefail
rm -fr /tmp/{C,d,D,e}
mkdir /tmp/{C,d,D,e}
@@ -21,10 +21,10 @@ d /tmp/d/2 0755 daemon daemon - -
EOF
test -d /tmp/d/1
-test $(stat -c %U:%G:%a /tmp/d/1) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/d/1)" = "daemon:daemon:755"
test -d /tmp/d/2
-test $(stat -c %U:%G:%a /tmp/d/2) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/d/2)" = "daemon:daemon:755"
#
# 'D'
@@ -39,10 +39,10 @@ D /tmp/D/2 0755 daemon daemon - -
EOF
test -d /tmp/D/1
-test $(stat -c %U:%G:%a /tmp/D/1) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/D/1)" = "daemon:daemon:755"
test -d /tmp/D/2
-test $(stat -c %U:%G:%a /tmp/D/2) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/D/2)" = "daemon:daemon:755"
systemd-tmpfiles --remove - <<EOF
D /tmp/D/2 0755 daemon daemon - -
@@ -63,15 +63,15 @@ e /tmp/e/1 0755 daemon daemon - -
e /tmp/e/2/* 0755 daemon daemon - -
EOF
-! test -d /tmp/e/1
+test ! -d /tmp/e/1
test -d /tmp/e/2
-test $(stat -c %U:%G:%a /tmp/e/2) = "root:root:777"
+test "$(stat -c %U:%G:%a /tmp/e/2)" = "root:root:777"
test -d /tmp/e/2/d1
-test $(stat -c %U:%G:%a /tmp/e/2/d1) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/e/2/d1)" = "daemon:daemon:755"
test -d /tmp/e/2/d2
-test $(stat -c %U:%G:%a /tmp/e/2/d2) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/e/2/d2)" = "daemon:daemon:755"
# 'e' operates on directories only
mkdir -p /tmp/e/3/{d1,d2}
@@ -80,19 +80,19 @@ chmod 777 /tmp/e/3/d*
touch /tmp/e/3/f1
chmod 644 /tmp/e/3/f1
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF
e /tmp/e/3/* 0755 daemon daemon - -
EOF
# the directories should have been processed although systemd-tmpfiles failed
# previously due to the presence of a file.
test -d /tmp/e/3/d1
-test $(stat -c %U:%G:%a /tmp/e/3/d1) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/e/3/d1)" = "daemon:daemon:755"
test -d /tmp/e/3/d2
-test $(stat -c %U:%G:%a /tmp/e/3/d2) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/e/3/d2)" = "daemon:daemon:755"
test -f /tmp/e/3/f1
-test $(stat -c %U:%G:%a /tmp/e/3/f1) = "root:root:644"
+test "$(stat -c %U:%G:%a /tmp/e/3/f1)" = "root:root:644"
#
# 'C'
@@ -111,12 +111,12 @@ C /tmp/C/2 0755 daemon daemon - /tmp/C/2-origin
EOF
test -d /tmp/C/1
-test $(stat -c %U:%G:%a /tmp/C/1/f1) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/C/1/f1)" = "daemon:daemon:755"
test -d /tmp/C/2
-test $(stat -c %U:%G:%a /tmp/C/2/f1) = "daemon:daemon:755"
+test "$(stat -c %U:%G:%a /tmp/C/2/f1)" = "daemon:daemon:755"
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF
C /tmp/C/3 0755 daemon daemon - /tmp/C/3-origin
EOF
-test $(stat -c %U:%G:%a /tmp/C/3/f1) = "root:root:644"
+test "$(stat -c %U:%G:%a /tmp/C/3/f1)" = "root:root:644"
diff --git a/test/units/testsuite-22.03.sh b/test/units/testsuite-22.03.sh
index 8d009fb5bb..1ed026eb70 100755
--- a/test/units/testsuite-22.03.sh
+++ b/test/units/testsuite-22.03.sh
@@ -1,10 +1,10 @@
-#! /bin/bash
+#!/bin/bash
#
# Basic tests for types creating/writing files
#
-set -e
-set -x
+set -eux
+set -o pipefail
rm -fr /tmp/{f,F,w}
mkdir /tmp/{f,F,w}
@@ -19,10 +19,10 @@ f /tmp/f/2 0644 - - - This string should be written
EOF
### '1' should exist and be empty
-test -f /tmp/f/1; ! test -s /tmp/f/1
-test $(stat -c %U:%G:%a /tmp/f/1) = "root:root:644"
+test -f /tmp/f/1; test ! -s /tmp/f/1
+test "$(stat -c %U:%G:%a /tmp/f/1)" = "root:root:644"
-test $(stat -c %U:%G:%a /tmp/f/2) = "root:root:644"
+test "$(stat -c %U:%G:%a /tmp/f/2)" = "root:root:644"
test "$(< /tmp/f/2)" = "This string should be written"
### The perms are supposed to be updated even if the file already exists.
@@ -31,31 +31,31 @@ f /tmp/f/1 0666 daemon daemon - This string should not be written
EOF
# file should be empty
-! test -s /tmp/f/1
-test $(stat -c %U:%G:%a /tmp/f/1) = "daemon:daemon:666"
+test ! -s /tmp/f/1
+test "$(stat -c %U:%G:%a /tmp/f/1)" = "daemon:daemon:666"
### But we shouldn't try to set perms on an existing file which is not a
### regular one.
mkfifo /tmp/f/fifo
chmod 644 /tmp/f/fifo
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/fifo 0666 daemon daemon - This string should not be written
EOF
test -p /tmp/f/fifo
-test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
+test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
### 'f' should not follow symlinks.
ln -s missing /tmp/f/dangling
ln -s /tmp/file-owned-by-root /tmp/f/symlink
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/dangling 0644 daemon daemon - -
f /tmp/f/symlink 0644 daemon daemon - -
EOF
-! test -e /tmp/f/missing
-test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
+test ! -e /tmp/f/missing
+test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
### Handle read-only filesystem gracefully: we shouldn't fail if the target
### already exists and have the correct perms.
@@ -70,27 +70,27 @@ mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs
systemd-tmpfiles --create - <<EOF
f /tmp/f/ro-fs/foo 0644 - - - - This string should not be written
EOF
-test -f /tmp/f/ro-fs/foo; ! test -s /tmp/f/ro-fs/foo
+test -f /tmp/f/ro-fs/foo; test ! -s /tmp/f/ro-fs/foo
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/ro-fs/foo 0666 - - - -
EOF
-test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
+test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/ro-fs/bar 0644 - - - -
EOF
-! test -e /tmp/f/ro-fs/bar
+test ! -e /tmp/f/ro-fs/bar
### 'f' shouldn't follow unsafe paths.
mkdir /tmp/f/daemon
ln -s /root /tmp/f/daemon/unsafe-symlink
chown -R --no-dereference daemon:daemon /tmp/f/daemon
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
EOF
-! test -e /tmp/f/daemon/unsafe-symlink/exploit
+test ! -e /tmp/f/daemon/unsafe-symlink/exploit
#
# 'F'
@@ -105,19 +105,19 @@ F /tmp/F/truncated 0666 daemon daemon - -
F /tmp/F/truncated-with-content 0666 daemon daemon - new content
EOF
-test -f /tmp/F/created; ! test -s /tmp/F/created
+test -f /tmp/F/created; test ! -s /tmp/F/created
test -f /tmp/F/created-with-content
test "$(< /tmp/F/created-with-content)" = "new content"
-test -f /tmp/F/truncated; ! test -s /tmp/F/truncated
-test $(stat -c %U:%G:%a /tmp/F/truncated) = "daemon:daemon:666"
+test -f /tmp/F/truncated; test ! -s /tmp/F/truncated
+test "$(stat -c %U:%G:%a /tmp/F/truncated)" = "daemon:daemon:666"
test -s /tmp/F/truncated-with-content
-test $(stat -c %U:%G:%a /tmp/F/truncated-with-content) = "daemon:daemon:666"
+test "$(stat -c %U:%G:%a /tmp/F/truncated-with-content)" = "daemon:daemon:666"
### We shouldn't try to truncate anything but regular files since the behavior is
### unspecified in the other cases.
mkfifo /tmp/F/fifo
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/fifo 0644 - - - -
EOF
@@ -127,12 +127,12 @@ test -p /tmp/F/fifo
ln -s missing /tmp/F/dangling
ln -s /tmp/file-owned-by-root /tmp/F/symlink
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/F/dangling 0644 daemon daemon - -
f /tmp/F/symlink 0644 daemon daemon - -
EOF
-! test -e /tmp/F/missing
-test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
+test ! -e /tmp/F/missing
+test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
### Handle read-only filesystem gracefully: we shouldn't fail if the target
### already exists and is empty.
@@ -147,40 +147,41 @@ mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs
systemd-tmpfiles --create - <<EOF
F /tmp/F/ro-fs/foo 0644 - - - -
EOF
-test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
+test -f /tmp/F/ro-fs/foo; test ! -s /tmp/F/ro-fs/foo
echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/ro-fs/foo 0644 - - - -
EOF
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/ro-fs/foo 0644 - - - - This string should not be written
EOF
-test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
+test -f /tmp/F/ro-fs/foo
+grep -q 'truncating is not allowed' /tmp/F/ro-fs/foo
# Trying to change the perms should fail.
->/tmp/F/rw-fs/foo
-! systemd-tmpfiles --create - <<EOF
+: >/tmp/F/rw-fs/foo
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/ro-fs/foo 0666 - - - -
EOF
-test $(stat -c %U:%G:%a /tmp/F/ro-fs/foo) = "root:root:644"
+test "$(stat -c %U:%G:%a /tmp/F/ro-fs/foo)" = "root:root:644"
### Try to create a new file.
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/ro-fs/bar 0644 - - - -
EOF
-! test -e /tmp/F/ro-fs/bar
+test ! -e /tmp/F/ro-fs/bar
### 'F' shouldn't follow unsafe paths.
mkdir /tmp/F/daemon
ln -s /root /tmp/F/daemon/unsafe-symlink
chown -R --no-dereference daemon:daemon /tmp/F/daemon
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
EOF
-! test -e /tmp/F/daemon/unsafe-symlink/exploit
+test ! -e /tmp/F/daemon/unsafe-symlink/exploit
#
# 'w'
@@ -191,10 +192,10 @@ touch /tmp/w/overwritten
systemd-tmpfiles --create - <<EOF
w /tmp/w/unexistent 0644 - - - new content
EOF
-! test -e /tmp/w/unexistent
+test ! -e /tmp/w/unexistent
### no argument given -> fails.
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
w /tmp/w/unexistent 0644 - - - -
EOF
@@ -230,7 +231,7 @@ mkdir /tmp/w/daemon
ln -s /root /tmp/w/daemon/unsafe-symlink
chown -R --no-dereference daemon:daemon /tmp/w/daemon
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/w/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
EOF
-! test -e /tmp/w/daemon/unsafe-symlink/exploit
+test ! -e /tmp/w/daemon/unsafe-symlink/exploit
diff --git a/test/units/testsuite-22.04.sh b/test/units/testsuite-22.04.sh
index f916086b1e..d814ed8786 100755
--- a/test/units/testsuite-22.04.sh
+++ b/test/units/testsuite-22.04.sh
@@ -1,10 +1,10 @@
-#! /bin/bash
+#!/bin/bash
#
# Basic tests for types creating fifos
#
-set -e
-set -x
+set -eux
+set -o pipefail
rm -fr /tmp/p
mkdir /tmp/p
@@ -15,10 +15,10 @@ p /tmp/p/fifo1 0666 - - - -
EOF
test -p /tmp/p/fifo1
-test $(stat -c %U:%G:%a /tmp/p/fifo1) = "root:root:666"
+test "$(stat -c %U:%G:%a /tmp/p/fifo1)" = "root:root:666"
-# it should refuse to overwrite an existing file
-! systemd-tmpfiles --create - <<EOF
+# Refuse to overwrite an existing file. Error is not propagated.
+systemd-tmpfiles --create - <<EOF
p /tmp/p/f1 0666 - - - -
EOF
@@ -30,7 +30,7 @@ p+ /tmp/p/f1 0666 - - - -
EOF
test -p /tmp/p/f1
-test $(stat -c %U:%G:%a /tmp/p/f1) = "root:root:666"
+test "$(stat -c %U:%G:%a /tmp/p/f1)" = "root:root:666"
#
# Must be fixed
diff --git a/test/units/testsuite-22.05.sh b/test/units/testsuite-22.05.sh
index 13c4ac80fc..eaabbc4517 100755
--- a/test/units/testsuite-22.05.sh
+++ b/test/units/testsuite-22.05.sh
@@ -1,7 +1,7 @@
#! /bin/bash
-set -e
-set -x
+set -eux
+set -o pipefail
rm -fr /tmp/{z,Z}
mkdir /tmp/{z,Z}
@@ -17,15 +17,15 @@ z /tmp/z/f1 0755 daemon daemon - -
z /tmp/z/d1 0755 daemon daemon - -
EOF
-test $(stat -c %U:%G /tmp/z/f1) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/z/d1) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/z/d1/f11) = "root:root"
+test "$(stat -c %U:%G /tmp/z/f1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/z/d1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/z/d1/f11)" = "root:root"
systemd-tmpfiles --create - <<EOF
z /tmp/z/d2/* 0755 daemon daemon - -
EOF
-test $(stat -c %U:%G /tmp/z/d2/f21) = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/z/d2/f21)" = "daemon:daemon"
#
# 'Z'
@@ -38,8 +38,8 @@ Z /tmp/Z/f1 0755 daemon daemon - -
Z /tmp/Z/d1 0755 daemon daemon - -
EOF
-test $(stat -c %U:%G /tmp/Z/f1) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/Z/d1) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/Z/d1/d11) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/Z/d1/f11) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/Z/d1/d11/f111) = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/f1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1/d11)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1/f11)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1/d11/f111)" = "daemon:daemon"
diff --git a/test/units/testsuite-22.06.sh b/test/units/testsuite-22.06.sh
index cd65ba6726..ce5b1bf698 100755
--- a/test/units/testsuite-22.06.sh
+++ b/test/units/testsuite-22.06.sh
@@ -1,9 +1,10 @@
-#! /bin/bash
+#!/bin/bash
#
# Inspired by https://github.com/systemd/systemd/issues/9508
#
-set -e
+set -eux
+set -o pipefail
test_snippet() {
systemd-tmpfiles "$@" - <<EOF
@@ -18,8 +19,8 @@ test -d /var/tmp/foobar-test-06
test -d /var/tmp/foobar-test-06/important
test_snippet --remove
-! test -f /var/tmp/foobar-test-06
-! test -f /var/tmp/foobar-test-06/important
+test ! -f /var/tmp/foobar-test-06
+test ! -f /var/tmp/foobar-test-06/important
test_snippet --create
test -d /var/tmp/foobar-test-06
@@ -35,4 +36,4 @@ test -f /var/tmp/foobar-test-06/something-else
test_snippet --create --remove
test -d /var/tmp/foobar-test-06
test -d /var/tmp/foobar-test-06/important
-! test -f /var/tmp/foobar-test-06/something-else
+test ! -f /var/tmp/foobar-test-06/something-else
diff --git a/test/units/testsuite-22.07.sh b/test/units/testsuite-22.07.sh
index 39c04b925c..8d61032d27 100755
--- a/test/units/testsuite-22.07.sh
+++ b/test/units/testsuite-22.07.sh
@@ -1,10 +1,10 @@
-#! /bin/bash
+#!/bin/bash
#
# Verifies the issues described by https://github.com/systemd/systemd/issues/10191
#
-set -e
-set -x
+set -eux
+set -o pipefail
rm -rf /tmp/test-prefix
@@ -16,8 +16,8 @@ r /tmp/test-prefix
r /tmp/test-prefix/file
EOF
-! test -f /tmp/test-prefix/file
-! test -f /tmp/test-prefix
+test ! -f /tmp/test-prefix/file
+test ! -f /tmp/test-prefix
mkdir /tmp/test-prefix
touch /tmp/test-prefix/file
@@ -27,5 +27,5 @@ r /tmp/test-prefix/file
r /tmp/test-prefix
EOF
-! test -f /tmp/test-prefix/file
-! test -f /tmp/test-prefix
+test ! -f /tmp/test-prefix/file
+test ! -f /tmp/test-prefix
diff --git a/test/units/testsuite-22.08.sh b/test/units/testsuite-22.08.sh
index e7bf044783..e4272f8ea3 100755
--- a/test/units/testsuite-22.08.sh
+++ b/test/units/testsuite-22.08.sh
@@ -1,4 +1,4 @@
-#! /bin/bash
+#!/bin/bash
#
# Verify tmpfiles can run in a root directory under a path prefix that contains
# directories owned by unprivileged users, for example when a root file system
@@ -7,7 +7,8 @@
# https://github.com/systemd/systemd/pull/11820
#
-set -e
+set -eux
+set -o pipefail
rm -fr /tmp/root /tmp/user
mkdir -p /tmp/root /tmp/user/root
@@ -22,10 +23,12 @@ test -d /tmp/root/test2
# Verify the command fails to write to a root-owned subdirectory under an
# unprivileged user's directory when it's not part of the prefix, as expected
# by the unsafe_transition function.
-! echo 'd /tmp/user/root/test' | systemd-tmpfiles --create -
-! test -e /tmp/user/root/test
-! echo 'd /user/root/test' | systemd-tmpfiles --root=/tmp --create -
-! test -e /tmp/user/root/test
+echo 'd /tmp/user/root/test' | systemd-tmpfiles --create - \
+ && { echo 'unexpected success'; exit 1; }
+test ! -e /tmp/user/root/test
+echo 'd /user/root/test' | systemd-tmpfiles --root=/tmp --create - \
+ && { echo 'unexpected success'; exit 1; }
+test ! -e /tmp/user/root/test
# Verify the above works when all user-owned directories are in the prefix.
echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create -
diff --git a/test/units/testsuite-22.09.sh b/test/units/testsuite-22.09.sh
index c558dfd4db..c852f778ef 100755
--- a/test/units/testsuite-22.09.sh
+++ b/test/units/testsuite-22.09.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
-set -e
-set -x
+set -eux
+set -o pipefail
# Make sure that the "stat" output is not locale dependent.
export LANG=C LC_ALL=C
diff --git a/test/units/testsuite-22.10.sh b/test/units/testsuite-22.10.sh
index 5ec297fbf8..d650b2145c 100755
--- a/test/units/testsuite-22.10.sh
+++ b/test/units/testsuite-22.10.sh
@@ -1,7 +1,6 @@
#!/usr/bin/env bash
-set -e
-set -x
+set -eux
set -o pipefail
systemd-tmpfiles --create - <<EOF
diff --git a/test/units/testsuite-22.11.sh b/test/units/testsuite-22.11.sh
new file mode 100755
index 0000000000..21ef210cd1
--- /dev/null
+++ b/test/units/testsuite-22.11.sh
@@ -0,0 +1,141 @@
+#! /bin/bash
+
+set -e
+set -x
+
+rm -fr /tmp/x
+mkdir /tmp/x
+
+#
+# 'x'
+#
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test ! -f /tmp/x/z1
+test ! -f /tmp/x/z2
+
+#
+# 'X'
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test ! -f /tmp/x/1/x1
+test ! -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test ! -f /tmp/x/z1
+test ! -f /tmp/x/z2
+
+#
+# 'x' with glob
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/[1345]
+x /tmp/x/z*
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+#
+# 'X' with glob
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/[1345]
+X /tmp/x/?[12]
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test ! -f /tmp/x/1/x1
+test ! -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+#
+# 'x' with 'r'
+#
+
+mkdir -p /tmp/x/{1,2}/a
+touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
+
+systemd-tmpfiles --clean - <<EOF
+# x/X is not supposed to influence r
+x /tmp/x/1/a
+X /tmp/x/2/a
+r /tmp/x/1
+r /tmp/x/2
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -d /tmp/x/1/a
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+#
+# 'x' with 'R'
+#
+
+mkdir -p /tmp/x/{1,2}/a
+touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
+
+systemd-tmpfiles --remove - <<EOF
+# X is not supposed to influence R
+X /tmp/x/1/a
+X /tmp/x/2/a
+R /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test ! -d /tmp/x/1
+test ! -d /tmp/x/1/a
+test ! -f /tmp/x/1/a/x1
+test ! -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
diff --git a/test/units/testsuite-22.sh b/test/units/testsuite-22.sh
index afce85aceb..65d0802969 100755
--- a/test/units/testsuite-22.sh
+++ b/test/units/testsuite-22.sh
@@ -1,12 +1,12 @@
#!/usr/bin/env bash
-set -x
-set -e
+set -eux
+set -o pipefail
->/failed
+: >/failed
-for t in ${0%.sh}.*.sh; do
- echo "Running $t"; ./$t
+for t in "${0%.sh}".*.sh; do
+ echo "Running $t"; ./"$t"
done
touch /testok
diff --git a/test/units/testsuite-23.sh b/test/units/testsuite-23.sh
index 5e2966f848..4ef7c878a8 100755
--- a/test/units/testsuite-23.sh
+++ b/test/units/testsuite-23.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
@@ -16,19 +16,19 @@ systemd-run --unit=three -p Type=simple /tmp/brokenbinary
# And now, do the same with Type=exec, where the latter two should fail
systemd-run --unit=four -p Type=exec /bin/sleep infinity
-! systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity
-! systemd-run --unit=six -p Type=exec /tmp/brokenbinary
+systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity && { echo 'unexpected success'; exit 1; }
+systemd-run --unit=six -p Type=exec /tmp/brokenbinary && { echo 'unexpected success'; exit 1; }
systemd-run --unit=seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity
# Both TERM and SIGINT happen to have the same number on all architectures
-test $(systemctl show --value -p KillSignal seven.service) -eq 15
-test $(systemctl show --value -p RestartKillSignal seven.service) -eq 2
+test "$(systemctl show --value -p KillSignal seven.service)" -eq 15
+test "$(systemctl show --value -p RestartKillSignal seven.service)" -eq 2
systemctl restart seven.service
systemctl stop seven.service
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-25.sh b/test/units/testsuite-25.sh
index e3dd43add3..fbe2d0b1d4 100755
--- a/test/units/testsuite-25.sh
+++ b/test/units/testsuite-25.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
export SYSTEMD_PAGER=cat
@@ -34,8 +34,8 @@ 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 ! -f /var/lib/machines/testimage.raw
+machinectl image-status testimage && { echo 'unexpected success'; exit 1; }
# Test export of clone
machinectl export-raw testimage3 /var/tmp/testimage3.raw
@@ -46,8 +46,8 @@ rm /var/tmp/testimage3.raw
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
+test ! -f /var/lib/machines/testimage3.raw
+machinectl image-status testimage3 && { echo 'unexpected success'; exit 1; }
cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw
# Test export of rename
@@ -57,8 +57,8 @@ rm /var/tmp/testimage4.raw
# Test removal
machinectl remove testimage4
-! test -f /var/lib/machines/testimage4.raw
-! machinectl image-status testimage4
+test ! -f /var/lib/machines/testimage4.raw
+machinectl image-status testimage4 && { echo 'unexpected success'; exit 1; }
# → And now, let's test directory trees ← #
@@ -67,7 +67,7 @@ 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
+echo "piep" >/var/tmp/scratch/adirectory/athirdfile
# Test import-fs
machinectl import-fs /var/tmp/scratch/
@@ -90,8 +90,8 @@ 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 ! -f /var/lib/machines/scratch
+machinectl image-status scratchi && { echo 'unexpected success'; exit 1; }
# Test clone
machinectl clone scratch2 scratch3
@@ -103,23 +103,24 @@ 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 ! -f /var/lib/machines/scratch2
+machinectl image-status scratch2 && { echo 'unexpected success'; exit 1; }
# 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
+test ! -f /var/lib/machines/scratch3
+machinectl image-status scratch3 && { echo 'unexpected success'; exit 1; }
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 ! -f /var/lib/machines/scratch4
+machinectl image-status scratch4 && { echo 'unexpected success'; exit 1; }
# 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
@@ -135,9 +136,9 @@ rm -rf /var/tmp/scratch
# Test removal
machinectl remove scratch5
-! test -f /var/lib/machines/scratch5
-! machinectl image-status scratch5
+test ! -f /var/lib/machines/scratch5
+machinectl image-status scratch5 && { echo 'unexpected success'; exit 1; }
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-26.sh b/test/units/testsuite-26.sh
index 89c0937c8d..7982099307 100755
--- a/test/units/testsuite-26.sh
+++ b/test/units/testsuite-26.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
# Make sure PATH is set
@@ -22,10 +22,10 @@ systemctl show-environment | grep -q '^FOO=BAR$'
systemctl unset-environment FOO PATH
# Check that one is gone and the other reverted to the built-in
-! (systemctl show-environment | grep -q '^FOO=$')
-! (systemctl show-environment | grep -q '^PATH=.*testaddition$')
+systemctl show-environment | grep '^FOO=$' && exit 1
+systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1
systemctl show-environment | grep -q '^PATH='
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-27.sh b/test/units/testsuite-27.sh
index 0e9ffe1189..2248380351 100755
--- a/test/units/testsuite-27.sh
+++ b/test/units/testsuite-27.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh
index 2e55c275bf..3408e6d71a 100755
--- a/test/units/testsuite-29.sh
+++ b/test/units/testsuite-29.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -ex
+set -eux
set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
@@ -93,6 +93,6 @@ umount /tmp/overlay
umount /tmp/rootdir
umount /tmp/app1
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-30.sh b/test/units/testsuite-30.sh
index a507ffcd7b..ac8a3e06cf 100755
--- a/test/units/testsuite-30.sh
+++ b/test/units/testsuite-30.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
@@ -13,19 +13,19 @@ 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
+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
+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
+while test ! -f /tmp/clock-changed ; do sleep .5 ; done
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-31.sh b/test/units/testsuite-31.sh
index fcff82d804..a9a50e1470 100755
--- a/test/units/testsuite-31.sh
+++ b/test/units/testsuite-31.sh
@@ -1,10 +1,10 @@
#!/usr/bin/env bash
-set -e
+set -eux
set -o pipefail
if journalctl -b -t systemd --grep '\.device: Changed plugged -> dead'; then
exit 1
fi
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-32.sh b/test/units/testsuite-32.sh
index c1704ab34e..2393601a00 100755
--- a/test/units/testsuite-32.sh
+++ b/test/units/testsuite-32.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
# Let's run this test only if the "memory.oom.group" cgroupfs attribute
@@ -9,7 +9,6 @@ set -o pipefail
# 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
systemd-analyze log-target console
@@ -23,12 +22,12 @@ if test -f /sys/fs/cgroup/system.slice/testsuite-32.service/memory.oom.group; th
echo f >/proc/sysrq-trigger
while : ; do
- STATE=`systemctl show -P ActiveState oomtest.service`
+ STATE="$(systemctl show -P ActiveState oomtest.service)"
[ "$STATE" = "failed" ] && break
sleep .5
done
- RESULT=`systemctl show -P Result oomtest.service`
+ RESULT="$(systemctl show -P Result oomtest.service)"
test "$RESULT" = "oom-kill"
systemd-analyze log-level info
diff --git a/test/units/testsuite-33.sh b/test/units/testsuite-33.sh
index 0a6ee57b99..6e750f63a3 100755
--- a/test/units/testsuite-33.sh
+++ b/test/units/testsuite-33.sh
@@ -1,10 +1,10 @@
#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -ex
+set -eux
set -o pipefail
-cat > /etc/systemd/system/testservice.service <<EOF
+cat >/etc/systemd/system/testservice.service <<EOF
[Service]
ConfigurationDirectory=testservice
RuntimeDirectory=testservice
@@ -18,11 +18,11 @@ EOF
systemctl daemon-reload
-! test -e /etc/testservice
-! test -e /run/testservice
-! test -e /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
+test ! -e /var/lib/testservice
+test ! -e /var/cache/testservice
+test ! -e /var/log/testservice
systemctl start testservice
@@ -32,7 +32,7 @@ test -d /var/lib/testservice
test -d /var/cache/testservice
test -d /var/log/testservice
-! systemctl clean testservice
+systemctl clean testservice && { echo 'unexpected success'; exit 1; }
systemctl stop testservice
@@ -44,7 +44,7 @@ test -d /var/log/testservice
systemctl clean testservice --what=configuration
-! test -e /etc/testservice
+test ! -e /etc/testservice
test -d /run/testservice
test -d /var/lib/testservice
test -d /var/cache/testservice
@@ -52,29 +52,29 @@ test -d /var/log/testservice
systemctl clean testservice
-! test -e /etc/testservice
-! test -e /run/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
test -d /var/lib/testservice
-! test -e /var/cache/testservice
+test ! -e /var/cache/testservice
test -d /var/log/testservice
systemctl clean testservice --what=logs
-! test -e /etc/testservice
-! test -e /run/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
test -d /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
+test ! -e /var/cache/testservice
+test ! -e /var/log/testservice
systemctl clean testservice --what=all
-! test -e /etc/testservice
-! test -e /run/testservice
-! test -e /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
+test ! -e /var/lib/testservice
+test ! -e /var/cache/testservice
+test ! -e /var/log/testservice
-cat > /etc/systemd/system/testservice.service <<EOF
+cat >/etc/systemd/system/testservice.service <<EOF
[Service]
DynamicUser=yes
ConfigurationDirectory=testservice
@@ -89,11 +89,11 @@ EOF
systemctl daemon-reload
-! test -e /etc/testservice
-! test -e /run/testservice
-! test -e /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
+test ! -e /var/lib/testservice
+test ! -e /var/cache/testservice
+test ! -e /var/log/testservice
systemctl restart testservice
@@ -107,7 +107,7 @@ test -L /var/lib/testservice
test -L /var/cache/testservice
test -L /var/log/testservice
-! systemctl clean testservice
+systemctl clean testservice && { echo 'unexpected success'; exit 1; }
systemctl stop testservice
@@ -123,7 +123,7 @@ test -L /var/log/testservice
systemctl clean testservice --what=configuration
-! test -d /etc/testservice
+test ! -d /etc/testservice
test -d /run/private/testservice
test -d /var/lib/private/testservice
test -d /var/cache/private/testservice
@@ -135,41 +135,41 @@ test -L /var/log/testservice
systemctl clean testservice
-! test -d /etc/testservice
-! test -d /run/private/testservice
+test ! -d /etc/testservice
+test ! -d /run/private/testservice
test -d /var/lib/private/testservice
-! test -d /var/cache/private/testservice
+test ! -d /var/cache/private/testservice
test -d /var/log/private/testservice
-! test -L /run/testservice
+test ! -L /run/testservice
test -L /var/lib/testservice
-! test -L /var/cache/testservice
+test ! -L /var/cache/testservice
test -L /var/log/testservice
systemctl clean testservice --what=logs
-! test -d /etc/testservice
-! test -d /run/private/testservice
+test ! -d /etc/testservice
+test ! -d /run/private/testservice
test -d /var/lib/private/testservice
-! test -d /var/cache/private/testservice
-! test -d /var/log/private/testservice
-! test -L /run/testservice
+test ! -d /var/cache/private/testservice
+test ! -d /var/log/private/testservice
+test ! -L /run/testservice
test -L /var/lib/testservice
-! test -L /var/cache/testservice
-! test -L /var/log/testservice
+test ! -L /var/cache/testservice
+test ! -L /var/log/testservice
systemctl clean testservice --what=all
-! test -d /etc/testservice
-! test -d /run/private/testservice
-! test -d /var/lib/private/testservice
-! test -d /var/cache/private/testservice
-! test -d /var/log/private/testservice
-! test -L /run/testservice
-! test -L /var/lib/testservice
-! test -L /var/cache/testservice
-! test -L /var/log/testservice
-
-cat > /etc/systemd/system/tmp-hoge.mount <<EOF
+test ! -d /etc/testservice
+test ! -d /run/private/testservice
+test ! -d /var/lib/private/testservice
+test ! -d /var/cache/private/testservice
+test ! -d /var/log/private/testservice
+test ! -L /run/testservice
+test ! -L /var/lib/testservice
+test ! -L /var/cache/testservice
+test ! -L /var/log/testservice
+
+cat >/etc/systemd/system/tmp-hoge.mount <<EOF
[Mount]
What=tmpfs
Type=tmpfs
@@ -182,11 +182,11 @@ EOF
systemctl daemon-reload
-! test -e /etc/hoge
-! test -e /run/hoge
-! test -e /var/lib/hoge
-! test -e /var/cache/hoge
-! test -e /var/log/hoge
+test ! -e /etc/hoge
+test ! -e /run/hoge
+test ! -e /var/lib/hoge
+test ! -e /var/cache/hoge
+test ! -e /var/log/hoge
systemctl start tmp-hoge.mount
@@ -196,7 +196,7 @@ test -d /var/lib/hoge
test -d /var/cache/hoge
test -d /var/log/hoge
-! systemctl clean tmp-hoge.mount
+systemctl clean tmp-hoge.mount && { echo 'unexpected success'; exit 1; }
test -d /etc/hoge
test -d /run/hoge
@@ -207,44 +207,44 @@ test -d /var/log/hoge
systemctl stop tmp-hoge.mount
test -d /etc/hoge
-! test -d /run/hoge
+test ! -d /run/hoge
test -d /var/lib/hoge
test -d /var/cache/hoge
test -d /var/log/hoge
systemctl clean tmp-hoge.mount --what=configuration
-! test -d /etc/hoge
-! test -d /run/hoge
+test ! -d /etc/hoge
+test ! -d /run/hoge
test -d /var/lib/hoge
test -d /var/cache/hoge
test -d /var/log/hoge
systemctl clean tmp-hoge.mount
-! test -d /etc/hoge
-! test -d /run/hoge
+test ! -d /etc/hoge
+test ! -d /run/hoge
test -d /var/lib/hoge
-! test -d /var/cache/hoge
+test ! -d /var/cache/hoge
test -d /var/log/hoge
systemctl clean tmp-hoge.mount --what=logs
-! test -d /etc/hoge
-! test -d /run/hoge
+test ! -d /etc/hoge
+test ! -d /run/hoge
test -d /var/lib/hoge
-! test -d /var/cache/hoge
-! test -d /var/log/hoge
+test ! -d /var/cache/hoge
+test ! -d /var/log/hoge
systemctl clean tmp-hoge.mount --what=all
-! test -d /etc/hoge
-! test -d /run/hoge
-! test -d /var/lib/hoge
-! test -d /var/cache/hoge
-! test -d /var/log/hoge
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test ! -d /var/lib/hoge
+test ! -d /var/cache/hoge
+test ! -d /var/log/hoge
-cat > /etc/systemd/system/testservice.socket <<EOF
+cat >/etc/systemd/system/testservice.socket <<EOF
[Socket]
ListenSequentialPacket=/run/testservice.socket
RemoveOnStop=yes
@@ -258,62 +258,62 @@ EOF
systemctl daemon-reload
-! test -e /etc/testsocket
-! test -e /run/testsocket
-! test -e /var/lib/testsocket
-! test -e /var/cache/testsocket
-! test -e /var/log/testsocket
+test ! -e /etc/testsocket
+test ! -e /run/testsocket
+test ! -e /var/lib/testsocket
+test ! -e /var/cache/testsocket
+test ! -e /var/log/testsocket
systemctl start testservice.socket
test -d /etc/testsocket
-! test -d /run/testsocket
+test -d /run/testsocket
test -d /var/lib/testsocket
test -d /var/cache/testsocket
test -d /var/log/testsocket
-! systemctl clean testservice.socket
+systemctl clean testservice.socket && { echo 'unexpected success'; exit 1; }
systemctl stop testservice.socket
test -d /etc/testsocket
-! test -d /run/testsocket
+test ! -d /run/testsocket
test -d /var/lib/testsocket
test -d /var/cache/testsocket
test -d /var/log/testsocket
systemctl clean testservice.socket --what=configuration
-! test -e /etc/testsocket
-! test -d /run/testsocket
+test ! -e /etc/testsocket
+test ! -d /run/testsocket
test -d /var/lib/testsocket
test -d /var/cache/testsocket
test -d /var/log/testsocket
systemctl clean testservice.socket
-! test -e /etc/testsocket
-! test -e /run/testsocket
+test ! -e /etc/testsocket
+test ! -e /run/testsocket
test -d /var/lib/testsocket
-! test -e /var/cache/testsocket
+test ! -e /var/cache/testsocket
test -d /var/log/testsocket
systemctl clean testservice.socket --what=logs
-! test -e /etc/testsocket
-! test -e /run/testsocket
+test ! -e /etc/testsocket
+test ! -e /run/testsocket
test -d /var/lib/testsocket
-! test -e /var/cache/testsocket
-! test -e /var/log/testsocket
+test ! -e /var/cache/testsocket
+test ! -e /var/log/testsocket
systemctl clean testservice.socket --what=all
-! test -e /etc/testsocket
-! test -e /run/testsocket
-! test -e /var/lib/testsocket
-! test -e /var/cache/testsocket
-! test -e /var/log/testsocket
+test ! -e /etc/testsocket
+test ! -e /run/testsocket
+test ! -e /var/lib/testsocket
+test ! -e /var/cache/testsocket
+test ! -e /var/log/testsocket
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-34.sh b/test/units/testsuite-34.sh
index 6d9488688a..394b220d60 100755
--- a/test/units/testsuite-34.sh
+++ b/test/units/testsuite-34.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
@@ -9,38 +9,41 @@ systemd-analyze log-target console
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz touch /var/lib/zzz/test
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
-! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
+ && { echo 'unexpected success'; exit 1; }
test -d /var/lib/zzz
-! test -L /var/lib/zzz
-! test -e /var/lib/private/zzz
+test ! -L /var/lib/zzz
+test ! -e /var/lib/private/zzz
test -f /var/lib/zzz/test
-! test -f /var/lib/zzz/test-missing
+test ! -f /var/lib/zzz/test-missing
# Convert to DynamicUser=1
systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test
-! systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
+ && { echo 'unexpected success'; exit 1; }
test -L /var/lib/zzz
test -d /var/lib/private/zzz
test -f /var/lib/zzz/test
-! test -f /var/lib/zzz/test-missing
+test ! -f /var/lib/zzz/test-missing
# Convert back
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
-! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
+ && { echo 'unexpected success'; exit 1; }
test -d /var/lib/zzz
-! test -L /var/lib/zzz
-! test -e /var/lib/private/zzz
+test ! -L /var/lib/zzz
+test ! -e /var/lib/private/zzz
test -f /var/lib/zzz/test
-! test -f /var/lib/zzz/test-missing
+test ! -f /var/lib/zzz/test-missing
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-36.sh b/test/units/testsuite-36.sh
index f5579ce825..94fd927a5d 100755
--- a/test/units/testsuite-36.sh
+++ b/test/units/testsuite-36.sh
@@ -1,13 +1,14 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
at_exit() {
- if [ $? -ne 0 ]; then
+ # shellcheck disable=SC2181
+ if [[ $? -ne 0 ]]; then
# We're exiting with a non-zero EC, let's dump test artifacts
# for easier debugging
- [ -f "$straceLog" ] && cat "$straceLog"
- [ -f "$journalLog" ] && cat "$journalLog"
+ [[ -v straceLog && -f "$straceLog" ]] && cat "$straceLog"
+ [[ -v journalLog && -f "$journalLog" ]] && cat "$journalLog"
fi
}
@@ -26,25 +27,28 @@ testUnitFile="/run/systemd/system/$testUnit"
testUnitNUMAConf="$testUnitFile.d/numa.conf"
# Sleep constants (we should probably figure out something better but nothing comes to mind)
-journalSleep=5
sleepAfterStart=1
# Journal cursor for easier navigation
journalCursorFile="jounalCursorFile"
startStrace() {
- coproc strace -qq -p 1 -o $straceLog -e set_mempolicy -s 1024 $1
+ coproc strace -qq -p 1 -o "$straceLog" -e set_mempolicy -s 1024 ${1:+"$1"}
# Wait for strace to properly "initialize"
sleep $sleepAfterStart
}
stopStrace() {
- kill -s TERM $COPROC_PID
+ [[ -v COPROC_PID ]] || return
+
+ local PID=$COPROC_PID
+ kill -s TERM "$PID"
# Make sure the strace process is indeed dead
- while kill -0 $COPROC_PID 2>/dev/null; do sleep 0.1; done
+ 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
}
@@ -55,7 +59,7 @@ stopJournalctl() {
# 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"
+ journalctl -u "$unit" --cursor-file="$journalCursorFile" >"$journalLog"
}
checkNUMA() {
@@ -64,21 +68,24 @@ checkNUMA() {
}
writePID1NUMAPolicy() {
- echo [Manager] > $confDir/numa.conf
- echo NUMAPolicy=$1 >> $confDir/numa.conf
- echo NUMAMask=$2>> $confDir/numa.conf
+ cat >"$confDir/numa.conf" <<EOF
+[Manager]
+NUMAPolicy=${1:?missing argument: NUMAPolicy}
+NUMAMask=${2:-""}
+EOF
}
writeTestUnit() {
- mkdir -p $testUnitFile.d/
- echo [Service] > $testUnitFile
- echo ExecStart=/bin/sleep 3600 >> $testUnitFile
+ mkdir -p "$testUnitFile.d/"
+ printf "[Service]\nExecStart=/bin/sleep 3600\n" >"$testUnitFile"
}
writeTestUnitNUMAPolicy() {
- echo [Service] > $testUnitNUMAConf
- echo NUMAPolicy=$1 >> $testUnitNUMAConf
- echo NUMAMask=$2>> $testUnitNUMAConf
+ cat >"$testUnitNUMAConf" <<EOF
+[Service]
+NUMAPolicy=${1:?missing argument: NUMAPolicy}
+NUMAMask=${2:-""}
+EOF
systemctl daemon-reload
}
@@ -97,32 +104,38 @@ pid1ReloadWithJournal() {
pid1StartUnitWithStrace() {
startStrace '-f'
- systemctl start $1
+ systemctl start "${1:?missing unit name}"
sleep $sleepAfterStart
stopStrace
}
pid1StartUnitWithJournal() {
startJournalctl
- systemctl start $1
+ systemctl start "${1:?missing unit name}"
sleep $sleepAfterStart
stopJournalctl
}
pid1StopUnit() {
- systemctl stop $1
+ systemctl stop "${1:?missing unit name}"
}
systemctlCheckNUMAProperties() {
- local LOGFILE="$(mktemp)"
- systemctl show -p NUMAPolicy $1 > "$LOGFILE"
- grep "NUMAPolicy=$2" "$LOGFILE"
+ local UNIT_NAME="${1:?missing unit name}"
+ local NUMA_POLICY="${2:?missing NUMAPolicy}"
+ local NUMA_MASK="${3:-""}"
+ local LOGFILE
+
+ LOGFILE="$(mktemp)"
- > "$LOGFILE"
+ systemctl show -p NUMAPolicy "$UNIT_NAME" >"$LOGFILE"
+ grep "NUMAPolicy=$NUMA_POLICY" "$LOGFILE"
- if [ -n "$3" ]; then
- systemctl show -p NUMAMask $1 > "$LOGFILE"
- grep "NUMAMask=$3" "$LOGFILE"
+ : >"$LOGFILE"
+
+ if [ -n "$NUMA_MASK" ]; then
+ systemctl show -p NUMAMask "$UNIT_NAME" >"$LOGFILE"
+ grep "NUMAMask=$NUMA_MASK" "$LOGFILE"
fi
}
@@ -145,10 +158,10 @@ if ! checkNUMA; then
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
+ systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
sleep $sleepAfterStart
- pid1StopUnit $runUnit
- stopJournalctl $runUnit
+ pid1StopUnit "$runUnit"
+ stopJournalctl "$runUnit"
grep "NUMA support not available, ignoring" "$journalLog"
else
@@ -156,43 +169,43 @@ else
writePID1NUMAPolicy "default"
pid1ReloadWithStrace
# Kernel requires that nodemask argument is set to NULL when setting default policy
- grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
+ 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
+ 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: Invalid argument" $journalLog
+ grep "Failed to set NUMA memory policy: Invalid argument" "$journalLog"
echo "PID1 NUMAPolicy support - Bind policy w/ mask"
writePID1NUMAPolicy "bind" "0"
pid1ReloadWithStrace
- grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog
+ 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: Invalid argument" $journalLog
+ grep "Failed to set NUMA memory policy: Invalid argument" "$journalLog"
echo "PID1 NUMAPolicy support - Interleave policy w/ mask"
writePID1NUMAPolicy "interleave" "0"
pid1ReloadWithStrace
- grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog
+ 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: Invalid argument" $journalLog
+ grep "Failed to set NUMA memory policy: 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
+ grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog"
echo "PID1 NUMAPolicy support - Local policy w/o mask"
writePID1NUMAPolicy "local"
@@ -202,140 +215,137 @@ else
# 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
+ 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
+ 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
+ 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"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "default" "0"
pid1StopUnit $testUnit
# Mask must be ignored
- grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
+ grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
echo "Unit file NUMAPolicy support - Bind policy w/o mask"
writeTestUnitNUMAPolicy "bind"
- pid1StartUnitWithJournal $testUnit
- pid1StopUnit $testUnit
- grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog
+ pid1StartUnitWithJournal "$testUnit"
+ pid1StopUnit "$testUnit"
+ grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" "$journalLog"
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
+ 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
- grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog
+ pid1StartUnitWithStrace "$testUnit"
+ pid1StopUnit "$testUnit"
+ grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" "$journalLog"
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
+ 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
- ! grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog
+ pid1StartUnitWithJournal "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "preferred"
+ pid1StopUnit "$testUnit"
+ grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" "$journalLog" && { 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
+ 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
+ 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
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "local" "0"
+ pid1StopUnit "$testUnit"
# Mask must be ignored
- grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
+ grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
echo "Unit file CPUAffinity=NUMA support"
writeTestUnitNUMAPolicy "bind" "0"
- echo "CPUAffinity=numa" >> $testUnitNUMAConf
+ echo "CPUAffinity=numa" >>"$testUnitNUMAConf"
systemctl daemon-reload
- systemctl start $testUnit
- systemctlCheckNUMAProperties $testUnit "bind" "0"
- pid=$(systemctl show --value -p MainPID $testUnit)
- cpulist=$(cat /sys/devices/system/node/node0/cpulist)
- affinity_systemd=$(systemctl show --value -p CPUAffinity $testUnit)
- [ $cpulist = $affinity_systemd ]
- pid1StopUnit $testUnit
+ 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=default --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=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "default" ""
+ pid1StopUnit "$runUnit"
- systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit $runUnit sleep 1000
- systemctlCheckNUMAProperties $runUnit "interleave" "0"
- pid1StopUnit $runUnit
+ systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "bind" "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=interleave -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "interleave" "0"
+ pid1StopUnit "$runUnit"
- systemd-run -p NUMAPolicy=local --unit $runUnit sleep 1000
- systemctlCheckNUMAProperties $runUnit "local"
- 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 -p NUMAMask=0 --unit $runUnit sleep 1000
- systemctlCheckNUMAProperties $runUnit "local" ""
- pid1StopUnit $runUnit
+ systemd-run -p NUMAPolicy=local --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
+ 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 $testDir
-rm -rf $confDir
+rm -rf "$confDir"
systemctl daemon-reload
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-37.sh b/test/units/testsuite-37.sh
index 32a9dd8694..c3eb5344d1 100755
--- a/test/units/testsuite-37.sh
+++ b/test/units/testsuite-37.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -ex
+set -eux
set -o pipefail
systemd-mount -p RuntimeDirectory=hoge -p RuntimeDirectoryPreserve=yes -t tmpfs tmpfs /tmp/aaa
@@ -12,8 +12,8 @@ touch /tmp/aaa/bbb
systemctl restart tmp-aaa.mount
test -e /run/hoge/foo
-! test -e /tmp/aaa/bbb
+test ! -e /tmp/aaa/bbb
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-38.sh b/test/units/testsuite-38.sh
index 18b7bd6dce..818f69bc63 100755
--- a/test/units/testsuite-38.sh
+++ b/test/units/testsuite-38.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
@@ -14,11 +14,11 @@ start_test_service() {
}
dbus_freeze() {
- local suffix=
- suffix="${1##*.}"
+ local name object_path suffix
- local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
- local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
+ suffix="${1##*.}"
+ name="${1%.$suffix}"
+ object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
busctl call \
org.freedesktop.systemd1 \
@@ -28,11 +28,11 @@ dbus_freeze() {
}
dbus_thaw() {
- local suffix=
- suffix="${1##*.}"
+ local name object_path suffix
- local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
- local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
+ suffix="${1##*.}"
+ name="${1%.$suffix}"
+ object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
busctl call \
org.freedesktop.systemd1 \
@@ -62,11 +62,11 @@ dbus_thaw_unit() {
}
dbus_can_freeze() {
- local suffix=
- suffix="${1##*.}"
+ local name object_path suffix
- local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
- local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
+ suffix="${1##*.}"
+ name="${1%.$suffix}"
+ object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
busctl get-property \
org.freedesktop.systemd1 \
@@ -76,11 +76,11 @@ dbus_can_freeze() {
}
check_freezer_state() {
- local suffix=
- suffix="${1##*.}"
+ local name object_path suffix
- local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
- local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
+ suffix="${1##*.}"
+ name="${1%.$suffix}"
+ object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
state=$(busctl get-property \
org.freedesktop.systemd1 \
@@ -245,7 +245,7 @@ test_preserve_state() {
echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):"
echo -n " - freeze from outside: "
- echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
+ echo 1 >/sys/fs/cgroup/"${slice}"/cgroup.freeze
# Give kernel some time to freeze the slice
sleep 1
@@ -259,7 +259,7 @@ test_preserve_state() {
echo "[ OK ]"
echo -n " - thaw from outside: "
- echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
+ echo 0 >/sys/fs/cgroup/"${slice}"/cgroup.freeze
sleep 1
check_freezer_state "${unit}" "running"
@@ -271,8 +271,8 @@ test_preserve_state() {
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
+ 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 ]"
@@ -293,5 +293,5 @@ test -e /sys/fs/cgroup/system.slice/cgroup.freeze && {
test_preserve_state
}
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-39.sh b/test/units/testsuite-39.sh
index eb7363fa6a..3b0d893a4c 100755
--- a/test/units/testsuite-39.sh
+++ b/test/units/testsuite-39.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
@@ -11,23 +11,23 @@ SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)"
SERVICE_NAME="${SERVICE_PATH##*/}"
echo "[#1] Failing ExecReload= should not kill the service"
-cat > "$SERVICE_PATH" << EOF
+cat >"$SERVICE_PATH" <<EOF
[Service]
ExecStart=/bin/sleep infinity
ExecReload=/bin/false
EOF
systemctl daemon-reload
-systemctl start $SERVICE_NAME
-systemctl status $SERVICE_NAME
+systemctl start "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
# The reload SHOULD fail but SHOULD NOT affect the service state
-! systemctl reload $SERVICE_NAME
-systemctl status $SERVICE_NAME
-systemctl stop $SERVICE_NAME
+systemctl reload "$SERVICE_NAME" && { echo 'unexpected success'; exit 1; }
+systemctl status "$SERVICE_NAME"
+systemctl stop "$SERVICE_NAME"
echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
-cat > "$SERVICE_PATH" << EOF
+cat >"$SERVICE_PATH" <<EOF
[Service]
ExecStart=/bin/sleep infinity
ExecReload=/bin/true
@@ -36,29 +36,29 @@ ExecReload=/bin/true
EOF
systemctl daemon-reload
-systemctl start $SERVICE_NAME
-systemctl status $SERVICE_NAME
+systemctl start "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
# The reload SHOULD fail but SHOULD NOT affect the service state
-! systemctl reload $SERVICE_NAME
-systemctl status $SERVICE_NAME
-systemctl stop $SERVICE_NAME
+systemctl reload "$SERVICE_NAME" && { echo 'unexpected success'; exit 1; }
+systemctl status "$SERVICE_NAME"
+systemctl stop "$SERVICE_NAME"
echo "[#3] Failing ExecReload=- should not affect reload's exit code"
-cat > "$SERVICE_PATH" << EOF
+cat >"$SERVICE_PATH" <<EOF
[Service]
ExecStart=/bin/sleep infinity
ExecReload=-/bin/false
EOF
systemctl daemon-reload
-systemctl start $SERVICE_NAME
-systemctl status $SERVICE_NAME
-systemctl reload $SERVICE_NAME
-systemctl status $SERVICE_NAME
-systemctl stop $SERVICE_NAME
+systemctl start "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+systemctl reload "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+systemctl stop "$SERVICE_NAME"
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-40.sh b/test/units/testsuite-40.sh
index 957d22031a..fdb052c39c 100755
--- a/test/units/testsuite-40.sh
+++ b/test/units/testsuite-40.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
@@ -41,6 +41,6 @@ done
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-41.sh b/test/units/testsuite-41.sh
index 81fa1716f1..e7993e8df7 100755
--- a/test/units/testsuite-41.sh
+++ b/test/units/testsuite-41.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
# wait this many secs for each test service to succeed in what is being tested
@@ -9,9 +9,10 @@ systemd-analyze log-level debug
systemd-analyze log-target console
# test one: Restart=on-failure should restart the service
-! systemd-run --unit=one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1"
+systemd-run --unit=one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1" \
+ && { echo 'unexpected success'; exit 1; }
-for ((secs=0; secs<$MAX_SECS; secs++)); do
+for ((secs = 0; secs < MAX_SECS; secs++)); do
[[ "$(systemctl show one.service -P NRestarts)" -le 0 ]] || break
sleep 1
done
@@ -25,10 +26,16 @@ TMP_FILE="/tmp/test-41-oneshot-restart-test"
# test two: make sure StartLimitBurst correctly limits the number of restarts
# and restarts execution of the unit from the first ExecStart=
-! systemd-run --unit=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"
+systemd-run --unit=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" \
+ && { echo 'unexpected success'; exit 1; }
# wait for at least 3 restarts
-for ((secs=0; secs<$MAX_SECS; secs++)); do
+for ((secs = 0; secs < MAX_SECS; secs++)); do
[[ $(cat $TMP_FILE) != "aaa" ]] || break
sleep 1
done
@@ -44,6 +51,6 @@ fi
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-42.sh b/test/units/testsuite-42.sh
index 154398dd66..e47fab46e6 100755
--- a/test/units/testsuite-42.sh
+++ b/test/units/testsuite-42.sh
@@ -1,21 +1,23 @@
#!/usr/bin/env bash
-set -ex
+set -eux
systemd-analyze log-level debug
systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple1' true
test -f /run/simple1
-! systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple2' false
+systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple2' false \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/simple2
systemd-run --unit=exec1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec1' sleep 1
test -f /run/exec1
-! systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false'
+systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false' \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/exec2
-cat > /tmp/forking1.sh <<EOF
+cat >/tmp/forking1.sh <<EOF
#!/usr/bin/env bash
set -eux
@@ -31,7 +33,7 @@ chmod +x /tmp/forking1.sh
systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking1' /tmp/forking1.sh
test -f /run/forking1
-cat > /tmp/forking2.sh <<EOF
+cat >/tmp/forking2.sh <<EOF
#!/usr/bin/env bash
set -eux
@@ -44,13 +46,15 @@ systemd-notify MAINPID=\$MAINPID
EOF
chmod +x /tmp/forking2.sh
-! systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh
+systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/forking2
systemd-run --unit=oneshot1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot1' true
test -f /run/oneshot1
-! systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot2' false
+systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot2' false \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/oneshot2
systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus1' \
@@ -58,10 +62,10 @@ systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=t
|| :
test -f /run/dbus1
-! systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus2' true
+systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus2' true
test -f /run/dbus2
-cat > /tmp/notify1.sh <<EOF
+cat >/tmp/notify1.sh <<EOF
#!/usr/bin/env bash
set -eux
@@ -73,17 +77,19 @@ chmod +x /tmp/notify1.sh
systemd-run --unit=notify1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify1' /tmp/notify1.sh
test -f /run/notify1
-! systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify2' true
+systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify2' true \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/notify2
systemd-run --unit=idle1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle1' true
test -f /run/idle1
-! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle2' false
+systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle2' false \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/idle2
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-43.sh b/test/units/testsuite-43.sh
index ec84868a21..c6399f9920 100755
--- a/test/units/testsuite-43.sh
+++ b/test/units/testsuite-43.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
@@ -7,6 +7,7 @@ systemd-analyze log-level debug
runas() {
declare userid=$1
shift
+ # shellcheck disable=SC2016
su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@"
}
@@ -34,9 +35,10 @@ test -e /home/testuser/works.txt
runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \
-p PrivateUsers=yes -p ProtectHome=read-only \
-P bash -c '
- test -e /home/testuser/works.txt
- ! touch /home/testuser/blocked.txt
- '
+ test -e /home/testuser/works.txt || exit 10
+ touch /home/testuser/blocked.txt && exit 11
+ ' \
+ && { echo 'unexpected success'; exit 1; }
test ! -e /home/testuser/blocked.txt
# Check that tmpfs hides the whole directory
@@ -45,6 +47,7 @@ runas testuser systemd-run --wait --user --unit=test-protect-home-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 PrivateUsers=yes -p ProtectHome=yes \
-P bash -c '
@@ -57,12 +60,13 @@ runas testuser systemd-run --wait --user --unit=test-protect-home-yes \
# 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 \
+runas testuser systemd-run --wait --user --unit=test-group-fail \
-p PrivateUsers=yes -p Group=daemon \
- -P true
+ -P true \
+ && { echo 'unexpected success'; exit 1; }
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-44.sh b/test/units/testsuite-44.sh
index 97541634d0..0100b31b57 100755
--- a/test/units/testsuite-44.sh
+++ b/test/units/testsuite-44.sh
@@ -1,19 +1,19 @@
#!/usr/bin/env bash
-set -ex
+set -eux
systemd-analyze log-level debug
-systemd-run -p LogNamespace=foobar echo "hello world"
+systemd-run --wait -p LogNamespace=foobar echo "hello world"
journalctl --namespace=foobar --sync
-journalctl --namespace=foobar > /tmp/hello-world
-journalctl > /tmp/no-hello-world
+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
+grep "^hello world$" /tmp/hello-world
+grep "^hello world$" /tmp/no-hello-world && { echo 'unexpected success'; exit 1; }
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-46.sh b/test/units/testsuite-46.sh
index 00bbdf507e..808270f33d 100755
--- a/test/units/testsuite-46.sh
+++ b/test/units/testsuite-46.sh
@@ -1,25 +1,25 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
# Check if homectl is installed, and if it isn't bail out early instead of failing
if ! test -x /usr/bin/homectl ; then
- echo OK > /testok
+ echo OK >/testok
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.
- homectl inspect $1 | tee /tmp/a
- userdbctl user $1 | tee /tmp/b
-
- local PATTERN='/^\s*Disk (Size|Free|Floor|Ceiling):/d'
- diff <(sed -r "$PATTERN" /tmp/a) <(sed -r "$PATTERN" /tmp/b)
- rm /tmp/a /tmp/b
+ # 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:?missing argument}"
+ homectl inspect "$USERNAME" | tee /tmp/a
+ userdbctl user "$USERNAME" | tee /tmp/b
+
+ diff -I '/^\s*Disk (Size|Free|Floor|Ceiling):/' /tmp/{a,b}
+ rm /tmp/{a,b}
}
systemd-analyze log-level debug
@@ -66,16 +66,20 @@ inspect test-user
PASSWORD=xEhErW0ndafV4s homectl deactivate test-user
inspect test-user
-! 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 -- test -f /home/test-user/xyz \
+ && { echo 'unexpected success'; exit 1; }
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 -- test -f /home/test-user/xyz \
+ && { echo 'unexpected success'; exit 1; }
homectl remove test-user
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-47-repro.sh b/test/units/testsuite-47-repro.sh
index 8c34289c52..b008f52e95 100755
--- a/test/units/testsuite-47-repro.sh
+++ b/test/units/testsuite-47-repro.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
sleep infinity &
-echo $! > /leakedtestpid
+echo $! >/leakedtestpid
wait $!
diff --git a/test/units/testsuite-47.sh b/test/units/testsuite-47.sh
index 50034cf3d9..c1714d14d0 100755
--- a/test/units/testsuite-47.sh
+++ b/test/units/testsuite-47.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
@@ -20,6 +20,6 @@ ps -p "$leaked_pid" && exit 42
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-48.sh b/test/units/testsuite-48.sh
index 03231e71b1..0ce8e9c067 100755
--- a/test/units/testsuite-48.sh
+++ b/test/units/testsuite-48.sh
@@ -1,9 +1,9 @@
#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -ex
+set -eux
-cat > /run/systemd/system/testservice-48.target <<EOF
+cat >/run/systemd/system/testservice-48.target <<EOF
[Unit]
Wants=testservice-48.service
EOF
@@ -23,7 +23,7 @@ systemctl start testservice-48.target
# May 07 23:12:20 systemd-testsuite testsuite-48.sh[53]: ef53
sleep 3.1
-cat > /run/systemd/system/testservice-48.service <<EOF
+cat >/run/systemd/system/testservice-48.service <<EOF
[Service]
ExecStart=/bin/sleep infinity
EOF
@@ -39,7 +39,7 @@ systemctl daemon-reload
sleep 3.1
-cat > /run/systemd/system/testservice-48.service <<EOF
+cat >/run/systemd/system/testservice-48.service <<EOF
[Service]
ExecStart=/bin/sleep infinity
EOF
@@ -61,7 +61,7 @@ systemctl daemon-reload
sleep 3.1
-cat > /run/systemd/system/testservice-48.target <<EOF
+cat >/run/systemd/system/testservice-48.target <<EOF
[Unit]
Conflicts=shutdown.target
Wants=testservice-48.service
@@ -71,7 +71,7 @@ systemctl daemon-reload
systemctl start testservice-48.target
-cat > /run/systemd/system/testservice-48.service <<EOF
+cat >/run/systemd/system/testservice-48.service <<EOF
[Service]
ExecStart=/bin/sleep infinity
EOF
@@ -80,6 +80,6 @@ systemctl restart testservice-48.target
systemctl is-active testservice-48.service
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-49.sh b/test/units/testsuite-49.sh
index 07bb20d99c..8e360b4951 100755
--- a/test/units/testsuite-49.sh
+++ b/test/units/testsuite-49.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
-set -ex
+set -eux
-echo "MARKER_FIXED" > /run/testservice-49-fixed
+echo "MARKER_FIXED" >/run/testservice-49-fixed
mkdir -p /run/inaccessible
systemctl start testsuite-49-namespaced.service
@@ -11,7 +11,7 @@ set +e
systemctl bind --mkdir testsuite-49-namespaced.service /run/testservice-49-fixed /run/inaccessible/testfile_fixed && exit 1
set -e
-echo "MARKER_RUNTIME" > /run/testservice-49-runtime
+echo "MARKER_RUNTIME" >/run/testservice-49-runtime
systemctl bind --mkdir testsuite-49-namespaced.service /run/testservice-49-runtime /tmp/testfile_runtime
@@ -38,6 +38,6 @@ set +e
systemctl is-active testsuite-49-non-namespaced.service && exit 1
set -e
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh
index e2d8ef6da2..505d93c13b 100755
--- a/test/units/testsuite-50.sh
+++ b/test/units/testsuite-50.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -ex
+set -eux
set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
@@ -26,48 +26,48 @@ 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 $os_release
-
-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 $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
-cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f $os_release
-cat ${image_dir}/mount/etc/os-release | grep -q -F -f $os_release
-cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
+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")
+
+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=$(ls -1 /dev/mapper/ | grep -c verity)
+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
+if [ "${verity_count}" -lt 1 ]; then
echo "Verity device ${image}.raw not found in /dev/mapper/"
exit 1
fi
-umount ${image_dir}/mount
-umount ${image_dir}/mount2
+umount "${image_dir}/mount"
+umount "${image_dir}/mount2"
-systemd-run -t -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 -t -p RootImage=${image}.raw -p RootHash=${image}.foohash -p RootVerity=${image}.fooverity cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -t -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 -t -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 -t --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
+systemd-run -t --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)"
@@ -100,51 +100,54 @@ else
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)"
+root_size="$(du -k "${image}.raw" | cut -f1)"
+verity_size="$(du -k "${image}.verity" | cut -f1)"
# 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))
+dd if=/dev/zero of="${image}.gpt" bs=512 count=$((8192+root_size*2+verity_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"
+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"
-uuid="$(head -c 32 ${image}.roothash | cut -c -8)-$(head -c 32 ${image}.roothash | cut -c 9-12)-$(head -c 32 ${image}.roothash | cut -c 13-16)-$(head -c 32 ${image}.roothash | cut -c 17-20)-$(head -c 32 ${image}.roothash | cut -c 21-)"
-echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk ${image}.gpt
-uuid="$(tail -c 32 ${image}.roothash | cut -c -8)-$(tail -c 32 ${image}.roothash | cut -c 9-12)-$(tail -c 32 ${image}.roothash | cut -c 13-16)-$(tail -c 32 ${image}.roothash | cut -c 17-20)-$(tail -c 32 ${image}.roothash | cut -c 21-)"
-echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk ${image}.gpt --append
-sfdisk --part-label ${image}.gpt 1 "Root Partition"
-sfdisk --part-label ${image}.gpt 2 "Verity Partition"
-loop="$(losetup --show -P -f ${image}.gpt)"
-dd if=${image}.raw of=${loop}p1
-dd if=${image}.verity of=${loop}p2
-losetup -d ${loop}
+verity_size="$((verity_size * 2))KiB"
+# 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
+sfdisk --part-label "${image}.gpt" 1 "Root Partition"
+sfdisk --part-label "${image}.gpt" 2 "Verity Partition"
+loop="$(losetup --show -P -f "${image}.gpt")"
+dd if="${image}.raw" of="${loop}p1"
+dd if="${image}.verity" of="${loop}p2"
+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-)
+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":"yes","node":'
-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,"node":'
-systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
-systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f $os_release
+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":"yes","node":'
+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,"node":'
+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")
-systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount
-cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f $os_release
-cat ${image_dir}/mount/etc/os-release | grep -q -F -f $os_release
-cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
-umount ${image_dir}/mount
+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"
+umount "${image_dir}/mount"
# add explicit -p MountAPIVFS=yes once to test the parser
-systemd-run -t -p RootImage=${image}.gpt -p RootHash=${roothash} -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -t -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -t -p RootImage=${image}.raw -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -t -p RootImage=${image}.gpt -p RootImageOptions="root:ro,noatime root:ro,dev" mount | grep -F "squashfs" | grep -q -F "noatime"
+systemd-run -t -p RootImage="${image}.raw" -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -t -p RootImage="${image}.gpt" -p RootImageOptions="root:ro,noatime root:ro,dev" mount | grep -F "squashfs" | grep -q -F "noatime"
-mkdir -p mkdir -p ${image_dir}/result
+mkdir -p "${image_dir}/result"
cat >/run/systemd/system/testservice-50a.service <<EOF
[Service]
Type=oneshot
@@ -156,8 +159,8 @@ 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"
+grep -F "squashfs" "${image_dir}/result/a" | grep -q -F "noatime"
+grep -F "squashfs" "${image_dir}/result/a" | grep -q -F -v "nosuid"
cat >/run/systemd/system/testservice-50b.service <<EOF
[Service]
@@ -172,7 +175,7 @@ RootImageOptions=home:ro,dev nosuid,dev,%%foo
MountAPIVFS=yes
EOF
systemctl start testservice-50b.service
-grep -F "squashfs" ${image_dir}/result/b | grep -q -F "noatime"
+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"
@@ -184,9 +187,9 @@ systemd-run -t -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nos
systemd-run -t -p MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t -p MountImages="${image}.raw:/run/img2\:3" cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t -p MountImages="${image}.raw:/run/img2\:3:nosuid" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -t -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 -t -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 -t -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"
+systemd-run -t -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 -t -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 -t -p TemporaryFileSystem=/run -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
cat >/run/systemd/system/testservice-50c.service <<EOF
[Service]
MountAPIVFS=yes
@@ -201,13 +204,13 @@ 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"
+grep -q -F "MARKER=1" "${image_dir}/result/c"
+grep -F "squashfs" "${image_dir}/result/c" | grep -q -F "noatime"
+grep -F "squashfs" "${image_dir}/result/c" | grep -q -F -v "nosuid"
# Adding a new mounts at runtime works if the unit is in the active state,
# so use Type=notify to make sure there's no race condition in the test
-cat > /run/systemd/system/testservice-50d.service <<EOF
+cat >/run/systemd/system/testservice-50d.service <<EOF
[Service]
RuntimeMaxSec=300
Type=notify
@@ -218,7 +221,7 @@ ExecStart=/bin/sh -c 'systemd-notify --ready; while ! grep -q -F MARKER /tmp/img
EOF
systemctl start testservice-50d.service
-systemctl mount-image --mkdir testservice-50d.service ${image}.raw /tmp/img root:nosuid
+systemctl mount-image --mkdir testservice-50d.service "${image}.raw" /tmp/img root:nosuid
while systemctl show -P SubState testservice-50d.service | grep -q running
do
@@ -228,12 +231,12 @@ done
systemctl is-active testservice-50d.service
# ExtensionImages will set up an overlay
-systemd-run -t --property ExtensionImages=/usr/share/app0.raw --property RootImage=${image}.raw cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -t --property ExtensionImages=/usr/share/app0.raw --property RootImage=${image}.raw cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -t --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage=${image}.raw cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -t --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage=${image}.raw cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -t --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage=${image}.raw cat /opt/script1.sh | grep -q -F "extension-release.app1"
-systemd-run -t --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage=${image}.raw cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -t --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -t --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -t --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -t --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -t --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app1"
+systemd-run -t --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
cat >/run/systemd/system/testservice-50e.service <<EOF
[Service]
MountAPIVFS=yes
diff --git a/test/units/testsuite-51.sh b/test/units/testsuite-51.sh
index 246412a079..06bc1602f8 100755
--- a/test/units/testsuite-51.sh
+++ b/test/units/testsuite-51.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemctl start testsuite-51-repro-1
diff --git a/test/units/testsuite-53.sh b/test/units/testsuite-53.sh
index 3536c24271..3eef94bc14 100755
--- a/test/units/testsuite-53.sh
+++ b/test/units/testsuite-53.sh
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
->/failed
+: >/failed
# Reset host date to current time, 3 days in the past.
date -s "-3 days"
diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh
index aabc56f348..f8ddff4a30 100755
--- a/test/units/testsuite-54.sh
+++ b/test/units/testsuite-54.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
-set -ex
+# shellcheck disable=SC2016
+set -eux
systemd-analyze log-level debug
@@ -10,22 +11,24 @@ systemd-run -p LoadCredential=passwd:/etc/passwd \
-p DynamicUser=1 \
--wait \
--pipe \
- cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' > /tmp/ts54-concat
+ cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' >/tmp/ts54-concat
( cat /etc/passwd /etc/shadow && echo -n wuff ) | cmp /tmp/ts54-concat
rm /tmp/ts54-concat
# Verify that the creds are immutable
-! systemd-run -p LoadCredential=passwd:/etc/passwd \
+systemd-run -p LoadCredential=passwd:/etc/passwd \
-p DynamicUser=1 \
--wait \
- touch '${CREDENTIALS_DIRECTORY}/passwd'
-! systemd-run -p LoadCredential=passwd:/etc/passwd \
+ touch '${CREDENTIALS_DIRECTORY}/passwd' \
+ && { echo 'unexpected success'; exit 1; }
+systemd-run -p LoadCredential=passwd:/etc/passwd \
-p DynamicUser=1 \
--wait \
- rm '${CREDENTIALS_DIRECTORY}/passwd'
+ rm '${CREDENTIALS_DIRECTORY}/passwd' \
+ && { echo 'unexpected success'; exit 1; }
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-55-slowgrowth.sh b/test/units/testsuite-55-slowgrowth.sh
index ff5a747348..68bffad454 100755
--- a/test/units/testsuite-55-slowgrowth.sh
+++ b/test/units/testsuite-55-slowgrowth.sh
@@ -1,32 +1,39 @@
#!/usr/bin/env bash
-set -eu -o pipefail
+# Don't use set -x here, since it generates a lot of output and slows
+# the script down, causing unexpected test fails.
+set -eu
+set -o pipefail
PAGE_SIZE=$(getconf PAGE_SIZE)
-BLOAT_ITERATION_TARGET=$(( 100 << 20 )) # 100 MB
+BLOAT_ITERATION_TARGET=$((100 << 20)) # 100 MB
BLOAT_HOLDER=()
PID="$$"
function bloat {
- local set_size=$(cat "/proc/$PID/statm" | cut -d " " -f2)
- local mem_usage=$(( "$set_size" * "$PAGE_SIZE" ))
- local target_mem_size=$(( "$mem_usage" + "$1" ))
+ local set_size mem_usage target_mem_size
+
+ # Following `| cat` weirdness is intentional to generate some reclaim
+ # activity in case there's no swap available.
+ set_size=$(cut -d " " -f2 "/proc/$PID/statm" | cat)
+ mem_usage=$((set_size * PAGE_SIZE))
+ target_mem_size=$((mem_usage + $1))
BLOAT_HOLDER=()
while [[ "$mem_usage" -lt "$target_mem_size" ]]; do
echo "target $target_mem_size"
echo "mem usage $mem_usage"
- BLOAT_HOLDER+=( $(printf "%0.sg" {1..1000000}) )
- set_size=$(cat "/proc/$PID/statm" | cut -d " " -f2)
- mem_usage=$(( "$set_size" * "$PAGE_SIZE" ))
+ BLOAT_HOLDER+=("$(printf "=%0.s" {1..1000000})")
+ set_size=$(cut -d " " -f2 "/proc/$PID/statm" | cat)
+ mem_usage=$((set_size * PAGE_SIZE))
done
}
function run {
local arr=()
- while [[ true ]]; do
+ while :; do
bloat "$BLOAT_ITERATION_TARGET"
- arr+=( "$BLOAT_HOLDER" )
+ arr+=("${BLOAT_HOLDER[@]}")
sleep 1
done
}
diff --git a/test/units/testsuite-55.sh b/test/units/testsuite-55.sh
index f7896ada42..63549c8a78 100755
--- a/test/units/testsuite-55.sh
+++ b/test/units/testsuite-55.sh
@@ -1,24 +1,27 @@
#!/usr/bin/env bash
-set -ex
+set -eux
set -o pipefail
systemd-analyze log-level debug
systemd-analyze log-target console
# Loose checks to ensure the environment has the necessary features for systemd-oomd
-[[ -e /proc/pressure ]] || echo "no PSI" >> /skipped
-cgroup_type=$(stat -fc %T /sys/fs/cgroup/)
+[[ -e /proc/pressure ]] || echo "no PSI" >>/skipped
+cgroup_type="$(stat -fc %T /sys/fs/cgroup/)"
if [[ "$cgroup_type" != *"cgroup2"* ]] && [[ "$cgroup_type" != *"0x63677270"* ]]; then
- echo "no cgroup2" >> /skipped
+ echo "no cgroup2" >>/skipped
fi
if [ ! -f /usr/lib/systemd/systemd-oomd ] && [ ! -f /lib/systemd/systemd-oomd ]; then
- echo "no oomd" >> /skipped
+ echo "no oomd" >>/skipped
+fi
+
+if [[ -e /skipped ]]; then
+ exit 0
fi
-[[ -e /skipped ]] && exit 0 || true
rm -rf /etc/systemd/system/testsuite-55-testbloat.service.d
-echo "DefaultMemoryPressureDurationSec=5s" >> /etc/systemd/oomd.conf
+echo "DefaultMemoryPressureDurationSec=5s" >>/etc/systemd/oomd.conf
systemctl start testsuite-55-testchill.service
systemctl start testsuite-55-testbloat.service
@@ -30,7 +33,7 @@ oomctl | grep "Default Memory Pressure Duration: 5s"
# systemd-oomd watches for elevated pressure for 5 seconds before acting.
# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
-timeout=$(date -ud "2 minutes" +%s)
+timeout="$(date -ud "2 minutes" +%s)"
while [[ $(date -u +%s) -le $timeout ]]; do
if ! systemctl status testsuite-55-testbloat.service; then
break
@@ -47,16 +50,16 @@ if setfattr -n user.xattr_test -v 1 /sys/fs/cgroup/; then
sleep 120 # wait for systemd-oomd kill cool down and elevated memory pressure to come down
mkdir -p /etc/systemd/system/testsuite-55-testbloat.service.d/
- echo "[Service]" > /etc/systemd/system/testsuite-55-testbloat.service.d/override.conf
- echo "ManagedOOMPreference=avoid" >> /etc/systemd/system/testsuite-55-testbloat.service.d/override.conf
+ echo "[Service]" >/etc/systemd/system/testsuite-55-testbloat.service.d/override.conf
+ echo "ManagedOOMPreference=avoid" >>/etc/systemd/system/testsuite-55-testbloat.service.d/override.conf
systemctl daemon-reload
systemctl start testsuite-55-testchill.service
systemctl start testsuite-55-testmunch.service
systemctl start testsuite-55-testbloat.service
- timeout=$(date -ud "2 minutes" +%s)
- while [[ $(date -u +%s) -le $timeout ]]; do
+ timeout="$(date -ud "2 minutes" +%s)"
+ while [[ "$(date -u +%s)" -le "$timeout" ]]; do
if ! systemctl status testsuite-55-testmunch.service; then
break
fi
@@ -71,6 +74,6 @@ fi
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-56.sh b/test/units/testsuite-56.sh
index 9de03e1dea..27bd3ca8f7 100755
--- a/test/units/testsuite-56.sh
+++ b/test/units/testsuite-56.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-set -ex
+set -eux
systemd-analyze log-level debug
@@ -29,11 +29,13 @@ systemd-run --wait --unit=one -p ExitType=cgroup /tmp/test56-exit-cgroup.sh
systemd-run --wait --unit=two -p ExitType=cgroup -p ExecCondition=true /tmp/test56-exit-cgroup.sh
# false exec condition: systemd-run should exit immediately with status code: 1
-! systemd-run --wait --unit=three -p ExitType=cgroup -p ExecCondition=false /tmp/test56-exit-cgroup.sh
+systemd-run --wait --unit=three -p ExitType=cgroup -p ExecCondition=false /tmp/test56-exit-cgroup.sh \
+ && { echo 'unexpected success'; exit 1; }
# service should exit uncleanly
(sleep 1; systemctl kill --signal 9 four) &
-! systemd-run --wait --unit=four -p ExitType=cgroup /tmp/test56-exit-cgroup.sh
+systemd-run --wait --unit=four -p ExitType=cgroup /tmp/test56-exit-cgroup.sh \
+ && { echo 'unexpected success'; exit 1; }
# Multiple level process tree, parent process exits quickly
@@ -55,7 +57,8 @@ systemd-run --wait --unit=five -p ExitType=cgroup /tmp/test56-exit-cgroup-parent
# service should exit uncleanly
(sleep 1; systemctl kill --signal 9 six) &
-! systemd-run --wait --unit=six -p ExitType=cgroup /tmp/test56-exit-cgroup-parentless.sh
+systemd-run --wait --unit=six -p ExitType=cgroup /tmp/test56-exit-cgroup-parentless.sh \
+ && { echo 'unexpected success'; exit 1; }
# Multiple level process tree, parent process exits uncleanly but last process exits cleanly
@@ -85,10 +88,11 @@ EOF
chmod +x /tmp/test56-exit-cgroup-unclean.sh
# service should exit uncleanly after 1 second
-! systemd-run --wait --unit=eight -p ExitType=cgroup /tmp/test56-exit-cgroup-unclean.sh
+systemd-run --wait --unit=eight -p ExitType=cgroup /tmp/test56-exit-cgroup-unclean.sh \
+ && { echo 'unexpected success'; exit 1; }
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
diff --git a/test/units/testsuite-58.service b/test/units/testsuite-58.service
new file mode 100644
index 0000000000..d8ad589ca0
--- /dev/null
+++ b/test/units/testsuite-58.service
@@ -0,0 +1,6 @@
+[Unit]
+Description=TEST-56-EXIT-TYPE
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh
new file mode 100755
index 0000000000..772f1b78d4
--- /dev/null
+++ b/test/units/testsuite-58.sh
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+set -eux
+
+export SYSTEMD_LOG_LEVEL=debug
+export PAGER=cat
+
+mkdir -p /tmp/testsuite-58-defs/
+
+# First part: create a disk image and verify its in order
+
+cat > /tmp/testsuite-58-defs/esp.conf <<EOF
+[Partition]
+Type=esp
+SizeMinBytes=10M
+Format=vfat
+EOF
+
+cat > /tmp/testsuite-58-defs/usr.conf <<EOF
+[Partition]
+Type=usr
+SizeMinBytes=10M
+Format=ext4
+ReadOnly=yes
+EOF
+
+cat > /tmp/testsuite-58-defs/root.conf <<EOF
+[Partition]
+Type=root
+SizeMinBytes=10M
+Format=ext4
+MakeDirectories=/usr /efi
+EOF
+
+systemd-repart --definitions=/tmp/testsuite-58-defs/ --empty=create --size=auto --seed=750b6cd5c4ae4012a15e7be3c29e6a47 /var/tmp/testsuite-58.img
+
+sfdisk --dump /var/tmp/testsuite-58.img > /tmp/testsuite-58.dump
+
+grep -qxF '/var/tmp/testsuite-58.img1 : start= 2048, size= 20480, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=39107B09-615D-48FB-BA37-C663885FCE67, name="esp"' /tmp/testsuite-58.dump
+grep -qxF '/var/tmp/testsuite-58.img2 : start= 22528, size= 20480, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0, name="root-x86-64"' /tmp/testsuite-58.dump
+grep -qxF '/var/tmp/testsuite-58.img3 : start= 43008, size= 20480, type=8484680C-9521-48C6-9C11-B0720656F69E, uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16, name="usr-x86-64", attrs="GUID:60"' /tmp/testsuite-58.dump
+
+# Second part, duplicate it with CopyBlocks=auto
+
+cat > /tmp/testsuite-58-defs/esp.conf <<EOF
+[Partition]
+Type=esp
+CopyBlocks=auto
+EOF
+
+cat > /tmp/testsuite-58-defs/usr.conf <<EOF
+[Partition]
+Type=usr
+ReadOnly=yes
+CopyBlocks=auto
+EOF
+
+cat > /tmp/testsuite-58-defs/root.conf <<EOF
+[Partition]
+Type=root
+CopyBlocks=auto
+EOF
+
+systemd-repart --definitions=/tmp/testsuite-58-defs/ --empty=create --size=auto --seed=750b6cd5c4ae4012a15e7be3c29e6a47 --image=/var/tmp/testsuite-58.img /var/tmp/testsuite-58.2.img
+
+cmp /var/tmp/testsuite-58.img /var/tmp/testsuite-58.2.img
+
+rm -f /var/tmp/testsuite-58.img /var/tmp/testsuite-58.2.img /tmp/testsuite-58.dump
+rm -rf /tmp/testsuite-58-defs/
+
+echo OK >/testok
+
+exit 0
diff --git a/test/units/testsuite-59.service b/test/units/testsuite-59.service
new file mode 100644
index 0000000000..66d06866c8
--- /dev/null
+++ b/test/units/testsuite-59.service
@@ -0,0 +1,6 @@
+[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
new file mode 100755
index 0000000000..459d6e6403
--- /dev/null
+++ b/test/units/testsuite-59.sh
@@ -0,0 +1,90 @@
+#!/usr/bin/env bash
+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
+systemd-analyze log-target console
+
+
+cat >/run/systemd/system/testservice-fail-59.service <<EOF
+[Unit]
+Description=TEST-59-RELOADING-RESTART Normal exit
+
+[Service]
+Type=notify
+ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+EOF
+
+cat >/run/systemd/system/testservice-fail-restart-59.service <<EOF
+[Unit]
+Description=TEST-59-RELOADING-RESTART Restart=on-failure
+
+[Service]
+Type=notify
+ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+Restart=on-failure
+StartLimitBurst=1
+EOF
+
+
+cat >/run/systemd/system/testservice-abort-restart-59.service <<EOF
+[Unit]
+Description=TEST-59-RELOADING-RESTART Restart=on-abort
+
+[Service]
+Type=notify
+ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1"
+Restart=on-abort
+EOF
+
+systemctl daemon-reload
+
+# This service sends a RELOADING=1 message then exits before it sends a
+# READY=1. Ensure it enters failed state and does not linger in reloading
+# state.
+systemctl start testservice-fail-59.service
+wait_on_state_or_fail "testservice-fail-59.service" "failed" "30"
+
+# This service sends a RELOADING=1 message then exits before it sends a
+# READY=1. It should automatically restart on failure. Ensure it enters failed
+# state and does not linger in reloading state.
+systemctl start testservice-fail-restart-59.service
+wait_on_state_or_fail "testservice-fail-restart-59.service" "failed" "30"
+
+# This service sends a RELOADING=1 message then exits before it sends a
+# READY=1. It should automatically restart on abort. It will sleep for 5s
+# to allow us to send it a SIGABRT. Ensure the service enters the failed state
+# and does not linger in reloading state.
+systemctl start testservice-abort-restart-59.service
+systemctl --signal=SIGABRT kill testservice-abort-restart-59.service
+wait_on_state_or_fail "testservice-abort-restart-59.service" "failed" "30"
+
+systemd-analyze log-level info
+
+echo OK >/testok
+
+exit 0
diff --git a/tools/add-git-hook.sh b/tools/add-git-hook.sh
index 66bbcd64ea..8cff62e864 100755
--- a/tools/add-git-hook.sh
+++ b/tools/add-git-hook.sh
@@ -2,9 +2,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
-cd "$MESON_SOURCE_ROOT"
+cd "${MESON_SOURCE_ROOT:?}"
-if [ ! -f .git/hooks/pre-commit.sample -o -f .git/hooks/pre-commit ]; then
+if [ ! -f .git/hooks/pre-commit.sample ] || [ -f .git/hooks/pre-commit ]; then
exit 2 # not needed
fi
diff --git a/tools/check-api-docs.sh b/tools/check-api-docs.sh
index 283e7a64d7..0bf053b5ff 100755
--- a/tools/check-api-docs.sh
+++ b/tools/check-api-docs.sh
@@ -1,31 +1,32 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
sd_good=0
sd_total=0
udev_good=0
udev_total=0
-deprecated="
+deprecated=(
-e sd_bus_try_close
-e sd_bus_process_priority
-e sd_bus_message_get_priority
-e sd_bus_message_set_priority
-e sd_seat_can_multi_session
-e sd_journal_open_container
-"
+)
-for symbol in `nm -g --defined-only "$@" | grep " T " | cut -d" " -f3 | grep -wv $deprecated | sort -u` ; do
- if test -f ${MESON_BUILD_ROOT}/man/$symbol.3 ; then
+for symbol in $(nm -g --defined-only "$@" | grep " T " | cut -d" " -f3 | grep -wv "${deprecated[@]}" | sort -u); do
+ if test -f "${MESON_BUILD_ROOT:?}/man/$symbol.3"; then
echo "✓ Symbol $symbol() is documented."
good=1
else
- printf " \x1b[1;31mSymbol $symbol() lacks documentation.\x1b[0m\n"
+ echo -e " \x1b[1;31mSymbol $symbol() lacks documentation.\x1b[0m"
good=0
fi
- case $symbol in
+ case "$symbol" in
sd_*)
((sd_good+=good))
((sd_total+=1))
diff --git a/tools/check-directives.sh b/tools/check-directives.sh
index 2274d36e60..0661da4d3b 100755
--- a/tools/check-directives.sh
+++ b/tools/check-directives.sh
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
SOURCE_ROOT="${1:?Missing argument: project source root}"
BUILD_ROOT="${2:?Missing argument: project build root}"
diff --git a/tools/check-help.sh b/tools/check-help.sh
index 721dec4c64..8e7d236dd9 100755
--- a/tools/check-help.sh
+++ b/tools/check-help.sh
@@ -1,30 +1,41 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+set -o pipefail
+# Note: `grep ... >/dev/null` instead of just `grep -q` is used intentionally
+# here, since `grep -q` exits on the first match causing SIGPIPE being
+# sent to the sender.
+
+BINARY="${1:?}"
export SYSTEMD_LOG_LEVEL=info
+if [[ ! -x "$BINARY" ]]; then
+ echo "$BINARY is not an executable"
+ exit 1
+fi
+
# output width
-if "$1" --help | grep -v 'default:' | grep -E -q '.{80}.'; then
- echo "$(basename "$1") --help output is too wide:"
- "$1" --help | awk 'length > 80' | grep -E --color=yes '.{80}'
+if "$BINARY" --help | grep -v 'default:' | grep -E '.{80}.' >/dev/null; then
+ echo "$(basename "$BINARY") --help output is too wide:"
+ "$BINARY" --help | awk 'length > 80' | grep -E --color=yes '.{80}'
exit 1
fi
# --help prints something. Also catches case where args are ignored.
-if ! "$1" --help | grep -q .; then
- echo "$(basename "$1") --help output is empty."
+if ! "$BINARY" --help | grep . >/dev/null; then
+ echo "$(basename "$BINARY") --help output is empty."
exit 2
fi
# no --help output to stdout
-if "$1" --help 2>&1 1>/dev/null | grep .; then
- echo "$(basename "$1") --help prints to stderr"
+if "$BINARY" --help 2>&1 1>/dev/null | grep .; then
+ echo "$(basename "$BINARY") --help prints to stderr"
exit 3
fi
# error output to stderr
-if ! "$1" --no-such-parameter 2>&1 1>/dev/null | grep -q .; then
- echo "$(basename "$1") with an unknown parameter does not print to stderr"
+if ! ("$BINARY" --no-such-parameter 2>&1 1>/dev/null || :) | grep . >/dev/null; then
+ echo "$(basename "$BINARY") with an unknown parameter does not print to stderr"
exit 4
fi
diff --git a/tools/find-build-dir.sh b/tools/find-build-dir.sh
index e449b6e865..79a79fcc58 100755
--- a/tools/find-build-dir.sh
+++ b/tools/find-build-dir.sh
@@ -1,12 +1,12 @@
#!/bin/sh
# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
+set -eu
# Try to guess the build directory:
# we look for subdirectories of the parent directory that look like ninja build dirs.
-if [ -n "$BUILD_DIR" ]; then
- echo "$(realpath "$BUILD_DIR")"
+if [ -n "${BUILD_DIR:=}" ]; then
+ realpath "$BUILD_DIR"
exit 0
fi
@@ -14,20 +14,20 @@ root="$(dirname "$(realpath "$0")")"
found=
for i in "$root"/../*/build.ninja; do
- c="$(dirname $i)"
+ c="$(dirname "$i")"
[ -d "$c" ] || continue
[ "$(basename "$c")" != mkosi.builddir ] || continue
if [ -n "$found" ]; then
- echo 'Found multiple candidates, specify build directory with $BUILD_DIR' >&2
+ echo "Found multiple candidates, specify build directory with \$BUILD_DIR" >&2
exit 2
fi
found="$c"
done
if [ -z "$found" ]; then
- echo 'Specify build directory with $BUILD_DIR' >&2
+ echo "Specify build directory with \$BUILD_DIR" >&2
exit 1
fi
-echo "$(realpath $found)"
+realpath "$found"
diff --git a/tools/find-double-newline.sh b/tools/find-double-newline.sh
index 7ea6de8311..2999a58c0b 100755
--- a/tools/find-double-newline.sh
+++ b/tools/find-double-newline.sh
@@ -1,38 +1,40 @@
#!/bin/sh
# SPDX-License-Identifier: LGPL-2.1-or-later
-TOP=`git rev-parse --show-toplevel`
+set -eu
-case "$1" in
+TOP="$(git rev-parse --show-toplevel)"
+
+case "${1:-}" in
recdiff)
- if [ "$2" = "" ] ; then
+ if [ "${2:-}" = "" ] ; then
DIR="$TOP"
else
DIR="$2"
fi
- find $DIR -type f \( -name '*.[ch]' -o -name '*.xml' \) -exec $0 diff \{\} \;
+ find "$DIR" -type f \( -name '*.[ch]' -o -name '*.xml' \) -exec "$0" diff \{\} \;
;;
recpatch)
- if [ "$2" = "" ] ; then
+ if [ "${2:-}" = "" ] ; then
DIR="$TOP"
else
DIR="$2"
fi
- find $DIR -type f \( -name '*.[ch]' -o -name '*.xml' \) -exec $0 patch \{\} \;
+ find "$DIR" -type f \( -name '*.[ch]' -o -name '*.xml' \) -exec "$0" patch \{\} \;
;;
diff)
- T=`mktemp`
- sed '/^$/N;/^\n$/D' < "$2" > "$T"
+ T="$(mktemp)"
+ sed '/^$/N;/^\n$/D' <"${2:?}" >"$T"
diff -u "$2" "$T"
rm -f "$T"
;;
patch)
- sed -i '/^$/N;/^\n$/D' "$2"
+ sed -i '/^$/N;/^\n$/D' "${2:?}"
;;
*)
diff --git a/tools/find-tabs.sh b/tools/find-tabs.sh
index 54d922975c..6cea339ac6 100755
--- a/tools/find-tabs.sh
+++ b/tools/find-tabs.sh
@@ -1,38 +1,40 @@
#!/bin/sh
# SPDX-License-Identifier: LGPL-2.1-or-later
-TOP=`git rev-parse --show-toplevel`
+set -eu
-case "$1" in
+TOP="$(git rev-parse --show-toplevel)"
+
+case "${1:-}" in
recdiff)
- if [ "$2" = "" ] ; then
+ if [ "${2:-}" = "" ] ; then
DIR="$TOP"
else
DIR="$2"
fi
- find $DIR -type f \( -name '*.[ch]' -o -name '*.xml' \) -exec $0 diff \{\} \;
+ find "$DIR" -type f \( -name '*.[ch]' -o -name '*.xml' \) -exec "$0" diff \{\} \;
;;
recpatch)
- if [ "$2" = "" ] ; then
+ if [ "${2:-}" = "" ] ; then
DIR="$TOP"
else
DIR="$2"
fi
- find $DIR -type f \( -name '*.[ch]' -o -name '*.xml' \) -exec $0 patch \{\} \;
+ find "$DIR" -type f \( -name '*.[ch]' -o -name '*.xml' \) -exec "$0" patch \{\} \;
;;
diff)
- T=`mktemp`
- sed 's/\t/ /g' < "$2" > "$T"
+ T="$(mktemp)"
+ sed 's/\t/ /g' <"${2:?}" >"$T"
diff -u "$2" "$T"
rm -f "$T"
;;
patch)
- sed -i 's/\t/ /g' "$2"
+ sed -i 's/\t/ /g' "${2:?}"
;;
*)
diff --git a/tools/meson-apply-m4.sh b/tools/meson-apply-m4.sh
index 7b4801ff94..35d8d159ef 100755
--- a/tools/meson-apply-m4.sh
+++ b/tools/meson-apply-m4.sh
@@ -1,25 +1,21 @@
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
-CONFIG=$1
-TARGET=$2
+CONFIG="${1:?Missing path to config.h}"
+TARGET="${2:?Missing target m4 file}"
-if [ $# -ne 2 ]; then
- echo 'Invalid number of arguments.'
- exit 1
-fi
-
-if [ ! -f $CONFIG ]; then
+if [ ! -f "$CONFIG" ]; then
echo "$CONFIG not found."
exit 2
fi
-if [ ! -f $TARGET ]; then
+if [ ! -f "$TARGET" ]; then
echo "$TARGET not found."
exit 3
fi
-DEFINES=$(awk '$1 == "#define" && $3 == "1" { printf "-D%s ", $2 }' $CONFIG)
+DEFINES=()
+mapfile -t DEFINES < <(awk '$1 == "#define" && $3 == "1" { printf "-D%s\n", $2 }' "$CONFIG")
-m4 -P $DEFINES $TARGET
+m4 -P "${DEFINES[@]}" "$TARGET"
diff --git a/tools/meson-make-symlink.sh b/tools/meson-make-symlink.sh
index 96f5892281..653a73b0bd 100755
--- a/tools/meson-make-symlink.sh
+++ b/tools/meson-make-symlink.sh
@@ -2,6 +2,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
+SOURCE="${1:?}"
+TARGET="${2:?}"
+
if [ "${MESON_INSTALL_QUIET:-0}" = 1 ] ; then
VERBOSE=""
else
@@ -11,9 +14,9 @@ fi
# this is needed mostly because $DESTDIR is provided as a variable,
# and we need to create the target directory...
-mkdir -${VERBOSE}p "$(dirname "${DESTDIR:-}$2")"
-if [ "$(dirname $1)" = . -o "$(dirname $1)" = .. ]; then
- ln -${VERBOSE}fs -T -- "$1" "${DESTDIR:-}$2"
+mkdir -${VERBOSE}p "$(dirname "${DESTDIR:-}$TARGET")"
+if [ "$(dirname "$SOURCE")" = . ] || [ "$(dirname "$SOURCE")" = .. ]; then
+ ln -${VERBOSE}fs -T -- "$SOURCE" "${DESTDIR:-}$TARGET"
else
- ln -${VERBOSE}fs -T --relative -- "${DESTDIR:-}$1" "${DESTDIR:-}$2"
+ ln -${VERBOSE}fs -T --relative -- "${DESTDIR:-}$SOURCE" "${DESTDIR:-}$TARGET"
fi
diff --git a/tools/meson-vcs-tag.sh b/tools/meson-vcs-tag.sh
index 1ec04c76b7..8ce692498a 100755
--- a/tools/meson-vcs-tag.sh
+++ b/tools/meson-vcs-tag.sh
@@ -4,8 +4,8 @@
set -eu
set -o pipefail
-dir="$1"
-fallback="$2"
+dir="${1:?}"
+fallback="${2:?}"
# Apparently git describe has a bug where it always considers the work-tree
# dirty when invoked with --git-dir (even though 'git status' is happy). Work
diff --git a/tools/update-hwdb-autosuspend.sh b/tools/update-hwdb-autosuspend.sh
index 7d5a9a8cf5..c69773087d 100755
--- a/tools/update-hwdb-autosuspend.sh
+++ b/tools/update-hwdb-autosuspend.sh
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
-cd "$1"
+cd "${1:?}"
(curl --fail -L 'https://chromium.googlesource.com/chromiumos/platform2/+/master/power_manager/udev/gen_autosuspend_rules.py?format=TEXT'; echo) \
| base64 -d > tools/chromiumos/gen_autosuspend_rules.py
diff --git a/tools/update-hwdb.sh b/tools/update-hwdb.sh
index 773a959dcf..abbbb82f4b 100755
--- a/tools/update-hwdb.sh
+++ b/tools/update-hwdb.sh
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
-cd "$1"
+cd "${1:?}"
unset permissive
if [ "${2:-}" = "-p" ]; then
@@ -28,6 +28,6 @@ if [ "${2:-}" != "-n" ]; then (
set -x
./acpi-update.py >20-acpi-vendor.hwdb.base
patch -p0 -o- 20-acpi-vendor.hwdb.base <20-acpi-vendor.hwdb.patch >20-acpi-vendor.hwdb
-! diff -u 20-acpi-vendor.hwdb.base 20-acpi-vendor.hwdb >20-acpi-vendor.hwdb.patch
+diff -u 20-acpi-vendor.hwdb.base 20-acpi-vendor.hwdb >20-acpi-vendor.hwdb.patch && exit 1
./ids_parser.py
diff --git a/tools/update-syscall-tables.sh b/tools/update-syscall-tables.sh
index 4f56aecedd..5aa8536882 100755
--- a/tools/update-syscall-tables.sh
+++ b/tools/update-syscall-tables.sh
@@ -2,10 +2,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eu
-cd "$1" && shift
+cd "${1:?}" && shift
curl --fail -L -o syscall-list.txt 'https://raw.githubusercontent.com/hrw/syscalls-table/master/syscall-names.text'
for arch in "$@"; do
- curl --fail -L -o syscalls-$arch.txt "https://raw.githubusercontent.com/hrw/syscalls-table/master/tables/syscalls-$arch"
+ curl --fail -L -o "syscalls-$arch.txt" "https://raw.githubusercontent.com/hrw/syscalls-table/master/tables/syscalls-$arch"
done
diff --git a/units/initrd-fs.target b/units/initrd-fs.target
index 8c6bdf35f5..674b7ae74d 100644
--- a/units/initrd-fs.target
+++ b/units/initrd-fs.target
@@ -10,9 +10,9 @@
[Unit]
Description=Initrd File Systems
Documentation=man:systemd.special(7)
+AssertPathExists=/etc/initrd-release
OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly
-AssertPathExists=/etc/initrd-release
After=initrd-parse-etc.service
DefaultDependencies=no
Conflicts=shutdown.target
diff --git a/units/initrd-usr-fs.target b/units/initrd-usr-fs.target
new file mode 100644
index 0000000000..7219655c9c
--- /dev/null
+++ b/units/initrd-usr-fs.target
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Initrd /usr File System
+Documentation=man:systemd.special(7)
+AssertPathExists=/etc/initrd-release
+OnFailure=emergency.target
+OnFailureJobMode=replace-irreversibly
+DefaultDependencies=no
+Conflicts=shutdown.target
diff --git a/units/initrd.target b/units/initrd.target
index 655158a58b..fc8fbff4bd 100644
--- a/units/initrd.target
+++ b/units/initrd.target
@@ -14,6 +14,6 @@ OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly
AssertPathExists=/etc/initrd-release
Requires=basic.target
-Wants=initrd-root-fs.target initrd-root-device.target initrd-fs.target initrd-parse-etc.service
-After=initrd-root-fs.target initrd-root-device.target initrd-fs.target basic.target rescue.service rescue.target
+Wants=initrd-root-fs.target initrd-root-device.target initrd-fs.target initrd-usr-fs.target initrd-parse-etc.service
+After=initrd-root-fs.target initrd-root-device.target initrd-fs.target initrd-usr-fs.target basic.target rescue.service rescue.target
AllowIsolate=yes
diff --git a/units/meson.build b/units/meson.build
index 0d0eeea753..fe8e95ff91 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -38,6 +38,7 @@ units = [
['initrd-switch-root.service', 'ENABLE_INITRD'],
['initrd-switch-root.target', 'ENABLE_INITRD'],
['initrd-udevadm-cleanup-db.service', 'ENABLE_INITRD'],
+ ['initrd-usr-fs.target', 'ENABLE_INITRD'],
['initrd.target', 'ENABLE_INITRD'],
['kexec.target', ''],
['ldconfig.service', 'ENABLE_LDCONFIG',
@@ -209,7 +210,8 @@ in_units = [
['systemd-networkd.service', 'ENABLE_NETWORKD'],
['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD'],
['systemd-nspawn@.service', ''],
- ['systemd-oomd.service', 'ENABLE_OOMD'],
+ ['systemd-oomd.service', 'ENABLE_OOMD',
+ 'dbus-org.freedesktop.oom1.service'],
['systemd-portabled.service', 'ENABLE_PORTABLED',
'dbus-org.freedesktop.portable1.service'],
['systemd-userdbd.service', 'ENABLE_USERDB'],
diff --git a/units/system-systemd\x2dcryptsetup.slice b/units/system-systemd\x2dcryptsetup.slice
index 9369b73ae2..98206a9d06 100644
--- a/units/system-systemd\x2dcryptsetup.slice
+++ b/units/system-systemd\x2dcryptsetup.slice
@@ -9,5 +9,5 @@
[Unit]
Description=Cryptsetup Units Slice
-Documentation=man:systemd.special(7)
+Documentation=man:systemd-cryptsetup@.service(8)
DefaultDependencies=no
diff --git a/units/systemd-firstboot.service b/units/systemd-firstboot.service
index 2e57b064c1..70bc67e1d6 100644
--- a/units/systemd-firstboot.service
+++ b/units/systemd-firstboot.service
@@ -29,10 +29,12 @@ StandardError=tty
# Optionally, pick up basic fields from credentials passed to the service
# manager. This is useful for importing this data from nspawn's
# --set-credential= switch.
-LoadCredential=passwd.hashed-password.root
-LoadCredential=passwd.plaintext-password.root
-LoadCredential=passwd.shell.root
-LoadCredential=firstboot.locale
-LoadCredential=firstboot.locale-messages
-LoadCredential=firstboot.keymap
-LoadCredential=firstboot.timezone
+# FIXME: temporarily disabled as it causes asserts on v247/v248, see:
+# https://github.com/systemd/systemd/issues/19178
+#LoadCredential=passwd.hashed-password.root
+#LoadCredential=passwd.plaintext-password.root
+#LoadCredential=passwd.shell.root
+#LoadCredential=firstboot.locale
+#LoadCredential=firstboot.locale-messages
+#LoadCredential=firstboot.keymap
+#LoadCredential=firstboot.timezone
diff --git a/units/systemd-networkd.socket b/units/systemd-networkd.socket
index aa9ad7e02c..2d8d1c3b91 100644
--- a/units/systemd-networkd.socket
+++ b/units/systemd-networkd.socket
@@ -12,7 +12,8 @@ Description=Network Service Netlink Socket
Documentation=man:systemd-networkd.service(8) man:rtnetlink(7)
ConditionCapability=CAP_NET_ADMIN
DefaultDependencies=no
-Before=sockets.target
+Before=sockets.target shutdown.target
+Conflicts=shutdown.target
[Socket]
ReceiveBuffer=128M
diff --git a/units/systemd-repart.service.in b/units/systemd-repart.service.in
index a5565834eb..4634d983c1 100644
--- a/units/systemd-repart.service.in
+++ b/units/systemd-repart.service.in
@@ -12,13 +12,18 @@ Description=Repartition Root Disk
Documentation=man:systemd-repart.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
-After=sysroot.mount
+After=initrd-usr-fs.target
Before=initrd-root-fs.target shutdown.target
ConditionVirtualization=!container
ConditionDirectoryNotEmpty=|/usr/lib/repart.d
ConditionDirectoryNotEmpty=|/usr/local/lib/repart.d
ConditionDirectoryNotEmpty=|/etc/repart.d
ConditionDirectoryNotEmpty=|/run/repart.d
+ConditionDirectoryNotEmpty=|/sysroot/usr/lib/repart.d
+ConditionDirectoryNotEmpty=|/sysroot/usr/local/lib/repart.d
+ConditionDirectoryNotEmpty=|/sysroot/etc/repart.d
+ConditionDirectoryNotEmpty=|/sysusr/usr/lib/repart.d
+ConditionDirectoryNotEmpty=|/sysusr/usr/local/lib/repart.d
[Service]
Type=oneshot
diff --git a/units/systemd-sysusers.service b/units/systemd-sysusers.service
index 47373307b3..460ff387f2 100644
--- a/units/systemd-sysusers.service
+++ b/units/systemd-sysusers.service
@@ -25,6 +25,8 @@ TimeoutSec=90s
# Optionally, pick up a root password and shell for the root user from a
# credential passed to the service manager. This is useful for importing this
# data from nspawn's --set-credential= switch.
-LoadCredential=passwd.hashed-password.root
-LoadCredential=passwd.plaintext-password.root
-LoadCredential=passwd.shell.root
+# FIXME: temporarily disabled as it causes asserts on v247/v248, see:
+# https://github.com/systemd/systemd/issues/19178
+#LoadCredential=passwd.hashed-password.root
+#LoadCredential=passwd.plaintext-password.root
+#LoadCredential=passwd.shell.root
diff --git a/units/systemd-volatile-root.service.in b/units/systemd-volatile-root.service.in
index 5ecc702b6d..468d85f968 100644
--- a/units/systemd-volatile-root.service.in
+++ b/units/systemd-volatile-root.service.in
@@ -12,7 +12,7 @@ Description=Enforce Volatile Root File Systems
Documentation=man:systemd-volatile-root.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
-After=sysroot.mount systemd-repart.service
+After=sysroot.mount sysroot-usr.mount systemd-repart.service
Before=initrd-root-fs.target shutdown.target
AssertPathExists=/etc/initrd-release