]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #22791 from keszybz/bootctl-invert-order
authorLennart Poettering <lennart@poettering.net>
Wed, 23 Mar 2022 10:39:31 +0000 (11:39 +0100)
committerGitHub <noreply@github.com>
Wed, 23 Mar 2022 10:39:31 +0000 (11:39 +0100)
Invert order of entries w/o sort-key in sd-boot menu

366 files changed:
NEWS
TODO
catalog/systemd.catalog.in
docs/BOOT_LOADER_SPECIFICATION.md
docs/BUILDING_IMAGES.md [new file with mode: 0644]
docs/UIDS-GIDS.md
hwdb.d/60-keyboard.hwdb
man/kernel-install.xml
man/loader.conf.xml
man/meson.build
man/rules/meson.build
man/systemd-boot.xml
man/systemd-stub.xml
man/systemd-system.conf.xml
man/systemd-sysupdate.xml [new file with mode: 0644]
man/systemd.exec.xml
man/systemd.network.xml
man/systemd.service.xml
man/sysupdate.d.xml [new file with mode: 0644]
man/udevadm.xml
meson.build
meson_options.txt
po/it.po
shell-completion/bash/meson.build
shell-completion/bash/oomctl [new file with mode: 0644]
shell-completion/bash/udevadm
shell-completion/zsh/_oomctl [new file with mode: 0644]
shell-completion/zsh/_udevadm
shell-completion/zsh/meson.build
src/activate/activate.c
src/analyze/analyze-calendar.c
src/analyze/analyze-cat-config.c
src/analyze/analyze-condition.c
src/analyze/analyze-critical-chain.c
src/analyze/analyze-dot.c
src/analyze/analyze-filesystems.c
src/analyze/analyze-inspect-elf.c
src/analyze/analyze-security.c
src/analyze/analyze-syscall-filter.c
src/analyze/analyze-timespan.c
src/analyze/analyze-timestamp.c
src/analyze/analyze-unit-files.c
src/analyze/analyze-unit-paths.c
src/analyze/analyze-verify-util.c
src/analyze/analyze-verify.c
src/ask-password/ask-password.c
src/basic/conf-files.c
src/basic/env-file.c
src/basic/env-util.c
src/basic/errno-to-name.awk
src/basic/escape.c
src/basic/fileio.c
src/basic/fs-util.c
src/basic/fs-util.h
src/basic/hashmap.c
src/basic/io-util.c
src/basic/list.h
src/basic/ordered-set.c
src/basic/os-util.c
src/basic/path-lookup.c
src/basic/path-util.c
src/basic/process-util.c
src/basic/random-util.h
src/basic/strv.c
src/basic/strv.h
src/basic/time-util.h
src/basic/tmpfile-util.c
src/basic/tmpfile-util.h
src/basic/unit-file.c
src/binfmt/binfmt.c
src/boot/bless-boot.c
src/boot/bootctl.c
src/boot/efi/boot.c
src/busctl/busctl.c
src/cgls/cgls.c
src/core/bpf-firewall.c
src/core/bpf-foreign.c
src/core/bpf-socket-bind.c
src/core/cgroup.c
src/core/core-varlink.c
src/core/dbus-cgroup.c
src/core/dbus-execute.c
src/core/dbus-manager.c
src/core/dbus-path.c
src/core/dbus-socket.c
src/core/dbus-timer.c
src/core/dbus-unit.c
src/core/device.c
src/core/execute.c
src/core/load-dropin.c
src/core/load-fragment.c
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/mount.c
src/core/namespace.c
src/core/path.c
src/core/service.c
src/core/service.h
src/core/socket.c
src/core/swap.c
src/core/timer.c
src/core/transaction.c
src/core/unit-serialize.c
src/core/unit.c
src/core/unit.h
src/coredump/coredumpctl.c
src/creds/creds.c
src/cryptenroll/cryptenroll.c
src/cryptsetup/cryptsetup-keyfile.c
src/cryptsetup/cryptsetup.c
src/debug-generator/debug-generator.c
src/delta/delta.c
src/dissect/dissect.c
src/environment-d-generator/environment-d-generator.c
src/escape/escape.c
src/fstab-generator/fstab-generator.c
src/home/homectl.c
src/home/homed-manager-bus.c
src/home/homed-manager.c
src/home/homed-varlink.c
src/home/homework-cifs.c
src/home/homework-fscrypt.c
src/home/homework-luks.c
src/home/homework-pkcs11.c
src/home/homework.c
src/home/user-record-pwquality.c
src/home/user-record-util.c
src/id128/id128.c
src/journal-remote/journal-remote-main.c
src/journal-remote/microhttpd-util.c
src/journal/journalctl.c
src/journal/journald-rate-limit.c
src/kernel-install/kernel-install
src/libsystemd-network/dhcp-option.c
src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/fuzz-dhcp-server-relay.c [moved from src/libsystemd-network/fuzz-dhcp-server-relay-message.c with 100% similarity]
src/libsystemd-network/meson.build
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd-network/sd-radv.c
src/libsystemd/sd-bus/bus-dump.c
src/libsystemd/sd-bus/bus-match.c
src/libsystemd/sd-bus/bus-message.c
src/libsystemd/sd-bus/bus-objects.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-bus/test-bus-address.c
src/libsystemd/sd-device/device-enumerator-private.h
src/libsystemd/sd-device/device-enumerator.c
src/libsystemd/sd-device/device-private.c
src/libsystemd/sd-event/event-util.c
src/libsystemd/sd-event/event-util.h
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-journal/catalog.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/mmap-cache.c
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/sd-netlink.c
src/libsystemd/sd-path/sd-path.c
src/libudev/libudev-enumerate.c
src/libudev/libudev-list.c
src/locale/keymap-util.c
src/locale/localectl.c
src/locale/localed.c
src/login/loginctl.c
src/login/logind-dbus.c
src/login/logind-device.c
src/login/logind-seat-dbus.c
src/login/logind-seat.c
src/login/logind-session.c
src/login/logind-user-dbus.c
src/login/logind-user.c
src/login/logind.c
src/login/pam_systemd.c
src/machine/machine.c
src/modules-load/modules-load.c
src/network/generator/network-generator.c
src/network/netdev/netdev.c
src/network/netdev/wireguard.c
src/network/networkctl.c
src/network/networkd-dhcp-server.c
src/network/networkd-json.c
src/network/networkd-link-bus.c
src/network/networkd-link.c
src/network/networkd-ndisc.c
src/network/networkd-network.c
src/nspawn/nspawn-bind-user.c
src/nspawn/nspawn-expose-ports.c
src/nspawn/nspawn-mount.c
src/nspawn/nspawn-network.c
src/nspawn/nspawn-oci.c
src/nspawn/nspawn-seccomp.c
src/nspawn/nspawn-stub-pid1.c
src/nspawn/nspawn.c
src/nss-systemd/userdb-glue.c
src/oom/oomd-util.c
src/oom/test-oomd-util.c
src/partition/repart.c
src/portable/portable.c
src/portable/portablectl.c
src/resolve/resolvectl.c
src/resolve/resolved-bus.c
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-search-domain.c
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-trust-anchor.c
src/resolve/resolved-dns-zone.c
src/resolve/resolved-dnssd-bus.c
src/resolve/resolved-dnssd.c
src/resolve/resolved-etc-hosts.c
src/resolve/resolved-link-bus.c
src/resolve/resolved-link.c
src/resolve/resolved-manager.c
src/resolve/resolved-mdns.c
src/rfkill/rfkill.c
src/run-generator/run-generator.c
src/shared/acl-util.c
src/shared/bootspec.c
src/shared/bootspec.h
src/shared/bus-polkit.c
src/shared/bus-unit-procs.c
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/cgroup-setup.c
src/shared/clock-util.c
src/shared/condition.c
src/shared/conf-parser.c
src/shared/discover-image.c
src/shared/dissect-image.c
src/shared/dropin.c
src/shared/exec-util.c
src/shared/find-esp.c [new file with mode: 0644]
src/shared/find-esp.h [new file with mode: 0644]
src/shared/format-table.c
src/shared/hwdb-util.c
src/shared/install.c
src/shared/libcrypt-util.c
src/shared/libfido2-util.c
src/shared/meson.build
src/shared/mount-setup.c
src/shared/mount-util.c
src/shared/net-condition.c
src/shared/nscd-flush.c
src/shared/pkcs11-util.c
src/shared/pretty-print.c
src/shared/seccomp-util.c
src/shared/serialize.c
src/shared/tests.c
src/shared/user-record-show.c
src/shared/varlink.c
src/shutdown/test-umount.c
src/shutdown/umount.c
src/sleep/sleep.c
src/sysctl/sysctl.c
src/sysext/sysext.c
src/systemctl/systemctl-cancel-job.c
src/systemctl/systemctl-clean-or-freeze.c
src/systemctl/systemctl-edit.c
src/systemctl/systemctl-enable.c
src/systemctl/systemctl-is-active.c
src/systemctl/systemctl-is-enabled.c
src/systemctl/systemctl-kill.c
src/systemctl/systemctl-list-dependencies.c
src/systemctl/systemctl-list-machines.c
src/systemctl/systemctl-list-units.c
src/systemctl/systemctl-logind.c
src/systemctl/systemctl-reset-failed.c
src/systemctl/systemctl-set-environment.c
src/systemctl/systemctl-set-property.c
src/systemctl/systemctl-show.c
src/systemctl/systemctl-start-unit.c
src/systemctl/systemctl-util.c
src/systemd/sd-messages.h
src/sysupdate/meson.build [new file with mode: 0644]
src/sysupdate/sysupdate-cache.c [new file with mode: 0644]
src/sysupdate/sysupdate-cache.h [new file with mode: 0644]
src/sysupdate/sysupdate-instance.c [new file with mode: 0644]
src/sysupdate/sysupdate-instance.h [new file with mode: 0644]
src/sysupdate/sysupdate-partition.c [new file with mode: 0644]
src/sysupdate/sysupdate-partition.h [new file with mode: 0644]
src/sysupdate/sysupdate-pattern.c [new file with mode: 0644]
src/sysupdate/sysupdate-pattern.h [new file with mode: 0644]
src/sysupdate/sysupdate-resource.c [new file with mode: 0644]
src/sysupdate/sysupdate-resource.h [new file with mode: 0644]
src/sysupdate/sysupdate-transfer.c [new file with mode: 0644]
src/sysupdate/sysupdate-transfer.h [new file with mode: 0644]
src/sysupdate/sysupdate-update-set.c [new file with mode: 0644]
src/sysupdate/sysupdate-update-set.h [new file with mode: 0644]
src/sysupdate/sysupdate-util.c [new file with mode: 0644]
src/sysupdate/sysupdate-util.h [new file with mode: 0644]
src/sysupdate/sysupdate.c [new file with mode: 0644]
src/sysupdate/sysupdate.h [new file with mode: 0644]
src/sysusers/sysusers.c
src/sysv-generator/sysv-generator.c
src/test/meson.build
src/test/test-bpf-foreign-programs.c
src/test/test-bpf-lsm.c
src/test/test-copy.c
src/test/test-env-util.c
src/test/test-exec-util.c
src/test/test-execute.c
src/test/test-fileio.c
src/test/test-fs-util.c
src/test/test-kbd-util.c
src/test/test-list.c
src/test/test-locale-util.c
src/test/test-nss-hosts.c
src/test/test-nss-users.c
src/test/test-path-lookup.c
src/test/test-path-util.c
src/test/test-path.c
src/test/test-sd-path.c
src/test/test-socket-bind.c
src/test/test-string-util.c
src/test/test-strv.c
src/test/test-sysctl-util.c
src/test/test-time-util.c
src/test/test-unit-file.c
src/timedate/timedated.c
src/timesync/timesyncd-bus.c
src/timesync/timesyncd-conf.c
src/timesync/timesyncd-manager.c
src/timesync/timesyncd-manager.h
src/timesync/timesyncd-server.c
src/timesync/timesyncd.c
src/tmpfiles/tmpfiles.c
src/tty-ask-password-agent/tty-ask-password-agent.c
src/udev/net/link-config.c
src/udev/udev-event.c
src/udev/udev-rules.c
src/udev/udevadm-info.c
src/udev/udevadm-trigger.c
src/udev/udevd.c
src/userdb/userdbctl.c
src/userdb/userdbd-manager.c
src/xdg-autostart-generator/xdg-autostart-condition.c
src/xdg-autostart-generator/xdg-autostart-generator.c
test/TEST-69-SHUTDOWN/test.sh
test/TEST-72-SYSUPDATE/Makefile [new symlink]
test/TEST-72-SYSUPDATE/test.sh [new file with mode: 0755]
test/fuzz/fuzz-catalog/language-too-short [moved from test/fuzz/fuzz-catalog/clusterfuzz-testcase-minimized-fuzz-catalog-5674475278827520 with 100% similarity]
test/fuzz/fuzz-dhcp-client/parse-memleak [moved from test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50a with 100% similarity]
test/fuzz/fuzz-dhcp-server-relay/7d924e16295cd14e12a01a5631ea94a3d11d1b52 [moved from test/fuzz/fuzz-dhcp-server-relay-message/7d924e16295cd14e12a01a5631ea94a3d11d1b52 with 100% similarity]
test/fuzz/fuzz-dhcp-server-relay/fe4344e65d495388540dc1bf8eae70c46f8b867c [moved from test/fuzz/fuzz-dhcp-server-relay-message/fe4344e65d495388540dc1bf8eae70c46f8b867c with 100% similarity]
test/fuzz/fuzz-dhcp-server-relay/too-large-packet [moved from test/fuzz/fuzz-dhcp-server-relay-message/clusterfuzz-testcase-minimized-fuzz-dhcp-server-relay-message-4972399731277824 with 100% similarity]
test/fuzz/fuzz-dhcp-server/duplicate-input-data [moved from test/fuzz/fuzz-dhcp-server/clusterfuzz-testcase-minimized-fuzz-dhcp-server-4916534286352384 with 100% similarity]
test/units/testsuite-69.service
test/units/testsuite-72.service [new file with mode: 0644]
test/units/testsuite-72.sh [new file with mode: 0755]
tools/dbus_exporter.py
tools/update-man-rules.py
units/meson.build
units/systemd-sysupdate-reboot.service.in [new file with mode: 0644]
units/systemd-sysupdate-reboot.timer [new file with mode: 0644]
units/systemd-sysupdate.service.in [new file with mode: 0644]
units/systemd-sysupdate.timer [new file with mode: 0644]
units/systemd-udev-trigger.service

diff --git a/NEWS b/NEWS
index c591694a72726807a6680edffa9dd3cd492978fe..df5cba1b4ff3d97f4a58b3a69bb5da7ab0e26fda 100644 (file)
--- a/NEWS
+++ b/NEWS
 systemd System and Service Manager
 
-CHANGES WITH 251:
-        * Incompatibility and Regression note:
-          In v250, the feature that automatically configures routes to addresses
-          specified in AllowedIPs= was added and enabled by default. However,
-          this feature causes network connectivity issues on many existing
-          setups. Hence, this is disabled by default since v250.3. The feature
-          can still be used by explicitly configuring RouteTable= setting in
-          .netdev files.
+CHANGES WITH 251 in spe:
+
+        Backwards-incompatible changes:
+
+        * In v250, a systemd-networkd feature that automatically configures
+          routes to addresses specified in AllowedIPs= was added and enabled by
+          default. However, this causes network connectivity issues in many
+          existing setups. Hence, it has been disabled by default since
+          systemd-stable 250.3. The feature can still be used by explicitly
+          configuring RouteTable= setting in .netdev files.
 
         * Jobs started via StartUnitWithFlags() will no longer return 'skipped'
           when a Condition*= check does not succeed, restoring the JobRemoved
           signal to the behaviour it had before v250.
 
         * The org.freedesktop.portable1 methods GetMetadataWithExtensions and
-          GetImageMetadataWithExtensions have been fixed to provide an extra return
-          parameter, containing the actual extensions release metadata. The
-          current implementation was judged to be broken and unusable, and thus
-          the usual procedure of adding a new set of methods is skipped, opting
-          for breaking backward compatibility instead, as nobody should be
-          affected, given the state of the current interface.
-
-        * Service monitor environment variables will only be passed to
-          OnFailure=/OnSuccess= handlers if exactly one unit lists the handler
-          unit as OnFailure=/OnSuccess=.  Therefore, $MONITOR_METADATA is no
-          longer used, and instead separate variables are used:
-          $MONITOR_SERVICE_RESULT, $MONITOR_EXIT_CODE, $MONITOR_EXIT_STATUS,
-          $MONITOR_INVOCATION_ID and $MONITOR_UNIT.  For cases when a single
-          handler needs to watch multiple units, use a templated handler.
+          GetImageMetadataWithExtensions have been fixed to provide an extra
+          return parameter, containing the actual extension release metadata.
+          The current implementation was judged to be broken and unusable, and
+          thus the usual procedure of adding a new set of methods was skipped,
+          and backward compatibility broken instead on the assumption that
+          nobody can be affected given the current state of this interface.
+
+        * All kernels supported by systemd mix RDRAND (or similar) into the
+          entropy pool at early boot. This means that on those systems, even if
+          /dev/urandom is not yet initialized, it still returns bytes that that
+          are at least as high quality as RDRAND. For that reason, we no longer
+          have reason to invoke RDRAND from systemd itself, which has
+          historically been a source of bugs. Furthermore, kernels â‰¥5.6 provide
+          the getrandom(GRND_INSECURE) interface for returning random bytes
+          before the entropy pool is initialized without warning into kmsg,
+          which is what we attempt to use if available. systemd's direct usage
+          of RDRAND has been removed. x86 systems â‰¥Broadwell that are running
+          an older kernel may experience kmsg warnings that were not seen with
+          250. For newer kernels, non-x86 systems, or older x86 systems, there
+          should be no visible changes.
+
+        * sd-boot will now measure the kernel command line into TPM PCR 12
+          rather than PCR 8. This improves usefulness of the measurements on
+          systems where sd-boot is chainloaded from Grub. Grub measures all
+          commands its executes into PCR 8, which makes it very hard to use
+          reasonably, hence separate ourselves from that and use PCR 12
+          instead, which is what certain Ubuntu editions already do. To retain
+          compatibility with systems running older systemd systems a new Meson
+          option 'efi-tpm-pcr-compat' has been added (which defaults to false).
+          If enabled, the measurement is done twice: into the new-style PCR 12
+          *and* the old-style PCR 8. It's strongly advised to migrate all users
+          to PCR 12 for this purpose in the long run, as we intend to remove
+          this compatibility feature in two year's time.
+
+        * busctl capture now writes output in the newer pcapng format instead
+          of pcap.
+
+        * An udev rule that imported hwdb matches for USB devices with
+          lowercase hexadecimal digits was added in systemd 250. This has been
+          reverted, since uppercase hexadecimal digits are supposed to be used,
+          and we already had a rule that with the appropriate match.
+
+          Users might need to adjust their local hwdb entries.
+
+        * arch_prctl(2) was moved to the @default set in the syscall filters.
+          It is apparently used by the linker now.
+
+        New functionality and other changes:
 
         * kernel-install's and bootctl's Boot Loader Specification Type #1
           entry generation logic has been reworked. The user may now pick
           explicitly by which "token" string to name the installation's boot
-          entries, through the new /etc/kernel/entry-token file or the new
+          entries, via the new /etc/kernel/entry-token file or the new
           --entry-token= switch to bootctl. By default â€” as before â€” the
           entries are named after the local machine ID. However, in "golden
           image" environments, where the machine ID shall be initialized on
           first boot (as opposed to at installation time before first boot) the
-          machine ID is not be available at build time to name the entry
-          after. In this case the --entry-token= switch to bootctl (or the
-          /etc/kernel/entry-token file) may be used to override the "token" to
-          identify the entry by, and use another ID, for example the IMAGE_ID=
-          or ID= fields from /etc/os-release. This will make the OS images
-          independent of any machine ID, and ensure that the images will not
-          carry any identifiable information before first boot, but on the
-          other hand means that multiple parallel installations of the very
-          same image on the same disk cannot be supported. Summary: if you are
-          building golden images that shall acquire identity information
-          exclusively on first boot, make sure to both remove /etc/machine-id
-          *and* to write /etc/kernel/entry-token to the value of the IMAGE_ID
-          or ID field of /etc/os-release or another suitable identifier before
-          deploying the image.
-
-        * sd-boot gained a new *experimental* setting "reboot-for-bitlocker" in
-          loader.conf that implements booting Microsoft Windows from the
-          sd-boot in a way that first reboots the system, to reset the TPM
-          PCRs. This improves compatibility with BitLocker's TPM use, as the
-          PCRs will only record the Windows boot process, and not sd-boot
-          itself, thus retaining the PCR measurements not involving
-          sd-boot. Note that this feature is experimental for now, and is
-          likely going to be generalized, renamed and removed in its current
-          form in a future release, without retaining compatibility with its
-          current implementation.
+          machine ID is not be available at build time. In this case the
+          --entry-token= switch to bootctl (or the /etc/kernel/entry-token
+          file) may be used to override the "token" for the entries, for
+          example the IMAGE_ID= or ID= fields from /etc/os-release. This will
+          make the OS images independent of any machine ID, and ensure that the
+          images will not carry any identifiable information before first boot,
+          but on the other hand means that multiple parallel installations of
+          the very same image on the same disk cannot be supported.
+
+          Summary: if you are building golden images that shall acquire
+          identity information exclusively on first boot, make sure to both
+          remove /etc/machine-id *and* to write /etc/kernel/entry-token to the
+          value of the IMAGE_ID or ID field of /etc/os-release or another
+          suitable identifier before deploying the image.
+
+        * The Boot Loader Specification has been extended with
+          /loader/entries.srel file that disambiguates the format of the
+          entries in the /loader/entries directory. For entries that follow the
+          Specification, "type1" should be used.
+
+          bootctl will now write this file automatically when creating Type #1
+          entries.
 
-        * The --make-machine-id-directory= switch to bootctl has been replaced
-          by --make-entry-directory=, given that the entry directory is not
-          necessarily named after the machine ID, but after some other suitable
-          ID as selected via --entry-token= described above. The old name of
-          the option is still understood to maximize compatibility.
+        * kernel-install supports a new initrd_generator= setting in
+          /etc/kernel/install.conf, that is exported as
+          $KERNEL_INSTALL_INITRD_GENERATOR to kernel-install plugins. This
+          allows a different initrd generator to be hooked up.
 
-        * Services with Restart=always and a failing ExecCondition= will no longer
-          be restarted, to bring ExecCondition= in line with Condition*= settings.
+        * kernel-install will now create a "staging area" (an initially-empty
+          directory to gather files for a Boot Loader Specification Type #1
+          entry). The path to this directory is exported as
+          $KERNEL_INSTALL_STAGING_AREA to kernel-install plugins, which should
+          drop files there instead of writing them directly to the final
+          location. kernel-install will move them when all files have been
+          prepared successfully.
 
         * Starting with v250 systemd-homed uses UID/GID mapping on the mounts
           of activated home directories it manages (if the kernel and selected
@@ -78,8 +115,8 @@ CHANGES WITH 251:
           range from 0…60000, the user's own UID, and the range 60514…65534,
           leaving everything else unmapped (in other words, the 16bit UID range
           is mapped almost fully, with the exception of the UID subrange used
-          for systemd-homed users, with one exception from that: the user's own
-          UID). Unmapped UIDs may not be used for file ownership in the home
+          for systemd-homed users, with one exception: the user's own UID).
+          Unmapped UIDs may not be used for file ownership in the home
           directory â€” any chown() attempts with them will fail. With this
           release a fourth range is added to these mappings:
           524288…1879048191. This range is the UID range intended for container
@@ -106,32 +143,156 @@ CHANGES WITH 251:
           handling, and improving compatibility with home directories intended
           to be portable like the ones managed by systemd-homed.
 
-        * All kernels supported by systemd mix RDRAND (or similar) into the
-          entropy pool at early boot. This means that on those systems, even
-          if /dev/urandom is not yet initialized, it still returns bytes that
-          that are at least as high quality as RDRAND. For that reason, we no
-          longer have reason to invoke RDRAND from systemd itself, which has
-          historically been a source of bugs. Furthermore, kernels â‰¥5.6 provide
-          the getrandom(GRND_INSECURE) interface for returning random bytes
-          before the entropy pool is initialized without warning into kmsg,
-          which is what we attempt to use if available. By removing systemd's
-          direct usage of RDRAND, x86 systems â‰¥Broadwell that are running an
-          older kernel may experience kmsg warnings that were not seen with
-          250. For newer kernels, non-x86 systems, or older x86 systems,
-          there should be no visible changes.
+        * The journal JSON export format has been added to listed of stable
+          interfaces (https://systemd.io/PORTABILITY_AND_STABILITY/).
+
+        * /etc/locale.conf is now populated through tmpfiles.d factory /etc
+          handling with the values that were configured during systemd build
+          (if /etc/locale.conf has not been created through some other
+          mechanism). This means that /etc/locale.conf should always have
+          reasonable contents and we avoid a potential mismatch in defaults.
+
+        * A new libsystemd-core-<version>.so private shared library is
+          installed under /usr/lib/systemd/system, mirroring the existing
+          libsystemd-shared-<version>.so library. This allows the total
+          installation size to be reduced by code reuse.
+
+        * The <version> tag used by libsystemd-shared.so and libsystemd-core.so
+          can be configured. Distributions may build subsequent versions of the
+          systemd package with unique tags (e.g. the full package version),
+          thus allowing multiple installations of those shared libraries to be
+          available at the same time. This is intended to fix an issue where
+          programs that link to those libraries would fail to execute because
+          they were installed earlier or later than the appropriate version of
+          the library.
+
+        * A new set of service monitor environment variables will be passed to
+          OnFailure=/OnSuccess= handlers, but only if exactly one unit lists the
+          handler unit as OnFailure=/OnSuccess=. The variables are:
+          $MONITOR_SERVICE_RESULT, $MONITOR_EXIT_CODE, $MONITOR_EXIT_STATUS,
+          $MONITOR_INVOCATION_ID and $MONITOR_UNIT. For cases when a single
+          handler needs to watch multiple units, use a templated handler.
+
+        * A new ExtensionDirectories= setting allows system extensions to be
+          loaded from a directory. (It is similar to ExtensionImages=, but
+          takes a path to a directory, instead of an image.)
+
+          'portablectl attach --extension' now also accepts directory paths.
+
+        * VENDOR= and MODEL= can be set in /etc/machine-info to override the
+          values gleaned from the hwdb.
+
+        * A ID_CHASSIS property can be set in the hwdb (for the DMI modalias)
+          to override the chassis that is reported by hostnamed.
+
+        * Two new hwdb files have been started to lists "handhelds" (PDAs,
+          calculators, etc.) and AV devices (DJ tables, keypads, etc.) that
+          should accessible to the seat owner by default.
+
+        * A new unit systemd-networkd-wait-online@<interface>.service can be
+          used to wait for a specific interface to be up.
+
+        * systemd-resolved is started earlier (in sysinit.target), so it
+          available earlier and will also be started in the initrd if installed
+          there.
+
+        * udevadm trigger gained a new --prioritized-subsystem option to
+          process certain subsystems (and all parent devices) earlier.
+
+          systemd-udev-trigger.service now uses this new option to trigger
+          block and TPM devices first, hopefully making the boot a bit faster.
+
+        * udevadm trigger now implements --type=all, --initialized-match,
+          --initialized-nomatch to trigger both subsystems and devices, only
+          already-initialized devices, and only devices which haven't been
+          initialized yet, respectively.
+
+        * systemd-cryptenroll can now control whether to require the user to
+          enter a PIN when unlocking a volume via the new --tpm2-with-pin=
+          option.
+
+          Option tpm2-pin= can be used in /etc/crypttab.
+
+        * The user.delegate and user.invocation_id attributes on cgroups are
+          used in addition to trusted.delegate and trusted.invocation_id. The
+          latter pair requires privileges to set, but the former doesn't and
+          can be also set by the unprivileged user manager.
+
+          (Only supported on kernels â‰¥5.6.)
+
+        * New option sort-key= has been added to the Boot Loader Specification
+          to override the entry sorty order. It is read by sd-boot and bootctl,
+          and will be written by kernel-install, with the default value of
+          IMAGE_ID= or ID= fields from os-release. Together, this means that
+          on multiboot installations, entries should be grouped and sorted
+          in a predictable way.
+
+        * sd-boot can now beep when the menu is shown and menu entries are
+          selected, which can be useful on machines without a working display.
+
+        * %y/%Y specifiers can be used in unit files to refer to unit file
+          path, which is particularly useful for linked unit files.
+
+          %R specifier resolves to the pretty hostname.
+
+          %d specifier resolves to the credentials directory (same as
+           $CREDENTIALS_DIRECTORY).
+
+        * The --make-machine-id-directory= switch to bootctl has been replaced
+          by --make-entry-directory=, given that the entry directory is not
+          necessarily named after the machine ID, but after some other suitable
+          ID as selected via --entry-token= described above. The old name of
+          the option is still understood to maximize compatibility.
+
+        * Services with Restart=always and a failing ExecCondition= will no longer
+          be restarted, to bring ExecCondition= in line with Condition*= settings.
+
+        * LoadCredential= now accepts a directory as the argument; all files
+          from the directory will be loaded.
+
+        * systemd-networkd gained a new [Bridge] Isolated=true|false setting
+          that configures the eponymous kernel attribute on the bridge.
+
+        * .link files gained support for [Match] Firmware= setting to match on
+          the device firmware description string. By mistake, it was previously
+          only supported in .network files.
+
+        * .link/.network files gained support for [Match] Kind= setting to match
+          on device kind ("bond", "bridge", "gre", "tun", "veth", etc.)
+
+          This value is also shown by 'networkctl status'.
+
+        * The Local= setting for various virtual network devices gained support
+          for specifying, in addition to the network address, the name of a
+          local interface which must have the specified address.
+
+        * New [DHCPServer] BootServerName=, BootServerAddress=, and
+          BootFilename= settings can be used to configure the server address,
+          server name, and file name sent in the DHCP packet (e.g. to configure
+          PXE boot).
+
+        * journalctl --list-boots now supports JSON output and the --reverse option.
+
+        * Under docs/: JOURNAL_EXPORT_FORMATS was imported from the wiki and
+          updated, BUILDING_IMAGES is new.
+
+        Experimental features:
+
+        * sd-boot gained a new *experimental* setting "reboot-for-bitlocker" in
+          loader.conf that implements booting Microsoft Windows from the
+          sd-boot in a way that first reboots the system, to reset the TPM
+          PCRs. This improves compatibility with BitLocker's TPM use, as the
+          PCRs will only record the Windows boot process, and not sd-boot
+          itself, thus retaining the PCR measurements not involving sd-boot.
+          Note that this feature is experimental for now, and is likely going
+          to be generalized and renamed in a future release, without retaining
+          compatibility with the current implementation.
+
+        * A new systemd-sysupdate component has been added that automatically
+          discovers, downloads, and installs A/B-style updates for the host
+          installation itself, or container images, portable service images,
+          and other assets. See the new systemd-sysupdate man page for updates.
 
-        * sd-boot will now measure the kernel command line into TPM PCR 12
-          rather than PCR 8. This improves usefulness of the measurements on
-          sytems where sd-boot is chainloaded from Grub. Grub measures all
-          commands its executes into PCR 8, which makes it very hard to use
-          reasonably, hence separate ourselves from that and use PCR 12
-          instead, which is already what certain Ubuntu editions use it for. To
-          retain compatibility with systems running older systemd systems a new
-          Meson option 'efi-tpm-pcr-compat' has been added (which defaults to
-          false). If enabled, the measurement is done twice: into the new-style
-          PCR 12 *and* the old-style PCR 8. It's strongly advised to migrate
-          all users to PCR 12 for this purpose in the long run, as we intend to
-          remove this compatibility feature again in two year's time.
 
 CHANGES WITH 250:
 
@@ -656,15 +817,13 @@ CHANGES WITH 250:
           may be used to set the boot menu time-out of the boot loader (for all
           or just the subsequent boot).
 
-        * bootctl and kernel-install will now read KERNEL_INSTALL_MACHINE_ID
-          and KERNEL_INSTALL_LAYOUT from kernel/install.conf. The first
-          variable specifies the machine-id to use for installation. It would
-          previously be used if set in the environment, and now it'll also be
-          read automatically from the config file. The second variable is new.
-          When set, it specifies the layout to use for installation directories
-          on the boot partition, so that tools don't need to guess it based on
-          the already-existing directories. The only value that is defined
-          natively is "bls", corresponding to the layout specified in
+        * bootctl and kernel-install will now read variables
+          KERNEL_INSTALL_LAYOUT= from /etc/machine-info and layout= from
+          /etc/kernel/install.conf. When set, it specifies the layout to use
+          for installation directories on the boot partition, so that tools
+          don't need to guess it based on the already-existing directories. The
+          only value that is defined natively is "bls", corresponding to the
+          layout specified in
           https://systemd.io/BOOT_LOADER_SPECIFICATION/. Plugins for
           kernel-install that implement a different layout can declare other
           values for this variable.
@@ -12493,7 +12652,7 @@ CHANGES WITH 197:
           based on a calendar time specification such as "Thu,Fri
           2013-*-1,5 11:12:13" which refers to 11:12:13 of the first
           or fifth day of any month of the year 2013, given that it is
-          a thursday or friday. This brings timer event support
+          a Thursday or a Friday. This brings timer event support
           considerably closer to cron's capabilities. For details on
           the supported calendar time specification language see
           systemd.time(7).
diff --git a/TODO b/TODO
index b4ee6d135c35349c38e2ab23406e699593e2198c..04d690355ebef48088ec3ad616f4902b65adafa0 100644 (file)
--- a/TODO
+++ b/TODO
@@ -78,6 +78,17 @@ Janitorial Clean-ups:
 
 Features:
 
+* per-service sandboxing option: ProtectIds=. If used, will overmount
+  /etc/machine-id and /proc/sys/kernel/random/boot_id with synthetic files, to
+  make it harder for the service to identify the host. Depending on the user
+  setting it should be fully randomized at invocation time, or a hash of the
+  real thing, keyed by the unit name or so. Of course, there are other ways to
+  get these IDs (e.g. journal) or similar ids (e.g. MAC addresses, DMI ids, CPU
+  ids), so this knob would only be useful in combination with other lockdown
+  options. Particularly useful for portable services, and anything else that
+  uses RootDirectory= or RootImage=. (Might also over-mount
+  /sys/class/dmi/id/*{uuid,serial} with /dev/null).
+
 * journalctl/timesyncd: whenever timesyncd acquires a synchronization from NTP,
   create a structured log entry that contains boot ID, monotonic clock and
   realtime clock (I mean, this requires no special work, as these three fields
@@ -251,11 +262,26 @@ Features:
   that images cannot be misused.
 
 * New udev block device symlink names:
-  /dev/disk/by-parttypelabel/<pttype>/<ptlabel>. Use case: if pt label is used
+  /dev/disk/by-parttypelabel/<pttype>-<ptlabel>. Use case: if pt label is used
   as partition image version string, this is a safe way to reference a specific
   version of a specific partition type, in particular where related partitions
   are processed (e.g. verity + rootfs both named "LennartOS_0.7").
 
+* sysupdate:
+  - add fuzzing to the pattern parser
+  - support casync as download mechanism
+  - direct TPM2 PCR change handling, possible renrolling LUKS2 media if needed.
+  - "systemd-sysupdate update --all" support, that iterates through all components
+    defined on the host, plus all images installed into /var/lib/machines/,
+    /var/lib/portable/ and so on.
+  - figure out what to do about system extensions (i.e. they need to imply an
+    update component, since otherwise system extenion' sysupdate.d/ files would
+    override the host's update files.)
+  - Allow invocation with a single transfer definition, i.e. with
+    --definitions= pointing to a file rather than a dir.
+  - add ability to disable implicit decompression of downloaded artifacts,
+    i.e. a Compress=no option in the transfer definitions
+
 * in sd-id128: also parse UUIDs in RFC4122 URN syntax (i.e. chop off urn:uuid: prefix)
 
 * DynamicUser= + StateDirectory= â†’ use uid mapping mounts, too, in order to
index a5d7dc6f78d6636a36d94efc22af8f2db8f8a3f4..a3f05c0698c568a81b7809f44b5c47528d87d4ac 100644 (file)
@@ -526,3 +526,11 @@ be updated to operate in a hotplug fashion without depending on
 systemd-udev-settle.service:
 
     @OFFENDING_UNITS@
+
+-- 7c8a41f37b764941a0e1780b1be2f037
+Subject: Initial clock synchronization
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+For the first time during the current boot an NTP synchronization has been
+acquired and the local system clock adjustment has been initiated.
index 7f15a231647f01f796a1b850d69a7d5816d70f67..375efeb1d1975712df106adeccfdce62219a2c8a 100644 (file)
@@ -309,6 +309,18 @@ focus for this specification. More specifically, on non-EFI systems
 configuration snippets following this specification cannot be used to spawn
 other operating systems (such as Windows).
 
+Unfortunately, there are implementations of boot loading infrastructure that
+are also using the /loader/entries/ directory, but place files in them that are
+not valid by this specification. In order to minimize confusion a boot loader
+implementation may place a file /loader/entries.srel next to the
+/loader/entries/ directory containing the ASCII string "type1" (suffixed
+with a UNIX newline). Tools that need to determine whether an existing
+directory implements the semantics described here may check for this file and
+contents: if it exists and contains the mentioned string, it shall assume a
+standards compliant implementation is in place. If it exists but contains a
+different string it shall assume non-standard semantics are implemented. If the
+file does not exist no assumptions should be made.
+
 ### Type #2 EFI Unified Kernel Images
 
 A unified kernel image is a single EFI PE executable combining an EFI stub
diff --git a/docs/BUILDING_IMAGES.md b/docs/BUILDING_IMAGES.md
new file mode 100644 (file)
index 0000000..8b486a9
--- /dev/null
@@ -0,0 +1,227 @@
+---
+title: Safely Building Images
+category: Concepts
+layout: default
+SPDX-License-Identifier: LGPL-2.1-or-later
+---
+
+# Safely Building Images
+
+In many scenarios OS installations are shipped as pre-built images, that
+require no further installation process beyond simple `dd`-ing the image to
+disk and booting it up. When building such "golden" OS images for
+`systemd`-based OSes a few points should be taken into account.
+
+Most of the points described here are implemented by the
+[`mkosi`](https://github.com/systemd/mkosi) OS image builder developed and
+maintained by the systemd project. If you are using or working on another image
+builder it's recommended to keep the following concepts and recommendations in
+mind.
+
+## Resources to Reset
+
+Typically the same OS image shall be deployable in multiple instances, and each
+instance should automatically acquire its own identifying credentials on first
+boot. For that it's essential to:
+
+1. Remove the
+   [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html)
+   file or write the string `uninitialized\n` into it. This file is supposed to
+   carry a 128bit identifier unique to the system. Only when it is reset it
+   will be auto-generated on first boot and thus be truly unique. If this file
+   is not reset, and carries a valid ID every instance of the system will come
+   up with the same ID and that will likely lead to problems sooner or later,
+   as many network-visible identifiers are commonly derived from the machine
+   ID, for example IPv6 addresses or transient MAC addresses.
+
+2. Remove the `/var/lib/systemd/random-seed` file (see
+   [`systemd-random-seed(8)`](https://www.freedesktop.org/software/systemd/man/systemd-random-seed.service.html),
+   which is used to seed the kernel's random pool on boot. If this file is
+   shipped pre-initialized, every instance will seed its random pool with the
+   same random data that is included in the image, and thus possibly generate
+   random data that is more similar to other instances booted off the same image
+   than advisable.
+
+3. Remove the `/loader/random-seed` file (see
+   [`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
+   from the UEFI System Partition (ESP), in case the `systemd-boot` boot loader
+   is used in the image.
+
+4. It might also make sense to remove `/etc/hostname` and `/etc/machine-info`
+   which carry additional identifying information about the OS image.
+
+## Boot Menu Entry Identifiers
+
+The `kernel-install` logic used to generate [Boot Loader Specification Type
+1](https://systemd.io/BOOT_LOADER_SPECIFICATION) entries by default uses the
+machine ID as stored in `/etc/machine-id` for naming boot menu entries and the
+directories in the ESP to place kernel images in. This is done in order to
+allow multiple installations of the same OS on the same system without
+conflicts. However, this is problematic if the machine ID shall be generated
+automatically on first boot: if the ID is not known before the first boot it
+cannot be used to name the most basic resources required for the boot process
+to complete.
+
+Thus, for images that shall acquire their identity on first boot only, it is
+required to use a different identifier for naming boot menu entries. To allow
+this the `kernel-install` logic knows the generalized *entry* *token* concept,
+which can be a freely chosen string to use for identifying the boot menu
+resources of the OS. If not configured explicitly it defaults to the machine
+ID. The file `/etc/kernel/entry-token` may be used to configure this string
+explicitly. Thus, golden image builders should write a suitable identifier into
+this file, for example the `IMAGE_ID=` or `ID=` field from
+`/etc/os-release`. It is recommended to do this before the `kernel-install`
+functionality is invoked (i.e. before the package manager is used to install
+packages into the OS tree being prepared), so that the selected string is
+automatically used for all entries to be generated.
+
+## Booting with Empty `/var/` and/or Empty Root File System
+
+`systemd` is designed to be able to come up safely and robustly if the `/var/`
+file system or even the entire root file system (with exception of `/usr/`,
+i.e. the vendor OS resources) is empty (i.e. "unpopulated"). With this in mind
+it's relatively easy to build images that only ship a `/usr/` tree, and
+otherwise carry no other data, populating the rest of the directory hierarchy
+on first boot as needed.
+
+Specifically, the following mechanisms are in place:
+
+1. The `swich-root` logic in systemd, that is used to switch from the initrd
+   phase to the host will create the basic OS hierarchy skeleton if missing. It
+   will create a couple of directories strictly necessary to boot up
+   successfully, plus essential symlinks (such as those necessary for the
+   dynamic loader `ld.so` to function).
+
+2. PID 1 will initialize `/etc/machine-id` automatically if not initialized yet
+   (see above).
+
+3. The `nss-systemd` glibc NSS module ensures the `root` and `nobody` users and
+   groups remain resolvable, even without `/etc/passwd` and `/etc/group` around.
+
+4. The
+   [`systemd-sysusers`](https://www.freedesktop.org/software/systemd/man/systemd-sysusers.service.html)
+   will component automatically populate `/etc/passwd` and `/etc/group` on
+   first boot with further necessary system users.
+
+5. The
+   [`systemd-tmpfiles`](https://www.freedesktop.org/software/systemd/man/systemd-tmpfiles-setup.service.html)
+   component ensures that various files and directories below `/etc/`, `/var/`
+   and other places are created automatically at boot if missing. Unlike the
+   directories/symlinks created by the `switch-root` logic above this logic is
+   extensible by packages, and can adjust access modes, file ownership and
+   more. Among others this will also link `/etc/os-release` â†’
+   `/usr/lib/os-release`, ensuring that the OS release information is
+   unconditionally accessible through `/etc/os-release`.
+
+6. The `nss-myhostname` glibc NSS module will ensure the local host name as
+   well as `localhost` remains resolvable, even without `/etc/hosts` around.
+
+With these mechanisms the hierarchies below `/var/` and `/etc/` can be safely
+and robustly populated on first boot, so that the OS can safely boot up. Note
+that some auxiliary package are not prepared to operate correctly if their
+configuration data in `/etc/` or their state directories in `/var/` are
+missing. This can typically be addressed via `systemd-tmpfiles` lines that
+ensure the missing files and directories are created if missing. In particular,
+configuration files that are necessary for operation can be automatically
+copied or symlinked from the `/usr/share/factory/etc/` tree via the `C` or `L`
+line types. That said, we recommend that all packages safely fall back to
+internal defaults if their configuration is missing, making such additional
+steps unnecessary.
+
+Note that while `systemd` itself explicitly supports booting up with entirely
+unpopulated images (`/usr/` being the only required directory to be populated)
+distributions might not be there yet: depending on your distribution further,
+manual work might be required to make this scenario work.
+
+## Adapting OS Images to Storage
+
+Typically, if an image is `dd`-ed onto a target disk it will be minimal:
+i.e. only consist of necessary vendor data, and lack "payload" data, that shall
+be individual to the system, and dependent on host parameters. On first boot,
+the OS should take possession of the backing storage as necessary, dynamically
+using available space. Specifically:
+
+1. Additional partitions should be created, that make no sense to ship
+   pre-built in the image. For example `/tmp/` or `/home/` partitions, or even
+   `/var/` or the root file system (see above).
+
+2. Additional partitions should be created that shall function as A/B
+   secondaries for partitions shipped in the original image. In other words: if
+   the `/usr/` file system shall be updated in an A/B fashion it typically
+   makes sense to ship the original A file system in the deployed image, but
+   create the B partition on first boot.
+
+3. Partitions covering only a part of the disk should be grown to the full
+   extent of the disk.
+
+4. File systems in uninitialized partitions should be formatted with a file
+   system of choice.
+
+5. File systems covering only a part of a partition should be grown to the full
+   extent of the partition.
+
+6. Partitions should be encrypted with cryptographic keys generated locally on
+   the machine the system is first booted on, ensuring these keys remain local
+   and are not shared with any other instance of the OS image.
+
+Or any combination of the above: i.e. first create a partition, then encrypt
+it, then format it.
+
+`systemd` provides multiple tools to implement the above logic:
+
+1. The
+   [`systemd-repart`](https://www.freedesktop.org/software/systemd/man/systemd-repart.service.html)
+   component may manipulate GPT partition tables automatically on boot, growing
+   partitions or adding in partitions taking the backing storage size into
+   account. It can also encrypt partitions automatically it creates (even bind
+   to TPM2, automatically) and populate partitions from various sources. It
+   does this all in a robust fashion so that aborted invocations will not leave
+   incompletely set up partitions around.
+
+2. The
+   [`systemd-makefs@(8).service`](https://www.freedesktop.org/software/systemd/man/systemd-growfs.html)
+   tool can automatically grow a file system to the partition it is contained
+   in. The `x-systemd.growfs` `/etc/fstab` mount option is sufficient to enable
+   this logic for specific mounts. If the file system is already grown it
+   executes no operation.
+
+3. Similar, the `systemd-makefs@.service` and `systemd-makeswap@.service`
+   services can format file systems and swap spaces before first use, if they
+   carry no file system signature yet. The `x-systemd.makefs` mount option in
+   `/etc/fstab` may be used to request this functionality.
+
+## Provisioning Image Settings
+
+While a lot of work has gone into ensuring `systemd` systems can safely boot
+with unpopulated `/etc/` trees, it sometimes is desirable to set a couple of
+basic settings *after* `dd`-ing the image to disk, but *before* first boot. For
+this the tool
+[`systemd-firstboot`](https://www.freedesktop.org/software/systemd/man/systemd-firstboot.html)
+can be useful, with its `--image=` switch. It may be used to set very basic
+settings, such as the root password or hostname on an OS disk image or
+installed block device.
+
+## Distinguishing First Boot
+
+For various purposes it's useful to be able to distinguish the first boot-up of
+the system from later boot-ups (for example, to set up TPM hardware
+specifically, or register a system somewhere). `systemd` provides mechanisms to
+implement that. Specifically, the `ConditionFirstBoot=` and `AssertFirstBoot=`
+settings may be used to conditionalize units to only run on first boot. See
+[`systemd.unit(5)`](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConditionFirstBoot=)
+for details.
+
+A special target unit `first-boot-complete.target` may be used as milestone to
+safely handle first boots where the system is powered off too early: if the
+first boot process is aborted before this target is reached, the following boot
+process will be considered a first boot, too. Once the target is reached,
+subsequent boots will not be considered first boots anymore, even if the boot
+process is aborted immediately after. Thus, services that must complete fully
+before a system shall be considered fully past the first boot should be ordered
+before this target unit.
+
+Whether a system will come up in first boot state or not is derived from the
+initialization status of `/etc/machine-id`: if the file already carries a valid
+ID the system is already past the first boot. If it is not initialized yet it
+is still considered in the first boot state. For details see
+[`machine-id(5)`](https://www.freedesktop.org/software/systemd/man/machine-id.html).
index 6931536185075c94fdbcdb16d90a6fd8b6759a77..e90d6f0596679fce8eaad83ab0516b66748830b6 100644 (file)
@@ -283,11 +283,12 @@ pre-defined purposes between Linux, generic low-level distributions and
 ranges.
 
 Note that the range 2147483648…4294967294 (i.e. 2^31…2^32-2) should be handled
-with care. Various programs (including kernel file systems, see `devpts`) have
-trouble with UIDs outside of the signed 32bit range, i.e any UIDs equal to or
-above 2147483648. It is thus strongly recommended to stay away from this range
-in order to avoid complications. This range should be considered reserved for
-future, special purposes.
+with care. Various programs (including kernel file systems â€” see `devpts` â€” or
+even kernel syscalls â€“ see `setfsuid()`) have trouble with UIDs outside of the
+signed 32bit range, i.e any UIDs equal to or above 2147483648. It is thus
+strongly recommended to stay away from this range in order to avoid
+complications. This range should be considered reserved for future, special
+purposes.
 
 ## Notes on resolvability of user and group names
 
index 780ed6183ef9e7eb3b03bf66ee10ab6dfe7af379..eeadd1a059d969ebe675072685e006d4eb92567d 100644 (file)
@@ -602,6 +602,18 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*G60*Notebook*PC:*
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2570p*:*
  KEYBOARD_KEY_f8=wlan                                   # Wireless HW switch button
 
+# Elitebook 2760p
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2760p*:*
+ KEYBOARD_KEY_89=battery                                # Fn+F8
+ KEYBOARD_KEY_f8=unknown                                # rfkill is also reported by HP Wireless hotkeys
+ KEYBOARD_KEY_86=volumeup
+ KEYBOARD_KEY_87=volumedown
+ KEYBOARD_KEY_92=brightnessdown
+ KEYBOARD_KEY_97=brightnessup
+ KEYBOARD_KEY_d8=!f23                                   # touchpad off
+ KEYBOARD_KEY_d9=!f22                                   # touchpad on
+ KEYBOARD_KEY_b3=unknown                                # FIXME: Auto brightness
+
 # TX2
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[tT][xX]2*:*
  KEYBOARD_KEY_c2=media
index 5ec76999ad5d551de61bb259025ba6d07e1965b6..d6a5e43031ed931bbd6f835c0b985c47b5caf55d 100644 (file)
     </variablelist>
 
     <para><varname>$KERNEL_INSTALL_INITRD_GENERATOR</varname> is set for plugins to select the initrd
-    generator.  This should be configured as <varname>initrd_generator=</varname> in
-    <filename>install.conf</filename>.
-    </para>
+    generator. This may be configured as <varname>initrd_generator=</varname> in
+    <filename>install.conf</filename>. See below.</para>
 
     <para><varname>$KERNEL_INSTALL_STAGING_AREA</varname> is set for plugins to a path to a directory.
     Plugins may drop files in that directory, and they will be installed as part of the loader entry, based
           <filename>/etc/kernel/install.conf</filename>
         </term>
           <listitem>
-            <para>Configuration options for <command>kernel-install</command>,
-            as a series of <varname>KEY=</varname><replaceable>VALUE</replaceable> assignments,
-            compatible with shell syntax.
-            See the Environment variables section for supported keys.</para>
+            <para>Configuration options for <command>kernel-install</command>, as a series of
+            <varname>KEY=</varname><replaceable>VALUE</replaceable> assignments, compatible with shell
+            syntax.  This currently supports two keys: <varname>layout=</varname> and
+            <varname>initrd_generator=</varname>, for details see the Environment variables section above.</para>
           </listitem>
       </varlistentry>
     </variablelist>
index e5453c7dcd60c5c68066aaeffa69c3ae117a3346..d5abb1c04ec479e902d808d14e205d077f27b4a0 100644 (file)
@@ -23,6 +23,7 @@
   <refsynopsisdiv>
     <para><filename><replaceable>ESP</replaceable>/loader/loader.conf</filename>,
     <filename><replaceable>ESP</replaceable>/loader/entries/*.conf</filename>
+    <filename><replaceable>XBOOTLDR</replaceable>/loader/entries/*.conf</filename>
     </para>
   </refsynopsisdiv>
 
     <title>Description</title>
 
     <para>
-    <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-    will read <filename><replaceable>ESP</replaceable>/loader/loader.conf</filename> and any files with the
+    <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> will
+    read <filename><replaceable>ESP</replaceable>/loader/loader.conf</filename>, and any files with the
     <literal>.conf</literal> extension under
-    <filename><replaceable>ESP</replaceable>/loader/entries/</filename> on the EFI system partition (ESP).
+    <filename><replaceable>ESP</replaceable>/loader/entries/</filename> on the EFI system partition (ESP),
+    and <filename><replaceable>XBOOTLDR</replaceable>/loader/entries/</filename> on the extended boot loader
+    partition (XBOOTLDR) as defined by <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
+    Specification</ulink>.
     </para>
 
-    <para>Each configuration file must consist of an option name, followed by
-    whitespace, and the option value. <literal>#</literal> may be used to start
-    a comment line. Empty and comment lines are ignored.</para>
+    <para>Each of these configuration files must consist of series of newline (i.e. ASCII code 10) separated
+    lines, each consisting of an option name, followed by whitespace, and the option
+    value. <literal>#</literal> may be used to start a comment line. Empty and comment lines are ignored. The
+    files use UTF-8 encoding.</para>
 
     <para>Boolean arguments may be written as
     <literal>yes</literal>/<literal>y</literal>/<literal>true</literal>/<literal>t</literal>/<literal>on</literal>/<literal>1</literal> or
   <refsect1>
     <title>Options</title>
 
-    <para>The following configuration options in <filename>loader.conf</filename> are understood:</para>
+    <para>The configuration options supported by
+    <filename><replaceable>ESP</replaceable>/loader/entries/*.conf</filename> and
+    <filename><replaceable>XBOOTLDR</replaceable>/loader/entries/*.conf</filename> files are defined as part
+    of the <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
+    Specification</ulink>.</para>
+
+    <para>The following configuration are supported by the <filename>loader.conf</filename> configuration
+    file:</para>
 
     <variablelist>
       <varlistentry>
index 069772467eb782caadd8963c7da7c3f2b2abe158..83b368115b4b552a67669c9ed237520b426abf2c 100644 (file)
@@ -233,8 +233,7 @@ endif
 update_man_rules = custom_target(
         'update-man-rules',
         output : 'update-man-rules',
-        command : [sh, '-c',
-                   'cd @0@ && '.format(project_build_root) +
-                   'python3 @0@/tools/update-man-rules.py $(find @0@ -wholename "*/man/*.xml") >t && '.format(project_source_root) +
-                   'mv t @0@/rules/meson.build'.format(meson.current_source_dir())],
+        command : [update_man_rules_py,
+                   '@0@/man/*.xml'.format(project_source_root),
+                   '@0@/rules/meson.build'.format(meson.current_source_dir())],
         depends : custom_entities_ent)
index 2e334ff331d0d2fc90241c8ffbb37594298d1458..aaa69d30385c27bb2c2773662e109d5fbf0a5480 100644 (file)
@@ -971,7 +971,7 @@ manpages = [
  ['systemd-stdio-bridge', '1', [], ''],
  ['systemd-stub',
   '7',
-  ['linuxaa64.efi.stub', 'linuxia32.efi.stub', 'linuxx64.efi.stub'],
+  ['linuxaa64.efi.stub', 'linuxia32.efi.stub', 'linuxx64.efi.stub', 'sd-stub'],
   'HAVE_GNU_EFI'],
  ['systemd-suspend.service',
   '8',
@@ -987,6 +987,13 @@ manpages = [
   '5',
   ['system.conf.d', 'systemd-user.conf', 'user.conf.d'],
   ''],
+ ['systemd-sysupdate',
+  '8',
+  ['systemd-sysupdate-reboot.service',
+   'systemd-sysupdate-reboot.timer',
+   'systemd-sysupdate.service',
+   'systemd-sysupdate.timer'],
+  'ENABLE_SYSUPDATE'],
  ['systemd-sysusers', '8', ['systemd-sysusers.service'], ''],
  ['systemd-sysv-generator', '8', [], 'HAVE_SYSV_COMPAT'],
  ['systemd-time-wait-sync.service',
@@ -1058,6 +1065,7 @@ manpages = [
  ['systemd.time', '7', [], ''],
  ['systemd.timer', '5', [], ''],
  ['systemd.unit', '5', [], ''],
+ ['sysupdate.d', '5', [], 'ENABLE_SYSUPDATE'],
  ['sysusers.d', '5', [], 'ENABLE_SYSUSERS'],
  ['telinit', '8', [], 'HAVE_SYSV_COMPAT'],
  ['timedatectl', '1', [], 'ENABLE_TIMEDATECTL'],
index ceea368ef2470264e2c7d05bf1e4e2f0edc12d7c..393c0312fa3c9831f615790e403f751fcbe7cdfc 100644 (file)
 
     <para><command>systemd-boot</command> loads boot entry information from the EFI system partition (ESP),
     usually mounted at <filename>/efi/</filename>, <filename>/boot/</filename>, or
-    <filename>/boot/efi/</filename> during OS runtime, as well as from the Extended Boot Loader partition if
-    it exists (usually mounted to <filename>/boot/</filename>). Configuration file fragments, kernels,
-    initrds and other EFI images to boot generally need to reside on the ESP or the Extended Boot Loader
-    partition. Linux kernels must be built with <option>CONFIG_EFI_STUB</option> to be able to be directly
-    executed as an EFI image. During boot <command>systemd-boot</command> automatically assembles a list of
-    boot entries from the following sources:</para>
+    <filename>/boot/efi/</filename> during OS runtime, as well as from the Extended Boot Loader partition
+    (XBOOTLDR) if it exists (usually mounted to <filename>/boot/</filename>). Configuration file fragments,
+    kernels, initrds and other EFI images to boot generally need to reside on the ESP or the Extended Boot
+    Loader partition. Linux kernels must be built with <option>CONFIG_EFI_STUB</option> to be able to be
+    directly executed as an EFI image. During boot <command>systemd-boot</command> automatically assembles a
+    list of boot entries from the following sources:</para>
 
     <itemizedlist>
       <listitem><para>Boot entries defined with <ulink
-      url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> description files
-      located in <filename>/loader/entries/</filename> on the ESP and the Extended Boot Loader
-      Partition. These usually describe Linux kernel images with associated initrd images, but alternatively
-      may also describe arbitrary other EFI executables.</para></listitem>
-
-      <listitem><para>Unified kernel images following the <ulink
-      url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>, as executable EFI
-      binaries in <filename>/EFI/Linux/</filename> on the ESP and the Extended Boot Loader Partition.
-      </para></listitem>
+      url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> Type #1
+      description files located in <filename>/loader/entries/</filename> on the ESP and the Extended Boot
+      Loader Partition. These usually describe Linux kernel images with associated initrd images, but
+      alternatively may also describe other arbitrary EFI executables.</para></listitem>
+
+      <listitem><para>Unified kernel images, <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
+      Loader Specification</ulink> Type #2, which are executable EFI binaries in
+      <filename>/EFI/Linux/</filename> on the ESP and the Extended Boot Loader Partition.</para></listitem>
 
-      <listitem><para>The Microsoft Windows EFI boot manager, if installed</para></listitem>
+      <listitem><para>The Microsoft Windows EFI boot manager, if installed.</para></listitem>
 
-      <listitem><para>The Apple macOS boot manager, if installed</para></listitem>
+      <listitem><para>The Apple macOS boot manager, if installed.</para></listitem>
 
-      <listitem><para>The EFI Shell binary, if installed</para></listitem>
+      <listitem><para>The EFI Shell binary, if installed.</para></listitem>
 
-      <listitem><para>A reboot into the UEFI firmware setup option, if supported by the firmware</para></listitem>
+      <listitem><para>A reboot into the UEFI firmware setup option, if supported by the firmware.</para></listitem>
     </itemizedlist>
 
     <para><command>systemd-boot</command> supports the following features:</para>
index 28c6ba938cbe6e51297fa2a25fa904a03ef6d80d..f2d3b91e20f38bfc96b13da0fb5d609f0e371ade 100644 (file)
@@ -17,6 +17,7 @@
 
   <refnamediv>
     <refname>systemd-stub</refname>
+    <refname>sd-stub</refname>
     <refname>linuxx64.efi.stub</refname>
     <refname>linuxia32.efi.stub</refname>
     <refname>linuxaa64.efi.stub</refname>
index 400e35c457452e96fc65539f137d1088e88a143b..b104044cc265b94d6a577bdc24a61a3437f4ac24 100644 (file)
         <term><varname>DefaultOOMPolicy=</varname></term>
 
         <listitem><para>Configure the default policy for reacting to processes being killed by the Linux
-        Out-Of-Memory (OOM) killer. This may be used to pick a global default for the per-unit
+        Out-Of-Memory (OOM) killer or <command>systemd-oomd</command>. This may be used to pick a global default for the per-unit
         <varname>OOMPolicy=</varname> setting. See
         <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for details. Note that this default is not used for services that have <varname>Delegate=</varname>
diff --git a/man/systemd-sysupdate.xml b/man/systemd-sysupdate.xml
new file mode 100644 (file)
index 0000000..77c1635
--- /dev/null
@@ -0,0 +1,287 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-sysupdate" conditional='ENABLE_SYSUPDATE'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-sysupdate</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-sysupdate</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-sysupdate</refname>
+    <refname>systemd-sysupdate.service</refname>
+    <refname>systemd-sysupdate.timer</refname>
+    <refname>systemd-sysupdate-reboot.service</refname>
+    <refname>systemd-sysupdate-reboot.timer</refname>
+    <refpurpose>Automatically Update OS or Other Resources</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>systemd-sysupdate</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+    </cmdsynopsis>
+
+    <para><filename>systemd-sysupdate.service</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-sysupdate</command> atomically updates the host OS, container images, portable
+    service images or other sources, based on the transfer configuration files described in
+    <citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+    <para>This tool implements file, directory, or partition based update schemes, supporting multiple
+    parallel installed versions of specific resources in an A/B (or even: A/B/C, A/B/C/D/, â€¦) style. A/B
+    updating means that when one version of a resource is currently being used, the next version can be
+    downloaded, unpacked, and prepared in an entirely separate location, independently of the first, and â€” once
+    complete â€” be activated, swapping the roles so that it becomes the used one and the previously used one
+    becomes the one that is replaced by the next update, and so on. The resources to update are defined
+    in transfer files, one for each resource to be updated. For example, resources that may be updated with
+    this tool could be: a root file system partition, a matching Verity partition plus one kernel image. The
+    combination of the three would be considered a complete OS update.</para>
+
+    <para>The tool updates partitions, files or directory trees always in whole, and operates with at least
+    two versions of each of these resources: the <emphasis>current</emphasis> version, plus the
+    <emphasis>next</emphasis> version: the one that is being updated to, and which is initially incomplete as
+    the downloaded data is written to it; plus optionally more versions. Once the download of a newer version
+    is complete it becomes the current version, releasing the version previously considered current for
+    deletion/replacement/updating.</para>
+
+    <para>When installing new versions the tool will directly download, decompress, unpack and write the new
+    version into the destination. This is done in a robust fashion so that an incomplete download can be
+    recognized on next invocation, and flushed out before a new attempt is initiated.</para>
+
+    <para>Note that when writing updates to a partition, the partition has to exist already, as
+    <command>systemd-sysupdate</command> will not automatically create new partitions. Use a tool such as
+    <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry> to
+    automatically create additional partitions to be used with <command>systemd-sysupdate</command> on
+    boot.</para>
+
+    <para>The tool can both be used on the running OS, to update the OS in "online" state from within itself,
+    and on "offline" disk images, to update them from the outside based on transfer files
+    embedded in the disk images. For the latter, see <option>--image=</option> below. The latter is
+    particularly interesting to update container images or portable service images.</para>
+
+    <para>The <filename>systemd-sysupdate.service</filename> system service will automatically update the
+    host OS based on the installed transfer files. It is triggered in regular intervals via
+    <filename>systemd-sysupdate.timer</filename>. The <filename>systemd-sysupdate-reboot.service</filename>
+    will automatically reboot the system after a new version is installed. It is triggered via
+    <filename>systemd-sysupdate-reboot.timer</filename>. The two services are separate from each other as it
+    is typically advisable to download updates regularly while the system is up, but delay reboots until the
+    appropriate time (i.e. typically at night). The two sets of service/timer units may be enabled
+    separately.</para>
+
+    <para>For details about transfer files and examples see
+    <citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Command</title>
+
+    <para>The following commands are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>list</option> <optional><replaceable>VERSION</replaceable></optional></term>
+
+        <listitem><para>If invoked without an argument, enumerates downloadable and installed versions, and
+        shows a summarizing table with the discovered versions and their properties, including whether
+        there's a newer candidate version to update to. If a version argument is specified, shows details
+        about the specific version, including the individual files that need to be transferred to acquire the
+        version.</para>
+
+        <para>If no command is explicitly specified this command is implied.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>check-new</option></term>
+
+        <listitem><para>Checks if there's a new version available. This internally enumerates downloadable and
+        installed versions and returns exit status 0 if there's a new version to update to, non-zero
+        otherwise. If there is a new version to update to, its version identifier is written to standard
+        output.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>update</option> <optional><replaceable>VERSION</replaceable></optional></term>
+
+        <listitem><para>Installs (updates to) the specified version, or if none is specified to the newest
+        version available. If the version is already installed or no newer version available, no operation is
+        executed.</para>
+
+        <para>If a new version to install/update to is found, old installed versions are deleted until at
+        least one new version can be installed, as configured via <varname>InstanceMax=</varname> in
+        <citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>, or
+        via the available partition slots of the right type. This implicit operation can also be invoked
+        explicitly via the <command>vacuum</command> command described below.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>vacuum</option></term>
+
+        <listitem><para>Deletes old installed versions until the limits configured via
+        <varname>InstanceMax=</varname> in
+        <citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> are
+        met again. Normally, it should not be necessary to invoke this command explicitly, since it is
+        implicitly invoked whenever a new update is initiated.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>pending</option></term>
+
+        <listitem><para>Checks whether a newer version of the OS is installed than the one currently
+        running. Returns zero if so, non-zero otherwise. This compares the newest installed version's
+        identifier with the OS image version as reported by the <varname>IMAGE_VERSION=</varname> field in
+        <filename>/etc/os-release</filename>. If the former is newer than the latter, an update was
+        apparently completed but not activated (i.e. rebooted into) yet.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>reboot</option></term>
+
+        <listitem><para>Similar to the <option>pending</option> command but immediately reboots in case a
+        newer version of the OS has been installed than the one currently running. This operation can be done
+        implicitly together with the <command>update</command> command, after a completed update via the
+        <option>--reboot</option> switch, see below. This command will execute no operation (and return
+        success) if no update has been installed, and thus the system was not rebooted.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>components</option></term>
+
+        <listitem><para>Lists components that can be updated. This enumerates the
+        <filename>/etc/sysupdate.*.d/</filename>, <filename>/run/sysupdate.*.d/</filename> and
+        <filename>/usr/lib/sysupdate.*.d/</filename> directories that contain transfer files. This command is
+        useful to list possible parameters for <option>--component=</option> (see below).</para></listitem>
+      </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are understood:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><option>--component=</option></term>
+        <term><option>-C</option></term>
+
+        <listitem><para>Selects the component to update. Takes a component name as argument. This has the
+        effect of slightly altering the search logic for transfer files. If this switch is not used, the
+        transfer files are loaded from <filename>/etc/sysupdate.d/*.conf</filename>,
+        <filename>/run/sysupdate.d/*.conf</filename> and <filename>/usr/lib/sysupdate.d/*.conf</filename>. If
+        this switch is used, the specified component name is used to alter the directories to look in to be
+        <filename>/etc/sysupdate.<replaceable>component</replaceable>.d/*.conf</filename>,
+        <filename>/run/sysupdate.<replaceable>component</replaceable>.d/*.conf</filename> and
+        <filename>/usr/lib/sysupdate.<replaceable>component</replaceable>.d/*.conf</filename>, each time with
+        the <filename><replaceable>component</replaceable></filename> string replaced with the specified
+        component name.</para>
+
+        <para>Use the <command>components</command> command to list available components to update. This enumerates
+        the directories matching this naming rule.</para>
+
+        <para>Components may be used to define a separate set of transfer files for different components of
+        the OS that shall be updated separately. Do not use this concept for resources that shall always be
+        updated together in a synchronous fashion. Simply define multiple transfer files within the same
+        <filename>sysupdate.d/</filename> directory for these cases.</para>
+
+        <para>This option may not be combined with <option>--definitions=</option>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--definitions=</option></term>
+
+        <listitem><para>A path to a directory. If specified, the transfer <filename>*.conf</filename> files
+        are read from this directory instead of <filename>/usr/lib/sysupdate.d/*.conf</filename>,
+        <filename>/etc/sysupdate.d/*.conf</filename>, and <filename>/run/sysupdate.d/*.conf</filename>.</para>
+
+        <para>This option may not be combined with <option>--component=</option>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--root=</option></term>
+
+        <listitem><para>Takes a path to a directory to use as root file system when searching for
+        <filename>sysupdate.d/*.conf</filename> files.</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. If this is used and partition resources are updated this is done
+        inside the specified disk image.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--instances-max=</option></term>
+        <term><option>-m</option></term>
+
+        <listitem><para>Takes a decimal integer greater than or equal to 2. Controls how many versions to
+        keep at any time. This option may also be configured inside the transfer files, via the
+        <varname>InstancesMax=</varname> setting, see
+        <citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        details.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--sync=</option></term>
+
+        <listitem><para>Takes a boolean argument, defaults to yes. This may be used to specify whether the
+        newly updated resource versions shall be synchronized to disk when appropriate (i.e. after the
+        download is complete, before it is finalized, and again after finalization). This should not be
+        turned off, except to improve runtime performance in testing environments.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--verify=</option></term>
+
+        <listitem><para>Takes a boolean argument, defaults to yes. Controls whether to cryptographically
+        verify downloads. Do not turn this off, except in testing environments.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--reboot</option></term>
+
+        <listitem><para>When used in combination with the <command>update</command> command and a new version is
+        installed, automatically reboots the system immediately afterwards.</para></listitem>
+      </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="no-pager" />
+      <xi:include href="standard-options.xml" xpointer="no-legend" />
+      <xi:include href="standard-options.xml" xpointer="json" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Exit status</title>
+
+    <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 89a6c3375947b73f9ef36b929e2e20cb0fa618b8..5bb1679aea43846757ceb683d106e55325308091 100644 (file)
@@ -1039,7 +1039,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         normally at 0.</para>
 
         <para>Use the <varname>OOMPolicy=</varname> setting of service units to configure how the service
-        manager shall react to the kernel OOM killer terminating a process of the service.  See
+        manager shall react to the kernel OOM killer or <command>systemd-oomd</command> terminating a process of the service.  See
         <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for details.</para></listitem>
       </varlistentry>
index ec270e5e962acdc67172893daf86e05ecd28c673..bc2d1615f9348c58005e93427c0fe8aff33656bf 100644 (file)
@@ -2629,10 +2629,10 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
         <term><varname>BootServerAddress=</varname></term>
 
         <listitem>
-          <para>Takes an IPv4 address of the boot server used by e.g. PXE boot systems. When specified,
-          the address is set to the <literal>siaddr</literal> field of the DHCP message header. See
-          <ulink url="https://www.rfc-editor.org/rfc/rfc2131.html">RFC 2131</ulink> for more details.
-          Defaults to unset.</para>
+          <para>Takes an IPv4 address of the boot server used by e.g. PXE boot systems. When specified, this
+          address is sent in the <option>siaddr</option> field of the DHCP message header. See <ulink
+          url="https://www.rfc-editor.org/rfc/rfc2131.html">RFC 2131</ulink> for more details. Defaults to
+          unset.</para>
         </listitem>
       </varlistentry>
 
@@ -2640,13 +2640,13 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
         <term><varname>BootServerName=</varname></term>
 
         <listitem>
-          <para>Takes a name of the boot server used by e.g. PXE boot systems. When specified, the
-          server name is set to the DHCP option 66. See
-          <ulink url="https://www.rfc-editor.org/rfc/rfc2132.html">RFC 2132</ulink> for more details.
-          Defaults to unset.</para>
-          <para>Note that typically one of
-          <varname>BootServerName=</varname>/<varname>BootServerAddress=</varname> is sufficient to be
-          set, but both can be set too, if desired.</para>
+          <para>Takes a name of the boot server used by e.g. PXE boot systems. When specified, this name is
+          sent in the DHCP option 66 ("TFTP server name"). See <ulink
+          url="https://www.rfc-editor.org/rfc/rfc2132.html">RFC 2132</ulink> for more details. Defaults to
+          unset.</para>
+
+          <para>Note that typically setting one of <varname>BootServerName=</varname> or
+          <varname>BootServerAddress=</varname> is sufficient, but both can be set too, if desired.</para>
         </listitem>
       </varlistentry>
 
@@ -2654,10 +2654,10 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
         <term><varname>BootFilename=</varname></term>
 
         <listitem>
-          <para>Takes a path or URL to a file loaded by e.g. a PXE boot loader. The specified path is
-          set to the DHCP option 67. See
-          <ulink url="https://www.rfc-editor.org/rfc/rfc2132.html">RFC 2132</ulink> for more details.
-          Defaults to unset.</para>
+          <para>Takes a path or URL to a file loaded by e.g. a PXE boot loader. When specified, this path is
+          sent in the DHCP option 67 ("Bootfile name"). See <ulink
+          url="https://www.rfc-editor.org/rfc/rfc2132.html">RFC 2132</ulink> for more details. Defaults to
+          unset.</para>
         </listitem>
       </varlistentry>
 
index 95cb0aca3d467e07e782ac779e4ae4d130f71f0d..4e4a9732e414cb7266ab15740a6fac63dcab9d8b 100644 (file)
         shall be considered preferred or less preferred candidates for process termination by the Linux OOM
         killer logic. See
         <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
-        details.</para></listitem>
+        details.</para>
+
+        <para>This setting also applies to <command>systemd-oomd</command>, similar to kernel OOM kills
+        this setting determines the state of the service after <command>systemd-oomd</command> kills a cgroup associated
+        with the service.</para></listitem>
       </varlistentry>
 
     </variablelist>
diff --git a/man/sysupdate.d.xml b/man/sysupdate.d.xml
new file mode 100644 (file)
index 0000000..03d27b9
--- /dev/null
@@ -0,0 +1,885 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="sysupdate.d" conditional='ENABLE_SYSUPDATE'
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sysupdate.d</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sysupdate.d</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sysupdate.d</refname>
+    <refpurpose>Transfer Definition Files for Automatic Updates</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><literallayout><filename>/etc/sysupdate.d/*.conf</filename>
+<filename>/run/sysupdate.d/*.conf</filename>
+<filename>/usr/lib/sysupdate.d/*.conf</filename>
+    </literallayout></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>sysupdate.d/*.conf</filename> files describe how specific resources on the local system
+    shall be updated from a remote source. Each such file defines one such transfer: typically a remote
+    HTTP/HTTPS resource as source; and a local file, directory or partition as target. This may be used as a
+    simple, automatic, atomic update mechanism for the OS itself, for containers, portable services or system
+    extension images â€” but in fact may be used to update any kind of file from a remote source.</para>
+
+    <para>The
+    <citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    command reads these files and uses them to determine which local resources should be updated, and then
+    executes the update.</para>
+
+    <para>Both the remote HTTP/HTTPS source and the local target typically exist in multiple, concurrent
+    versions, in order to implement flexible update schemes, e.g. A/B updating (or a superset thereof,
+    e.g. A/B/C, A/B/C/D, â€¦).</para>
+
+    <para>Each <filename>*.conf</filename> file defines one transfer, i.e. describes one resource to
+    update. Typically, multiple of these files (i.e. multiple of such transfers) are defined together, and
+    are bound together by a common version identifier in order to update multiple resources at once on each
+    update operation, for example to update a kernel, a root file system and a Verity partition in a single,
+    combined, synchronized operation, so that only a combined update of all three together constitutes a
+    complete update.</para>
+
+    <para>Each <filename>*.conf</filename> file contains three sections: [Transfer], [Source] and [Target].</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Basic Mode of Operation</title>
+
+    <para>Disk-image based OS updates typically consist of multiple different resources that need to be
+    updated together, for example a secure OS update might consist of a root file system image to drop into a
+    partition, a matching Verity integrity data partition image, and a kernel image prepared to boot into the
+    combination of the two partitions. The first two resources are files that are downloaded and placed in a
+    disk partition, the latter is a file that is downloaded and placed in a regular file in the boot file
+    system (e.g. EFI system partition). Hence, during an update of a hypothetical operating system "foobarOS"
+    to a hypothetical version 47 the following operations should take place:</para>
+
+    <orderedlist>
+      <listitem><para>A file <literal>https://download.example.com/foobarOS_47.root.xz</literal> should be
+      downloaded, decompressed and written to a previously unused partition with GPT partition type UUID
+      4f68bce3-e8cd-4db1-96e7-fbcaf984b709 for x86-64, as per <ulink
+      url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
+      Specification</ulink>.</para></listitem>
+
+      <listitem><para>Similarly, a file <literal>https://download.example.com/foobarOS_47.verity.xz</literal>
+      should be downloaded, decompressed and written to a previously empty partition with GPT partition type
+      UUID of 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 (i.e the partition type for Verity integrity information
+      for x86-64 root file systems).</para></listitem>
+
+      <listitem><para>Finally, a file <literal>https://download.example.com/foobarOS_47.efi.xz</literal> (a
+      unified kernel, as per <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader
+      Specification</ulink> Type #2) should be downloaded, decompressed and written to the ESP file system,
+      i.e. to <filename>EFI/Linux/foobarOS_47.efi</filename> in the ESP.</para></listitem>
+    </orderedlist>
+
+    <para>The version-independent generalization of this would be (using the special marker
+    <literal>@v</literal> as wildcard for the version identifier):</para>
+
+    <orderedlist>
+      <listitem><para>A transfer of a file <literal>https://download.example.com/foobarOS_@v.root.xz</literal>
+      â†’ a local, previously empty GPT partition of type 4f68bce3-e8cd-4db1-96e7-fbcaf984b709, with the label to
+      be set to <literal>foobarOS_@v</literal>.</para></listitem>
+
+      <listitem><para>A transfer of a file <literal>https://download.example.com/foobarOS_@v.verity.xz</literal>
+      â†’ a local, previously empty GPT partition of type 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, with the label to be
+      set to <literal>foobarOS_@v_verity</literal>.</para></listitem>
+
+      <listitem><para>A transfer of a file <literal>https://download.example.com/foobarOS_@v.efi.xz</literal>
+      â†’ a local file <filename>/efi/EFI/Linux/foobarOS_@v.efi</filename>.</para></listitem>
+    </orderedlist>
+
+    <para>An update can only complete if the relevant URLs provide their resources for the same version,
+    i.e. for the same value of <literal>@v</literal>.</para>
+
+    <para>The above may be translated into three <filename>*.conf</filename> files in
+    <filename>sysupdate.d/</filename>, one for each resource to transfer. The <filename>*.conf</filename>
+    files configure the type of download, and what place to write the download to (i.e. whether to a
+    partition or a file in the file system). Most importantly these files contain the URL, partition name and
+    filename patterns shown above that describe how these resources are called on the source and how they
+    shall be called on the target.</para>
+
+    <para>In order to enumerate available versions and figuring out candidates to update to, a mechanism is
+    necessary to list suitable files:</para>
+
+    <itemizedlist>
+      <listitem><para>For partitions: the surrounding GPT partition table contains a list of defined
+      partitions, including a partition type UUID and a partition label (in this scheme the partition label
+      plays a role for the partition similar to the filename for a regular file)</para></listitem>
+
+      <listitem><para>For regular files: the directory listing of the directory the files are contained in
+      provides a list of existing files in a straightforward way.</para></listitem>
+
+      <listitem><para>For HTTP/HTTPS sources a simple scheme is used: a manifest file
+      <filename>SHA256SUMS</filename>, following the format defined by <citerefentry
+      project='man-pages'><refentrytitle>sha256sum</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      lists file names and their SHA256 hashes.</para></listitem>
+    </itemizedlist>
+
+    <para>Transfers are done in the alphabetical order of the <filename>.conf</filename> file names they are
+    defined in. First, the resource data is downloaded directly into a target file/directory/partition. Once
+    this is completed for all defined transfers, in a second step the files/directories/partitions are
+    renamed to their final names as defined by the target <varname>MatchPattern=</varname>, again in the
+    order the <filename>.conf</filename> transfer file names dictate. This step is not atomic, however it is
+    guaranteed to be executed strictly in order with suitable disk synchronization in place. Typically, when
+    updating an OS one of the transfers defines the entry point when booting. Thus it is generally a good idea
+    to order the resources via the transfer configuration file names so that the entry point is written
+    last, ensuring that any abnormal termination does not leave an entry point around whose backing is not
+    established yet. In the example above it would hence make sense to establish the EFI kernel image last
+    and thus give its transfer configuration file the alphabetically last name.</para>
+
+    <para>See below for an extended, more specific example based on the above.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Resource Types</title>
+
+    <para>Each transfer file defines one source resource to transfer to one target resource. The following
+    resource types are supported:</para>
+
+    <orderedlist>
+
+      <listitem><para>Resources of type <literal>url-file</literal> encapsulate a file on a web server,
+      referenced via a HTTP or HTTPS URL. When an update takes place, the file is downloaded and decompressed
+      and then written to the target file or partition. This resource type is only available for sources, not
+      for targets. The list of available versions of resources of this type is encoded in
+      <filename>SHA256SUMS</filename> manifest files, accompanied by
+      <filename>SHA256SUMS.gpg</filename> detached signatures.</para></listitem>
+
+      <listitem><para>The <literal>url-tar</literal> resource type is similar, but the file must be a
+      <filename>.tar</filename> archive. When an update takes place, the file is decompressed and unpacked
+      into a directory or btrfs subvolume. This resource type is only available for sources, not for
+      targets. Just like <literal>url-file</literal>, <literal>url-tar</literal> version enumeration makes
+      use of <filename>SHA256SUMS</filename> files, authenticated via
+      <filename>SHA256SUMS.gpg</filename>.</para></listitem>
+
+      <listitem><para>The <literal>regular-file</literal> resource type encapsulates a local regular file on
+      disk. During updates the file is uncompressed and written to the target file or partition. This
+      resource type is available both as source and as target. When updating no integrity or authentication
+      verification is done for resources of this type.</para></listitem>
+
+      <listitem><para>The <literal>partition</literal> resource type is similar to
+      <literal>regular-file</literal>, and encapsulates a GPT partition on disk. When updating, the partition
+      must exist already, and have the correct GPT partition type. A partition whose GPT partition label is
+      set to <literal>_empty</literal> is considered empty, and a candidate to place a newly downloaded
+      resource in. The GPT partition label is used to store version information, once a partition is
+      updated. This resource type is only available for target resources.</para></listitem>
+
+      <listitem><para>The <literal>tar</literal> resource type encapsulates local <filename>.tar</filename>
+      archive files. When an update takes place, the files are uncompressed and unpacked into a target
+      directory or btrfs subvolume. Behaviour of <literal>tar</literal> and <literal>url-tar</literal> is
+      generally similar, but the latter downloads from remote sources, and does integrity and authentication
+      checks while the former does not. The <literal>tar</literal> resource type is only available for source
+      resources.</para></listitem>
+
+      <listitem><para>The <literal>directory</literal> resource type encapsulates local directory trees. This
+      type is available both for source and target resources. If an update takes place on a source resource
+      of this type, a recursive copy of the directory is done.</para></listitem>
+
+      <listitem><para>The <literal>subvolume</literal> resource type is identical to
+      <literal>directory</literal>, except when used as the target, in which case the file tree is placed in
+      a btrfs subvolume instead of a plain directory, if the backing file system supports it (i.e. is
+      btrfs).</para></listitem>
+    </orderedlist>
+
+    <para>As already indicated, only a subset of source and target resource type combinations are
+    supported:</para>
+
+    <table>
+      <title>Resource Types</title>
+
+      <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+        <colspec colname="name" />
+        <colspec colname="explanation" />
+
+        <thead>
+          <row>
+            <entry>Identifier</entry>
+            <entry>Description</entry>
+            <entry>Usable as Source</entry>
+            <entry>When Used as Source: Compatible Targets</entry>
+            <entry>When Used as Source: Integrity + Authentication</entry>
+            <entry>When Used as Source: Decompression</entry>
+            <entry>Usable as Target</entry>
+            <entry>When Used as Target: Compatible Sources</entry>
+          </row>
+        </thead>
+
+        <tbody>
+          <row>
+            <entry><constant>url-file</constant></entry>
+            <entry>HTTP/HTTPS files</entry>
+            <entry>yes</entry>
+            <entry><constant>regular-file</constant>, <constant>partition</constant></entry>
+            <entry>yes</entry>
+            <entry>yes</entry>
+            <entry>no</entry>
+            <entry>-</entry>
+          </row>
+
+          <row>
+            <entry><constant>url-tar</constant></entry>
+            <entry>HTTP/HTTPS <filename>.tar</filename> archives</entry>
+            <entry>yes</entry>
+            <entry><constant>directory</constant>, <constant>subvolume</constant></entry>
+            <entry>yes</entry>
+            <entry>yes</entry>
+            <entry>no</entry>
+            <entry>-</entry>
+          </row>
+
+          <row>
+            <entry><constant>regular-file</constant></entry>
+            <entry>Local files</entry>
+            <entry>yes</entry>
+            <entry><constant>regular-file</constant>, <constant>partition</constant></entry>
+            <entry>no</entry>
+            <entry>yes</entry>
+            <entry>yes</entry>
+            <entry><constant>url-file</constant>, <constant>regular-file</constant></entry>
+          </row>
+
+          <row>
+            <entry><constant>partition</constant></entry>
+            <entry>Local GPT partitions</entry>
+            <entry>no</entry>
+            <entry>-</entry>
+            <entry>-</entry>
+            <entry>-</entry>
+            <entry>yes</entry>
+            <entry><constant>url-file</constant>, <constant>regular-file</constant></entry>
+          </row>
+
+          <row>
+            <entry><constant>tar</constant></entry>
+            <entry>Local <filename>.tar</filename> archives</entry>
+            <entry>yes</entry>
+            <entry><constant>directory</constant>, <constant>subvolume</constant></entry>
+            <entry>no</entry>
+            <entry>yes</entry>
+            <entry>no</entry>
+            <entry>-</entry>
+          </row>
+
+          <row>
+            <entry><constant>directory</constant></entry>
+            <entry>Local directories</entry>
+            <entry>yes</entry>
+            <entry><constant>directory</constant>, <constant>subvolume</constant></entry>
+            <entry>no</entry>
+            <entry>no</entry>
+            <entry>yes</entry>
+            <entry><constant>url-tar</constant>, <constant>tar</constant>, <constant>directory</constant>, <constant>subvolume</constant></entry>
+          </row>
+
+          <row>
+            <entry><constant>subvolume</constant></entry>
+            <entry>Local btrfs subvolumes</entry>
+            <entry>yes</entry>
+            <entry><constant>directory</constant>, <constant>subvolume</constant></entry>
+            <entry>no</entry>
+            <entry>no</entry>
+            <entry>yes</entry>
+            <entry><constant>url-tar</constant>, <constant>tar</constant>, <constant>directory</constant>, <constant>subvolume</constant></entry>
+          </row>
+
+        </tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    <title>Match Patterns</title>
+
+    <para>Both the source and target resources typically exist in multiple versions concurrently. An update
+    operation is done whenever the newest of the source versions is newer than the newest of the target
+    versions. To determine the newest version of the resources a directory listing, partition listing or
+    manifest listing is used, a subset of qualifying entries selected from that, and the version identifier
+    extracted from the file names or partition labels of these selected entries. Subset selection and
+    extraction of the version identifier (plus potentially other metadata) is done via match patterns,
+    configured in <varname>MatchPattern=</varname> in the [Source] and [Target] sections. These patterns are
+    strings that describe how files or partitions are named, with named wildcards for specific fields such as
+    the version identifier. The following wildcards are defined:</para>
+
+    <table>
+      <title>Match Pattern Wildcards</title>
+
+      <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+        <colspec colname="name" />
+        <colspec colname="explanation" />
+
+        <thead>
+          <row>
+            <entry>Wildcard</entry>
+            <entry>Description</entry>
+            <entry>Format</entry>
+            <entry>Notes</entry>
+          </row>
+        </thead>
+
+        <tbody>
+          <row>
+            <entry><literal>@v</literal></entry>
+            <entry>Version identifier</entry>
+            <entry>Valid version string</entry>
+            <entry>Mandatory</entry>
+          </row>
+
+          <row>
+            <entry><literal>@u</literal></entry>
+            <entry>GPT partition UUID</entry>
+            <entry>Valid 128-Bit UUID string</entry>
+            <entry>Only relevant if target resource type chosen as <constant>partition</constant></entry>
+          </row>
+
+          <row>
+            <entry><literal>@f</literal></entry>
+            <entry>GPT partition flags</entry>
+            <entry>Formatted hexadecimal integer</entry>
+            <entry>Only relevant if target resource type chosen as <constant>partition</constant></entry>
+          </row>
+
+          <row>
+            <entry><literal>@a</literal></entry>
+            <entry>GPT partition flag NoAuto</entry>
+            <entry>Either <literal>0</literal> or <literal>1</literal></entry>
+            <entry>Controls NoAuto bit of the GPT partition flags, as per <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>; only relevant if target resource type chosen as <constant>partition</constant></entry>
+          </row>
+
+          <row>
+            <entry><literal>@g</literal></entry>
+            <entry>GPT partition flag GrowFileSystem</entry>
+            <entry>Either <literal>0</literal> or <literal>1</literal></entry>
+            <entry>Controls GrowFileSystem bit of the GPT partition flags, as per <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>; only relevant if target resource type chosen as <constant>partition</constant></entry>
+          </row>
+
+          <row>
+            <entry><literal>@r</literal></entry>
+            <entry>Read-only flag</entry>
+            <entry>Either <literal>0</literal> or <literal>1</literal></entry>
+            <entry>Controls ReadOnly bit of the GPT partition flags, as per <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink> and other output read-only flags, see <varname>ReadOnly=</varname> below.</entry>
+          </row>
+
+          <row>
+            <entry><literal>@t</literal></entry>
+            <entry>File modification time</entry>
+            <entry>Formatted decimal integer, Âµs since UNIX epoch Jan 1st 1970</entry>
+            <entry>Only relevant if target resource type chosen as <constant>regular-file</constant></entry>
+          </row>
+
+          <row>
+            <entry><literal>@m</literal></entry>
+            <entry>File access mode</entry>
+            <entry>Formatted octal integer, in UNIX fashion</entry>
+            <entry>Only relevant if target resource type chosen as <constant>regular-file</constant></entry>
+          </row>
+
+          <row>
+            <entry><literal>@s</literal></entry>
+            <entry>File size after decompression</entry>
+            <entry>Formatted decimal integer</entry>
+            <entry>Useful for measuring progress and to improve partition allocation logic</entry>
+          </row>
+
+          <row>
+            <entry><literal>@d</literal></entry>
+            <entry>Tries done</entry>
+            <entry>Formatted decimal integer</entry>
+            <entry>Useful when operating with kernel image files, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot Assessment</ulink></entry>
+          </row>
+
+          <row>
+            <entry><literal>@l</literal></entry>
+            <entry>Tries left</entry>
+            <entry>Formatted decimal integer</entry>
+            <entry>Useful when operating with kernel images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot Assessment</ulink></entry>
+          </row>
+
+          <row>
+            <entry><literal>@h</literal></entry>
+            <entry>SHA256 hash of compressed file</entry>
+            <entry>64 hexadecimal characters</entry>
+            <entry>The SHA256 hash of the compressed file; not useful for <constant>url-file</constant> or <constant>url-tar</constant> where the SHA256 hash is already included in the manifest file anyway.</entry>
+          </row>
+        </tbody>
+      </tgroup>
+    </table>
+
+    <para>Of these wildcards only <literal>@v</literal> must be present in a valid pattern, all other
+    wildcards are optional. Each wildcard may be used at most once in each pattern. A typical wildcard
+    matching a file system source image could be <literal>MatchPattern=foobar_@v.raw.xz</literal>, i.e. any file
+    whose name begins with <literal>foobar_</literal>, followed by a version ID and suffixed by
+    <literal>.raw.xz</literal>.</para>
+
+    <para>Do not confuse the <literal>@</literal> pattern matching wildcard prefix with the
+    <literal>%</literal> specifier expansion prefix. The former encapsulate a variable part of a match
+    pattern string, the latter are simple shortcuts that are expanded while the drop-in files are
+    parsed. For details about specifiers, see below.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>[Transfer] Section Options</title>
+
+    <para>This section defines general properties of this transfer.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>MinVersion=</varname></term>
+
+        <listitem><para>Specifies the minimum version to require for this transfer to take place. If the
+        source or target patterns in this transfer definition match files older than this version they will
+        be considered obsolete, and never be considered for the update operation.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>ProtectVersion=</varname></term>
+
+        <listitem><para>Takes one or more version strings to mark as "protected". Protected versions are
+        never removed while making room for new, updated versions. This is useful to ensure that the
+        currently booted OS version (or auxiliary resources associated with it) is not replaced/overwritten
+        during updates, in order to avoid runtime file system corruptions.</para>
+
+        <para>Like many of the settings in these configuration files this setting supports specifier
+        expansion. It's particularly useful to set this setting to one of the <literal>%A</literal>,
+        <literal>%B</literal> or <literal>%w</literal> specifiers to automatically refer to the current OS
+        version of the running system. See below for details on supported specifiers.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Verify=</varname></term>
+
+        <listitem><para>Takes a boolean, defaults to yes. Controls whether to cryptographically verify
+        downloaded resources (specifically: validate the GPG signatures for downloaded
+        <filename>SHA256SUMS</filename> manifest files, via their detached signature files
+        <filename>SHA256SUMS.gpg</filename> in combination with the system keyring
+        <filename>/usr/lib/systemd/import-pubring.gpg</filename> or
+        <filename>/etc/systemd/import-pubring.gpg</filename>).</para>
+
+        <para>This option is essential to provide integrity guarantees for downloaded resources and thus
+        should be left enabled, outside of test environments.</para>
+
+        <para>Note that the downloaded payload files are unconditionally checked against the SHA256 hashes
+        listed in the manifest. This option only controls whether the signatures of these manifests are
+        verified.</para>
+
+        <para>This option only has an effect if the source resource type is selected as
+        <constant>url-file</constant> or <constant>url-tar</constant>, as integrity and authentication
+        checking is only available for transfers from remote sources.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[Source] Section Options</title>
+
+    <para>This section defines properties of the transfer source:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>Type=</varname></term>
+
+        <listitem><para>Specifies the resource type of the source for the transfer. Takes one of
+        <constant>url-file</constant>, <constant>url-tar</constant>, <constant>tar</constant>,
+        <constant>regular-file</constant>, <constant>directory</constant> or
+        <constant>subvolume</constant>. For details about the resource types, see above. This option is
+        mandatory.</para>
+
+        <para>Note that only some combinations of source and target resource types are supported, see
+        above.</para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>Path=</varname></term>
+
+        <listitem><para>Specifies where to find source versions of this resource.</para>
+
+        <para>If the source type is selected as <constant>url-file</constant> or
+        <constant>url-tar</constant> this must be a HTTP/HTTPS URL. The URL is suffixed with
+        <filename>/SHA256SUMS</filename> to acquire the manifest file, with
+        <filename>/SHA256SUMS.gpg</filename> to acquire the detached signature file for it, and with the file
+        names listed in the manifest file in case an update is executed and a resource shall be
+        downloaded.</para>
+
+        <para>For all other source resource types this must be a local path in the file system, referring to
+        a local directory to find the versions of this resource in.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MatchPattern=</varname></term>
+
+        <listitem><para>Specifies one or more file name match patterns that select the subset of files that
+        are update candidates as source for this transfer. See above for details on match patterns.</para>
+
+        <para>This option is mandatory. Any pattern listed must contain at least the <literal>@v</literal>
+        wildcard, so that a version identifier may be extracted from the filename. All other wildcards are
+        optional.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[Target] Section Options</title>
+
+    <para>This section defines properties of the transfer target:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>Type=</varname></term>
+
+        <listitem><para>Specifies the resource type of the target for the transfer. Takes one of
+        <constant>partition</constant>, <constant>regular-file</constant>, <constant>directory</constant> or
+        <constant>subvolume</constant>. For details about the resource types, see above. This option is
+        mandatory.</para>
+
+        <para>Note that only some combinations of source and target resource types are supported, see above.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Path=</varname></term>
+
+        <listitem><para>Specifies a file system path where to look for already installed versions or place
+        newly downloaded versions of this configured resource. If <varname>Type=</varname> is set to
+        <constant>partition</constant>, expects a path to a (whole) block device node, or the special string
+        <literal>auto</literal> in which case the block device the root file system of the currently booted
+        system is automatically determined and used. If <varname>Type=</varname> is set to
+        <constant>regular-file</constant>, <constant>directory</constant> or <constant>subvolume</constant>,
+        must refer to a path in the local file system referencing the directory to find or place the version
+        files or directories under.</para>
+
+        <para>Note that this mechanism cannot be used to create or remove partitions, in case
+        <varname>Type=</varname> is set to <constant>partition</constant>. Partitions must exist already, and
+        a special partition label <literal>_empty</literal> is used to indicate empty partitions. To
+        automatically generate suitable partitions on first boot, use a tool such as
+        <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MatchPattern=</varname></term>
+
+        <listitem><para>Specifies one or more file name or partition label match patterns that select the
+        subset of files or partitions that are update candidates as targets for this transfer. See above for
+        details on match patterns.</para>
+
+        <para>This option is mandatory. Any pattern listed must contain at least the <literal>@v</literal>
+        wildcard, so that a version identifier may be extracted from the filename. All other wildcards are
+        optional.</para>
+
+        <para>This pattern is both used for matching existing installed versions and for determining the name
+        of new versions to install. If multiple patterns are specified, the first specified is used for
+        naming newly installed versions.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MatchPartitionType=</varname></term>
+
+        <listitem><para>When the target <varname>Type=</varname> is chosen as <constant>partition</constant>,
+        specifies the GPT partition type to look for. Only partitions of this type are considered, all other
+        partitions are ignored. If not specified, the GPT partition type <constant>linux-generic</constant>
+        is used. Accepts either a literal type UUID or a symbolic type identifier. For a list of supported
+        type identifiers, see the <varname>Type=</varname> setting in
+        <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>PartitionUUID=</varname></term>
+        <term><varname>PartitionFlags=</varname></term>
+        <term><varname>PartitionNoAuto=</varname></term>
+        <term><varname>PartitionGrowFileSystem=</varname></term>
+
+        <listitem><para>When the target <varname>Type=</varname> is picked as <constant>partition</constant>,
+        selects the GPT partition UUID and partition flags to use for the updated partition. Expects a valid
+        UUID string, a hexadecimal integer, or booleans, respectively. If not set, but the source match
+        pattern includes wildcards for these fields (i.e. <literal>@u</literal>, <literal>@f</literal>,
+        <literal>@a</literal>, or <literal>@g</literal>), the values from the patterns are used. If neither
+        configured with wildcards or these explicit settings, the values are left untouched. If both the
+        overall <varname>PartitionFlags=</varname> flags setting and the individual flag settings
+        <varname>PartitionNoAuto=</varname> and <varname>PartitionGrowFileSystem=</varname> are used (or the
+        wildcards for them), then the latter override the former, i.e. the individual flag bit overrides the
+        overall flags value. See <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable
+        Partitions Specification</ulink> for details about these flags.</para>
+
+        <para>Note that these settings are not used for matching, they only have effect on newly written
+        partitions in case a transfer takes place.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>ReadOnly=</varname></term>
+
+        <listitem><para>Controls whether to mark the resulting file, subvolume or partition read-only. If the
+        target type is <constant>partition</constant> this controls the ReadOnly partition flag, as per
+        <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
+        Specification</ulink>, similar to the <varname>PartitionNoAuto=</varname> and
+        <varname>PartitionGrowFileSystem=</varname> flags described above. If the target type is
+        <constant>regular-file</constant>, the writable bit is removed from the access mode. If the the
+        target type is <constant>subvolume</constant>, the subvolume will be marked read-only as a
+        whole. Finally, if the target <varname>Type=</varname> is selected as <constant>directory</constant>,
+        the "immutable" file attribute is set, see <citerefentry
+        project='man-pages'><refentrytitle>chattr</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+        details.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Mode=</varname></term>
+
+        <listitem><para>The UNIX file access mode to use for newly created files in case the target resource
+        type is picked as <constant>regular-file</constant>. Expects an octal integer, in typical UNIX
+        fashion. If not set, but the source match pattern includes a wildcard for this field
+        (i.e. <literal>@t</literal>), the value from the pattern is used.</para>
+
+        <para>Note that this setting is not used for matching, it only has an effect on newly written
+        files when a transfer takes place.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>TriesDone=</varname></term>
+        <term><varname>TriesLeft=</varname></term>
+
+        <listitem><para>These options take positive, decimal integers, and control the number of attempts
+        done and left for this file. These settings are useful for managing kernel images, following the
+        scheme defined in <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot
+        Assessment</ulink>, and only have an effect if the target pattern includes the <literal>@d</literal>
+        or <literal>@l</literal> wildcards.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>InstancesMax=</varname></term>
+
+        <listitem><para>Takes a decimal integer equal to or greater than 2. This configures how many concurrent
+        versions of the resource to keep. Whenever a new update is initiated it is made sure that no more
+        than the number of versions specified here minus one exist in the target. Any excess versions are
+        deleted (in case the target <varname>Type=</varname> of <constant>regular-file</constant>,
+        <constant>directory</constant>, <constant>subvolume</constant> is used) or emptied (in case the
+        target <varname>Type=</varname> of <constant>partition</constant> is used; emptying in this case
+        simply means to set the partition label to the special string <literal>_empty</literal>; note that no
+        partitions are actually removed). After an update is completed the number of concurrent versions of
+        the target resources is equal to or below the number specified here.</para>
+
+        <para>Note that this setting may be set differently for each transfer. However, it generally is
+        advisable to keep this setting the same for all transfers, since otherwise incomplete combinations of
+        files or partitions will be left installed.</para>
+
+        <para>If the target <varname>Type=</varname> is selected as <constant>partition</constant>, the number
+        of concurrent versions to keep is additionally restricted by the number of partition slots of the
+        right type in the partition table. i.e. if there are only 2 partition slots for the selected
+        partition type, setting this value larger than 2 is without effect, since no more than 2 concurrent
+        versions could be stored in the image anyway.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>RemoveTemporary=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If this option is enabled (which is the default) before
+        initiating an update, all left-over, incomplete updates from a previous attempt are removed from the
+        target directory. This only has an effect if the target resource <varname>Type=</varname> is selected
+        as <constant>regular-file</constant>, <constant>directory</constant> or
+        <constant>subvolume</constant>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>CurrentSymlink=</varname></term>
+
+        <listitem><para>Takes a symlink name as argument. If this option is used, as the last step of the
+        update a symlink under the specified name is created/updated pointing to the completed update. This
+        is useful in to provide a stable name always pointing to the newest version of the resource. This is
+        only supported if the target resource <varname>Type=</varname> is selected as
+        <constant>regular-file</constant>, <constant>directory</constant> or
+        <constant>subvolume</constant>.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Specifiers</title>
+
+    <para>Specifiers may be used in the <varname>MinVersion=</varname>, <varname>ProtectVersion=</varname>,
+    <varname>Path=</varname>, <varname>MatchPattern=</varname> and <varname>CurrentSymlink=</varname>
+    settings. The following expansions are understood:</para>
+      <table class='specifiers'>
+        <title>Specifiers available</title>
+        <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+          <colspec colname="spec" />
+          <colspec colname="mean" />
+          <colspec colname="detail" />
+          <thead>
+            <row>
+              <entry>Specifier</entry>
+              <entry>Meaning</entry>
+              <entry>Details</entry>
+            </row>
+          </thead>
+          <tbody>
+            <xi:include href="standard-specifiers.xml" xpointer="a"/>
+            <xi:include href="standard-specifiers.xml" xpointer="A"/>
+            <xi:include href="standard-specifiers.xml" xpointer="b"/>
+            <xi:include href="standard-specifiers.xml" xpointer="B"/>
+            <xi:include href="standard-specifiers.xml" xpointer="H"/>
+            <xi:include href="standard-specifiers.xml" xpointer="l"/>
+            <xi:include href="standard-specifiers.xml" xpointer="m"/>
+            <xi:include href="standard-specifiers.xml" xpointer="M"/>
+            <xi:include href="standard-specifiers.xml" xpointer="o"/>
+            <xi:include href="standard-specifiers.xml" xpointer="v"/>
+            <xi:include href="standard-specifiers.xml" xpointer="w"/>
+            <xi:include href="standard-specifiers.xml" xpointer="W"/>
+            <xi:include href="standard-specifiers.xml" xpointer="T"/>
+            <xi:include href="standard-specifiers.xml" xpointer="V"/>
+            <xi:include href="standard-specifiers.xml" xpointer="percent"/>
+          </tbody>
+        </tgroup>
+      </table>
+
+    <para>Do not confuse the <literal>%</literal> specifier expansion prefix with the <literal>@</literal>
+    pattern matching wildcard prefix. The former are simple shortcuts that are expanded while the drop-in
+    files are parsed, the latter encapsulate a variable part of a match pattern string. For details about
+    pattern matching wildcards, see above.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Updates for a Verity Enabled Secure OS</title>
+
+      <para>With the following three files we define a root file system partition, a matching Verity
+      partition, and a unified kernel image to update as one. This example is an extension of the example
+      discussed earlier in this man page.</para>
+
+      <para><programlisting># /usr/lib/sysupdate.d/50-verity.conf
+[Transfer]
+ProtectVersion=%A
+
+[Source]
+Type=url-file
+Path=https://download.example.com/
+MatchPattern=foobarOS_@v_@u.verity.xz
+
+[Target]
+Type=partition
+Path=auto
+MatchPattern=foobarOS_@v_verity
+MatchPartitionType=root-verity
+PartitionFlags=0
+PartitionReadOnly=1</programlisting></para>
+
+      <para>The above defines the update mechanism for the Verity partition of the root file system. Verity
+      partition images are downloaded from
+      <literal>https://download.example.com/foobarOS_@v_@u.verity.xz</literal> and written to a suitable
+      local partition, which is marked read-only. Under the assumption this update is run from the image
+      itself the current image version (i.e. the <literal>%A</literal> specifier) is marked as protected, to
+      ensure it is not corrupted while booted. Note that the partition UUID for the target partition is
+      encoded in the source file name. Fixating the partition UUID can be useful to ensure that
+      <literal>roothash=</literal> on the kernel command line is sufficient to pinpoint both the Verity and
+      root file system partition, and also encode the Verity root level hash (under the assumption the UUID
+      in the file names match their top-level hash, the way
+      <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      suggests).</para>
+
+      <para><programlisting># /usr/lib/sysupdate.d/60-root.conf
+[Transfer]
+ProtectVersion=%A
+
+[Source]
+Type=url-file
+Path=https://download.example.com/
+MatchPattern=foobarOS_@v_@u.root.xz
+
+[Target]
+Type=partition
+Path=auto
+MatchPattern=foobarOS_@v
+MatchPartitionType=root
+PartitionFlags=0
+PartitionReadOnly=1</programlisting></para>
+
+      <para>The above defines a matching transfer definition for the root file system.</para>
+
+      <para><programlisting># /usr/lib/sysupdate.d/70-kernel.conf
+[Transfer]
+ProtectVersion=%A
+
+[Source]
+Type=url-file
+Path=https://download.example.com/
+MatchPattern=foobarOS_@v.efi.xz
+
+[Target]
+Type=file
+Path=/efi/EFI/Linux
+MatchPattern=foobarOS_@v+@l-@d.efi \
+             foobarOS_@v+@l.efi \
+             foobarOS_@v.efi
+Mode=0444
+TriesLeft=3
+TriesDone=0
+InstancesMax=2</programlisting></para>
+
+        <para>The above installs a unified kernel image into the ESP (which is mounted to
+        <filename>/efi/</filename>), as per <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
+        Loader Specification</ulink> Type #2. This defines three possible patterns for the names of the
+        kernel images images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot
+        Assessment</ulink>, and ensures when installing new kernels, they are set up with 3 tries left. No
+        more than two parallel kernels are kept.</para>
+
+        <para>With this setup the web server would serve the following files, for a hypothetical version 7 of
+        the OS:</para>
+
+        <itemizedlist>
+          <listitem><para><filename>SHA256SUMS</filename> â€“ The manifest file, containing available files and their SHA256 hashes</para></listitem>
+          <listitem><para><filename>SHA256SUMS.gpg</filename> â€“ The detached cryptographic signature for the manifest file</para></listitem>
+          <listitem><para><filename>foobarOS_7_8b8186b1-2b4e-4eb6-ad39-8d4d18d2a8fb.verity.xz</filename> â€“ The Verity image for version 7</para></listitem>
+          <listitem><para><filename>foobarOS_7_f4d1234f-3ebf-47c4-b31d-4052982f9a2f.root.xz</filename> â€“ The root file system image for version 7</para></listitem>
+          <listitem><para><filename>foobarOS_7_efi.xz</filename> â€“ The unified kernel image for version 7</para></listitem>
+        </itemizedlist>
+
+        <para>For each new OS release a new set of the latter three files would be added, each time with an
+        updated version. The <filename>SHA256SUMS</filename> manifest should then be updated accordingly,
+        listing all files for all versions that shall be offered for download.</para>
+    </example>
+
+    <example>
+      <title>Updates for Plain Directory Container Image</title>
+
+      <para><programlisting>
+[Source]
+Type=url-tar
+Path=https://download.example.com/
+MatchPattern=myContainer_@v.tar.gz
+
+[Target]
+Type=subvolume
+Path=/var/lib/machines
+MatchPattern=myContainer_@v
+CurrentSymlink=myContainer</programlisting></para>
+
+      <para>On updates this downloads <literal>https://download.example.com/myContainer_@v.tar.gz</literal>
+      and decompresses/unpacks it to <filename>/var/lib/machines/myContainer_@v</filename>. After each update
+      a symlink <filename>/var/lib/machines/myContainer</filename> is created/updated always pointing to the
+      most recent update.</para>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 1f872111e56944a554b5656ee28f28bb03aea10d..af485711222e21a9e9844f3f50123e7f5b605188 100644 (file)
           <term><option>-t</option></term>
           <term><option>--type=<replaceable>TYPE</replaceable></option></term>
           <listitem>
-            <para>Trigger a specific type of devices. Valid types are:
-            <command>devices</command>, <command>subsystems</command>.
-            The default value is <command>devices</command>.</para>
+            <para>Trigger a specific type of devices. Valid types are <literal>all</literal>,
+            <literal>devices</literal>, and <literal>subsystems</literal>. The default value is
+            <literal>devices</literal>.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             </para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><option>--prioritized-subsystem=<replaceable>SUBSYSTEM<optional>,<replaceable>SUBSYSTEM</replaceable>…</optional></replaceable></option></term>
+          <listitem>
+            <para>Takes a comma separated list of subsystems. When triggering events for devices, the
+            devices from the specified subsystems and their parents are triggered first. For example,
+            if <option>--prioritized-subsystem=block,net</option>, then firstly all block devices and
+            their parents are triggered, in the next all network devices and their parents are
+            triggered, and lastly the other devices are triggered. This option can be specified
+            multiple times, and in that case the lists of the subsystems will be merged. That is,
+            <option>--prioritized-subsystem=block --prioritized-subsystem=net</option> is equivalent to
+            <option>--prioritized-subsystem=block,net</option>.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><option>-s</option></term>
           <term><option>--subsystem-match=<replaceable>SUBSYSTEM</replaceable></option></term>
             then each matching result is ORed, that is, all children of each specified device are triggered.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><option>--initialized-match</option></term>
+          <term><option>--initialized-nomatch</option></term>
+          <listitem>
+            <para>When <option>--initialized-match</option> is specified, trigger events for devices
+            that are already initialized by <command>systemd-udevd</command>, and skip devices that
+            are not initialized yet.</para>
+            <para>When <option>--initialized-nomatch</option> is specified, trigger events for devices
+            that are not initialized by <command>systemd-udevd</command> yet, and skip devices that
+            are already initialized.</para>
+            <para>Here, initialized devices are those for which at least one udev rule already
+            completed execution â€“ for any action but <literal>remove</literal> â€” that set a property
+            or other device setting (and thus has an entry in the udev device database). Devices are
+            no longer considered initialized if a <literal>remove</literal> action is seen for them
+            (which removes their entry in the udev device database). Note that devices that have no
+            udev rules are never considered initialized, but might still be announced via the sd-device
+            API (or similar). Typically, it is thus essential that applications which intend to use
+            such a match, make sure a suitable udev rule is installed that sets at least one property
+            on devices that shall be matched.</para>
+            <para>WARNING: <option>--initialized-nomatch</option> can potentially save a significant
+            amount of time compared to re-triggering all devices in the system and e.g. can be used to
+            optimize boot time. However, this is not safe to be used in a boot sequence in general.
+            Especially, when udev rules for a device depend on its parent devices (e.g.
+            <literal>ATTRS</literal> or <literal>IMPORT{parent}</literal> keys, see
+            <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+            for more details), the final state of the device becomes easily unstable with this option.
+            </para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><option>-w</option></term>
           <term><option>--settle</option></term>
index 107192b2112f938f7c0d1081213e77347e6a4580..7de1baaf70a95d571d1c9a406f1ae02f252e72ea 100644 (file)
@@ -1644,6 +1644,18 @@ conf.set('DEFAULT_DNSSEC_MODE',
          'DNSSEC_' + default_dnssec.underscorify().to_upper())
 conf.set_quoted('DEFAULT_DNSSEC_MODE_STR', default_dnssec)
 
+want_sysupdate = get_option('sysupdate')
+if want_sysupdate != 'false'
+        have = (conf.get('HAVE_OPENSSL') == 1 and
+                conf.get('HAVE_LIBFDISK') == 1)
+        if want_sysupdate == 'true' and not have
+                error('sysupdate support was requested, but dependencies are not available')
+        endif
+else
+        have = false
+endif
+conf.set10('ENABLE_SYSUPDATE', have)
+
 want_importd = get_option('importd')
 if want_importd != 'false'
         have = (conf.get('HAVE_LIBCURL') == 1 and
@@ -1791,6 +1803,7 @@ make_directive_index_py = find_program('tools/make-directive-index.py')
 make_man_index_py = find_program('tools/make-man-index.py')
 meson_render_jinja2 = find_program('tools/meson-render-jinja2.py')
 update_dbus_docs_py = find_program('tools/update-dbus-docs.py')
+update_man_rules_py = find_program('tools/update-man-rules.py')
 update_hwdb_sh = find_program('tools/update-hwdb.sh')
 update_hwdb_autosuspend_sh = find_program('tools/update-hwdb-autosuspend.sh')
 update_syscall_tables_sh = find_program('tools/update-syscall-tables.sh')
@@ -2006,6 +2019,7 @@ subdir('src/rpm')
 subdir('src/shutdown')
 subdir('src/sysext')
 subdir('src/systemctl')
+subdir('src/sysupdate')
 subdir('src/timedate')
 subdir('src/timesync')
 subdir('src/tmpfiles')
@@ -3074,6 +3088,22 @@ if conf.get('ENABLE_REPART') == 1
         endif
 endif
 
+if conf.get('ENABLE_SYSUPDATE') == 1
+        exe = executable(
+                'systemd-sysupdate',
+                systemd_sysupdate_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libblkid,
+                                libfdisk,
+                                libopenssl],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+        public_programs += exe
+endif
+
 if conf.get('ENABLE_VCONSOLE') == 1
         executable(
                 'systemd-vconsole-setup',
@@ -4117,6 +4147,7 @@ foreach tuple : [
         ['rfkill'],
         ['sysext'],
         ['systemd-analyze',       conf.get('ENABLE_ANALYZE') == 1],
+        ['sysupdate'],
         ['sysusers'],
         ['timedated'],
         ['timesyncd'],
index 284109cadf4f3a11330c621d9811e770f1da6b43..27cfa9b697e0eec968f829162ed5169f5c2851c0 100644 (file)
@@ -100,6 +100,8 @@ option('binfmt', type : 'boolean',
        description : 'support for custom binary formats')
 option('repart', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'install the systemd-repart tool')
+option('sysupdate', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'install the systemd-sysupdate tool')
 option('coredump', type : 'boolean',
        description : 'install the coredump handler')
 option('pstore', type : 'boolean',
index d50acdf56ca2175e9d4cdb878fbd8a8a0c680116..5b0f3ff837cad5cdf9e42496310d86def2ea987c 100644 (file)
--- a/po/it.po
+++ b/po/it.po
@@ -66,7 +66,7 @@ msgstr "Ricarica lo stato di systemd"
 
 #: src/core/org.freedesktop.systemd1.policy.in:65
 msgid "Authentication is required to reload the systemd state."
-msgstr "Autenticazione richiesta per riavviare lo stato di sistemd."
+msgstr "Autenticazione richiesta per ricaricare lo stato di systemd."
 
 #: src/home/org.freedesktop.home1.policy:13
 msgid "Create a home area"
index c6668e5ea33e31bc07363dc9de4c7a2e06612464..963a11b6ceeeafcb1809be6a6f9fd2141054683d 100644 (file)
@@ -40,6 +40,7 @@ items = [['busctl',              ''],
          ['loginctl',            'ENABLE_LOGIND'],
          ['machinectl',          'ENABLE_MACHINED'],
          ['networkctl',          'ENABLE_NETWORKD'],
+         ['oomctl',              'ENABLE_OOMD'],
          ['portablectl',         'ENABLE_PORTABLED'],
          ['resolvectl',          'ENABLE_RESOLVE'],
          ['systemd-resolve',     'ENABLE_RESOLVE'],
diff --git a/shell-completion/bash/oomctl b/shell-completion/bash/oomctl
new file mode 100644 (file)
index 0000000..cc77819
--- /dev/null
@@ -0,0 +1,57 @@
+# oomctl(1) completion                               -*- shell-script -*-
+# 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.
+#
+# systemd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+__contains_word () {
+    local w word=$1; shift
+    for w in "$@"; do
+        [[ $w = "$word" ]] && return
+    done
+}
+
+_oomctl() {
+    local i verb comps
+    local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+    local OPTS='-h --help --version --no-pager'
+
+    if [[ "$cur" = -* ]]; then
+        COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
+        return 0
+    fi
+
+    local -A VERBS=(
+        [STANDALONE]='help dump'
+    )
+
+    for ((i=0; i < COMP_CWORD; i++)); do
+        if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]}; then
+            verb=${COMP_WORDS[i]}
+            break
+        fi
+    done
+
+    if [[ -z ${verb-} ]]; then
+        comps=${VERBS[*]}
+    elif __contains_word "$verb" ${VERBS[STANDALONE]}; then
+        comps=''
+    fi
+
+    COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+    return 0
+}
+
+complete -F _oomctl oomctl
index 08b4ab43a0e6e556fa4ae0bb64d2a577bb979e1f..23ce02365c7f0fe3bb782daa239219d963e2b6e4 100644 (file)
@@ -51,10 +51,12 @@ _udevadm() {
         [INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db
                            -w --wait-for-initialization --value'
         [INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file --property'
-        [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid'
+        [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid
+                              --initialized-match --initialized-nomatch'
         [TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
                        -a --attr-match -A --attr-nomatch -p --property-match
-                       -g --tag-match -y --sysname-match --name-match -b --parent-match'
+                       -g --tag-match -y --sysname-match --name-match -b --parent-match
+                       --prioritized-subsystem'
         [SETTLE]='-t --timeout -E --exit-if-exists'
         [CONTROL_STANDALONE]='-e --exit -s --stop-exec-queue -S --start-exec-queue -R --reload --ping'
         [CONTROL_ARG]='-l --log-priority -p --property -m --children-max -t --timeout'
@@ -117,7 +119,7 @@ _udevadm() {
             if __contains_word "$prev" ${OPTS[TRIGGER_ARG]}; then
                 case $prev in
                     -t|--type)
-                        comps='devices subsystems'
+                        comps='all devices subsystems'
                         ;;
                     -c|--action)
                         comps=$( udevadm trigger --action help )
diff --git a/shell-completion/zsh/_oomctl b/shell-completion/zsh/_oomctl
new file mode 100644 (file)
index 0000000..f956340
--- /dev/null
@@ -0,0 +1,28 @@
+#compdef oomctl
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+(( $+functions[_oomctl_commands] )) || _oomctl_commands()
+{
+    local -a _oomctl_cmds
+    _oomctl_cmds=(
+        "dump:Show the current state of the cgroup(s) and system context(s)"
+        "help:Prints a short help text and exits."
+    )
+    if (( CURRENT == 1 )); then
+        _describe -t commands 'oomctl command' _oomctl_cmds
+    else
+        local curcontext="$curcontext"
+        cmd="${${_oomctl_cmds[(r)$words[1]:*]%%:*}}"
+        if (( $+functions[_oomctl_$cmd] )); then
+            _oomctl_$cmd
+        else
+            _message "no more options"
+        fi
+    fi
+}
+
+_arguments \
+    {-h,--help}'[Prints a short help text and exits.]' \
+    '--version[Prints a short version string and exits.]' \
+    '--no-pager[Do not pipe output into a pager]' \
+    '*::oomctl command:_oomctl_commands'
index 14efe24808658f1f63034d8893b1b3b1679df8a6..63df8b7c9eccf9468d6cd73a80cfb304c0d6a5ba 100644 (file)
@@ -24,7 +24,7 @@ _udevadm_trigger(){
         '--verbose[Print the list of devices which will be triggered.]' \
         '--dry-run[Do not actually trigger the event.]' \
         '--quiet[Suppress error logging in triggering events.]' \
-        '--type=[Trigger a specific type of devices.]:types:(devices subsystems failed)' \
+        '--type=[Trigger a specific type of devices.]:types:(all devices subsystems failed)' \
         '--action=[Type of event to be triggered.]:actions:(add change remove move online offline bind unbind)' \
         '--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]' \
         '--subsystem-nomatch=[Do not trigger events for devices which belong to a matching subsystem.]' \
@@ -34,7 +34,10 @@ _udevadm_trigger(){
         '--tag-match=property[Trigger events for devices with a matching tag.]' \
         '--sysname-match=[Trigger events for devices with a matching sys device name.]' \
         '--parent-match=[Trigger events for all children of a given device.]' \
-        '--uuid[Print synthetic uevent UUID.]'
+        '--initialized-match[Trigger events for devices that are already initialized.]' \
+        '--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \
+        '--uuid[Print synthetic uevent UUID.]' \
+        '--prioritized-subsystem=[Trigger events for devices which belong to a matching subsystem earlier.]'
 }
 
 (( $+functions[_udevadm_settle] )) ||
index a0615c4df97f707d9f5c3d867c9e43e4c043caa0..6dca9dd59582f9eb2437859cd9257da10a59d7c0 100644 (file)
@@ -34,6 +34,7 @@ items = [['_busctl',                   ''],
          ['_loginctl',                 'ENABLE_LOGIND'],
          ['_machinectl',               'ENABLE_MACHINED'],
          ['_networkctl',               'ENABLE_NETWORKD'],
+         ['_oomctl',                   'ENABLE_OOMD'],
          ['_systemd-inhibit',          'ENABLE_LOGIND'],
          ['_resolvectl',               'ENABLE_RESOLVE'],
          ['_systemd-tmpfiles',         'ENABLE_TMPFILES'],
index 0c321526719fd30b2cf7b3010f11892ab229ca83..d39a8b4415dfa0545ca739d41d5b7293ed460023 100644 (file)
@@ -49,7 +49,6 @@ static int add_epoll(int epoll_fd, int fd) {
 }
 
 static int open_sockets(int *epoll_fd, bool accept) {
-        char **address;
         int n, fd, r, count = 0;
 
         n = sd_listen_fds(true);
@@ -125,7 +124,6 @@ static int open_sockets(int *epoll_fd, bool accept) {
 static int exec_process(const char *name, char **argv, int start_fd, size_t n_fds) {
         _cleanup_strv_free_ char **envp = NULL;
         const char *var;
-        char **s;
         int r;
 
         if (arg_inetd && n_fds != 1)
@@ -390,7 +388,6 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case ARG_FDNAME: {
                         _cleanup_strv_free_ char **names = NULL;
-                        char **s;
 
                         names = strv_split(optarg, ":");
                         if (!names)
index ed4697f8659c354fe93b7ca8314d032721efbd28..8f6517fa8e52c70b1eb3bf936c1da2eadfa0a41b 100644 (file)
@@ -124,7 +124,6 @@ static int test_calendar_one(usec_t n, const char *p) {
 
 int verb_calendar(int argc, char *argv[], void *userdata) {
         int ret = 0, r;
-        char **p;
         usec_t n;
 
         if (arg_base_time != USEC_INFINITY)
index 079cad311b9d46aa63ef8105593d21270f682ec1..85ed8b01e0d5f7e74f35808fab42b9390f599610 100644 (file)
@@ -10,7 +10,7 @@
 #include "strv.h"
 
 int verb_cat_config(int argc, char *argv[], void *userdata) {
-        char **arg, **list;
+        char **list;
         int r;
 
         pager_open(arg_pager_flags);
index a382c356dcccb34835c58d88e2fa96488d2340ba..58986bc406f2e1285069d8f0dc5fcaff5ce5d4b0 100644 (file)
@@ -114,8 +114,6 @@ static int verify_conditions(char **lines, UnitFileScope scope, const char *unit
                 if (r < 0)
                         return r;
         } else {
-                char **line;
-
                 r = unit_new_for_name(m, sizeof(Service), "test.service", &u);
                 if (r < 0)
                         return log_error_errno(r, "Failed to create test.service: %m");
index d3169791d9f2494871c51186ecdc51f0c5ccc793..3a6b777053180ed622a250173af98461659f291f 100644 (file)
@@ -76,7 +76,6 @@ static bool times_in_range(const UnitTimes *times, const BootTimes *boot) {
 
 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) {
         _cleanup_strv_free_ char **deps = NULL;
-        char **c;
         int r;
         usec_t service_longest = 0;
         int to_print = 0;
@@ -225,11 +224,10 @@ int verb_critical_chain(int argc, char *argv[], void *userdata) {
         puts("The time when unit became active or started is printed after the \"@\" character.\n"
              "The time the unit took to start is printed after the \"+\" character.\n");
 
-        if (argc > 1) {
-                char **name;
+        if (argc > 1)
                 STRV_FOREACH(name, strv_skip(argv, 1))
                         list_dependencies(bus, *name);
-        else
+        else
                 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
 
         h = hashmap_free(h);
index 3a1fe043fc90d698503a8fd471a1af2813f6abf8..13bea4598fce0c85cba13770fe26f0572f40e71e 100644 (file)
@@ -18,9 +18,8 @@ static int graph_one_property(
                 char *to_patterns[]) {
 
         _cleanup_strv_free_ char **units = NULL;
-        char **unit;
-        int r;
         bool match_patterns;
+        int r;
 
         assert(u);
         assert(prop);
@@ -84,7 +83,6 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *fro
 
 static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
         _cleanup_strv_free_ char **expanded_patterns = NULL;
-        char **pattern;
         int r;
 
         STRV_FOREACH(pattern, patterns) {
index 28b2fc13391ba4017ca3655e711234a1930a99f8..66d8397e31298e79da2aaa52602172698f214351 100644 (file)
@@ -145,7 +145,6 @@ int verb_filesystems(int argc, char *argv[], void *userdata) {
 
                 if (!set_isempty(known)) {
                         _cleanup_free_ char **l = NULL;
-                        char **filesystem;
 
                         printf("\n"
                                "# %sUngrouped filesystems%s (known but not included in any of the groups except @known):\n",
@@ -188,7 +187,6 @@ int verb_filesystems(int argc, char *argv[], void *userdata) {
                         log_notice_errno(k, "# Not showing unlisted filesystems, couldn't retrieve kernel filesystem list: %m");
                 } else if (!set_isempty(kernel)) {
                         _cleanup_free_ char **l = NULL;
-                        char **filesystem;
 
                         printf("\n"
                                "# %sUnlisted filesystems%s (available to the local kernel, but not included in any of the groups listed above):\n",
@@ -203,9 +201,7 @@ int verb_filesystems(int argc, char *argv[], void *userdata) {
                         STRV_FOREACH(filesystem, l)
                                 printf("#   %s\n", *filesystem);
                 }
-        } else {
-                char **name;
-
+        } else
                 STRV_FOREACH(name, strv_skip(argv, 1)) {
                         const FilesystemSet *set;
 
@@ -224,7 +220,6 @@ int verb_filesystems(int argc, char *argv[], void *userdata) {
                         dump_filesystem_set(set);
                         first = false;
                 }
-        }
 
         return 0;
 }
index cf953cd37b129325928ecbf808f3621a21bcc23d..155c611c7176abcff29b025e94683a1bef433f6f 100644 (file)
@@ -12,7 +12,6 @@
 #include "strv.h"
 
 static int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
-        char **filename;
         int r;
 
         STRV_FOREACH(filename, filenames) {
index f1d3ff748053f2625d6d1ec536ce63f109a030aa..458b681143a12a7b3b81de499f8fd9adfedb8f6e 100644 (file)
@@ -2666,7 +2666,6 @@ static int offline_security_checks(char **filenames,
         _cleanup_free_ char *var = NULL;
         int r, k;
         size_t count = 0;
-        char **filename;
 
         if (strv_isempty(filenames))
                 return 0;
@@ -2789,7 +2788,6 @@ static int analyze_security(sd_bus *bus,
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
                 _cleanup_strv_free_ char **list = NULL;
                 size_t n = 0;
-                char **i;
 
                 r = sd_bus_call_method(
                                 bus,
@@ -2841,9 +2839,7 @@ static int analyze_security(sd_bus *bus,
                                 ret = r;
                 }
 
-        } else {
-                char **i;
-
+        } else
                 STRV_FOREACH(i, units) {
                         _cleanup_free_ char *mangled = NULL, *instance = NULL;
                         const char *name;
@@ -2875,7 +2871,6 @@ static int analyze_security(sd_bus *bus,
                         if (r < 0 && ret >= 0)
                                 ret = r;
                 }
-        }
 
         if (overview_table) {
                 if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) {
index 50cd62a71c14bed44c5f369a2711c26d18c95ad6..582a043088fb49d292827946232e1bc4d11375c6 100644 (file)
@@ -120,7 +120,6 @@ int verb_syscall_filters(int argc, char *argv[], void *userdata) {
 
                 if (!set_isempty(known)) {
                         _cleanup_free_ char **l = NULL;
-                        char **syscall;
 
                         printf("\n"
                                "# %sUngrouped System Calls%s (known but not included in any of the groups except @known):\n",
@@ -143,7 +142,6 @@ int verb_syscall_filters(int argc, char *argv[], void *userdata) {
                                 log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
                 } else if (!set_isempty(kernel)) {
                         _cleanup_free_ char **l = NULL;
-                        char **syscall;
 
                         printf("\n"
                                "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
@@ -158,9 +156,7 @@ int verb_syscall_filters(int argc, char *argv[], void *userdata) {
                         STRV_FOREACH(syscall, l)
                                 printf("#   %s\n", *syscall);
                 }
-        } else {
-                char **name;
-
+        } else
                 STRV_FOREACH(name, strv_skip(argv, 1)) {
                         const SyscallFilterSet *set;
 
@@ -179,7 +175,6 @@ int verb_syscall_filters(int argc, char *argv[], void *userdata) {
                         dump_syscall_filter(set);
                         first = false;
                 }
-        }
 
         return 0;
 }
index 8d7cd2ddd5924632952c023cc5f6d13c90cb045e..f244ace04ac0af596cbcac900ef12e5a2a303cc6 100644 (file)
@@ -9,8 +9,6 @@
 #include "terminal-util.h"
 
 int verb_timespan(int argc, char *argv[], void *userdata) {
-        char **input_timespan;
-
         STRV_FOREACH(input_timespan, strv_skip(argv, 1)) {
                 _cleanup_(table_unrefp) Table *table = NULL;
                 usec_t output_usecs;
index 91282073389903ee1c33e89a52951e0ced69eb35..ddf34ab75fa4805c23e438a95f3020e3b017d699 100644 (file)
@@ -80,7 +80,6 @@ static int test_timestamp_one(const char *p) {
 
 int verb_timestamp(int argc, char *argv[], void *userdata) {
         int ret = 0, r;
-        char **p;
 
         STRV_FOREACH(p, strv_skip(argv, 1)) {
                 r = test_timestamp_one(*p);
index 81714914e32c1c5c67de207eb050544e5b6ec999..60fbdcb2306e88314618e016d27aff636912333e 100644 (file)
@@ -6,8 +6,6 @@
 #include "strv.h"
 
 static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int flags) {
-        char **s;
-
         STRV_FOREACH(s, strv)
                 if (strv_fnmatch_or_empty(patterns, *s, flags))
                         return true;
index e2e8c66b7e2625af54b38d4ed88168b69760514d..32c97b2e520dd7d3f7cb0b4ff4c98a460ee2e132 100644 (file)
@@ -8,7 +8,6 @@
 int verb_unit_paths(int argc, char *argv[], void *userdata) {
         _cleanup_(lookup_paths_free) LookupPaths paths = {};
         int r;
-        char **p;
 
         r = lookup_paths_init(&paths, arg_scope, 0, NULL);
         if (r < 0)
index 6c28cc0ca9d9002c3acf0620c02adfb1bac82d22..7702d83ddcfb1322e7eb23b786649c9b94361ee4 100644 (file)
@@ -74,10 +74,8 @@ int verify_prepare_filename(const char *filename, char **ret) {
 }
 
 int verify_generate_path(char **var, char **filenames) {
-        const char *old;
-        char **filename;
-
         _cleanup_strv_free_ char **ans = NULL;
+        const char *old;
         int r;
 
         STRV_FOREACH(filename, filenames) {
@@ -184,7 +182,6 @@ static int verify_executables(Unit *u, const char *root) {
 }
 
 static int verify_documentation(Unit *u, bool check_man) {
-        char **p;
         int r = 0, k;
 
         STRV_FOREACH(p, u->documentation) {
@@ -258,7 +255,6 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
         Unit *units[strv_length(filenames)];
         _cleanup_free_ char *var = NULL;
         int r, k, i, count = 0;
-        char **filename;
 
         if (strv_isempty(filenames))
                 return 0;
index d97b6ed9c821d562298d7f62eddfac1cb320796d..35e4e1eb31d666a3b96b58ac9f4aff2f22bfdf56 100644 (file)
@@ -9,7 +9,6 @@
 
 static int process_aliases(char *argv[], char *tempdir, char ***ret) {
         _cleanup_strv_free_ char **filenames = NULL;
-        char **filename;
         int r;
 
         assert(argv);
index a100679af211f58e37488d6717ba038fc1badc16..093533182f28cc4acbb3702565dd1bba7b19ea96 100644 (file)
@@ -223,7 +223,6 @@ static int parse_argv(int argc, char *argv[]) {
 static int run(int argc, char *argv[]) {
         _cleanup_strv_free_erase_ char **l = NULL;
         usec_t timeout;
-        char **p;
         int r;
 
         log_show_color(true);
index 287428b561254abc49fa7510be1a711116a06c05..82c6dc5677b132350b8cbb54c1458707b8717f67 100644 (file)
@@ -145,7 +145,7 @@ static int conf_files_list_strv_internal(
 
         _cleanup_hashmap_free_ Hashmap *fh = NULL;
         _cleanup_set_free_free_ Set *masked = NULL;
-        char **files, **p;
+        char **files;
         int r;
 
         assert(ret);
@@ -202,11 +202,8 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
                 int c;
 
                 c = base_cmp((char* const*) *strv + i, (char* const*) &path);
-                if (c == 0) {
-                        char **dir;
-
+                if (c == 0)
                         /* Oh, there already is an entry with a matching name (the last component). */
-
                         STRV_FOREACH(dir, dirs) {
                                 _cleanup_free_ char *rdir = NULL;
                                 char *p1, *p2;
@@ -233,7 +230,7 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
                                 }
                         }
 
-                else if (c > 0)
+                else if (c > 0)
                         /* Following files have lower priority, let's go insert our
                          * new entry. */
                         break;
index e268c5c644a254d9836950b45fa824eba86a8367..3efd77909c617f2e445e7180ab54c4aaea5308dd 100644 (file)
@@ -537,7 +537,6 @@ static void write_env_var(FILE *f, const char *v) {
 int write_env_file(const char *fname, char **l) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char *p = NULL;
-        char **i;
         int r;
 
         assert(fname);
index 96b9613d24ad1f715d5089c2e8695628582216da..b60c9f9fdc61a37bb902457d7d7392026ad6fea0 100644 (file)
@@ -96,8 +96,6 @@ bool env_assignment_is_valid(const char *e) {
 }
 
 bool strv_env_is_valid(char **e) {
-        char **p, **q;
-
         STRV_FOREACH(p, e) {
                 size_t k;
 
@@ -115,8 +113,6 @@ bool strv_env_is_valid(char **e) {
 }
 
 bool strv_env_name_is_valid(char **l) {
-        char **p;
-
         STRV_FOREACH(p, l) {
                 if (!env_name_is_valid(*p))
                         return false;
@@ -129,8 +125,6 @@ bool strv_env_name_is_valid(char **l) {
 }
 
 bool strv_env_name_or_assignment_is_valid(char **l) {
-        char **p;
-
         STRV_FOREACH(p, l) {
                 if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
                         return false;
@@ -272,7 +266,7 @@ static bool env_entry_has_name(const char *entry, const char *name) {
 
 char **strv_env_delete(char **x, size_t n_lists, ...) {
         size_t n, i = 0;
-        char **k, **r;
+        char **r;
         va_list ap;
 
         /* Deletes every entry from x that is mentioned in the other
@@ -287,7 +281,7 @@ char **strv_env_delete(char **x, size_t n_lists, ...) {
         STRV_FOREACH(k, x) {
                 va_start(ap, n_lists);
                 for (size_t v = 0; v < n_lists; v++) {
-                        char **l, **j;
+                        char **l;
 
                         l = va_arg(ap, char**);
                         STRV_FOREACH(j, l)
@@ -379,7 +373,6 @@ char **strv_env_unset_many(char **l, ...) {
 
 int strv_env_replace_consume(char ***l, char *p) {
         const char *t, *name;
-        char **f;
         int r;
 
         assert(p);
@@ -467,8 +460,6 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
 }
 
 char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
-        char **i;
-
         assert(name);
 
         if (k <= 0)
@@ -496,7 +487,7 @@ char *strv_env_get(char **l, const char *name) {
 }
 
 char *strv_env_pairs_get(char **l, const char *name) {
-        char **key, **value, *result = NULL;
+        char *result = NULL;
 
         assert(name);
 
@@ -508,7 +499,6 @@ char *strv_env_pairs_get(char **l, const char *name) {
 }
 
 char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
-        char **p, **q;
         int k = 0;
 
         STRV_FOREACH(p, e) {
@@ -702,7 +692,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
 }
 
 char **replace_env_argv(char **argv, char **env) {
-        char **ret, **i;
+        char **ret;
         size_t k = 0, l = 0;
 
         l = strv_length(argv);
@@ -832,7 +822,6 @@ int setenv_systemd_exec_pid(bool update_only) {
 int getenv_path_list(const char *name, char ***ret_paths) {
         _cleanup_strv_free_ char **l = NULL;
         const char *e;
-        char **p;
         int r;
 
         assert(name);
index 6b18a90e134b25ef749d4c95b7c6f6849f2b5114..844212448870d623f841a6208dd04e82be6e40af 100644 (file)
@@ -3,7 +3,7 @@
 BEGIN{
         print "static const char* const errno_names[] = { "
 }
-!/EDEADLOCK/ && !/EWOULDBLOCK/ && !/ENOTSUP/ {
+!/(EDEADLOCK|EWOULDBLOCK|ENOTSUP)/ {
         printf "        [%s] = \"%s\",\n", $1, $1
 }
 END{
index ce57fcc76223ac563cac55af711951da92c99d75..1cb7ced545dd9f2e925688303ea7f5c2a0859691 100644 (file)
@@ -549,7 +549,6 @@ char* quote_command_line(char **argv, ShellEscapeFlags flags) {
 
         assert(argv);
 
-        char **a;
         STRV_FOREACH(a, argv) {
                 _cleanup_free_ char *t = NULL;
 
index cac547949165d7da2e7686cfd70db41906385be3..e7b670ab2e4528b085f31efc725b4f1ac7b37bc7 100644 (file)
@@ -1042,8 +1042,6 @@ static int search_and_fopen_internal(
                 FILE **ret,
                 char **ret_path) {
 
-        char **i;
-
         assert(path);
         assert(mode);
         assert(ret);
index 11039fd75d587ebc60c3ab2891319e61fa3ed3d1..e7e8ee236bc2825fe387f3a368578613e81bb615 100644 (file)
@@ -1083,3 +1083,50 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
 
         return TAKE_FD(fd);
 }
+
+int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created) {
+        unsigned attempts = 7;
+        int fd;
+
+        /* Just like openat(), but adds one thing: optionally returns whether we created the file anew or if
+         * it already existed before. This is only relevant if O_CREAT is set without O_EXCL, and thus will
+         * shortcut to openat() otherwise */
+
+        if (!ret_newly_created)
+                return RET_NERRNO(openat(dirfd, pathname, flags, mode));
+
+        if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL)) {
+                fd = openat(dirfd, pathname, flags, mode);
+                if (fd < 0)
+                        return -errno;
+
+                *ret_newly_created = FLAGS_SET(flags, O_CREAT);
+                return fd;
+        }
+
+        for (;;) {
+                /* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */
+                fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode);
+                if (fd >= 0) {
+                        *ret_newly_created = false;
+                        return fd;
+                }
+                if (errno != ENOENT)
+                        return -errno;
+
+                /* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL. */
+                fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL, mode);
+                if (fd >= 0) {
+                        *ret_newly_created = true;
+                        return fd;
+                }
+                if (errno != EEXIST)
+                        return -errno;
+
+                /* Hmm, so now we got EEXIST? So it apparently exists now? If so, let's try to open again
+                 * without the two flags. But let's not spin forever, hence put a limit on things */
+
+                if (--attempts == 0) /* Give up eventually, somebody is playing with us */
+                        return -EEXIST;
+        }
+}
index 0bbb3f629814b5e90e27180eaedfcaf035938b4c..e48cf6800fac1796ba7d403a592adb2c1fe4d603 100644 (file)
@@ -110,3 +110,5 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
 int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path);
 
 int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
+
+int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
index b51d70bc87928d0a708ba7b079110dde7f6d8be0..bd7a6b0d6e710cc03fed176e9207928a935098f9 100644 (file)
@@ -1864,7 +1864,6 @@ int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p
 
 int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l  HASHMAP_DEBUG_PARAMS) {
         int n = 0, r;
-        char **i;
 
         assert(s);
 
index 783ca1309dede27c8f2245cc34c542f50ceeb1a4..a591a75c3741ad70afc5050bad593271059eb90a 100644 (file)
@@ -159,7 +159,6 @@ int pipe_eof(int fd) {
 }
 
 int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
-        struct timespec ts;
         int r;
 
         assert(fds || nfds == 0);
@@ -167,7 +166,7 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
         if (nfds == 0)
                 return 0;
 
-        r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL);
+        r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL);
         if (r < 0)
                 return -errno;
         if (r == 0)
index f827e721ebc2b44910c474085604bbdae9e10509..58e83a6cb2bfab17259e15428422528f4be662e6 100644 (file)
 #define LIST_JUST_US(name,item)                                         \
         (!(item)->name##_prev && !(item)->name##_next)
 
+/* The type of the iterator 'i' is automatically determined by the type of 'head', and declared in the
+ * loop. Hence, do not declare the same variable in the outer scope. Sometimes, we set 'head' through
+ * hashmap_get(). In that case, you need to explicitly cast the result. */
+#define LIST_FOREACH_WITH_NEXT(name,i,n,head)                           \
+        for (typeof(*(head)) *n, *i = (head); i && (n = i->name##_next, true); i = n)
+
 #define LIST_FOREACH(name,i,head)                                       \
-        for ((i) = (head); (i); (i) = (i)->name##_next)
+        LIST_FOREACH_WITH_NEXT(name, i, UNIQ_T(n, UNIQ), head)
 
-#define LIST_FOREACH_SAFE(name,i,n,head)                                \
-        for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
+#define _LIST_FOREACH_WITH_PREV(name,i,p,start)                         \
+        for (typeof(*(start)) *p, *i = (start); i && (p = i->name##_prev, true); i = p)
 
-#define LIST_FOREACH_BACKWARDS(name,i,p)                                \
-        for ((i) = (p); (i); (i) = (i)->name##_prev)
+#define LIST_FOREACH_BACKWARDS(name,i,start)                            \
+        _LIST_FOREACH_WITH_PREV(name, i, UNIQ_T(p, UNIQ), start)
 
 /* Iterate through all the members of the list p is included in, but skip over p */
 #define LIST_FOREACH_OTHERS(name,i,p)                                   \
-        for (({                                                         \
-                (i) = (p);                                              \
-                while ((i) && (i)->name##_prev)                         \
-                        (i) = (i)->name##_prev;                         \
-                if ((i) == (p))                                         \
-                        (i) = (p)->name##_next;                         \
-             });                                                        \
-             (i);                                                       \
-             (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next)
-
-/* Loop starting from p->next until p->prev.
-   p can be adjusted meanwhile. */
+        for (typeof(*(p)) *_p = (p), *i = ({                            \
+                                typeof(*_p) *_j = _p;                   \
+                                while (_j && _j->name##_prev)           \
+                                        _j = _j->name##_prev;           \
+                                if (_j == _p)                           \
+                                        _j = _p->name##_next;           \
+                                _j;                                     \
+                        });                                             \
+             i;                                                         \
+             i = i->name##_next == _p ? _p->name##_next : i->name##_next)
+
+/* Loop starting from p->next until p->prev. p can be adjusted meanwhile. */
 #define LIST_LOOP_BUT_ONE(name,i,head,p)                                \
-        for ((i) = (p)->name##_next ? (p)->name##_next : (head);        \
-             (i) != (p);                                                \
-             (i) = (i)->name##_next ? (i)->name##_next : (head))
+        for (typeof(*(p)) *i = (p)->name##_next ? (p)->name##_next : (head); \
+             i != (p);                                                  \
+             i = i->name##_next ? i->name##_next : (head))
 
 #define LIST_IS_EMPTY(head)                                             \
         (!(head))
index 0a76f0456165b46a48aaeabda7d4c2396c19cf95..b4c2588395db997a948f308f05ee7697464b9d61 100644 (file)
@@ -58,7 +58,6 @@ int _ordered_set_put_strdup(OrderedSet **s, const char *p  HASHMAP_DEBUG_PARAMS)
 
 int _ordered_set_put_strdupv(OrderedSet **s, char **l  HASHMAP_DEBUG_PARAMS) {
         int n = 0, r;
-        char **i;
 
         STRV_FOREACH(i, l) {
                 r = _ordered_set_put_strdup(s, *i  HASHMAP_DEBUG_PASS_ARGS);
index 75c8500e516fb82a51599975ca915aa1ce241c29..ee3a2114993c505c8db7528160423f3062467fdc 100644 (file)
@@ -273,7 +273,6 @@ int load_os_release_pairs(const char *root, char ***ret) {
 
 int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret) {
         _cleanup_strv_free_ char **os_release_pairs = NULL, **os_release_pairs_prefixed = NULL;
-        char **p, **q;
         int r;
 
         r = load_os_release_pairs(root, &os_release_pairs);
index 921a30cef78be2fc961e9458773b148f54f87a3a..2dd587fd8a223288d4ff81effda03085adbbc722 100644 (file)
@@ -463,7 +463,6 @@ static int patch_root_prefix(char **p, const char *root_dir) {
 }
 
 static int patch_root_prefix_strv(char **l, const char *root_dir) {
-        char **i;
         int r;
 
         if (!root_dir)
index 42fae3d992af71fa87023374780ef630220f1a4b..7e81f06650ae9f07f1c0e152c4d609647cb11307 100644 (file)
@@ -202,9 +202,9 @@ int path_make_relative(const char *from, const char *to, char **ret) {
 }
 
 char* path_startswith_strv(const char *p, char **set) {
-        char **s, *t;
-
         STRV_FOREACH(s, set) {
+                char *t;
+
                 t = path_startswith(p, *s);
                 if (t)
                         return t;
@@ -214,7 +214,6 @@ char* path_startswith_strv(const char *p, char **set) {
 }
 
 int path_strv_make_absolute_cwd(char **l) {
-        char **s;
         int r;
 
         /* Goes through every item in the string list and makes it
@@ -236,7 +235,6 @@ int path_strv_make_absolute_cwd(char **l) {
 }
 
 char **path_strv_resolve(char **l, const char *root) {
-        char **s;
         unsigned k = 0;
         bool enomem = false;
         int r;
@@ -700,12 +698,12 @@ int find_executable_full(const char *name, const char *root, char **exec_search_
                 p = DEFAULT_PATH;
 
         if (exec_search_path) {
-                char **element;
-
                 STRV_FOREACH(element, exec_search_path) {
                         _cleanup_free_ char *full_path = NULL;
+
                         if (!path_is_absolute(*element))
                                 continue;
+
                         full_path = path_join(*element, name);
                         if (!full_path)
                                 return -ENOMEM;
@@ -754,7 +752,6 @@ int find_executable_full(const char *name, const char *root, char **exec_search_
 
 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
         bool changed = false, originally_unset;
-        const char* const* i;
 
         assert(timestamp);
 
@@ -1339,7 +1336,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
 
                 _cleanup_strv_free_ char **names = NULL;
                 _cleanup_free_ char *path = NULL;
-                char *c, **name;
+                char *c;
 
                 path = path_join(root, pattern);
                 if (!path)
@@ -1411,8 +1408,6 @@ bool empty_or_root(const char *path) {
 }
 
 bool path_strv_contains(char **l, const char *path) {
-        char **i;
-
         STRV_FOREACH(i, l)
                 if (path_equal(*i, path))
                         return true;
@@ -1421,10 +1416,9 @@ bool path_strv_contains(char **l, const char *path) {
 }
 
 bool prefixed_path_strv_contains(char **l, const char *path) {
-        char **i, *j;
-
         STRV_FOREACH(i, l) {
-                j = *i;
+                const char *j = *i;
+
                 if (*j == '-')
                         j++;
                 if (*j == '+')
index 369edce816b2dc341c7580cfac7f255ea44cbfb2..72807d039c348825dc54a2f387c01ec3ecfbec97 100644 (file)
@@ -215,7 +215,6 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
                 assert(!(flags & PROCESS_CMDLINE_USE_LOCALE));
 
                 _cleanup_strv_free_ char **args = NULL;
-                char **p;
 
                 args = strv_parse_nulstr(t, k);
                 if (!args)
@@ -820,13 +819,12 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
         for (;;) {
                 usec_t n;
                 siginfo_t status = {};
-                struct timespec ts;
 
                 n = now(CLOCK_MONOTONIC);
                 if (n >= until)
                         break;
 
-                r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)));
+                r = RET_NERRNO(sigtimedwait(&mask, NULL, TIMESPEC_STORE(until - n)));
                 /* Assuming we woke due to the child exiting. */
                 if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
                         if (status.si_pid == pid) {
index df23dfe14a009088e65b4d828258163a932596fe..ccee32792f331118d3371c93cba820d9573353fc 100644 (file)
@@ -28,7 +28,7 @@ static inline uint32_t random_u32(void) {
 }
 
 /* Some limits on the pool sizes when we deal with the kernel random pool */
-#define RANDOM_POOL_SIZE_MIN 512U
+#define RANDOM_POOL_SIZE_MIN 32U
 #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U)
 
 size_t random_pool_size(void);
index cf573a378383590ba6d864e5b9ade265e3534757..c4e3dad4461b7e906b1c1278d24b16ca58dcc2fb 100644 (file)
@@ -17,8 +17,6 @@
 #include "strv.h"
 
 char* strv_find(char * const *l, const char *name) {
-        char * const *i;
-
         assert(name);
 
         STRV_FOREACH(i, l)
@@ -29,8 +27,6 @@ char* strv_find(char * const *l, const char *name) {
 }
 
 char* strv_find_case(char * const *l, const char *name) {
-        char * const *i;
-
         assert(name);
 
         STRV_FOREACH(i, l)
@@ -41,8 +37,6 @@ char* strv_find_case(char * const *l, const char *name) {
 }
 
 char* strv_find_prefix(char * const *l, const char *name) {
-        char * const *i;
-
         assert(name);
 
         STRV_FOREACH(i, l)
@@ -53,14 +47,14 @@ char* strv_find_prefix(char * const *l, const char *name) {
 }
 
 char* strv_find_startswith(char * const *l, const char *name) {
-        char * const *i, *e;
-
         assert(name);
 
         /* Like strv_find_prefix, but actually returns only the
          * suffix, not the whole item */
 
         STRV_FOREACH(i, l) {
+                char *e;
+
                 e = startswith(*i, name);
                 if (e)
                         return e;
@@ -70,18 +64,13 @@ char* strv_find_startswith(char * const *l, const char *name) {
 }
 
 char** strv_free(char **l) {
-        if (!l)
-                return NULL;
-
-        for (char **k = l; *k; k++)
+        STRV_FOREACH(k, l)
                 free(*k);
 
         return mfree(l);
 }
 
 char** strv_free_erase(char **l) {
-        char **i;
-
         STRV_FOREACH(i, l)
                 erase_and_freep(i);
 
@@ -89,32 +78,29 @@ char** strv_free_erase(char **l) {
 }
 
 char** strv_copy(char * const *l) {
-        char **r, **k;
+        _cleanup_strv_free_ char **result = NULL;
+        char **k;
 
-        k = r = new(char*, strv_length(l) + 1);
-        if (!r)
+        result = new(char*, strv_length(l) + 1);
+        if (!result)
                 return NULL;
 
-        if (l)
-                for (; *l; k++, l++) {
-                        *k = strdup(*l);
-                        if (!*k) {
-                                strv_free(r);
-                                return NULL;
-                        }
-                }
+        k = result;
+        STRV_FOREACH(i, l) {
+                *k = strdup(*i);
+                if (!*k)
+                        return NULL;
+                k++;
+        }
 
         *k = NULL;
-        return r;
+        return TAKE_PTR(result);
 }
 
 size_t strv_length(char * const *l) {
         size_t n = 0;
 
-        if (!l)
-                return 0;
-
-        for (; *l; l++)
+        STRV_FOREACH(i, l)
                 n++;
 
         return n;
@@ -171,8 +157,8 @@ char** strv_new_internal(const char *x, ...) {
 }
 
 int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
-        char * const *s, **t;
         size_t p, q, i = 0;
+        char **t;
 
         assert(a);
 
@@ -217,7 +203,6 @@ rollback:
 }
 
 int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) {
-        char * const *s;
         int r;
 
         STRV_FOREACH(s, b) {
@@ -367,7 +352,6 @@ int strv_split_colon_pairs(char ***t, const char *s) {
 }
 
 char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
-        char * const *s;
         char *r, *e;
         size_t n, k, m;
 
@@ -595,8 +579,6 @@ int strv_extend_front(char ***l, const char *value) {
 }
 
 char** strv_uniq(char **l) {
-        char **i;
-
         /* Drops duplicate entries. The first identical string will be
          * kept, the others dropped */
 
@@ -607,8 +589,6 @@ char** strv_uniq(char **l) {
 }
 
 bool strv_is_uniq(char * const *l) {
-        char * const *i;
-
         STRV_FOREACH(i, l)
                 if (strv_contains(i+1, *i))
                         return false;
@@ -717,7 +697,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
          */
 
         _cleanup_free_ char *m = NULL;
-        char * const *i;
         size_t n = 0;
 
         assert(ret);
@@ -754,8 +733,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
 }
 
 bool strv_overlap(char * const *a, char * const *b) {
-        char * const *i;
-
         STRV_FOREACH(i, a)
                 if (strv_contains(b, *i))
                         return true;
@@ -795,8 +772,6 @@ int strv_compare(char * const *a, char * const *b) {
 }
 
 void strv_print(char * const *l) {
-        char * const *s;
-
         STRV_FOREACH(s, l)
                 puts(*s);
 }
@@ -830,8 +805,6 @@ char** strv_reverse(char **l) {
 }
 
 char** strv_shell_escape(char **l, const char *bad) {
-        char **s;
-
         /* Escapes every character in every string in l that is in bad,
          * edits in-place, does not roll-back on error. */
 
@@ -914,7 +887,6 @@ rollback:
 
 int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
         bool b = false;
-        char * const *s;
         int r;
 
         /* Like fputs(), but for strv, and with a less stupid argument order */
index 092d40c84b085679b3d7ac6b147d39bfba46cc5a..bc76a2861cb322cc33bbfe5d42cabdeb21d2e3cd 100644 (file)
@@ -122,19 +122,30 @@ static inline int strv_from_nulstr(char ***a, const char *nulstr) {
 
 bool strv_overlap(char * const *a, char * const *b) _pure_;
 
+#define _STRV_FOREACH(s, l, i)                                          \
+        for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++)
+
 #define STRV_FOREACH(s, l)                      \
-        for ((s) = (l); (s) && *(s); (s)++)
+        _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ))
+
+#define _STRV_FOREACH_BACKWARDS(s, l, h, i)                             \
+        for (typeof(*(l)) *s, *h = (l), *i = ({                         \
+                                size_t _len = strv_length(h);           \
+                                _len > 0 ? h + _len - 1 : NULL;         \
+                        });                                             \
+             (s = i);                                                   \
+             i > h ? i-- : (i = NULL))
+
+#define STRV_FOREACH_BACKWARDS(s, l)                                    \
+        _STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ))
 
-#define STRV_FOREACH_BACKWARDS(s, l)                                \
-        for (s = ({                                                 \
-                        typeof(l) _l = l;                           \
-                        _l ? _l + strv_length(_l) - 1U : NULL;      \
-                        });                                         \
-             (l) && ((s) >= (l));                                   \
-             (s)--)
+#define _STRV_FOREACH_PAIR(x, y, l, i)                          \
+        for (typeof(*l) *x, *y, *i = (l);                       \
+             i && *(x = i) && *(y = i + 1);                     \
+             i += 2)
 
-#define STRV_FOREACH_PAIR(x, y, l)               \
-        for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
+#define STRV_FOREACH_PAIR(x, y, l)                      \
+        _STRV_FOREACH_PAIR(x, y, l, UNIQ_T(i, UNIQ))
 
 char** strv_sort(char **l);
 void strv_print(char * const *l);
@@ -185,7 +196,7 @@ void strv_print(char * const *l);
 #define STARTSWITH_SET(p, ...)                                  \
         ({                                                      \
                 const char *_p = (p);                           \
-                char  *_found = NULL, **_i;                     \
+                char *_found = NULL;                            \
                 STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) {      \
                         _found = startswith(_p, *_i);           \
                         if (_found)                             \
@@ -197,7 +208,7 @@ void strv_print(char * const *l);
 #define ENDSWITH_SET(p, ...)                                    \
         ({                                                      \
                 const char *_p = (p);                           \
-                char  *_found = NULL, **_i;                     \
+                char *_found = NULL;                            \
                 STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) {      \
                         _found = endswith(_p, *_i);             \
                         if (_found)                             \
index 01a72026e395cdf60d950c700ed4d4d5e635207c..d3a88ed6e4bf274a1323fefcf694a52b52d7167f 100644 (file)
@@ -115,9 +115,13 @@ nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
 struct timespec* timespec_store(struct timespec *ts, usec_t u);
 struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n);
 
+#define TIMESPEC_STORE(u) timespec_store(&(struct timespec) {}, (u))
+
 usec_t timeval_load(const struct timeval *tv) _pure_;
 struct timeval* timeval_store(struct timeval *tv, usec_t u);
 
+#define TIMEVAL_STORE(u) timeval_store(&(struct timeval) {}, (u))
+
 char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_;
 char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_;
 char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_;
index cf3bbad1c427d87d857a602c824c418b05c4fdb2..e0a338c163a726b595ecd17c934c81f39aab4be7 100644 (file)
@@ -275,6 +275,28 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
         return fd;
 }
 
+int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file) {
+        _cleanup_free_ char *path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_close_ int fd = -1;
+
+        assert(target);
+        assert(ret_file);
+        assert(ret_path);
+
+        fd = open_tmpfile_linkable(target, flags, &path);
+        if (fd < 0)
+                return fd;
+
+        f = take_fdopen(&fd, "w");
+        if (!f)
+                return -ENOMEM;
+
+        *ret_path = TAKE_PTR(path);
+        *ret_file = TAKE_PTR(f);
+        return 0;
+}
+
 int link_tmpfile(int fd, const char *path, const char *target) {
         assert(fd >= 0);
         assert(target);
@@ -292,6 +314,23 @@ int link_tmpfile(int fd, const char *path, const char *target) {
         return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW));
 }
 
+int flink_tmpfile(FILE *f, const char *path, const char *target) {
+        int fd, r;
+
+        assert(f);
+        assert(target);
+
+        fd = fileno(f);
+        if (fd < 0) /* Not all FILE* objects encapsulate fds */
+                return -EBADF;
+
+        r = fflush_sync_and_check(f);
+        if (r < 0)
+                return r;
+
+        return link_tmpfile(fd, path, target);
+}
+
 int mkdtemp_malloc(const char *template, char **ret) {
         _cleanup_free_ char *p = NULL;
         int r;
index 45255fc062fc954f128c9fd2bb46793ed20a6699..610cbaf87e76eb4bc55c5f113ea8b8f6edcb7431 100644 (file)
@@ -13,7 +13,9 @@ int tempfn_random_child(const char *p, const char *extra, char **ret);
 
 int open_tmpfile_unlinkable(const char *directory, int flags);
 int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file);
 
 int link_tmpfile(int fd, const char *path, const char *target);
+int flink_tmpfile(FILE *f, const char *path, const char *target);
 
 int mkdtemp_malloc(const char *template, char **ret);
index faea92f66dd44b705d518faedb4d9cd01f8ed07b..c2512dc9abe2d78337ec8a408d3e944a5281fb05 100644 (file)
@@ -209,8 +209,7 @@ bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_
 
         siphash24_init(&state, HASH_KEY.bytes);
 
-        char **dir;
-        STRV_FOREACH(dir, (char**) lp->search_path) {
+        STRV_FOREACH(dir, lp->search_path) {
                 struct stat st;
 
                 if (lookup_paths_mtime_exclude(lp, *dir))
@@ -281,7 +280,6 @@ int unit_file_build_name_map(
         _cleanup_hashmap_free_ Hashmap *ids = NULL, *names = NULL;
         _cleanup_set_free_free_ Set *paths = NULL;
         uint64_t timestamp_hash;
-        char **dir;
         int r;
 
         /* Before doing anything, check if the timestamp hash that was passed is still valid.
@@ -299,7 +297,7 @@ int unit_file_build_name_map(
                         return log_oom();
         }
 
-        STRV_FOREACH(dir, (char**) lp->search_path) {
+        STRV_FOREACH(dir, lp->search_path) {
                 _cleanup_closedir_ DIR *d = NULL;
 
                 d = opendir(*dir);
@@ -550,7 +548,7 @@ static int add_names(
                 Set **names,
                 const char *name) {
 
-        char **aliases, **alias;
+        char **aliases;
         int r;
 
         assert(name_type == UNIT_NAME_PLAIN || instance);
index 18231c2618d5efb0c7fa6012de41e7e8cefe98d9..817ee387ffd5894ed61a1b7511e917e8aa45391b 100644 (file)
@@ -213,7 +213,6 @@ static int run(int argc, char *argv[]) {
                 }
         else {
                 _cleanup_strv_free_ char **files = NULL;
-                char **f;
 
                 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("binfmt.d"));
                 if (r < 0)
index 2cbabc6c59205cabdbb0960bdfa4f06980b4da7d..259ae33bb5626218914b8b98038e78b649926241 100644 (file)
@@ -8,6 +8,7 @@
 #include "efi-loader.h"
 #include "efivars.h"
 #include "fd-util.h"
+#include "find-esp.h"
 #include "fs-util.h"
 #include "log.h"
 #include "main-func.h"
@@ -324,7 +325,6 @@ static const char *skip_slash(const char *path) {
 static int verb_status(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
         uint64_t left, done;
-        char **p;
         int r;
 
         r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix);
@@ -401,7 +401,6 @@ static int verb_set(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL, *parent = NULL;
         const char *target, *source1, *source2;
         uint64_t done;
-        char **p;
         int r;
 
         r = acquire_boot_count_path(&path, &prefix, NULL, &done, &suffix);
index 557b66b1e5d6c1b01d8a5b7c13dfaad4402276f6..8103a1cf097f7115162d5df169a2ab09830bd1e0 100644 (file)
@@ -24,6 +24,7 @@
 #include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "find-esp.h"
 #include "fs-util.h"
 #include "glyph-util.h"
 #include "main-func.h"
@@ -143,38 +144,70 @@ static int acquire_xbootldr(
         return 1;
 }
 
-static int load_install_machine_id_and_layout(void) {
-        /* Figure out the right machine-id for operations. If KERNEL_INSTALL_MACHINE_ID is configured in
-         * /etc/machine-info, let's use that. Otherwise, just use the real machine-id.
-         *
-         * Also load KERNEL_INSTALL_LAYOUT.
-         */
+static int load_etc_machine_id(void) {
+        int r;
+
+        r = sd_id128_get_machine(&arg_machine_id);
+        if (IN_SET(r, -ENOENT, -ENOMEDIUM)) /* Not set or empty */
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to get machine-id: %m");
+
+        log_debug("Loaded machine ID %s from /etc/machine-id.", SD_ID128_TO_STRING(arg_machine_id));
+        return 0;
+}
+
+static int load_etc_machine_info(void) {
+        /* systemd v250 added support to store the kernel-install layout setting and the machine ID to use
+         * for setting up the ESP in /etc/machine-info. The newer /etc/kernel/entry-token file, as well as
+         * the $layout field in /etc/kernel/install.conf are better replacements for this though, hence this
+         * has been deprecated and is only returned for compatibility. */
         _cleanup_free_ char *s = NULL, *layout = NULL;
         int r;
 
         r = parse_env_file(NULL, "/etc/machine-info",
                            "KERNEL_INSTALL_LAYOUT", &layout,
                            "KERNEL_INSTALL_MACHINE_ID", &s);
-        if (r < 0 && r != -ENOENT)
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
                 return log_error_errno(r, "Failed to parse /etc/machine-info: %m");
 
-        if (isempty(s)) {
-                r = sd_id128_get_machine(&arg_machine_id);
-                if (r < 0 && !IN_SET(r, -ENOENT, -ENOMEDIUM))
-                        return log_error_errno(r, "Failed to get machine-id: %m");
-        } else {
+        if (!isempty(s)) {
+                log_notice("Read $KERNEL_INSTALL_MACHINE_ID from /etc/machine-info. Please move it to /etc/kernel/entry-token.");
+
                 r = sd_id128_from_string(s, &arg_machine_id);
                 if (r < 0)
                         return log_error_errno(r, "Failed to parse KERNEL_INSTALL_MACHINE_ID=%s in /etc/machine-info: %m", s);
 
+                log_debug("Loaded KERNEL_INSTALL_MACHINE_ID=%s from KERNEL_INSTALL_MACHINE_ID in /etc/machine-info.",
+                          SD_ID128_TO_STRING(arg_machine_id));
         }
-        log_debug("Using KERNEL_INSTALL_MACHINE_ID=%s from %s.",
-                  SD_ID128_TO_STRING(arg_machine_id),
-                  isempty(s) ? "/etc/machine_id" : "KERNEL_INSTALL_MACHINE_ID in /etc/machine-info");
 
         if (!isempty(layout)) {
+                log_notice("Read $KERNEL_INSTALL_LAYOUT from /etc/machine-info. Please move it to the layout= setting of /etc/kernel/install.conf.");
+
                 log_debug("KERNEL_INSTALL_LAYOUT=%s is specified in /etc/machine-info.", layout);
-                arg_install_layout = TAKE_PTR(layout);
+                free_and_replace(arg_install_layout, layout);
+        }
+
+        return 0;
+}
+
+static int load_etc_kernel_install_conf(void) {
+        _cleanup_free_ char *layout = NULL;
+        int r;
+
+        r = parse_env_file(NULL, "/etc/kernel/install.conf",
+                           "layout", &layout);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse /etc/kernel/install.conf: %m");
+
+        if (!isempty(layout)) {
+                log_debug("layout=%s is specified in /etc/machine-info.", layout);
+                free_and_replace(arg_install_layout, layout);
         }
 
         return 0;
@@ -281,7 +314,15 @@ static bool use_boot_loader_spec_type1(void) {
 static int settle_make_entry_directory(void) {
         int r;
 
-        r = load_install_machine_id_and_layout();
+        r = load_etc_machine_id();
+        if (r < 0)
+                return r;
+
+        r = load_etc_machine_info();
+        if (r < 0)
+                return r;
+
+        r = load_etc_kernel_install_conf();
         if (r < 0)
                 return r;
 
@@ -601,7 +642,6 @@ static int boot_entry_show(
         if (e->kernel)
                 boot_entry_file_list("linux", e->root, e->kernel, &status);
 
-        char **s;
         STRV_FOREACH(s, e->initrd)
                 boot_entry_file_list(s == e->initrd ? "initrd" : NULL,
                                      e->root,
@@ -842,7 +882,6 @@ static const char *const dollar_boot_subdirs[] = {
 };
 
 static int create_subdirs(const char *root, const char * const *subdirs) {
-        const char *const *i;
         int r;
 
         STRV_FOREACH(i, subdirs) {
@@ -1247,7 +1286,6 @@ static int remove_loader_variables(void) {
 static int install_loader_config(const char *esp_path) {
         _cleanup_(unlink_and_freep) char *t = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        _cleanup_close_ int fd = -1;
         const char *p;
         int r;
 
@@ -1257,13 +1295,9 @@ static int install_loader_config(const char *esp_path) {
         if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
                 return 0;
 
-        fd = open_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t);
-        if (fd < 0)
-                return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p);
-
-        f = take_fdopen(&fd, "w");
-        if (!f)
-                return log_oom();
+        r = fopen_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t, &f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open \"%s\" for writing: %m", p);
 
         fprintf(f, "#timeout 3\n"
                    "#console-mode keep\n");
@@ -1273,11 +1307,36 @@ static int install_loader_config(const char *esp_path) {
                 fprintf(f, "default %s-*\n", arg_entry_token);
         }
 
-        r = fflush_sync_and_check(f);
+        r = flink_tmpfile(f, t, p);
+        if (r == -EEXIST)
+                return 0; /* Silently skip creation if the file exists now (recheck) */
         if (r < 0)
-                return log_error_errno(r, "Failed to write \"%s\": %m", p);
+                return log_error_errno(r, "Failed to move \"%s\" into place: %m", p);
+
+        t = mfree(t);
+        return 1;
+}
+
+static int install_loader_specification(const char *root) {
+        _cleanup_(unlink_and_freep) char *t = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        p = path_join(root, "/loader/entries.srel");
+        if (!p)
+                return log_oom();
 
-        r = link_tmpfile(fileno(f), t, p);
+        if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
+                return 0;
+
+        r = fopen_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t, &f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open \"%s\" for writing: %m", p);
+
+        fprintf(f, "type1\n");
+
+        r = flink_tmpfile(f, t, p);
         if (r == -EEXIST)
                 return 0; /* Silently skip creation if the file exists now (recheck) */
         if (r < 0)
@@ -1999,6 +2058,10 @@ static int verb_install(int argc, char *argv[], void *userdata) {
                         if (r < 0)
                                 return r;
                 }
+
+                r = install_loader_specification(arg_dollar_boot_path());
+                if (r < 0)
+                        return r;
         }
 
         (void) sync_everything();
@@ -2038,6 +2101,10 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
         if (q < 0 && r >= 0)
                 r = q;
 
+        q = remove_file(arg_esp_path, "/loader/entries.srel");
+        if (q < 0 && r >= 0)
+                r = q;
+
         q = remove_subdirs(arg_esp_path, esp_subdirs);
         if (q < 0 && r >= 0)
                 r = q;
@@ -2051,7 +2118,12 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
                 r = q;
 
         if (arg_xbootldr_path) {
-                /* Remove the latter two also in the XBOOTLDR partition if it exists */
+                /* Remove a subset of these also from the XBOOTLDR partition if it exists */
+
+                q = remove_file(arg_xbootldr_path, "/loader/entries.srel");
+                if (q < 0 && r >= 0)
+                        r = q;
+
                 q = remove_subdirs(arg_xbootldr_path, dollar_boot_subdirs);
                 if (q < 0 && r >= 0)
                         r = q;
index e5399f372829ae36c8be5204bedfd88c8a92d9ae..e7e5a6a367a124f8cf1fbd3b565aac2a6b4de423 100644 (file)
@@ -53,7 +53,7 @@ typedef struct {
         CHAR16 *id;         /* The unique identifier for this entry (typically the filename of the file defining the entry) */
         CHAR16 *title_show; /* The string to actually display (this is made unique before showing) */
         CHAR16 *title;      /* The raw (human readable) title string of the entry (not necessarily unique) */
-        CHAR16 *sort_key;   /* The string to use as primary sory key, usually ID= from os-release, possibly suffixed */
+        CHAR16 *sort_key;   /* The string to use as primary sort key, usually ID= from os-release, possibly suffixed */
         CHAR16 *version;    /* The raw (human readable) version string of the entry */
         CHAR16 *machine_id;
         EFI_HANDLE *device;
@@ -429,6 +429,17 @@ static CHAR16 *update_timeout_efivar(UINT32 *t, BOOLEAN inc) {
         }
 }
 
+static BOOLEAN unicode_supported(void) {
+        static INTN cache = -1;
+
+        if (cache < 0)
+                /* Basic unicode box drawing support is mandated by the spec, but it does
+                 * not hurt to make sure it works. */
+                cache = !EFI_ERROR(ST->ConOut->TestString(ST->ConOut, (CHAR16 *) L"─"));
+
+        return cache;
+}
+
 static void ps_string(const CHAR16 *fmt, const void *value) {
         assert(fmt);
         if (value)
@@ -444,7 +455,11 @@ static BOOLEAN ps_continue(void) {
         UINT64 key;
         EFI_STATUS err;
 
-        Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
+        if (unicode_supported())
+                Print(L"\n─── Press any key to continue, ESC or q to quit. â”€â”€â”€\n\n");
+        else
+                Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
+
         err = console_key_read(&key, UINT64_MAX);
         return !EFI_ERROR(err) && !IN_SET(key, KEYPRESS(0, SCAN_ESC, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
 }
@@ -600,7 +615,7 @@ static BOOLEAN menu_run(
         UINTN x_start = 0, y_start = 0, y_status = 0;
         UINTN x_max, y_max;
         _cleanup_(strv_freep) CHAR16 **lines = NULL;
-        _cleanup_freepool_ CHAR16 *clearline = NULL, *status = NULL;
+        _cleanup_freepool_ CHAR16 *clearline = NULL, *separator = NULL, *status = NULL;
         UINT32 timeout_efivar_saved = config->timeout_sec_efivar;
         UINT32 timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec;
         BOOLEAN exit = FALSE, run = TRUE, firmware_setup = FALSE;
@@ -621,12 +636,11 @@ static BOOLEAN menu_run(
                 log_error_stall(L"Error switching console mode: %r", err);
         }
 
+        UINTN line_width = 0, entry_padding = 3;
         while (!exit) {
                 UINT64 key;
 
                 if (new_mode) {
-                        UINTN line_width = 0, entry_padding = 3;
-
                         console_query_mode(&x_max, &y_max);
 
                         /* account for padding+status */
@@ -645,6 +659,7 @@ static BOOLEAN menu_run(
                         idx_last = idx_first + visible_max - 1;
 
                         /* length of the longest entry */
+                        line_width = 0;
                         for (UINTN i = 0; i < config->entry_count; i++)
                                 line_width = MAX(line_width, StrLen(config->entries[i]->title_show));
                         line_width = MIN(line_width + 2 * entry_padding, x_max);
@@ -657,10 +672,11 @@ static BOOLEAN menu_run(
                                 y_start = 0;
 
                         /* Put status line after the entry list, but give it some breathing room. */
-                        y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 4, y_max - 1);
+                        y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 1, y_max - 1);
 
                         lines = strv_free(lines);
                         clearline = mfree(clearline);
+                        separator = mfree(separator);
 
                         /* menu entries title lines */
                         lines = xnew(CHAR16*, config->entry_count + 1);
@@ -684,9 +700,13 @@ static BOOLEAN menu_run(
                         lines[config->entry_count] = NULL;
 
                         clearline = xnew(CHAR16, x_max + 1);
-                        for (UINTN i = 0; i < x_max; i++)
+                        separator = xnew(CHAR16, x_max + 1);
+                        for (UINTN i = 0; i < x_max; i++) {
                                 clearline[i] = ' ';
+                                separator[i] = unicode_supported() ? L'─' : L'-';
+                        }
                         clearline[x_max] = 0;
+                        separator[x_max] = 0;
 
                         new_mode = FALSE;
                         clear = TRUE;
@@ -706,16 +726,16 @@ static BOOLEAN menu_run(
                                 if (i == config->idx_default_efivar)
                                         print_at(x_start, y_start + i - idx_first,
                                                  (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
-                                                 (CHAR16*) L"=>");
+                                                 (CHAR16*) (unicode_supported() ? L" â–º" : L"=>"));
                         }
                         refresh = FALSE;
                 } else if (highlight) {
                         print_at(x_start, y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, lines[idx_highlight_prev]);
                         print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, lines[idx_highlight]);
                         if (idx_highlight_prev == config->idx_default_efivar)
-                                print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) L"=>");
+                                print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) (unicode_supported() ? L" â–º" : L"=>"));
                         if (idx_highlight == config->idx_default_efivar)
-                                print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) L"=>");
+                                print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) (unicode_supported() ? L" â–º" : L"=>"));
                         highlight = FALSE;
                 }
 
@@ -724,20 +744,24 @@ static BOOLEAN menu_run(
                         status = xpool_print(L"Boot in %u s.", timeout_remain);
                 }
 
-                /* print status at last line of screen */
                 if (status) {
-                        UINTN len;
-                        UINTN x;
-
-                        /* center line */
-                        len = StrLen(status);
-                        if (len < x_max)
-                                x = (x_max - len) / 2;
-                        else
-                                x = 0;
-                        print_at(0, y_status, COLOR_NORMAL, clearline + (x_max - x));
+                        /* If we draw the last char of the last line, the screen will scroll and break our
+                         * input. Therefore, draw one less character then we could for the status message.
+                         * Note that the same does not apply for the separator line as it will never be drawn
+                         * on the last line. */
+                        UINTN len = StrnLen(status, x_max - 1);
+                        UINTN x = (x_max - len) / 2;
+                        status[len] = '\0';
+                        print_at(0, y_status, COLOR_NORMAL, clearline + x_max - x);
                         ST->ConOut->OutputString(ST->ConOut, status);
                         ST->ConOut->OutputString(ST->ConOut, clearline + 1 + x + len);
+
+                        len = MIN(MAX(len, line_width) + 2 * entry_padding, x_max);
+                        x = (x_max - len) / 2;
+                        print_at(x, y_status - 1, COLOR_NORMAL, separator + x_max - len);
+                } else {
+                        print_at(0, y_status - 1, COLOR_NORMAL, clearline);
+                        print_at(0, y_status, COLOR_NORMAL, clearline + 1); /* See comment above. */
                 }
 
                 /* Beep several times so that the selected entry can be distinguished. */
@@ -768,11 +792,7 @@ static BOOLEAN menu_run(
                 timeout_remain = 0;
 
                 /* clear status after keystroke */
-                if (status) {
-                        FreePool(status);
-                        status = NULL;
-                        print_at(0, y_status, COLOR_NORMAL, clearline + 1);
-                }
+                status = mfree(status);
 
                 idx_highlight_prev = idx_highlight;
 
index b532de4f9b0de842c706fdf7e93caede3ac01f4e..0f97015bd4b17fbc0aaaa7ff582381ee98949009 100644 (file)
@@ -143,7 +143,7 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_hashmap_free_ Hashmap *names = NULL;
         _cleanup_(table_unrefp) Table *table = NULL;
-        char **i, *k;
+        char *k;
         void *v;
         int r;
 
@@ -504,7 +504,6 @@ static int tree_one(sd_bus *bus, const char *service) {
 
 static int tree(int argc, char **argv, void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
-        char **i;
         int r;
 
         /* Do superficial verification of arguments before even opening the bus */
@@ -1208,7 +1207,6 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        char **i;
         uint32_t flags = 0;
         const char *unique_name;
         bool is_monitor = false;
@@ -2124,7 +2122,6 @@ static int emit_signal(int argc, char **argv, void *userdata) {
 static int get_property(int argc, char **argv, void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        char **i;
         int r;
 
         r = acquire_bus(false, &bus);
index 936ea4d3afdd557f77ec93943c5ffa031e94cdba..6af95204e3ddd5f58dcb0dd04bb51c8e51c89fbc 100644 (file)
@@ -201,7 +201,6 @@ static int run(int argc, char *argv[]) {
         if (arg_names) {
                 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
                 _cleanup_free_ char *root = NULL;
-                char **name;
 
                 STRV_FOREACH(name, arg_names) {
                         int q;
index 0297053add595efec4559afc1815407dbdf8812a..edcfed1eb47ab45234213c56365bb09ff7a15e80 100644 (file)
@@ -605,8 +605,6 @@ int bpf_firewall_compile(Unit *u) {
 }
 
 static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set) {
-        char **bpf_fs_path;
-
         set_clear(*set);
 
         STRV_FOREACH(bpf_fs_path, filter_paths) {
index 8538792b60db7994816483f84864477297af20a0..7f50f573891a0a179f389966a9a7670d90cc1788 100644 (file)
@@ -123,7 +123,6 @@ static int bpf_foreign_prepare(
 
 int bpf_foreign_install(Unit *u) {
         _cleanup_free_ char *cgroup_path = NULL;
-        CGroupBPFForeignProgram *p;
         CGroupContext *cc;
         int r;
 
index 806df84ea725f0f2746f85141b16752fbfcc6006..09f83dc667f386d9f76bbc2f704d5a3a9ab22846 100644 (file)
@@ -27,7 +27,6 @@ static int update_rules_map(
                 int map_fd,
                 CGroupSocketBindItem *head) {
 
-        CGroupSocketBindItem *item;
         uint32_t i = 0;
 
         assert(map_fd >= 0);
@@ -58,7 +57,6 @@ static int prepare_socket_bind_bpf(
         _cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL;
         size_t allow_count = 0, deny_count = 0;
         int allow_map_fd, deny_map_fd, r;
-        CGroupSocketBindItem *item;
 
         assert(ret_obj);
 
index 98bf5e8db72aa2202198d24fdc595f5a2faf0448..15ab363548ac900a018ae5f1c386c1d69c1890a8 100644 (file)
@@ -414,17 +414,8 @@ static char *format_cgroup_memory_limit_comparison(char *buf, size_t l, Unit *u,
 
 void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
         _cleanup_free_ char *disable_controllers_str = NULL, *cpuset_cpus = NULL, *cpuset_mems = NULL, *startup_cpuset_cpus = NULL, *startup_cpuset_mems = NULL;
-        CGroupIODeviceLimit *il;
-        CGroupIODeviceWeight *iw;
-        CGroupIODeviceLatency *l;
-        CGroupBlockIODeviceBandwidth *b;
-        CGroupBlockIODeviceWeight *w;
-        CGroupBPFForeignProgram *p;
-        CGroupDeviceAllow *a;
         CGroupContext *c;
-        CGroupSocketBindItem *bi;
         struct in_addr_prefix *iaai;
-        char **path;
 
         char cda[FORMAT_CGROUP_DIFF_MAX];
         char cdb[FORMAT_CGROUP_DIFF_MAX];
@@ -1219,7 +1210,6 @@ static int cgroup_apply_devices(Unit *u) {
         _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
         const char *path;
         CGroupContext *c;
-        CGroupDeviceAllow *a;
         CGroupDevicePolicy policy;
         int r;
 
@@ -1440,10 +1430,6 @@ static void cgroup_context_apply(
                 set_io_weight(u, weight);
 
                 if (has_io) {
-                        CGroupIODeviceLatency *latency;
-                        CGroupIODeviceLimit *limit;
-                        CGroupIODeviceWeight *w;
-
                         LIST_FOREACH(device_weights, w, c->io_device_weights)
                                 cgroup_apply_io_device_weight(u, w->path, w->weight);
 
@@ -1454,9 +1440,6 @@ static void cgroup_context_apply(
                                 cgroup_apply_io_device_latency(u, latency->path, latency->target_usec);
 
                 } else if (has_blockio) {
-                        CGroupBlockIODeviceWeight *w;
-                        CGroupBlockIODeviceBandwidth *b;
-
                         LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
                                 weight = cgroup_weight_blkio_to_io(w->weight);
 
@@ -1509,9 +1492,7 @@ static void cgroup_context_apply(
 
                         set_blkio_weight(u, weight);
 
-                        if (has_io) {
-                                CGroupIODeviceWeight *w;
-
+                        if (has_io)
                                 LIST_FOREACH(device_weights, w, c->io_device_weights) {
                                         weight = cgroup_weight_io_to_blkio(w->weight);
 
@@ -1520,32 +1501,24 @@ static void cgroup_context_apply(
 
                                         cgroup_apply_blkio_device_weight(u, w->path, weight);
                                 }
-                        } else if (has_blockio) {
-                                CGroupBlockIODeviceWeight *w;
-
+                        else if (has_blockio)
                                 LIST_FOREACH(device_weights, w, c->blockio_device_weights)
                                         cgroup_apply_blkio_device_weight(u, w->path, w->weight);
-                        }
                 }
 
                 /* The bandwidth limits are something that make sense to be applied to the host's root but not container
                  * roots, as there we want the container manager to handle it */
                 if (is_host_root || !is_local_root) {
-                        if (has_io) {
-                                CGroupIODeviceLimit *l;
-
+                        if (has_io)
                                 LIST_FOREACH(device_limits, l, c->io_device_limits) {
                                         log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth=%" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax= for %s",
                                                           l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path);
 
                                         cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX]);
                                 }
-                        } else if (has_blockio) {
-                                CGroupBlockIODeviceBandwidth *b;
-
+                        else if (has_blockio)
                                 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths)
                                         cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps);
-                        }
                 }
         }
 
@@ -2985,6 +2958,10 @@ static int on_cgroup_empty_event(sd_event_source *s, void *userdata) {
                         log_debug_errno(r, "Failed to reenable cgroup empty event source, ignoring: %m");
         }
 
+        /* Update state based on OOM kills before we notify about cgroup empty event */
+        (void) unit_check_oom(u);
+        (void) unit_check_oomd_kill(u);
+
         unit_add_to_gc_queue(u);
 
         if (UNIT_VTABLE(u)->notify_cgroup_empty)
@@ -3064,7 +3041,7 @@ int unit_check_oomd_kill(Unit *u) {
         else if (r == 0)
                 return 0;
 
-        r = cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "user.oomd_kill", &value);
+        r = cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "user.oomd_ooms", &value);
         if (r < 0 && r != -ENODATA)
                 return r;
 
@@ -3080,11 +3057,25 @@ int unit_check_oomd_kill(Unit *u) {
         if (!increased)
                 return 0;
 
+        n = 0;
+        value = mfree(value);
+        r = cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "user.oomd_kill", &value);
+        if (r >= 0 && !isempty(value))
+                (void) safe_atou64(value, &n);
+
         if (n > 0)
                 log_unit_struct(u, LOG_NOTICE,
                                 "MESSAGE_ID=" SD_MESSAGE_UNIT_OOMD_KILL_STR,
                                 LOG_UNIT_INVOCATION_ID(u),
-                                LOG_UNIT_MESSAGE(u, "systemd-oomd killed %"PRIu64" process(es) in this unit.", n));
+                                LOG_UNIT_MESSAGE(u, "systemd-oomd killed %"PRIu64" process(es) in this unit.", n),
+                                "N_PROCESSES=%" PRIu64, n);
+        else
+                log_unit_struct(u, LOG_NOTICE,
+                                "MESSAGE_ID=" SD_MESSAGE_UNIT_OOMD_KILL_STR,
+                                LOG_UNIT_INVOCATION_ID(u),
+                                LOG_UNIT_MESSAGE(u, "systemd-oomd killed some process(es) in this unit."));
+
+        unit_notify_cgroup_oom(u, /* ManagedOOM= */ true);
 
         return 1;
 }
@@ -3120,8 +3111,7 @@ int unit_check_oom(Unit *u) {
                         LOG_UNIT_INVOCATION_ID(u),
                         LOG_UNIT_MESSAGE(u, "A process of this unit has been killed by the OOM killer."));
 
-        if (UNIT_VTABLE(u)->notify_cgroup_oom)
-                UNIT_VTABLE(u)->notify_cgroup_oom(u);
+        unit_notify_cgroup_oom(u, /* ManagedOOM= */ false);
 
         return 1;
 }
index a75f9fb66fc8133976e039b693bb7dc77fdba245..40cfd0cc7a260d9077e0909e49445e7bc2a27df2 100644 (file)
@@ -157,9 +157,7 @@ static int build_managed_oom_cgroups_json(Manager *m, JsonVariant **ret) {
         if (r < 0)
                 return r;
 
-        for (size_t i = 0; i < ELEMENTSOF(supported_unit_types); i++) {
-                Unit *u;
-
+        for (size_t i = 0; i < ELEMENTSOF(supported_unit_types); i++)
                 LIST_FOREACH(units_by_type, u, m->units_by_type[supported_unit_types[i]]) {
                         CGroupContext *c;
 
@@ -188,7 +186,6 @@ static int build_managed_oom_cgroups_json(Manager *m, JsonVariant **ret) {
                                         return r;
                         }
                 }
-        }
 
         r = json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("cgroups", JSON_BUILD_VARIANT(arr))));
         if (r < 0)
index f0d8759e8517fcca53607fc805b82f595d31db61..9a31355a4da65e5e6adb880438a1fae90798779c 100644 (file)
@@ -112,7 +112,6 @@ static int property_get_io_device_weight(
                 sd_bus_error *error) {
 
         CGroupContext *c = userdata;
-        CGroupIODeviceWeight *w;
         int r;
 
         assert(bus);
@@ -142,7 +141,6 @@ static int property_get_io_device_limits(
                 sd_bus_error *error) {
 
         CGroupContext *c = userdata;
-        CGroupIODeviceLimit *l;
         int r;
 
         assert(bus);
@@ -178,7 +176,6 @@ static int property_get_io_device_latency(
                 sd_bus_error *error) {
 
         CGroupContext *c = userdata;
-        CGroupIODeviceLatency *l;
         int r;
 
         assert(bus);
@@ -208,7 +205,6 @@ static int property_get_blockio_device_weight(
                 sd_bus_error *error) {
 
         CGroupContext *c = userdata;
-        CGroupBlockIODeviceWeight *w;
         int r;
 
         assert(bus);
@@ -238,7 +234,6 @@ static int property_get_blockio_device_bandwidths(
                 sd_bus_error *error) {
 
         CGroupContext *c = userdata;
-        CGroupBlockIODeviceBandwidth *b;
         int r;
 
         assert(bus);
@@ -278,7 +273,6 @@ static int property_get_device_allow(
                 sd_bus_error *error) {
 
         CGroupContext *c = userdata;
-        CGroupDeviceAllow *a;
         int r;
 
         assert(bus);
@@ -364,7 +358,6 @@ static int property_get_bpf_foreign_program(
                 void *userdata,
                 sd_bus_error *error) {
         CGroupContext *c = userdata;
-        CGroupBPFForeignProgram *p;
         int r;
 
         r = sd_bus_message_open_container(reply, 'a', "(ss)");
@@ -390,7 +383,8 @@ static int property_get_socket_bind(
                 sd_bus_message *reply,
                 void *userdata,
                 sd_bus_error *error) {
-        CGroupSocketBindItem **items = userdata, *i;
+
+        CGroupSocketBindItem **items = userdata;
         int r;
 
         assert(items);
@@ -640,7 +634,6 @@ static int bus_cgroup_set_transient_property(
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *buf = NULL;
                         _cleanup_fclose_ FILE *f = NULL;
-                        char **entry;
                         size_t size = 0;
 
                         if (n == 0)
@@ -720,7 +713,6 @@ static int bus_cgroup_set_transient_property(
                 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)
@@ -1228,14 +1220,13 @@ 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 (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                                CGroupIODeviceLimit *a = NULL, *b;
+                                CGroupIODeviceLimit *a = NULL;
 
-                                LIST_FOREACH(device_limits, b, c->io_device_limits) {
+                                LIST_FOREACH(device_limits, b, c->io_device_limits)
                                         if (path_equal(path, b->path)) {
                                                 a = b;
                                                 break;
                                         }
-                                }
 
                                 if (!a) {
                                         CGroupIOLimitType type;
@@ -1269,15 +1260,13 @@ int bus_cgroup_set_property(
                         return r;
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                        CGroupIODeviceLimit *a;
                         _cleanup_free_ char *buf = NULL;
                         _cleanup_fclose_ FILE *f = NULL;
                         size_t size = 0;
 
-                        if (n == 0) {
+                        if (n == 0)
                                 LIST_FOREACH(device_limits, a, c->io_device_limits)
                                         a->limits[iol_type] = cgroup_io_limit_defaults[iol_type];
-                        }
 
                         unit_invalidate_cgroup(u, CGROUP_MASK_IO);
 
@@ -1287,8 +1276,8 @@ int bus_cgroup_set_property(
 
                         fprintf(f, "%s=\n", name);
                         LIST_FOREACH(device_limits, a, c->io_device_limits)
-                                        if (a->limits[iol_type] != cgroup_io_limit_defaults[iol_type])
-                                                fprintf(f, "%s=%s %" PRIu64 "\n", name, a->path, a->limits[iol_type]);
+                                if (a->limits[iol_type] != cgroup_io_limit_defaults[iol_type])
+                                        fprintf(f, "%s=%s %" PRIu64 "\n", name, a->path, a->limits[iol_type]);
 
                         r = fflush_and_check(f);
                         if (r < 0)
@@ -1316,14 +1305,13 @@ int bus_cgroup_set_property(
                                 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;
+                                CGroupIODeviceWeight *a = NULL;
 
-                                LIST_FOREACH(device_weights, b, c->io_device_weights) {
+                                LIST_FOREACH(device_weights, b, c->io_device_weights)
                                         if (path_equal(b->path, path)) {
                                                 a = b;
                                                 break;
                                         }
-                                }
 
                                 if (!a) {
                                         a = new0(CGroupIODeviceWeight, 1);
@@ -1351,13 +1339,11 @@ int bus_cgroup_set_property(
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *buf = NULL;
                         _cleanup_fclose_ FILE *f = NULL;
-                        CGroupIODeviceWeight *a;
                         size_t size = 0;
 
-                        if (n == 0) {
+                        if (n == 0)
                                 while (c->io_device_weights)
                                         cgroup_context_free_io_device_weight(c, c->io_device_weights);
-                        }
 
                         unit_invalidate_cgroup(u, CGROUP_MASK_IO);
 
@@ -1392,14 +1378,13 @@ 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 (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                                CGroupIODeviceLatency *a = NULL, *b;
+                                CGroupIODeviceLatency *a = NULL;
 
-                                LIST_FOREACH(device_latencies, b, c->io_device_latencies) {
+                                LIST_FOREACH(device_latencies, b, c->io_device_latencies)
                                         if (path_equal(b->path, path)) {
                                                 a = b;
                                                 break;
                                         }
-                                }
 
                                 if (!a) {
                                         a = new0(CGroupIODeviceLatency, 1);
@@ -1427,13 +1412,11 @@ int bus_cgroup_set_property(
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *buf = NULL;
                         _cleanup_fclose_ FILE *f = NULL;
-                        CGroupIODeviceLatency *a;
                         size_t size = 0;
 
-                        if (n == 0) {
+                        if (n == 0)
                                 while (c->io_device_latencies)
                                         cgroup_context_free_io_device_latency(c, c->io_device_latencies);
-                        }
 
                         unit_invalidate_cgroup(u, CGROUP_MASK_IO);
 
@@ -1473,14 +1456,13 @@ 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 (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                                CGroupBlockIODeviceBandwidth *a = NULL, *b;
+                                CGroupBlockIODeviceBandwidth *a = NULL;
 
-                                LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
+                                LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths)
                                         if (path_equal(path, b->path)) {
                                                 a = b;
                                                 break;
                                         }
-                                }
 
                                 if (!a) {
                                         a = new0(CGroupBlockIODeviceBandwidth, 1);
@@ -1514,19 +1496,17 @@ int bus_cgroup_set_property(
                         return r;
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                        CGroupBlockIODeviceBandwidth *a;
                         _cleanup_free_ char *buf = NULL;
                         _cleanup_fclose_ FILE *f = NULL;
                         size_t size = 0;
 
-                        if (n == 0) {
+                        if (n == 0)
                                 LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) {
                                         if (read)
                                                 a->rbps = CGROUP_LIMIT_MAX;
                                         else
                                                 a->wbps = CGROUP_LIMIT_MAX;
                                 }
-                        }
 
                         unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
 
@@ -1573,14 +1553,13 @@ int bus_cgroup_set_property(
                                 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;
+                                CGroupBlockIODeviceWeight *a = NULL;
 
-                                LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
+                                LIST_FOREACH(device_weights, b, c->blockio_device_weights)
                                         if (path_equal(b->path, path)) {
                                                 a = b;
                                                 break;
                                         }
-                                }
 
                                 if (!a) {
                                         a = new0(CGroupBlockIODeviceWeight, 1);
@@ -1608,13 +1587,11 @@ int bus_cgroup_set_property(
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *buf = NULL;
                         _cleanup_fclose_ FILE *f = NULL;
-                        CGroupBlockIODeviceWeight *a;
                         size_t size = 0;
 
-                        if (n == 0) {
+                        if (n == 0)
                                 while (c->blockio_device_weights)
                                         cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
-                        }
 
                         unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
 
@@ -1674,14 +1651,13 @@ int bus_cgroup_set_property(
                                 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;
+                                CGroupDeviceAllow *a = NULL;
 
-                                LIST_FOREACH(device_allow, b, c->device_allow) {
+                                LIST_FOREACH(device_allow, b, c->device_allow)
                                         if (path_equal(b->path, path)) {
                                                 a = b;
                                                 break;
                                         }
-                                }
 
                                 if (!a) {
                                         a = new0(CGroupDeviceAllow, 1);
@@ -1714,13 +1690,11 @@ int bus_cgroup_set_property(
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *buf = NULL;
                         _cleanup_fclose_ FILE *f = NULL;
-                        CGroupDeviceAllow *a;
                         size_t size = 0;
 
-                        if (n == 0) {
+                        if (n == 0)
                                 while (c->device_allow)
                                         cgroup_context_free_device_allow(c, c->device_allow);
-                        }
 
                         unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES);
 
@@ -1995,7 +1969,6 @@ int bus_cgroup_set_property(
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *buf = NULL;
                         _cleanup_fclose_ FILE *f = NULL;
-                        CGroupSocketBindItem *item;
                         size_t size = 0;
 
                         if (n == 0)
@@ -2050,7 +2023,6 @@ int bus_cgroup_set_property(
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *joined = NULL;
-                        char **s;
 
                         if (strv_isempty(l)) {
                                 c->restrict_network_interfaces_is_allow_list = false;
index 8a8d9c9b2e248ccd0c30e05f5b2017cbb5a50dea..65eec8f6b7e8e0768d232871710274c2d8912cf6 100644 (file)
@@ -74,7 +74,6 @@ static int property_get_environment_files(
                 sd_bus_error *error) {
 
         ExecContext *c = userdata;
-        char **j;
         int r;
 
         assert(bus);
@@ -960,7 +959,6 @@ static int property_get_root_image_options(
                 sd_bus_error *error) {
 
         ExecContext *c = userdata;
-        MountOptions *m;
         int r;
 
         assert(bus);
@@ -1005,8 +1003,6 @@ static int property_get_mount_images(
                 return r;
 
         for (size_t i = 0; i < c->n_mount_images; i++) {
-                MountOptions *m;
-
                 r = sd_bus_message_open_container(reply, SD_BUS_TYPE_STRUCT, "ssba(ss)");
                 if (r < 0)
                         return r;
@@ -1060,8 +1056,6 @@ static int property_get_extension_images(
                 return r;
 
         for (size_t i = 0; i < c->n_extension_images; i++) {
-                MountOptions *m;
-
                 r = sd_bus_message_open_container(reply, SD_BUS_TYPE_STRUCT, "sba(ss)");
                 if (r < 0)
                         return r;
@@ -1143,15 +1137,12 @@ static int bus_property_get_exec_dir_symlink(
         if (r < 0)
                 return r;
 
-        for (size_t i = 0; i < d->n_items; i++) {
-                char **dst;
-
+        for (size_t i = 0; i < d->n_items; i++)
                 STRV_FOREACH(dst, d->items[i].symlinks) {
                         r = sd_bus_message_append(reply, "(sst)", d->items[i].path, *dst, 0 /* flags, unused for now */);
                         if (r < 0)
                                 return r;
                 }
-        }
 
         return sd_bus_message_close_container(reply);
 }
@@ -1449,7 +1440,7 @@ int bus_property_get_exec_command_list(
                 void *userdata,
                 sd_bus_error *ret_error) {
 
-        ExecCommand *c = *(ExecCommand**) userdata;
+        ExecCommand *exec_command = *(ExecCommand**) userdata;
         int r;
 
         assert(bus);
@@ -1459,7 +1450,7 @@ int bus_property_get_exec_command_list(
         if (r < 0)
                 return r;
 
-        LIST_FOREACH(command, c, c) {
+        LIST_FOREACH(command, c, exec_command) {
                 r = append_exec_command(reply, c);
                 if (r < 0)
                         return r;
@@ -1477,7 +1468,7 @@ int bus_property_get_exec_ex_command_list(
                 void *userdata,
                 sd_bus_error *ret_error) {
 
-        ExecCommand *c, *exec_command = *(ExecCommand**) userdata;
+        ExecCommand *exec_command = *(ExecCommand**) userdata;
         int r;
 
         assert(bus);
@@ -1587,7 +1578,6 @@ int bus_set_transient_exec_command(
         if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                 _cleanup_free_ char *buf = NULL;
                 _cleanup_fclose_ FILE *f = NULL;
-                ExecCommand *c;
                 size_t size = 0;
 
                 if (n == 0)
@@ -2022,7 +2012,6 @@ int bus_exec_context_set_transient_property(
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *joined = NULL;
                         FilesystemParseFlags invert_flag = allow_list ? 0 : FILESYSTEM_PARSE_INVERT;
-                        char **s;
 
                         if (strv_isempty(l)) {
                                 c->restrict_filesystems_allow_list = false;
@@ -2068,7 +2057,6 @@ int bus_exec_context_set_transient_property(
 
         if (streq(name, "SupplementaryGroups")) {
                 _cleanup_strv_free_ char **l = NULL;
-                char **p;
 
                 r = sd_bus_message_read_strv(message, &l);
                 if (r < 0)
@@ -2433,7 +2421,6 @@ int bus_exec_context_set_transient_property(
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *joined = NULL;
                         SeccompParseFlags invert_flag = allow_list ? 0 : SECCOMP_PARSE_INVERT;
-                        char **s;
 
                         if (strv_isempty(l)) {
                                 c->syscall_allow_list = false;
@@ -2518,7 +2505,6 @@ int bus_exec_context_set_transient_property(
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *joined = NULL;
                         SeccompParseFlags invert_flag = allow_list ? 0 : SECCOMP_PARSE_INVERT;
-                        char **s;
 
                         if (strv_isempty(l)) {
                                 c->syscall_log_allow_list = false;
@@ -2570,9 +2556,7 @@ int bus_exec_context_set_transient_property(
 
                         if (strv_isempty(l))
                                 c->syscall_archs = set_free(c->syscall_archs);
-                        else {
-                                char **s;
-
+                        else
                                 STRV_FOREACH(s, l) {
                                         uint32_t a;
 
@@ -2585,8 +2569,6 @@ int bus_exec_context_set_transient_property(
                                                 return r;
                                 }
 
-                        }
-
                         joined = strv_join(l, " ");
                         if (!joined)
                                 return -ENOMEM;
@@ -2618,7 +2600,6 @@ int bus_exec_context_set_transient_property(
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *joined = NULL;
-                        char **s;
 
                         if (strv_isempty(l)) {
                                 c->address_families_allow_list = allow_list;
@@ -3147,7 +3128,6 @@ int bus_exec_context_set_transient_property(
                 _cleanup_fclose_ FILE *f = NULL;
                 _cleanup_strv_free_ char **l = NULL;
                 size_t size = 0;
-                char **i;
 
                 r = sd_bus_message_enter_container(message, 'a', "(sb)");
                 if (r < 0)
@@ -3266,7 +3246,6 @@ int bus_exec_context_set_transient_property(
                               "ExtensionDirectories")) {
                 _cleanup_strv_free_ char **l = NULL;
                 char ***dirs;
-                char **p;
 
                 r = sd_bus_message_read_strv(message, &l);
                 if (r < 0)
@@ -3320,16 +3299,15 @@ int bus_exec_context_set_transient_property(
 
         } else if (streq(name, "ExecSearchPath")) {
                 _cleanup_strv_free_ char **l = NULL;
-                char **p;
 
                 r = sd_bus_message_read_strv(message, &l);
                 if (r < 0)
                         return r;
 
-                STRV_FOREACH(p, l) {
+                STRV_FOREACH(p, l)
                         if (!path_is_absolute(*p) || !path_is_normalized(*p) || strchr(*p, ':'))
                                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
-                }
+
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         if (strv_isempty(l)) {
                                 c->exec_search_path = strv_free(c->exec_search_path);
@@ -3350,7 +3328,6 @@ int bus_exec_context_set_transient_property(
 
         } else if (STR_IN_SET(name, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
                 _cleanup_strv_free_ char **l = NULL;
-                char **p;
 
                 r = sd_bus_message_read_strv(message, &l);
                 if (r < 0)
@@ -3379,7 +3356,6 @@ int bus_exec_context_set_transient_property(
                                 unit_write_settingf(u, flags, name, "%s=", name);
                         } else {
                                 _cleanup_free_ char *joined = NULL;
-                                char **source;
 
                                 STRV_FOREACH(source, l) {
                                         r = exec_directory_add(&d->items, &d->n_items, *source, NULL);
index 49982d003015d0a63646e5b7c5894adf0dad54f9..c78e9ef2b2909a3093d0e158e4191a00a7b8b475 100644 (file)
@@ -890,7 +890,6 @@ static int method_list_units_by_names(sd_bus_message *message, void *userdata, s
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         Manager *m = userdata;
         int r;
-        char **unit;
         _cleanup_strv_free_ char **units = NULL;
 
         assert(message);
index f143ad5d4a642606b56379e6547335c264882db0..09b353ba3ff93fb99fea8e752ee695545f7878e3 100644 (file)
@@ -22,7 +22,6 @@ static int property_get_paths(
                 sd_bus_error *error) {
 
         Path *p = userdata;
-        PathSpec *k;
         int r;
 
         assert(bus);
index a3b1e0442f4f3c16149923aa883bebffb8b9202c..d1e0509e55d69686b8a4b1d42615a0dd77e27295 100644 (file)
@@ -32,7 +32,6 @@ static int property_get_listen(
                 sd_bus_error *error) {
 
         Socket *s = SOCKET(userdata);
-        SocketPort *p;
         int r;
 
         assert(bus);
@@ -326,16 +325,14 @@ static int bus_socket_set_transient_property(
 
         if (streq(name, "Symlinks")) {
                 _cleanup_strv_free_ char **l = NULL;
-                char **p;
 
                 r = sd_bus_message_read_strv(message, &l);
                 if (r < 0)
                         return r;
 
-                STRV_FOREACH(p, l) {
+                STRV_FOREACH(p, l)
                         if (!path_is_absolute(*p))
                                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Symlink path is not absolute: %s", *p);
-                }
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         if (strv_isempty(l)) {
index 9d823279dd353a596fd83802de7ae60b77c726a5..8110fb1fb7df91fb5dfc2ad258718c8210313d4b 100644 (file)
@@ -20,7 +20,6 @@ static int property_get_monotonic_timers(
                 sd_bus_error *error) {
 
         Timer *t = userdata;
-        TimerValue *v;
         int r;
 
         assert(bus);
@@ -69,7 +68,6 @@ static int property_get_calendar_timers(
                 sd_bus_error *error) {
 
         Timer *t = userdata;
-        TimerValue *v;
         int r;
 
         assert(bus);
index 6542d50330fa6e1edd73cf2194f72856573805da..098ebed8471dcc1e371272f024c4d2134df0988a 100644 (file)
@@ -273,7 +273,7 @@ static int property_get_conditions(
                 sd_bus_error *error) {
 
         const char *(*to_string)(ConditionType type) = NULL;
-        Condition **list = userdata, *c;
+        Condition **list = userdata;
         int r;
 
         assert(bus);
@@ -2242,16 +2242,14 @@ static int bus_unit_set_transient_property(
 
         if (streq(name, "Documentation")) {
                 _cleanup_strv_free_ char **l = NULL;
-                char **p;
 
                 r = sd_bus_message_read_strv(message, &l);
                 if (r < 0)
                         return r;
 
-                STRV_FOREACH(p, l) {
+                STRV_FOREACH(p, l)
                         if (!documentation_url_is_valid(*p))
                                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid URL in %s: %s", name, *p);
-                }
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         if (strv_isempty(l)) {
@@ -2307,7 +2305,6 @@ static int bus_unit_set_transient_property(
 
         } else if (streq(name, "RequiresMountsFor")) {
                 _cleanup_strv_free_ char **l = NULL;
-                char **p;
 
                 r = sd_bus_message_read_strv(message, &l);
                 if (r < 0)
index 43f49573b9115e2c813d38931cf64a230dea7963..21d1b50c42ca8ac229470358495447adee3f40ec 100644 (file)
@@ -307,13 +307,9 @@ static void device_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, strna(d->sysfs),
                 prefix, strna(s));
 
-        if (!strv_isempty(d->wants_property)) {
-                char **i;
-
-                STRV_FOREACH(i, d->wants_property)
-                        fprintf(f, "%sudev SYSTEMD_WANTS: %s\n",
-                                prefix, *i);
-        }
+        STRV_FOREACH(i, d->wants_property)
+                fprintf(f, "%sudev SYSTEMD_WANTS: %s\n",
+                        prefix, *i);
 }
 
 _pure_ static UnitActiveState device_active_state(Unit *u) {
@@ -419,9 +415,7 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) {
                 k = NULL;
         }
 
-        if (d->state != DEVICE_DEAD) {
-                char **i;
-
+        if (d->state != DEVICE_DEAD)
                 /* So here's a special hack, to compensate for the fact that the udev database's reload cycles are not
                  * synchronized with our own reload cycles: when we detect that the SYSTEMD_WANTS property of a device
                  * changes while the device unit is already up, let's manually trigger any new units listed in it not
@@ -431,7 +425,6 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) {
                  *
                  * We do this only if the device has been up already when we parse this, as otherwise the usual
                  * dependency logic that is run from the dead â†’ plugged transition will trigger these deps. */
-
                 STRV_FOREACH(i, added) {
                         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
 
@@ -442,7 +435,6 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) {
                         if (r < 0)
                                 log_unit_warning_errno(u, r, "Failed to enqueue SYSTEMD_WANTS= job, ignoring: %s", bus_error_message(&error, r));
                 }
-        }
 
         return strv_free_and_replace(d->wants_property, added);
 }
@@ -710,7 +702,7 @@ static void device_update_found_one(Device *d, DeviceFound found, DeviceFound ma
 }
 
 static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFound found, DeviceFound mask) {
-        Device *d, *l, *n;
+        Device *l;
 
         assert(m);
         assert(sysfs);
@@ -719,7 +711,7 @@ static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFo
                 return;
 
         l = hashmap_get(m->devices_by_sysfs, sysfs);
-        LIST_FOREACH_SAFE(same_sysfs, d, n, l)
+        LIST_FOREACH(same_sysfs, d, l)
                 device_update_found_one(d, found, mask);
 }
 
@@ -765,7 +757,7 @@ static bool device_is_ready(sd_device *dev) {
 
 static Unit *device_following(Unit *u) {
         Device *d = DEVICE(u);
-        Device *other, *first = NULL;
+        Device *first = NULL;
 
         assert(d);
 
@@ -788,7 +780,7 @@ static Unit *device_following(Unit *u) {
 }
 
 static int device_following_set(Unit *u, Set **_set) {
-        Device *d = DEVICE(u), *other;
+        Device *d = DEVICE(u);
         _cleanup_set_free_ Set *set = NULL;
         int r;
 
@@ -898,14 +890,14 @@ fail:
 }
 
 static void device_propagate_reload_by_sysfs(Manager *m, const char *sysfs) {
-        Device *d, *l, *n;
+        Device *l;
         int r;
 
         assert(m);
         assert(sysfs);
 
         l = hashmap_get(m->devices_by_sysfs, sysfs);
-        LIST_FOREACH_SAFE(same_sysfs, d, n, l) {
+        LIST_FOREACH(same_sysfs, d, l) {
                 if (d->state == DEVICE_DEAD)
                         continue;
 
index b6021397ce35286823ff2ec786592cf09cf58cd3..8652e339239d06c3b6d74181abdbdbb116d3b18d 100644 (file)
@@ -995,7 +995,6 @@ static int get_fixed_group(const ExecContext *c, const char **group, gid_t *gid)
 static int get_supplementary_groups(const ExecContext *c, const char *user,
                                     const char *group, gid_t gid,
                                     gid_t **supplementary_gids, int *ngids) {
-        char **i;
         int r, k = 0;
         int ngroups_max;
         bool keep_groups = false;
@@ -1187,7 +1186,6 @@ static int setup_pam(
         pam_handle_t *handle = NULL;
         sigset_t old_ss;
         int pam_code = PAM_SUCCESS, r;
-        char **nv;
         bool close_session = false;
         pid_t pam_pid = 0, parent_pid;
         int flags = 0;
@@ -2005,7 +2003,6 @@ static int build_environment(
 static int build_pass_environment(const ExecContext *c, char ***ret) {
         _cleanup_strv_free_ char **pass_env = NULL;
         size_t n_env = 0;
-        char **i;
 
         STRV_FOREACH(i, c->pass_environment) {
                 _cleanup_free_ char *x = NULL;
@@ -2281,7 +2278,6 @@ static bool exec_directory_is_private(const ExecContext *context, ExecDirectoryT
 
 static int create_many_symlinks(const char *root, const char *source, char **symlinks) {
         _cleanup_free_ char *src_abs = NULL;
-        char **dst;
         int r;
 
         assert(source);
@@ -3352,7 +3348,6 @@ static int compile_symlinks(
         for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
                 for (size_t i = 0; i < context->directories[dt].n_items; i++) {
                         _cleanup_free_ char *private_path = NULL, *path = NULL;
-                        char **symlink;
 
                         STRV_FOREACH(symlink, context->directories[dt].items[i].symlinks) {
                                 _cleanup_free_ char *src_abs = NULL, *dst_abs = NULL;
@@ -5349,7 +5344,6 @@ int exec_context_destroy_runtime_directory(const ExecContext *c, const char *run
                  * service next. */
                 (void) rm_rf(p, REMOVE_ROOT);
 
-                char **symlink;
                 STRV_FOREACH(symlink, c->directories[EXEC_DIRECTORY_RUNTIME].items[i].symlinks) {
                         _cleanup_free_ char *symlink_abs = NULL;
 
@@ -5423,12 +5417,9 @@ void exec_command_reset_status_array(ExecCommand *c, size_t n) {
 }
 
 void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
-        for (size_t i = 0; i < n; i++) {
-                ExecCommand *z;
-
+        for (size_t i = 0; i < n; i++)
                 LIST_FOREACH(command, z, c[i])
                         exec_status_reset(&z->exec_status);
-        }
 }
 
 typedef struct InvalidEnvInfo {
@@ -5523,7 +5514,6 @@ static int exec_context_named_iofds(
 
 static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
         _cleanup_strv_free_ char **v = NULL;
-        char **i;
         int r;
 
         assert(c);
@@ -5630,8 +5620,6 @@ bool exec_context_may_touch_console(const ExecContext *ec) {
 }
 
 static void strv_fprintf(FILE *f, char **l) {
-        char **g;
-
         assert(f);
 
         STRV_FOREACH(g, l)
@@ -5651,7 +5639,6 @@ static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv
 }
 
 void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
-        char **e, **d;
         int r;
 
         assert(c);
@@ -5713,8 +5700,6 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
 
         if (c->root_image_options) {
-                MountOptions *o;
-
                 fprintf(f, "%sRootImageOptions:", prefix);
                 LIST_FOREACH(mount_options, o, c->root_image_options)
                         if (!isempty(o->options))
@@ -6113,8 +6098,6 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
         }
 
         for (size_t i = 0; i < c->n_mount_images; i++) {
-                MountOptions *o;
-
                 fprintf(f, "%sMountImages: %s%s:%s", prefix,
                         c->mount_images[i].ignore_enoent ? "-": "",
                         c->mount_images[i].source,
@@ -6127,8 +6110,6 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
         }
 
         for (size_t i = 0; i < c->n_extension_images; i++) {
-                MountOptions *o;
-
                 fprintf(f, "%sExtensionImages: %s%s", prefix,
                         c->extension_images[i].ignore_enoent ? "-": "",
                         c->extension_images[i].source);
@@ -6280,7 +6261,6 @@ int exec_context_get_clean_directories(
                                         return r;
                         }
 
-                        char **symlink;
                         STRV_FOREACH(symlink, c->directories[t].items[i].symlinks) {
                                 j = path_join(prefix[t], *symlink);
                                 if (!j)
@@ -6395,8 +6375,8 @@ void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
 
         prefix = strempty(prefix);
 
-        LIST_FOREACH(command, c, c)
-                exec_command_dump(c, f, prefix);
+        LIST_FOREACH(command, i, c)
+                exec_command_dump(i, f, prefix);
 }
 
 void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
index 080a63bc7e733a26426170938a5b401ddfe69411..53d2a3daa12813df5ac480375c942e6749934e0e 100644 (file)
@@ -13,7 +13,6 @@
 
 static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suffix) {
         _cleanup_strv_free_ char **paths = NULL;
-        char **p;
         int r;
 
         r = unit_file_find_dropin_paths(NULL,
@@ -85,7 +84,6 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suff
 
 int unit_load_dropin(Unit *u) {
         _cleanup_strv_free_ char **l = NULL;
-        char **f;
         int r;
 
         assert(u);
index 92a52819e27d1bb4dcf7aa1166f8f92f0cd0b469..e0dc47ecd8d5248b459d834b71529cf83f62db96 100644 (file)
@@ -1634,7 +1634,6 @@ int config_parse_root_image_options(
 
         _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
         _cleanup_strv_free_ char **l = NULL;
-        char **first = NULL, **second = NULL;
         ExecContext *c = data;
         const Unit *u = userdata;
         int r;
@@ -4328,7 +4327,7 @@ int config_parse_io_limit(
                 void *userdata) {
 
         _cleanup_free_ char *path = NULL, *resolved = NULL;
-        CGroupIODeviceLimit *l = NULL, *t;
+        CGroupIODeviceLimit *l = NULL;
         CGroupContext *c = data;
         CGroupIOLimitType type;
         const char *p = rvalue;
@@ -4343,8 +4342,8 @@ int config_parse_io_limit(
         assert(type >= 0);
 
         if (isempty(rvalue)) {
-                LIST_FOREACH(device_limits, l, c->io_device_limits)
-                        l->limits[type] = cgroup_io_limit_defaults[type];
+                LIST_FOREACH(device_limits, t, c->io_device_limits)
+                        t->limits[type] = cgroup_io_limit_defaults[type];
                 return 0;
         }
 
@@ -4378,12 +4377,11 @@ int config_parse_io_limit(
                 }
         }
 
-        LIST_FOREACH(device_limits, t, c->io_device_limits) {
+        LIST_FOREACH(device_limits, t, c->io_device_limits)
                 if (path_equal(resolved, t->path)) {
                         l = t;
                         break;
                 }
-        }
 
         if (!l) {
                 CGroupIOLimitType ttype;
@@ -4486,7 +4484,7 @@ int config_parse_blockio_bandwidth(
                 void *userdata) {
 
         _cleanup_free_ char *path = NULL, *resolved = NULL;
-        CGroupBlockIODeviceBandwidth *b = NULL, *t;
+        CGroupBlockIODeviceBandwidth *b = NULL;
         CGroupContext *c = data;
         const char *p = rvalue;
         uint64_t bytes;
@@ -4500,9 +4498,9 @@ int config_parse_blockio_bandwidth(
         read = streq("BlockIOReadBandwidth", lvalue);
 
         if (isempty(rvalue)) {
-                LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
-                        b->rbps = CGROUP_LIMIT_MAX;
-                        b->wbps = CGROUP_LIMIT_MAX;
+                LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
+                        t->rbps = CGROUP_LIMIT_MAX;
+                        t->wbps = CGROUP_LIMIT_MAX;
                 }
                 return 0;
         }
@@ -4533,14 +4531,13 @@ int config_parse_blockio_bandwidth(
                 return 0;
         }
 
-        LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
+        LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths)
                 if (path_equal(resolved, t->path)) {
                         b = t;
                         break;
                 }
-        }
 
-        if (!t) {
+        if (!b) {
                 b = new0(CGroupBlockIODeviceBandwidth, 1);
                 if (!b)
                         return log_oom();
index c13534e98a2c795f84dc8e8498cc5cee134a269c..289a1ee01f00d2e1b89c7d2d99b2e5d2777b5d2a 100644 (file)
@@ -1571,8 +1571,6 @@ static void initialize_clock(void) {
 }
 
 static void apply_clock_update(void) {
-        struct timespec ts;
-
         /* This is called later than initialize_clock(), i.e. after we parsed configuration files/kernel
          * command line and such. */
 
@@ -1582,7 +1580,7 @@ static void apply_clock_update(void) {
         if (getpid_cached() != 1)
                 return;
 
-        if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, arg_clock_usec)) < 0)
+        if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(arg_clock_usec)) < 0)
                 log_error_errno(errno, "Failed to set system clock to time specified on kernel command line: %m");
         else
                 log_info("Set system clock to %s, as specified on the kernel command line.",
@@ -2321,7 +2319,6 @@ static void fallback_rlimit_memlock(const struct rlimit *saved_rlimit_memlock) {
 }
 
 static void setenv_manager_environment(void) {
-        char **p;
         int r;
 
         STRV_FOREACH(p, arg_manager_environment) {
index 4c59506ccb511170c4db66370f1afd0a5a4d5b4c..69717e5ba6e884b9799a97c58d315757480502be 100644 (file)
@@ -39,6 +39,7 @@
 #include "dirent-util.h"
 #include "env-util.h"
 #include "escape.h"
+#include "event-util.h"
 #include "exec-util.h"
 #include "execute.h"
 #include "exit-status.h"
@@ -406,13 +407,8 @@ static int manager_setup_time_change(Manager *m) {
                 return 0;
 
         m->time_change_event_source = sd_event_source_disable_unref(m->time_change_event_source);
-        m->time_change_fd = safe_close(m->time_change_fd);
 
-        m->time_change_fd = time_change_fd();
-        if (m->time_change_fd < 0)
-                return log_error_errno(m->time_change_fd, "Failed to create timer change timer fd: %m");
-
-        r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m);
+        r = event_add_time_change(m->event, &m->time_change_event_source, manager_dispatch_time_change_fd, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to create time change event source: %m");
 
@@ -421,8 +417,6 @@ static int manager_setup_time_change(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to set priority of time change event sources: %m");
 
-        (void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change");
-
         log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd.");
 
         return 0;
@@ -820,7 +814,6 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
                 .notify_fd = -1,
                 .cgroups_agent_fd = -1,
                 .signal_fd = -1,
-                .time_change_fd = -1,
                 .user_lookup_fds = { -1, -1 },
                 .private_listen_fd = -1,
                 .dev_autofs_fd = -1,
@@ -1222,7 +1215,6 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
                         is_bad = false;
         }
 
-        const UnitRef *ref;
         LIST_FOREACH(refs_by_target, ref, u->refs_by_target) {
                 unit_gc_sweep(ref->source, gc_marker);
 
@@ -1509,7 +1501,6 @@ Manager* manager_free(Manager *m) {
         safe_close(m->signal_fd);
         safe_close(m->notify_fd);
         safe_close(m->cgroups_agent_fd);
-        safe_close(m->time_change_fd);
         safe_close_pair(m->user_lookup_fds);
 
         manager_close_ask_password(m);
@@ -2653,9 +2644,7 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) {
                          * We only do this for the cgroup the PID belonged to. */
                         (void) unit_check_oom(u1);
 
-                        /* This only logs for now. In the future when the interface for kills/notifications
-                         * is more stable we can extend service results table similar to how kernel oom kills
-                         * are managed. */
+                        /* We check if systemd-oomd perfomed a kill so that we log and notify appropriately */
                         (void) unit_check_oomd_kill(u1);
 
                         manager_invoke_sigchld_event(m, u1, &si);
@@ -2913,7 +2902,6 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint
         Unit *u;
 
         assert(m);
-        assert(m->time_change_fd == fd);
 
         log_struct(LOG_DEBUG,
                    "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
@@ -3635,7 +3623,6 @@ void manager_check_finished(Manager *m) {
 }
 
 static bool generator_path_any(const char* const* paths) {
-        char **path;
         bool found = false;
 
         /* Optimize by skipping the whole process by not creating output directories
index e5d988b745652258db0daa82ed25c97d1816c335..c989ce9c32edbea5a982bf6ed857e00982f7d5cb 100644 (file)
@@ -60,9 +60,9 @@ typedef enum StatusType {
 } StatusType;
 
 typedef enum OOMPolicy {
-        OOM_CONTINUE,          /* The kernel kills the process it wants to kill, and that's it */
-        OOM_STOP,              /* The kernel kills the process it wants to kill, and we stop the unit */
-        OOM_KILL,              /* The kernel kills the process it wants to kill, and all others in the unit, and we stop the unit */
+        OOM_CONTINUE,          /* The kernel or systemd-oomd kills the process it wants to kill, and that's it */
+        OOM_STOP,              /* The kernel or systemd-oomd kills the process it wants to kill, and we stop the unit */
+        OOM_KILL,              /* The kernel or systemd-oomd kills the process it wants to kill, and all others in the unit, and we stop the unit */
         _OOM_POLICY_MAX,
         _OOM_POLICY_INVALID = -EINVAL,
 } OOMPolicy;
@@ -226,7 +226,6 @@ struct Manager {
 
         sd_event_source *sigchld_event_source;
 
-        int time_change_fd;
         sd_event_source *time_change_event_source;
 
         sd_event_source *timezone_change_event_source;
index c650b5abe2f9e2c7386899354ac331bb6c469e68..329abfdaa8b09172e365ccf65ceadd1f80c5347f 100644 (file)
@@ -1957,7 +1957,6 @@ static int drain_libmount(Manager *m) {
 static int mount_process_proc_self_mountinfo(Manager *m) {
         _cleanup_set_free_free_ Set *around = NULL, *gone = NULL;
         const char *what;
-        Unit *u;
         int r;
 
         assert(m);
index e6293882fe2c9507b22540b166a087c612fc0c7e..2d5bedbbcdbb4dbf99595e8d65b6e7e9df58929d 100644 (file)
@@ -314,8 +314,6 @@ static void mount_entry_done(MountEntry *p) {
 }
 
 static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, bool forcibly_require_prefix) {
-        char **i;
-
         assert(p);
 
         /* Adds a list of user-supplied READWRITE/READWRITE_IMPLICIT/READONLY/INACCESSIBLE entries */
@@ -350,8 +348,6 @@ static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, boo
 }
 
 static int append_empty_dir_mounts(MountEntry **p, char **strv) {
-        char **i;
-
         assert(p);
 
         /* Adds tmpfs mounts to provide readable but empty directories. This is primarily used to implement the
@@ -419,7 +415,6 @@ static int append_extensions(
                 char **extension_directories) {
 
         _cleanup_strv_free_ char **overlays = NULL;
-        char **hierarchy, **extension_directory;
         int r;
 
         if (n == 0 && strv_isempty(extension_directories))
@@ -1709,7 +1704,6 @@ static void drop_unused_mounts(const char *root_directory, MountEntry *mounts, s
 }
 
 static int create_symlinks_from_tuples(const char *root, char **strv_symlinks) {
-        char **src, **dst;
         int r;
 
         STRV_FOREACH_PAIR(src, dst, strv_symlinks) {
@@ -2562,7 +2556,6 @@ MountImage* mount_image_free_many(MountImage *m, size_t *n) {
 int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
         _cleanup_free_ char *s = NULL, *d = NULL;
         _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
-        MountOptions *i;
         MountImage *c;
 
         assert(m);
index 0b736f00bf8c979e262eec868c68600e3e1d14b6..d9b136f10c055143d3ac8fc88a2e9e366722195e 100644 (file)
@@ -292,7 +292,6 @@ static void path_done(Unit *u) {
 }
 
 static int path_add_mount_dependencies(Path *p) {
-        PathSpec *s;
         int r;
 
         assert(p);
@@ -401,7 +400,6 @@ static int path_load(Unit *u) {
 static void path_dump(Unit *u, FILE *f, const char *prefix) {
         Path *p = PATH(u);
         Unit *trigger;
-        PathSpec *s;
 
         assert(p);
         assert(f);
@@ -429,8 +427,6 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) {
 }
 
 static void path_unwatch(Path *p) {
-        PathSpec *s;
-
         assert(p);
 
         LIST_FOREACH(spec, s, p->specs)
@@ -439,7 +435,6 @@ static void path_unwatch(Path *p) {
 
 static int path_watch(Path *p) {
         int r;
-        PathSpec *s;
 
         assert(p);
 
@@ -539,8 +534,6 @@ fail:
 }
 
 static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) {
-        PathSpec *s;
-
         assert(p);
 
         LIST_FOREACH(spec, s, p->specs)
@@ -591,8 +584,6 @@ fail:
 }
 
 static void path_mkdir(Path *p) {
-        PathSpec *s;
-
         assert(p);
 
         if (!p->make_directory)
@@ -637,7 +628,6 @@ static int path_stop(Unit *u) {
 
 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
         Path *p = PATH(u);
-        PathSpec *s;
 
         assert(u);
         assert(f);
@@ -700,7 +690,6 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD
                         _cleanup_free_ char *unescaped = NULL;
                         ssize_t l;
                         PathType type;
-                        PathSpec *s;
 
                         type = path_type_from_string(type_str);
                         if (type < 0) {
@@ -742,7 +731,7 @@ _pure_ static const char *path_sub_state_to_string(Unit *u) {
 }
 
 static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
-        PathSpec *s = userdata;
+        PathSpec *s = userdata, *found = NULL;
         Path *p;
         int changed;
 
@@ -755,18 +744,18 @@ static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
         if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
                 return 0;
 
-        /* log_debug("inotify wakeup on %s.", UNIT(p)->id); */
-
-        LIST_FOREACH(spec, s, p->specs)
-                if (path_spec_owns_inotify_fd(s, fd))
+        LIST_FOREACH(spec, i, p->specs)
+                if (path_spec_owns_inotify_fd(i, fd)) {
+                        found = i;
                         break;
+                }
 
-        if (!s) {
+        if (!found) {
                 log_error("Got event on unknown fd.");
                 goto fail;
         }
 
-        changed = path_spec_fd_event(s, revents);
+        changed = path_spec_fd_event(found, revents);
         if (changed < 0)
                 goto fail;
 
index 92485875f7a81704eca5a8486af844d4f095378f..2d7a0868524b05e02f8b3f3c34e5194dd22b645d 100644 (file)
@@ -434,8 +434,8 @@ static int service_add_fd_store(Service *s, int fd, const char *name, bool do_po
                                  * Use this errno rather than E[NM]FILE to distinguish from
                                  * the case where systemd itself hits the file limit. */
 
-        LIST_FOREACH(fd_store, fs, s->fd_store) {
-                r = same_fd(fs->fd, fd);
+        LIST_FOREACH(fd_store, i, s->fd_store) {
+                r = same_fd(i->fd, fd);
                 if (r < 0)
                         return r;
                 if (r > 0) {
@@ -504,12 +504,10 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name, bo
 }
 
 static void service_remove_fd_store(Service *s, const char *name) {
-        ServiceFDStore *fs, *n;
-
         assert(s);
         assert(name);
 
-        LIST_FOREACH_SAFE(fd_store, fs, n, s->fd_store) {
+        LIST_FOREACH(fd_store, fs, s->fd_store) {
                 if (!streq(fs->fdname, name))
                         continue;
 
@@ -567,9 +565,7 @@ static int service_verify(Service *s) {
         assert(s);
         assert(UNIT(s)->load_state == UNIT_LOADED);
 
-        for (ServiceExecCommand c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) {
-                ExecCommand *command;
-
+        for (ServiceExecCommand c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++)
                 LIST_FOREACH(command, command, s->exec_command[c]) {
                         if (!path_is_absolute(command->path) && !filename_is_valid(command->path))
                                 return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC),
@@ -581,7 +577,6 @@ static int service_verify(Service *s) {
                                                             "Service has an empty argv in %s=. Refusing.",
                                                             service_exec_command_to_string(c));
                 }
-        }
 
         if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP] &&
             UNIT(s)->success_action == EMERGENCY_ACTION_NONE)
@@ -1334,7 +1329,6 @@ static int service_collect_fds(
         }
 
         if (s->n_fd_store > 0) {
-                ServiceFDStore *fs;
                 size_t n_fds;
                 char **nl;
                 int *t;
@@ -2656,7 +2650,6 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command
         ServiceExecCommand id;
         size_t length = 0;
         unsigned idx;
-        char **arg;
 
         assert(s);
         assert(f);
@@ -2712,7 +2705,6 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command
 
 static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
         Service *s = SERVICE(u);
-        ServiceFDStore *fs;
         int r;
 
         assert(u);
@@ -3412,10 +3404,13 @@ static void service_notify_cgroup_empty_event(Unit *u) {
         }
 }
 
-static void service_notify_cgroup_oom_event(Unit *u) {
+static void service_notify_cgroup_oom_event(Unit *u, bool managed_oom) {
         Service *s = SERVICE(u);
 
-        log_unit_debug(u, "Process of control group was killed by the OOM killer.");
+        if (managed_oom)
+                log_unit_debug(u, "Process(es) of control group were killed by systemd-oomd.");
+        else
+                log_unit_debug(u, "Process of control group was killed by the OOM killer.");
 
         if (s->oom_policy == OOM_CONTINUE)
                 return;
@@ -4092,7 +4087,6 @@ static void service_notify_message(
         Service *s = SERVICE(u);
         bool notify_dbus = false;
         const char *e;
-        char * const *i;
         int r;
 
         assert(u);
index 4116e40d8f3003b699fd8276a12605a0989035cf..91e02e6d7ee27850b8f3a3a93f0045fac717f30c 100644 (file)
@@ -75,7 +75,7 @@ typedef enum ServiceResult {
         SERVICE_FAILURE_CORE_DUMP,
         SERVICE_FAILURE_WATCHDOG,
         SERVICE_FAILURE_START_LIMIT_HIT,
-        SERVICE_FAILURE_OOM_KILL,
+        SERVICE_FAILURE_OOM_KILL, /* OOM Kill by the Kernel or systemd-oomd */
         SERVICE_SKIP_CONDITION,
         _SERVICE_RESULT_MAX,
         _SERVICE_RESULT_INVALID = -EINVAL,
index 8a5c7fdd0aa35daffe4c6a9e52108ffc7ff08140..802c6afde67c112cf4e86ecbb6c83acab512bdc7 100644 (file)
@@ -208,8 +208,6 @@ static int socket_arm_timer(Socket *s, usec_t usec) {
 }
 
 static bool have_non_accept_socket(Socket *s) {
-        SocketPort *p;
-
         assert(s);
 
         if (!s->accept)
@@ -228,7 +226,6 @@ static bool have_non_accept_socket(Socket *s) {
 }
 
 static int socket_add_mount_dependencies(Socket *s) {
-        SocketPort *p;
         int r;
 
         assert(s);
@@ -368,7 +365,6 @@ static int socket_add_extras(Socket *s) {
 
 static const char *socket_find_symlink_target(Socket *s) {
         const char *found = NULL;
-        SocketPort *p;
 
         LIST_FOREACH(port, p, s->ports) {
                 const char *f = NULL;
@@ -565,7 +561,6 @@ _const_ static const char* listen_lookup(int family, int type) {
 
 static void socket_dump(Unit *u, FILE *f, const char *prefix) {
         Socket *s = SOCKET(u);
-        SocketPort *p;
         const char *prefix2, *str;
 
         assert(s);
@@ -781,8 +776,6 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
                 fprintf(f, "%sSocketProtocol: %s\n", prefix, str);
 
         if (!strv_isempty(s->symlinks)) {
-                char **q;
-
                 fprintf(f, "%sSymlinks:", prefix);
                 STRV_FOREACH(q, s->symlinks)
                         fprintf(f, " %s", *q);
@@ -923,9 +916,6 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
 }
 
 static void socket_close_fds(Socket *s) {
-        SocketPort *p;
-        char **i;
-
         assert(s);
 
         LIST_FOREACH(port, p, s->ports) {
@@ -1271,7 +1261,6 @@ static int mq_address_create(
 
 static int socket_symlink(Socket *s) {
         const char *p;
-        char **i;
         int r;
 
         assert(s);
@@ -1624,7 +1613,6 @@ static int socket_open_fds(Socket *orig_s) {
         _cleanup_(socket_close_fdsp) Socket *s = orig_s;
         _cleanup_(mac_selinux_freep) char *label = NULL;
         bool know_label = false;
-        SocketPort *p;
         int r;
 
         assert(s);
@@ -1734,7 +1722,6 @@ static int socket_open_fds(Socket *orig_s) {
 }
 
 static void socket_unwatch_fds(Socket *s) {
-        SocketPort *p;
         int r;
 
         assert(s);
@@ -1753,7 +1740,6 @@ static void socket_unwatch_fds(Socket *s) {
 }
 
 static int socket_watch_fds(Socket *s) {
-        SocketPort *p;
         int r;
 
         assert(s);
@@ -1791,7 +1777,6 @@ enum {
 
 static int socket_check_open(Socket *s) {
         bool have_open = false, have_closed = false;
-        SocketPort *p;
 
         assert(s);
 
@@ -1995,7 +1980,6 @@ static int socket_chown(Socket *s, pid_t *_pid) {
         if (r == 0) {
                 uid_t uid = UID_INVALID;
                 gid_t gid = GID_INVALID;
-                SocketPort *p;
 
                 /* Child */
 
@@ -2294,7 +2278,7 @@ fail:
 }
 
 static void flush_ports(Socket *s) {
-        SocketPort *p;
+        assert(s);
 
         /* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy
          * anymore */
@@ -2563,7 +2547,6 @@ static int socket_stop(Unit *u) {
 
 static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
         Socket *s = SOCKET(u);
-        SocketPort *p;
         int r;
 
         assert(u);
@@ -2674,7 +2657,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
                 }
         } else if (streq(key, "fifo")) {
                 int fd, skip = 0;
-                SocketPort *p;
 
                 if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
                         log_unit_debug(u, "Failed to parse fifo value: %s", value);
@@ -2695,7 +2677,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
 
         } else if (streq(key, "special")) {
                 int fd, skip = 0;
-                SocketPort *p;
 
                 if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
                         log_unit_debug(u, "Failed to parse special value: %s", value);
@@ -2716,7 +2697,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
 
         } else if (streq(key, "mqueue")) {
                 int fd, skip = 0;
-                SocketPort *p;
 
                 if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
                         log_unit_debug(u, "Failed to parse mqueue value: %s", value);
@@ -2737,7 +2717,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
 
         } else if (streq(key, "socket")) {
                 int fd, type, skip = 0;
-                SocketPort *p;
 
                 if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
                         log_unit_debug(u, "Failed to parse socket value: %s", value);
@@ -2758,7 +2737,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
 
         } else if (streq(key, "netlink")) {
                 int fd, skip = 0;
-                SocketPort *p;
 
                 if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
                         log_unit_debug(u, "Failed to parse socket value: %s", value);
@@ -2778,7 +2756,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
 
         } else if (streq(key, "ffs")) {
                 int fd, skip = 0;
-                SocketPort *p;
 
                 if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
                         log_unit_debug(u, "Failed to parse ffs value: %s", value);
@@ -2805,7 +2782,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
 
 static void socket_distribute_fds(Unit *u, FDSet *fds) {
         Socket *s = SOCKET(u);
-        SocketPort *p;
 
         assert(u);
 
@@ -3227,7 +3203,6 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use
 
 int socket_collect_fds(Socket *s, int **fds) {
         size_t k = 0, n = 0;
-        SocketPort *p;
         int *rfds;
 
         assert(s);
index 9c0d4fb2277e04bc0601ab20f6ff1df2cc4fd0b4..0de73970ad2600c7d1546e3ee2475931d8280e2a 100644 (file)
@@ -537,7 +537,6 @@ static void swap_process_new(Manager *m, const char *device, int prio, bool set_
 
 static void swap_set_state(Swap *s, SwapState state) {
         SwapState old_state;
-        Swap *other;
 
         assert(s);
 
@@ -745,8 +744,6 @@ static void swap_enter_dead_or_active(Swap *s, SwapResult f) {
         assert(s);
 
         if (s->from_proc_swaps) {
-                Swap *other;
-
                 swap_enter_active(s, f);
 
                 LIST_FOREACH_OTHERS(same_devnode, other, s)
@@ -902,7 +899,7 @@ static void swap_cycle_clear(Swap *s) {
 }
 
 static int swap_start(Unit *u) {
-        Swap *s = SWAP(u), *other;
+        Swap *s = SWAP(u);
         int r;
 
         assert(s);
@@ -1207,7 +1204,6 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
 }
 
 static int swap_process_proc_swaps(Manager *m) {
-        Unit *u;
         int r;
 
         assert(m);
@@ -1300,7 +1296,7 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
 
 static Unit *swap_following(Unit *u) {
         Swap *s = SWAP(u);
-        Swap *other, *first = NULL;
+        Swap *first = NULL;
 
         assert(s);
 
@@ -1336,7 +1332,7 @@ static Unit *swap_following(Unit *u) {
 }
 
 static int swap_following_set(Unit *u, Set **_set) {
-        Swap *s = SWAP(u), *other;
+        Swap *s = SWAP(u);
         _cleanup_set_free_ Set *set = NULL;
         int r;
 
index a13b86474158cce93c3e2d98bdd8e794b364e2d3..69a4ea652a8569946b48d0c75091abb8d343a1f7 100644 (file)
@@ -83,7 +83,6 @@ static int timer_verify(Timer *t) {
 
 static int timer_add_default_dependencies(Timer *t) {
         int r;
-        TimerValue *v;
 
         assert(t);
 
@@ -236,7 +235,6 @@ static int timer_load(Unit *u) {
 static void timer_dump(Unit *u, FILE *f, const char *prefix) {
         Timer *t = TIMER(u);
         Unit *trigger;
-        TimerValue *v;
 
         trigger = UNIT_TRIGGER(u);
 
@@ -375,7 +373,6 @@ static void timer_enter_waiting(Timer *t, bool time_change) {
         bool found_monotonic = false, found_realtime = false;
         bool leave_around = false;
         triple_timestamp ts;
-        TimerValue *v;
         Unit *trigger;
         int r;
 
@@ -617,7 +614,6 @@ fail:
 
 static int timer_start(Unit *u) {
         Timer *t = TIMER(u);
-        TimerValue *v;
         int r;
 
         assert(t);
@@ -756,7 +752,6 @@ static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
 
 static void timer_trigger_notify(Unit *u, Unit *other) {
         Timer *t = TIMER(u);
-        TimerValue *v;
 
         assert(u);
         assert(other);
index ebe5f1910dddc26414cf4916ae227f8c73bd97ed..7bb8dfb0f4c6c8ad3e6c6b25befd399c5b437897 100644 (file)
@@ -46,7 +46,7 @@ void transaction_abort(Transaction *tr) {
 }
 
 static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
-        JobDependency *l;
+        assert(j);
 
         /* A recursive sweep through the graph that marks all units
          * that matter to the anchor job, i.e. are directly or
@@ -71,7 +71,7 @@ static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generat
 }
 
 static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) {
-        JobDependency *l, *last;
+        JobDependency *last;
 
         assert(j);
         assert(other);
@@ -124,8 +124,6 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other
 }
 
 _pure_ static bool job_is_conflicted_by(Job *j) {
-        JobDependency *l;
-
         assert(j);
 
         /* Returns true if this job is pulled in by a least one
@@ -138,10 +136,8 @@ _pure_ static bool job_is_conflicted_by(Job *j) {
         return false;
 }
 
-static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
-        Job *k;
-
-        assert(j);
+static int delete_one_unmergeable_job(Transaction *tr, Job *job) {
+        assert(job);
 
         /* Tries to delete one item in the linked list
          * j->transaction_next->transaction_next->... that conflicts
@@ -150,7 +146,7 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
 
         /* We rely here on the fact that if a merged with b does not
          * merge with c, either a or b merge with c neither */
-        LIST_FOREACH(transaction, j, j)
+        LIST_FOREACH(transaction, j, job)
                 LIST_FOREACH(transaction, k, j->transaction_next) {
                         Job *d;
 
@@ -226,7 +222,6 @@ static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) {
          * task conflict. If so, try to drop one of them. */
         HASHMAP_FOREACH(j, tr->jobs) {
                 JobType t;
-                Job *k;
 
                 t = j->type;
                 LIST_FOREACH(transaction, k, j->transaction_next) {
@@ -257,12 +252,12 @@ static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) {
         /* Second step, merge the jobs. */
         HASHMAP_FOREACH(j, tr->jobs) {
                 JobType t = j->type;
-                Job *k;
 
                 /* Merge all transaction jobs for j->unit */
                 LIST_FOREACH(transaction, k, j->transaction_next)
                         assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
 
+                Job *k;
                 while ((k = j->transaction_next)) {
                         if (tr->anchor_job == k) {
                                 transaction_merge_and_delete_job(tr, k, j, t);
@@ -293,7 +288,6 @@ static void transaction_drop_redundant(Transaction *tr) {
 
                 HASHMAP_FOREACH(j, tr->jobs) {
                         bool keep = false;
-                        Job *k;
 
                         LIST_FOREACH(transaction, k, j)
                                 if (tr->anchor_job == k ||
@@ -314,14 +308,15 @@ static void transaction_drop_redundant(Transaction *tr) {
         } while (again);
 }
 
-_pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) {
+_pure_ static bool unit_matters_to_anchor(Unit *u, Job *job) {
         assert(u);
-        assert(!j->transaction_prev);
+        assert(job);
+        assert(!job->transaction_prev);
 
         /* Checks whether at least one of the jobs for this unit
          * matters to the anchor. */
 
-        LIST_FOREACH(transaction, j, j)
+        LIST_FOREACH(transaction, j, job)
                 if (j->matters_to_anchor)
                         return true;
 
@@ -329,7 +324,7 @@ _pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) {
 }
 
 static char* merge_unit_ids(const char* unit_log_field, char **pairs) {
-        char **unit_id, **job_type, *ans = NULL;
+        char *ans = NULL;
         size_t size = 0, next;
 
         STRV_FOREACH_PAIR(unit_id, job_type, pairs) {
@@ -366,7 +361,6 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
         if (j->generation == generation) {
                 Job *k, *delete = NULL;
                 _cleanup_free_ char **array = NULL, *unit_ids = NULL;
-                char **unit_id, **job_type;
 
                 /* If the marker is NULL we have been here already and decided the job was loop-free from
                  * here. Hence shortcut things and return right-away. */
@@ -558,7 +552,7 @@ static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_erro
 }
 
 static void transaction_minimize_impact(Transaction *tr) {
-        Job *j;
+        Job *head;
 
         assert(tr);
 
@@ -566,8 +560,8 @@ static void transaction_minimize_impact(Transaction *tr) {
          * or that stop a running service. */
 
 rescan:
-        HASHMAP_FOREACH(j, tr->jobs) {
-                LIST_FOREACH(transaction, j, j) {
+        HASHMAP_FOREACH(head, tr->jobs) {
+                LIST_FOREACH(transaction, j, head) {
                         bool stops_running_service, changes_existing_job;
 
                         /* If it matters, we shouldn't drop it */
@@ -804,13 +798,13 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b
 
         f = hashmap_get(tr->jobs, unit);
 
-        LIST_FOREACH(transaction, j, f) {
-                assert(j->unit == unit);
+        LIST_FOREACH(transaction, i, f) {
+                assert(i->unit == unit);
 
-                if (j->type == type) {
+                if (i->type == type) {
                         if (is_new)
                                 *is_new = false;
-                        return j;
+                        return i;
                 }
         }
 
index 7d2e6bc130de5b3311c363404b788f9219a3043b..65ed110ce63a6dcf5c804dd083b9ca2c75262434 100644 (file)
@@ -623,7 +623,7 @@ static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependency
 }
 
 void unit_dump(Unit *u, FILE *f, const char *prefix) {
-        char *t, **j;
+        char *t;
         const char *prefix2;
         Unit *following;
         _cleanup_set_free_ Set *following_set = NULL;
index bd11049c77a162dc81207b075c97b3a0675ded07..42fb4220f60436b8264296a65c7f84982afa976b 100644 (file)
@@ -583,8 +583,6 @@ static void unit_clear_dependencies(Unit *u) {
 }
 
 static void unit_remove_transient(Unit *u) {
-        char **i;
-
         assert(u);
 
         if (!u->transient)
@@ -3620,7 +3618,6 @@ int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask m
 
 int unit_coldplug(Unit *u) {
         int r = 0, q;
-        char **i;
 
         assert(u);
 
@@ -3693,7 +3690,6 @@ static bool fragment_mtime_newer(const char *path, usec_t mtime, bool path_maske
 
 bool unit_need_daemon_reload(Unit *u) {
         _cleanup_strv_free_ char **t = NULL;
-        char **path;
 
         assert(u);
 
@@ -3805,6 +3801,13 @@ int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) {
         return UNIT_VTABLE(u)->kill(u, w, signo, error);
 }
 
+void unit_notify_cgroup_oom(Unit *u, bool managed_oom) {
+        assert(u);
+
+        if (UNIT_VTABLE(u)->notify_cgroup_oom)
+                UNIT_VTABLE(u)->notify_cgroup_oom(u, managed_oom);
+}
+
 static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) {
         _cleanup_set_free_ Set *pid_set = NULL;
         int r;
@@ -4278,7 +4281,6 @@ char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf) {
 char* unit_concat_strv(char **l, UnitWriteFlags flags) {
         _cleanup_free_ char *result = NULL;
         size_t n = 0;
-        char **i;
 
         /* Takes a list of strings, escapes them, and concatenates them. This may be used to format command lines in a
          * way suitable for ExecStart= stanzas */
@@ -5079,7 +5081,6 @@ int unit_fork_and_watch_rm_rf(Unit *u, char **paths, pid_t *ret_pid) {
                 return r;
         if (r == 0) {
                 int ret = EXIT_SUCCESS;
-                char **i;
 
                 STRV_FOREACH(i, paths) {
                         r = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK);
@@ -5869,7 +5870,7 @@ int unit_thaw_vtable_common(Unit *u) {
 }
 
 Condition *unit_find_failed_condition(Unit *u) {
-        Condition *c, *failed_trigger = NULL;
+        Condition *failed_trigger = NULL;
         bool has_succeeded_trigger = false;
 
         if (u->condition_result)
index 94f2180951cdc51ed1d936ebe2861744e278f260..733eeecd7f0d2f85783ecbc191a7a895b63f55de 100644 (file)
@@ -285,7 +285,7 @@ typedef struct Unit {
         nsec_t cpu_usage_base;
         nsec_t cpu_usage_last; /* the most recently read value */
 
-        /* The current counter of processes sent SIGKILL by systemd-oomd */
+        /* The current counter of OOM kills initiated by systemd-oomd */
         uint64_t managed_oom_kill_last;
 
         /* The current counter of the oom_kill field in the memory.events cgroup attribute */
@@ -596,7 +596,7 @@ typedef struct UnitVTable {
         void (*notify_cgroup_empty)(Unit *u);
 
         /* Called whenever an OOM kill event on this unit was seen */
-        void (*notify_cgroup_oom)(Unit *u);
+        void (*notify_cgroup_oom)(Unit *u, bool managed_oom);
 
         /* Called whenever a process of this unit sends us a message */
         void (*notify_message)(Unit *u, const struct ucred *ucred, char * const *tags, FDSet *fds);
@@ -811,6 +811,8 @@ int unit_reload(Unit *u);
 int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error);
 int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t control_pid, sd_bus_error *error);
 
+void unit_notify_cgroup_oom(Unit *u, bool managed_oom);
+
 typedef enum UnitNotifyFlags {
         UNIT_NOTIFY_RELOAD_FAILURE    = 1 << 0,
         UNIT_NOTIFY_WILL_AUTO_RESTART = 1 << 1,
index 2904de3728cd0fabb02282258425c1e7911b85fa..7235544285783904f8a25ca5bb85526c819c51a3 100644 (file)
@@ -91,7 +91,6 @@ static int add_match(sd_journal *j, const char *match) {
 }
 
 static int add_matches(sd_journal *j, char **matches) {
-        char **match;
         int r;
 
         r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0);
index 3d2841c07db170450001bebbe5707bbe0fd8cca8..2fd314ff37176decc95c99860ba5505175fe86b3 100644 (file)
@@ -311,7 +311,6 @@ static int write_blob(FILE *f, const void *data, size_t size) {
 static int verb_cat(int argc, char **argv, void *userdata) {
         _cleanup_(closedirp) DIR *d = NULL;
         int r, ret = 0;
-        char **cn;
 
         r = open_credential_directory(&d);
         if (r == -ENXIO)
index 2fd6d9080ec15b3729c32f4e6dd3a4cedfda7262..2e11ffe291e0b90c9ee55137ece2485bf29bf269 100644 (file)
@@ -489,7 +489,6 @@ static int prepare_luks(
 
                 for (;;) {
                         _cleanup_strv_free_erase_ char **passwords = NULL;
-                        char **p;
 
                         if (--i == 0)
                                 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY),
index 924555d262fb8fae0f9bbee5201786b349f7ce96..1867e9012ccb00616c0747faf84f2703238cf90a 100644 (file)
@@ -12,7 +12,6 @@ int find_key_file(
                 void **ret_key,
                 size_t *ret_key_size) {
 
-        char **i;
         int r;
 
         assert(key_file);
index f2b0269fc5c164d389283dd8b40dad228a33e01e..f92624fb1301645fa23bf194c11a697a8f082163 100644 (file)
@@ -573,7 +573,7 @@ static int get_password(
 
         _cleanup_free_ char *friendly = NULL, *text = NULL, *disk_path = NULL;
         _cleanup_strv_free_erase_ char **passwords = NULL;
-        char **p, *id;
+        char *id;
         int r = 0;
         AskPasswordFlags flags = arg_ask_password_flags | ASK_PASSWORD_PUSH_CACHE;
 
@@ -861,7 +861,6 @@ static int attach_luks2_by_fido2(
 #if HAVE_LIBCRYPTSETUP_PLUGINS
         AskPasswordFlags flags = ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
         _cleanup_strv_free_erase_ char **pins = NULL;
-        char **p;
         int r;
 
         r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags);
@@ -1547,7 +1546,6 @@ static int attach_luks_or_plain_or_bitlk_by_passphrase(
                 uint32_t flags,
                 bool pass_volume_key) {
 
-        char **p;
         int r;
 
         assert(cd);
index a724ae510d112684454a8b6fa166f095cadd2021..e2e1df57151ea0519f5edfc6cc557fff40fc5479 100644 (file)
@@ -89,7 +89,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 }
 
 static int generate_mask_symlinks(void) {
-        char **u;
         int r = 0;
 
         if (strv_isempty(arg_mask))
@@ -112,7 +111,6 @@ static int generate_mask_symlinks(void) {
 }
 
 static int generate_wants_symlinks(void) {
-        char **u;
         int r = 0;
 
         if (strv_isempty(arg_wants))
index e14ad36d03dd825e6d2441b37ab0458d0c38df12..aa5a546bce58f4ec56b6782f5f68383fdfea9bd6 100644 (file)
@@ -195,7 +195,6 @@ static int enumerate_dir_d(
         _cleanup_free_ char *unit = NULL;
         _cleanup_free_ char *path = NULL;
         _cleanup_strv_free_ char **list = NULL;
-        char **file;
         char *c;
         int r;
 
@@ -292,7 +291,6 @@ static int enumerate_dir(
         _cleanup_closedir_ DIR *d = NULL;
         _cleanup_strv_free_ char **files = NULL, **dirs = NULL;
         size_t n_files = 0, n_dirs = 0;
-        char **t;
         int r;
 
         assert(top);
index a9632a3f163583f5cd3f846136db31b3814c298d..1d78dc50858336413865342620eeab10a44e4ffb 100644 (file)
@@ -354,7 +354,6 @@ static int parse_argv(int argc, char *argv[]) {
 
 static int strv_pair_to_json(char **l, JsonVariant **ret) {
         _cleanup_strv_free_ char **jl = NULL;
-        char **a, **b;
 
         STRV_FOREACH_PAIR(a, b, l) {
                 char *j;
@@ -371,8 +370,6 @@ static int strv_pair_to_json(char **l, JsonVariant **ret) {
 }
 
 static void strv_pair_print(char **l, const char *prefix) {
-        char **p, **q;
-
         assert(prefix);
 
         STRV_FOREACH_PAIR(p, q, l) {
index 1171fdc29093515db75943ec6e80990c778c6b4a..39c46c7c2b28d517d2321cccb4a08122c6ae8556 100644 (file)
@@ -41,7 +41,6 @@ static int environment_dirs(char ***ret) {
 
 static int load_and_print(void) {
         _cleanup_strv_free_ char **dirs = NULL, **files = NULL, **env = NULL;
-        char **i;
         int r;
 
         r = environment_dirs(&dirs);
index 676b7dce54bcd8fa7bf2f0036e1cdafaab9d2151..0ad77f3f941d7af426b1263d0139b99777a5d2b9 100644 (file)
@@ -155,7 +155,6 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 static int run(int argc, char *argv[]) {
-        char **i;
         int r;
 
         log_setup();
index ca9b045e85952d8a5af9d7598b66494b2631e426..75523d0c07925a382a0022efb52f720e73d1ded4 100644 (file)
@@ -237,7 +237,6 @@ static int write_dependency(
 
         _cleanup_strv_free_ char **names = NULL, **units = NULL;
         _cleanup_free_ char *res = NULL;
-        char **s;
         int r;
 
         assert(f);
@@ -518,8 +517,6 @@ static int add_mount(
                         if (r < 0)
                                 return r;
                 } else {
-                        char **s;
-
                         STRV_FOREACH(s, wanted_by) {
                                 r = generator_add_symlink(dest, *s, "wants", name);
                                 if (r < 0)
index 3c179ca4aea673fb9943a2f4830fa5d3cb3ac327..671d5f5130eb6673dc0e75ede85c877cd5752db3 100644 (file)
@@ -557,7 +557,6 @@ static int acquire_passed_secrets(const char *user_name, UserRecord **ret) {
 static int activate_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r, ret = 0;
-        char **i;
 
         r = acquire_bus(&bus);
         if (r < 0)
@@ -606,7 +605,6 @@ static int activate_home(int argc, char *argv[], void *userdata) {
 static int deactivate_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r, ret = 0;
-        char **i;
 
         r = acquire_bus(&bus);
         if (r < 0)
@@ -693,7 +691,7 @@ static int inspect_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(strv_freep) char **mangled_list = NULL;
         int r, ret = 0;
-        char **items, **i;
+        char **items;
 
         pager_open(arg_pager_flags);
 
@@ -777,7 +775,7 @@ static int authenticate_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(strv_freep) char **mangled_list = NULL;
         int r, ret = 0;
-        char **i, **items;
+        char **items;
 
         items = mangle_user_list(strv_skip(argv, 1), &mangled_list);
         if (!items)
@@ -1087,7 +1085,6 @@ static int add_disposition(JsonVariant **v) {
 static int acquire_new_home_record(UserRecord **ret) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
-        char **i;
         int r;
 
         assert(ret);
@@ -1370,7 +1367,6 @@ static int create_home(int argc, char *argv[], void *userdata) {
 static int remove_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r, ret = 0;
-        char **i;
 
         r = acquire_bus(&bus);
         if (r < 0)
@@ -1408,7 +1404,6 @@ static int acquire_updated_home_record(
 
         _cleanup_(json_variant_unrefp) JsonVariant *json = NULL;
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
-        char **i;
         int r;
 
         assert(ret);
@@ -1858,7 +1853,6 @@ static int resize_home(int argc, char *argv[], void *userdata) {
 static int lock_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r, ret = 0;
-        char **i;
 
         r = acquire_bus(&bus);
         if (r < 0)
@@ -1890,7 +1884,6 @@ static int lock_home(int argc, char *argv[], void *userdata) {
 static int unlock_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r, ret = 0;
-        char **i;
 
         r = acquire_bus(&bus);
         if (r < 0)
index 47eb287aa9ede3d4e8ca33e1b3265d95554842bc..ba5ca42ed3faea9be12a172a80c6b5d34b03e79c 100644 (file)
@@ -39,7 +39,6 @@ static int property_get_auto_login(
         HASHMAP_FOREACH(h, m->homes_by_name) {
                 _cleanup_(strv_freep) char **seats = NULL;
                 _cleanup_free_ char *home_path = NULL;
-                char **s;
 
                 r = home_auto_login(h, &seats);
                 if (r < 0) {
index dc1bb070dc7bcfd6c6d73bdb02c74ebf929bdf52..9ed1348592f91fc084bfcf49fea61352dfeb2654 100644 (file)
@@ -1551,7 +1551,6 @@ static int manager_load_public_key_one(Manager *m, const char *path) {
 
 static int manager_load_public_keys(Manager *m) {
         _cleanup_strv_free_ char **files = NULL;
-        char **i;
         int r;
 
         assert(m);
index 96a6ea754e14e19607c8f4663e18151aadf1884e..3dd5a148027812c8a99abe18f6c0e92018e05007 100644 (file)
@@ -282,7 +282,6 @@ int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMet
 
         if (p.user_name) {
                 const char *last = NULL;
-                char **i;
 
                 h = hashmap_get(m->homes_by_name, p.user_name);
                 if (!h)
@@ -335,9 +334,7 @@ int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMet
         } else {
                 const char *last_user_name = NULL, *last_group_name = NULL;
 
-                HASHMAP_FOREACH(h, m->homes_by_name) {
-                        char **j;
-
+                HASHMAP_FOREACH(h, m->homes_by_name)
                         STRV_FOREACH(j, h->record->member_of) {
 
                                 if (last_user_name) {
@@ -353,7 +350,6 @@ int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMet
                                 last_user_name = h->user_name;
                                 last_group_name = *j;
                         }
-                }
 
                 if (last_user_name) {
                         assert(last_group_name);
index ed06d1f221db2b15dcbee6f1eca3972488d1245d..6d499f76b2442740007369e2ef8e8d789c6903fc 100644 (file)
@@ -20,7 +20,6 @@ int home_setup_cifs(
                 HomeSetup *setup) {
 
         _cleanup_free_ char *chost = NULL, *cservice = NULL, *cdir = NULL, *chost_and_service = NULL, *j = NULL;
-        char **pw;
         int r;
 
         assert(h);
index f9fef73f7580af80021545739d89777b39c0a123..afa706a1bf63b8c343305c6b7f35e6270fc28905 100644 (file)
@@ -197,7 +197,6 @@ static int fscrypt_slot_try_many(
                 const uint8_t match_key_descriptor[static FS_KEY_DESCRIPTOR_SIZE],
                 void **ret_decrypted, size_t *ret_decrypted_size) {
 
-        char **i;
         int r;
 
         STRV_FOREACH(i, passwords) {
@@ -499,7 +498,6 @@ int home_create_fscrypt(
         _cleanup_free_ char *d = NULL;
         uint32_t nr = 0;
         const char *ip;
-        char **i;
         int r;
 
         assert(h);
@@ -649,7 +647,6 @@ int home_passwd_fscrypt(
         size_t volume_key_size = 0;
         uint32_t slot = 0;
         const char *xa;
-        char **p;
         int r;
 
         assert(h);
index 38164fadc341f46ba89b3090ab4dcc971078f2fc..488cb30fe322aa1b0c2f5befa123d66eaaaed28b 100644 (file)
@@ -304,7 +304,6 @@ static int luks_try_passwords(
                 size_t *volume_key_size,
                 key_serial_t *ret_key_serial) {
 
-        char **pp;
         int r;
 
         assert(h);
@@ -1164,12 +1163,12 @@ static int lock_image_fd(int image_fd, const char *ip) {
 
         if (flock(image_fd, LOCK_EX|LOCK_NB) < 0) {
 
-                if (errno == EWOULDBLOCK)
+                if (errno == EAGAIN)
                         log_error_errno(errno, "Image file '%s' already locked, can't use.", ip);
                 else
                         log_error_errno(errno, "Failed to lock image file '%s': %m", ip);
 
-                return errno != EWOULDBLOCK ? -errno : -EADDRINUSE; /* Make error recognizable */
+                return errno != EAGAIN ? -errno : -EADDRINUSE; /* Make error recognizable */
         }
 
         log_info("Successfully locked image file '%s'.", ip);
@@ -1718,7 +1717,6 @@ static int luks_format(
         _cleanup_free_ char *text = NULL;
         size_t volume_key_size;
         int slot = 0, r;
-        char **pp;
 
         assert(node);
         assert(dm_name);
@@ -3659,7 +3657,6 @@ static int luks_try_resume(
                 const char *dm_name,
                 char **password) {
 
-        char **pp;
         int r;
 
         assert(cd);
index 15402b1002c05ff88885cbbb2abae49c690afe40..7868fb606476522794841c0f5a0c429ee042eb60 100644 (file)
@@ -20,7 +20,6 @@ int pkcs11_callback(
         CK_TOKEN_INFO updated_token_info;
         size_t decrypted_key_size;
         CK_OBJECT_HANDLE object;
-        char **i;
         CK_RV rv;
         int r;
 
index 9fdc74b77549578ab191e641bf690b9696e81d1e..0014a7f5986333538509fea9152908a86074f559 100644 (file)
@@ -97,9 +97,7 @@ int user_record_authenticate(
                 log_info("None of the supplied plaintext passwords unlock the user record's hashed recovery keys.");
 
         /* Second, test cached PKCS#11 passwords */
-        for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) {
-                char **pp;
-
+        for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++)
                 STRV_FOREACH(pp, cache->pkcs11_passwords) {
                         r = test_password_one(h->pkcs11_encrypted_key[n].hashed_password, *pp);
                         if (r < 0)
@@ -109,12 +107,9 @@ int user_record_authenticate(
                                 return 1;
                         }
                 }
-        }
 
         /* Third, test cached FIDO2 passwords */
-        for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) {
-                char **pp;
-
+        for (size_t n = 0; n < h->n_fido2_hmac_salt; n++)
                 /* See if any of the previously calculated passwords work */
                 STRV_FOREACH(pp, cache->fido2_passwords) {
                         r = test_password_one(h->fido2_hmac_salt[n].hashed_password, *pp);
@@ -125,7 +120,6 @@ int user_record_authenticate(
                                 return 1;
                         }
                 }
-        }
 
         /* Fourth, let's see if any of the PKCS#11 security tokens are plugged in and help us */
         for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) {
@@ -1096,7 +1090,6 @@ static int user_record_compile_effective_passwords(
 
         _cleanup_(strv_free_erasep) char **effective = NULL;
         size_t n;
-        char **i;
         int r;
 
         assert(h);
@@ -1116,7 +1109,6 @@ static int user_record_compile_effective_passwords(
 
         STRV_FOREACH(i, h->hashed_password) {
                 bool found = false;
-                char **j;
 
                 log_debug("Looking for plaintext password for: %s", *i);
 
@@ -1144,7 +1136,6 @@ static int user_record_compile_effective_passwords(
 
         for (n = 0; n < h->n_recovery_key; n++) {
                 bool found = false;
-                char **j;
 
                 log_debug("Looking for plaintext recovery key for: %s", h->recovery_key[n].hashed_password);
 
index 23c3357836cf16241cbf7ebc3dcc556652f8ae66..609e62051141695a3d001533611ef04860231c92 100644 (file)
@@ -17,7 +17,7 @@ int user_record_quality_check_password(
                 sd_bus_error *error) {
 
         _cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
-        char buf[PWQ_MAX_ERROR_MESSAGE_LEN], **pp;
+        char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
         void *auxerror;
         int r;
 
@@ -37,7 +37,6 @@ int user_record_quality_check_password(
         /* Iterate through all new passwords */
         STRV_FOREACH(pp, secret->password) {
                 bool called = false;
-                char **old;
 
                 r = test_password_many(hr->hashed_password, *pp);
                 if (r < 0)
index 6a892d45b5b4730b7280c795a7f4227b2b4ad28c..2b727df533b2215915fb17d088d761c89daa86e1 100644 (file)
@@ -563,7 +563,6 @@ int user_record_test_image_path_and_warn(UserRecord *h) {
 }
 
 int user_record_test_password(UserRecord *h, UserRecord *secret) {
-        char **i;
         int r;
 
         assert(h);
@@ -585,7 +584,6 @@ int user_record_test_password(UserRecord *h, UserRecord *secret) {
 }
 
 int user_record_test_recovery_key(UserRecord *h, UserRecord *secret) {
-        char **i;
         int r;
 
         assert(h);
@@ -779,7 +777,6 @@ int user_record_update_last_changed(UserRecord *h, bool with_password) {
 int user_record_make_hashed_password(UserRecord *h, char **secret, bool extend) {
         _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
         _cleanup_strv_free_ char **np = NULL;
-        char **i;
         int r;
 
         assert(h);
index 484a72677d76fd7e806cfca1d41ae231726e0eed..6f4d65103ce45a342b74817132d2ca15fe3a6fb6 100644 (file)
@@ -102,7 +102,6 @@ static int show_one(Table **table, const char *name, sd_id128_t uuid, bool first
 
 static int verb_show(int argc, char **argv, void *userdata) {
         _cleanup_(table_unrefp) Table *table = NULL;
-        char **p;
         int r;
 
         argv = strv_skip(argv, 1);
index 6ab91263b1dbe9abb0bd4b079c649340f33b8d59..13ca22ef268a19ff1d3eac5364c5acfc76536aaa 100644 (file)
@@ -594,7 +594,6 @@ static int create_remoteserver(
                 const char* trust) {
 
         int r, n, fd;
-        char **file;
 
         r = journal_remote_server_init(s, arg_output, arg_split_mode, arg_compress, arg_seal);
         if (r < 0)
index e6a82544912ddc3d941339857ac8685dbcd4d881..a8119bd687a1c6e3f130ea30404ab9828bc0e601 100644 (file)
@@ -151,18 +151,17 @@ static int log_enable_gnutls_category(const char *cat) {
 }
 
 int setup_gnutls_logger(char **categories) {
-        char **cat;
         int r;
 
         gnutls_global_set_log_function(log_func_gnutls);
 
-        if (categories) {
+        if (categories)
                 STRV_FOREACH(cat, categories) {
                         r = log_enable_gnutls_category(*cat);
                         if (r < 0)
                                 return r;
                 }
-        else
+        else
                 log_reset_gnutls_level();
 
         return 0;
index 63334c0ea4b94c192fbccbf93cfb96d17a0a0cd1..cff34fd585003437a8c994e7cc0bf28e97281626 100644 (file)
@@ -1146,7 +1146,6 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 static int add_matches(sd_journal *j, char **args) {
-        char **i;
         bool have_term = false;
 
         assert(j);
@@ -1334,7 +1333,7 @@ static int get_boots(
 
         bool skip_once;
         int r, count = 0;
-        BootId *head = NULL, *tail = NULL, *id;
+        BootId *head = NULL, *tail = NULL;
         const bool advance_older = boot_id && offset <= 0;
         sd_id128_t previous_boot_id;
 
@@ -1449,7 +1448,7 @@ finish:
 
 static int list_boots(sd_journal *j) {
         _cleanup_(table_unrefp) Table *table = NULL;
-        BootId *id, *all_ids;
+        BootId *all_ids;
         int count, i, r;
 
         assert(j);
@@ -1584,7 +1583,7 @@ static int get_possible_units(
                         return r;
 
                 SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
-                        char **pattern, *eq;
+                        char *eq;
                         size_t prefix;
                         _cleanup_free_ char *u = NULL;
 
@@ -1637,7 +1636,6 @@ static int get_possible_units(
 static int add_units(sd_journal *j) {
         _cleanup_strv_free_ char **patterns = NULL;
         int r, count = 0;
-        char **i;
 
         assert(j);
 
@@ -1782,7 +1780,6 @@ static int add_facilities(sd_journal *j) {
 
 static int add_syslog_identifier(sd_journal *j) {
         int r;
-        char **i;
 
         assert(j);
 
index 8accd7c7f59730e863ac49bc760914c82f7193ca..842882bc59699ec5fad78bf3cd8b98f9e01146a8 100644 (file)
@@ -185,10 +185,10 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) {
 }
 
 int journal_ratelimit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available) {
-        uint64_t h;
-        JournalRateLimitGroup *g;
+        JournalRateLimitGroup *g, *found = NULL;
         JournalRateLimitPool *p;
         unsigned burst;
+        uint64_t h;
         usec_t ts;
 
         assert(id);
@@ -208,23 +208,25 @@ int journal_ratelimit_test(JournalRateLimit *r, const char *id, usec_t rl_interv
         h = siphash24_string(id, r->hash_key);
         g = r->buckets[h % BUCKETS_MAX];
 
-        LIST_FOREACH(bucket, g, g)
-                if (streq(g->id, id))
+        LIST_FOREACH(bucket, i, g)
+                if (streq(i->id, id)) {
+                        found = i;
                         break;
+                }
 
-        if (!g) {
-                g = journal_ratelimit_group_new(r, id, rl_interval, ts);
-                if (!g)
+        if (!found) {
+                found = journal_ratelimit_group_new(r, id, rl_interval, ts);
+                if (!found)
                         return -ENOMEM;
         } else
-                g->interval = rl_interval;
+                found->interval = rl_interval;
 
         if (rl_interval == 0 || rl_burst == 0)
                 return 1;
 
         burst = burst_modulate(rl_burst, available);
 
-        p = &g->pools[priority_map[priority]];
+        p = &found->pools[priority_map[priority]];
 
         if (p->begin <= 0) {
                 p->suppressed = 0;
index 719abf2b81b465ba60977460488429cf78b0bb71..a09b998362195a788ef6b6f07cc2973847abb29d 100755 (executable)
@@ -155,10 +155,29 @@ done
 [ -z "$ENTRY_TOKEN" ] && ENTRY_TOKEN="$MACHINE_ID"
 
 if [ -z "$layout" ]; then
-    # Administrative decision: if not present, some scripts generate into /boot.
-    if [ -d "$BOOT_ROOT/$ENTRY_TOKEN" ]; then
+    # No layout configured by the administrator. Let's try to figure it out
+    # automatically from metadata already contained in $BOOT_ROOT.
+    if [ -e "$BOOT_ROOT/loader/entries.srel" ]; then
+        read -r ENTRIES_SREL <"$BOOT_ROOT/loader/entries.srel"
+        if [ "$ENTRIES_SREL" = "type1" ]; then
+            # The loader/entries.srel file clearly indicates that the installed
+            # boot loader implements the proper standard upstream boot loader
+            # spec for Type #1 entries. Let's default to that, then.
+            layout="bls"
+        else
+            # The loader/entries.srel file indicates some other spec is
+            # implemented and owns the /loader/entries/ directory. Since we
+            # have no idea what that means, let's stay away from it by default.
+            layout="other"
+        fi
+    elif [ -d "$BOOT_ROOT/$ENTRY_TOKEN" ]; then
+        # If the metadata in $BOOT_ROOT doesn't tell us anything, then check if
+        # the entry token directory already exists. If so, let's assume it's
+        # the standard boot loader spec, too.
         layout="bls"
     else
+        # There's no metadata in $BOOT_ROOT, and apparently no entry token
+        # directory installed? Then we really don't know anything.
         layout="other"
     fi
 fi
index ebe8eecc9dbf592561856be001f7000d5e43050c..efb676e60b3d879939035111831f2124e469c1d4 100644 (file)
@@ -58,7 +58,6 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
 
         case SD_DHCP_OPTION_USER_CLASS: {
                 size_t total = 0;
-                char **s;
 
                 if (strv_isempty((char **) optval))
                         return -EINVAL;
index 735fd2cde2b2f3f2acfd92c2b74901fee3566ca1..4e6ee7970e272ed738abb3774952ef4a0729e8bb 100644 (file)
@@ -328,7 +328,6 @@ static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct i
 
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
         struct ia_header header;
-        const DHCP6Address *addr;
         size_t ia_buflen;
         uint8_t *ia_hdr;
         uint16_t len;
@@ -418,7 +417,6 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
 int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) {
         _cleanup_free_ uint8_t *p = NULL;
         size_t total = 0, offset = 0;
-        char * const *s;
 
         assert(buf);
         assert(*buf);
@@ -451,7 +449,6 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const
         _cleanup_free_ uint8_t *p = NULL;
         uint32_t enterprise_identifier;
         size_t total, offset;
-        char * const *s;
 
         assert(buf);
         assert(*buf);
index 0aa96b68385611eb59cc7eaaba5d7c91f41ae91c..ff59ec0f37e3f0ef0a750632f6b6ac6ab132d099 100644 (file)
@@ -119,7 +119,7 @@ fuzzers += [
          [libsystemd_network,
           libshared]],
 
-        [files('fuzz-dhcp-server-relay-message.c'),
+        [files('fuzz-dhcp-server-relay.c'),
          [libsystemd_network,
           libshared]],
 
index 5456c85641acaca3d9d5e1b5abfb33f9afa23190..cdf49fc5865fcde989d060aa8a4dba76261e4f88 100644 (file)
@@ -595,7 +595,6 @@ int sd_dhcp_client_set_user_class(
                 sd_dhcp_client *client,
                 char * const *user_class) {
 
-        char * const *p;
         char **s = NULL;
 
         assert_return(client, -EINVAL);
index 84f3199076bc6794972af15881f2b3394b40e9de..e6e0e08ab2ff51122e8fe59934f0a413471ef3b3 100644 (file)
@@ -916,13 +916,15 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d
 }
 
 int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
-        struct sd_dhcp_raw_option *cur, *option;
+        struct sd_dhcp_raw_option *option, *before = NULL;
 
         assert(lease);
 
         LIST_FOREACH(options, cur, lease->private_options) {
-                if (tag < cur->tag)
+                if (tag < cur->tag) {
+                        before = cur;
                         break;
+                }
                 if (tag == cur->tag) {
                         log_debug("Ignoring duplicate option, tagged %i.", tag);
                         return 0;
@@ -941,7 +943,7 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const vo
                 return -ENOMEM;
         }
 
-        LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
+        LIST_INSERT_BEFORE(options, lease->private_options, before, option);
         return 0;
 }
 
@@ -961,7 +963,6 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {
 int dhcp_lease_save(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;
         struct in_addr address;
         const struct in_addr *addresses;
         const void *client_id, *data;
index 163a208a44f2114f3d7c87ec9b6c38ad1db2b636..a0faa1ec86812ce89734b2422c89b8bd99d5db3a 100644 (file)
@@ -403,7 +403,6 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud
 }
 
 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) {
-        char * const *p;
         char **s;
 
         assert_return(client, -EINVAL);
@@ -425,7 +424,6 @@ int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const
 }
 
 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) {
-        char * const *p;
         char **s;
 
         assert_return(client, -EINVAL);
index 941de2f68cc2f930ace79004a06153b13c643850..8734e5a824b88aec2cd3251b2fe554d6796ff157 100644 (file)
@@ -41,7 +41,6 @@ static usec_t sec2usec(uint32_t sec) {
 
 static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
         uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
-        DHCP6Address *a;
 
         assert(lease);
         assert(lease->ia_na || lease->ia_pd);
@@ -107,11 +106,9 @@ int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *re
 }
 
 void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
-        DHCP6Address *a, *n;
-
         assert(ia);
 
-        LIST_FOREACH_SAFE(addresses, a, n, ia->addresses)
+        LIST_FOREACH(addresses, a, ia->addresses)
                 free(a);
 
         ia->addresses = NULL;
index c2f633a82b87ff7ae5bd202088159355bf9ffec5..370a10a013a43d4c40f0cc4faf3e573d34d235e8 100644 (file)
@@ -148,8 +148,6 @@ static be32_t usec_to_be32_sec(usec_t usec) {
 }
 
 static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_usec) {
-        sd_radv_route_prefix *rt;
-        sd_radv_prefix *p;
         struct sockaddr_in6 dst_addr = {
                 .sin6_family = AF_INET6,
                 .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
@@ -578,7 +576,7 @@ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
 
 int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
         _cleanup_free_ char *addr_p = NULL;
-        sd_radv_prefix *cur, *found = NULL;
+        sd_radv_prefix *found = NULL;
         int r;
 
         assert_return(ra, -EINVAL);
@@ -661,8 +659,6 @@ void sd_radv_remove_prefix(
                 const struct in6_addr *prefix,
                 unsigned char prefixlen) {
 
-        sd_radv_prefix *cur;
-
         if (!ra)
                 return;
 
@@ -685,7 +681,7 @@ void sd_radv_remove_prefix(
 
 int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
         _cleanup_free_ char *addr_p = NULL;
-        sd_radv_route_prefix *cur, *found = NULL;
+        sd_radv_route_prefix *found = NULL;
         int r;
 
         assert_return(ra, -EINVAL);
@@ -803,7 +799,6 @@ int sd_radv_set_dnssl(
 
         _cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL;
         size_t len = 0;
-        char **s;
         uint8_t *p;
 
         assert_return(ra, -EINVAL);
index 19040452bc37a24933744ea4affbfbdf899c2b0d..050eec3a7cd07322b30ac6971c2ac811cc8e609b 100644 (file)
@@ -413,8 +413,6 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
 
         r = sd_bus_creds_get_cmdline(c, &cmdline);
         if (r >= 0) {
-                char **i;
-
                 fprintf(f, "%sCommandLine=%s", prefix, color);
                 STRV_FOREACH(i, cmdline) {
                         if (i != cmdline)
@@ -479,8 +477,6 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
                 fprintf(f, "%sUniqueName=%s%s%s", prefix, color, c->unique_name, suffix);
 
         if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) {
-                char **i;
-
                 fprintf(f, "%sWellKnownNames=%s", prefix, color);
                 STRV_FOREACH(i, well_known) {
                         if (i != well_known)
index 12a50845db108ba7bb1caf5aaced5074b4c927b7..d4da60717eba00fd3a5000f1e83083c957848ed7 100644 (file)
@@ -139,8 +139,6 @@ static bool value_node_test(
                         return true;
 
                 if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
-                        char **i;
-
                         /* on kdbus we have the well known names list
                          * in the credentials, let's make use of that
                          * for an accurate match */
@@ -174,8 +172,6 @@ static bool value_node_test(
                 return false;
 
         case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: {
-                char **i;
-
                 STRV_FOREACH(i, value_strv)
                         if (streq_ptr(node->value.str, *i))
                                 return true;
@@ -386,8 +382,6 @@ int bus_match_run(
                 if (test_str)
                         found = hashmap_get(node->compare.children, test_str);
                 else if (test_strv) {
-                        char **i;
-
                         STRV_FOREACH(i, test_strv) {
                                 found = hashmap_get(node->compare.children, *i);
                                 if (found) {
index 96529b422be3ef71e4c926fda41d870d153d403a..b77372c3a07d5a83a99b2a2375a2fa5c97f95beb 100644 (file)
@@ -2815,7 +2815,6 @@ _public_ int sd_bus_message_append_string_memfd(
 }
 
 _public_ int sd_bus_message_append_strv(sd_bus_message *m, char **l) {
-        char **i;
         int r;
 
         assert_return(m, -EINVAL);
index b8524754191062ac641018589ddde2370fb1c0a2..e06a05db4bf46a8a4beb872b3290bf650933e4bc 100644 (file)
@@ -101,7 +101,6 @@ static int add_enumerated_to_set(
                 OrderedSet *s,
                 sd_bus_error *error) {
 
-        struct node_enumerator *c;
         int r;
 
         assert(bus);
@@ -109,7 +108,7 @@ static int add_enumerated_to_set(
         assert(s);
 
         LIST_FOREACH(enumerators, c, first) {
-                char **children = NULL, **k;
+                char **children = NULL;
                 sd_bus_slot *slot;
 
                 if (bus->nodes_modified)
@@ -173,7 +172,6 @@ static int add_subtree_to_set(
                 OrderedSet *s,
                 sd_bus_error *error) {
 
-        struct node *i;
         int r;
 
         assert(bus);
@@ -219,28 +217,26 @@ static int get_child_nodes(
                 const char *prefix,
                 struct node *n,
                 unsigned flags,
-                OrderedSet **_s,
+                OrderedSet **ret,
                 sd_bus_error *error) {
 
-        OrderedSet *s = NULL;
+        _cleanup_ordered_set_free_free_ OrderedSet *s = NULL;
         int r;
 
         assert(bus);
         assert(prefix);
         assert(n);
-        assert(_s);
+        assert(ret);
 
         s = ordered_set_new(&string_hash_ops);
         if (!s)
                 return -ENOMEM;
 
         r = add_subtree_to_set(bus, prefix, n, flags, s, error);
-        if (r < 0) {
-                ordered_set_free_free(s);
+        if (r < 0)
                 return r;
-        }
 
-        *_s = s;
+        *ret = TAKE_PTR(s);
         return 0;
 }
 
@@ -251,7 +247,6 @@ static int node_callbacks_run(
                 bool require_fallback,
                 bool *found_object) {
 
-        struct node_callback *c;
         int r;
 
         assert(bus);
@@ -807,7 +802,6 @@ static int property_get_all_callbacks_run(
                 bool *found_object) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        struct node_vtable *c;
         bool found_interface;
         int r;
 
@@ -887,8 +881,6 @@ static int bus_node_exists(
                 const char *path,
                 bool require_fallback) {
 
-        struct node_vtable *c;
-        struct node_callback *k;
         int r;
 
         assert(bus);
@@ -936,7 +928,6 @@ int introspect_path(
 
         _cleanup_ordered_set_free_ OrderedSet *s = NULL;
         _cleanup_(introspect_free) struct introspect intro = {};
-        struct node_vtable *c;
         bool empty;
         int r;
 
@@ -1057,7 +1048,6 @@ static int object_manager_serialize_path(
 
         const char *previous_interface = NULL;
         bool found_something = false;
-        struct node_vtable *i;
         struct node *n;
         int r;
 
@@ -1481,7 +1471,7 @@ int bus_process_object(sd_bus *bus, sd_bus_message *m) {
         return 1;
 }
 
-static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
+static struct nodebus_node_allocate(sd_bus *bus, const char *path) {
         struct node *n, *parent;
         const char *e;
         _cleanup_free_ char *s = NULL;
@@ -1507,8 +1497,7 @@ static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
         if (streq(path, "/"))
                 parent = NULL;
         else {
-                e = strrchr(path, '/');
-                assert(e);
+                assert_se(e = strrchr(path, '/'));
 
                 p = strndupa_safe(path, MAX(1, e - path));
 
@@ -1787,7 +1776,7 @@ static int add_object_vtable_internal(
                 void *userdata) {
 
         sd_bus_slot *s = NULL;
-        struct node_vtable *i, *existing = NULL;
+        struct node_vtable *existing = NULL;
         const sd_bus_vtable *v;
         struct node *n;
         int r;
@@ -2068,9 +2057,7 @@ static int emit_properties_changed_on_interface(
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         bool has_invalidating = false, has_changing = false;
         struct vtable_member key = {};
-        struct node_vtable *c;
         struct node *n;
-        char **property;
         void *u = NULL;
         int r;
 
@@ -2355,7 +2342,6 @@ static int object_added_append_all_prefix(
                 bool require_fallback) {
 
         const char *previous_interface = NULL;
-        struct node_vtable *c;
         struct node *n;
         int r;
 
@@ -2575,7 +2561,6 @@ static int object_removed_append_all_prefix(
                 bool require_fallback) {
 
         const char *previous_interface = NULL;
-        struct node_vtable *c;
         struct node *n;
         int r;
 
@@ -2753,7 +2738,6 @@ static int interfaces_added_append_one_prefix(
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         bool found_interface = false;
-        struct node_vtable *c;
         struct node *n;
         void *u = NULL;
         int r;
@@ -2852,7 +2836,6 @@ static int interfaces_added_append_one(
 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         struct node *object_manager;
-        char **i;
         int r;
 
         assert_return(bus, -EINVAL);
index 9e1d29cc1d0c851de65182b6184a19660828ed13..f96149473be2dfd41042bcb0cb27ab6d3630d2cc 100644 (file)
@@ -2783,7 +2783,6 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
 
 static int process_filter(sd_bus *bus, sd_bus_message *m) {
         _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
-        struct filter_callback *l;
         int r;
 
         assert(bus);
index 00d3c15a8f48235acd92b1e5d223ce533483967e..ec9ee83fac9798fc8e7f2c8536ddab6dee87a408 100644 (file)
@@ -27,7 +27,6 @@ TEST(bus_set_address_system_remote) {
 
         assert_se(sd_bus_new(&b) >= 0);
         if (!strv_isempty(saved_argv + 1)) {
-                char **a;
                 STRV_FOREACH(a, saved_argv + 1)
                         test_one_address(b, *a, 0, NULL);
                 return;
index 9c6437dbed3b90963171fe577b6cf2ecf60207c4..11378d89376ba33c1282ff50de9d6e51ac08c581 100644 (file)
@@ -3,11 +3,22 @@
 
 #include "sd-device.h"
 
-int device_enumerator_scan_devices(sd_device_enumerator *enumeartor);
-int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor);
+typedef enum MatchInitializedType {
+        MATCH_INITIALIZED_NO,     /* only devices without a db entry */
+        MATCH_INITIALIZED_YES,    /* only devices with a db entry */
+        MATCH_INITIALIZED_ALL,    /* all devices */
+        MATCH_INITIALIZED_COMPAT, /* only devices that have no devnode/ifindex or have a db entry */
+        _MATCH_INITIALIZED_MAX,
+        _MATCH_INITIALIZED_INVALID = -EINVAL,
+} MatchInitializedType;
+
+int device_enumerator_scan_devices(sd_device_enumerator *enumerator);
+int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator);
+int device_enumerator_scan_devices_and_subsystems(sd_device_enumerator *enumerator);
 int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device);
-int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator);
+int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type);
 int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent);
+int device_enumerator_add_prioritized_subsystem(sd_device_enumerator *enumerator, const char *subsystem);
 sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator);
 sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator);
 sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size_t *ret_n_devices);
index 4942ee43a871e10b0dd67ef6a6ad667ed3dfd823..14794fb3af5393f07650c85fb8ce80321dbfdea1 100644 (file)
@@ -20,6 +20,7 @@
 typedef enum DeviceEnumerationType {
         DEVICE_ENUMERATION_TYPE_DEVICES,
         DEVICE_ENUMERATION_TYPE_SUBSYSTEMS,
+        DEVICE_ENUMERATION_TYPE_ALL,
         _DEVICE_ENUMERATION_TYPE_MAX,
         _DEVICE_ENUMERATION_TYPE_INVALID = -EINVAL,
 } DeviceEnumerationType;
@@ -28,10 +29,13 @@ struct sd_device_enumerator {
         unsigned n_ref;
 
         DeviceEnumerationType type;
+        Hashmap *devices_by_syspath;
         sd_device **devices;
         size_t n_devices, current_device_index;
         bool scan_uptodate;
+        bool sorted;
 
+        char **prioritized_subsystems;
         Set *match_subsystem;
         Set *nomatch_subsystem;
         Hashmap *match_sysattr;
@@ -40,7 +44,7 @@ struct sd_device_enumerator {
         Set *match_sysname;
         Set *match_tag;
         Set *match_parent;
-        bool match_allow_uninitialized;
+        MatchInitializedType match_initialized;
 };
 
 _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
@@ -55,6 +59,7 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
         *enumerator = (sd_device_enumerator) {
                 .n_ref = 1,
                 .type = _DEVICE_ENUMERATION_TYPE_INVALID,
+                .match_initialized = MATCH_INITIALIZED_COMPAT,
         };
 
         *ret = TAKE_PTR(enumerator);
@@ -62,13 +67,29 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
         return 0;
 }
 
+static void device_unref_many(sd_device **devices, size_t n) {
+        assert(devices || n == 0);
+
+        for (size_t i = 0; i < n; i++)
+                sd_device_unref(devices[i]);
+}
+
+static void device_enumerator_unref_devices(sd_device_enumerator *enumerator) {
+        assert(enumerator);
+
+        hashmap_clear_with_destructor(enumerator->devices_by_syspath, sd_device_unref);
+        device_unref_many(enumerator->devices, enumerator->n_devices);
+        enumerator->devices = mfree(enumerator->devices);
+        enumerator->n_devices = 0;
+}
+
 static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumerator) {
         assert(enumerator);
 
-        for (size_t i = 0; i < enumerator->n_devices; i++)
-                sd_device_unref(enumerator->devices[i]);
+        device_enumerator_unref_devices(enumerator);
 
-        free(enumerator->devices);
+        hashmap_free(enumerator->devices_by_syspath);
+        strv_free(enumerator->prioritized_subsystems);
         set_free(enumerator->match_subsystem);
         set_free(enumerator->nomatch_subsystem);
         hashmap_free(enumerator->match_sysattr);
@@ -83,6 +104,24 @@ static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumer
 
 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_enumerator, sd_device_enumerator, device_enumerator_free);
 
+int device_enumerator_add_prioritized_subsystem(sd_device_enumerator *enumerator, const char *subsystem) {
+        int r;
+
+        assert(enumerator);
+        assert(subsystem);
+
+        if (strv_contains(enumerator->prioritized_subsystems, subsystem))
+                return 0;
+
+        r = strv_extend(&enumerator->prioritized_subsystems, subsystem);
+        if (r < 0)
+                return r;
+
+        enumerator->scan_uptodate = false;
+
+        return 1;
+}
+
 _public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) {
         Set **set;
         int r;
@@ -206,82 +245,214 @@ _public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumera
 _public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) {
         assert_return(enumerator, -EINVAL);
 
-        enumerator->match_allow_uninitialized = true;
+        enumerator->match_initialized = MATCH_INITIALIZED_ALL;
 
         enumerator->scan_uptodate = false;
 
         return 1;
 }
 
-int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) {
+int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type) {
         assert_return(enumerator, -EINVAL);
+        assert_return(type >= 0 && type < _MATCH_INITIALIZED_MAX, -EINVAL);
 
-        enumerator->match_allow_uninitialized = false;
+        enumerator->match_initialized = type;
 
         enumerator->scan_uptodate = false;
 
         return 1;
 }
 
-static int device_compare(sd_device * const *_a, sd_device * const *_b) {
-        sd_device *a = *(sd_device **)_a, *b = *(sd_device **)_b;
-        const char *devpath_a, *devpath_b, *sound_a;
-        bool delay_a, delay_b;
-        int r;
+static int sound_device_compare(const char *devpath_a, const char *devpath_b) {
+        const char *sound_a, *sound_b;
+        size_t prefix_len;
+
+        assert(devpath_a);
+        assert(devpath_b);
 
-        assert_se(sd_device_get_devpath(a, &devpath_a) >= 0);
-        assert_se(sd_device_get_devpath(b, &devpath_b) >= 0);
+        /* For sound cards the control device must be enumerated last to make sure it's the final
+         * device node that gets ACLs applied. Applications rely on this fact and use ACL changes on
+         * the control node as an indicator that the ACL change of the entire sound card completed. The
+         * kernel makes this guarantee when creating those devices, and hence we should too when
+         * enumerating them. */
 
         sound_a = strstr(devpath_a, "/sound/card");
-        if (sound_a) {
-                /* For sound cards the control device must be enumerated last to
-                 * make sure it's the final device node that gets ACLs applied.
-                 * Applications rely on this fact and use ACL changes on the
-                 * control node as an indicator that the ACL change of the
-                 * entire sound card completed. The kernel makes this guarantee
-                 * when creating those devices, and hence we should too when
-                 * enumerating them. */
-                sound_a += STRLEN("/sound/card");
-                sound_a = strchr(sound_a, '/');
-
-                if (sound_a) {
-                        unsigned prefix_len;
-
-                        prefix_len = sound_a - devpath_a;
-
-                        if (strneq(devpath_a, devpath_b, prefix_len)) {
-                                const char *sound_b;
-
-                                sound_b = devpath_b + prefix_len;
-
-                                r = CMP(!!startswith(sound_a, "/controlC"),
-                                        !!startswith(sound_b, "/controlC"));
-                                if (r != 0)
-                                        return r;
-                        }
-                }
-        }
+        if (!sound_a)
+                return 0;
+
+        sound_a += STRLEN("/sound/card");
+        sound_a = strchr(devpath_a, '/');
+        if (!sound_a)
+                return 0;
+
+        prefix_len = sound_a - devpath_a;
+
+        if (!strneq(devpath_a, devpath_b, prefix_len))
+                return 0;
+
+        sound_b = devpath_b + prefix_len;
+
+        return CMP(!!startswith(sound_a, "/controlC"),
+                   !!startswith(sound_b, "/controlC"));
+}
+
+static bool devpath_is_late_block(const char *devpath) {
+        assert(devpath);
+
+        return strstr(devpath, "/block/md") || strstr(devpath, "/block/dm-");
+}
+
+static int device_compare(sd_device * const *a, sd_device * const *b) {
+        const char *devpath_a, *devpath_b;
+        int r;
+
+        assert(a);
+        assert(b);
+        assert(*a);
+        assert(*b);
+
+        assert_se(sd_device_get_devpath(*(sd_device**) a, &devpath_a) >= 0);
+        assert_se(sd_device_get_devpath(*(sd_device**) b, &devpath_b) >= 0);
+
+        r = sound_device_compare(devpath_a, devpath_b);
+        if (r != 0)
+                return r;
 
         /* md and dm devices are enumerated after all other devices */
-        delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-");
-        delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-");
-        r = CMP(delay_a, delay_b);
+        r = CMP(devpath_is_late_block(devpath_a), devpath_is_late_block(devpath_b));
         if (r != 0)
                 return r;
 
-        return strcmp(devpath_a, devpath_b);
+        return path_compare(devpath_a, devpath_b);
+}
+
+static int enumerator_sort_devices(sd_device_enumerator *enumerator) {
+        size_t n_sorted = 0, n = 0;
+        sd_device **devices;
+        sd_device *device;
+        int r;
+
+        assert(enumerator);
+
+        if (enumerator->sorted)
+                return 0;
+
+        devices = new(sd_device*, hashmap_size(enumerator->devices_by_syspath));
+        if (!devices)
+                return -ENOMEM;
+
+        STRV_FOREACH(prioritized_subsystem, enumerator->prioritized_subsystems) {
+
+                for (;;) {
+                        const char *syspath;
+                        size_t m = n;
+
+                        HASHMAP_FOREACH_KEY(device, syspath, enumerator->devices_by_syspath) {
+                                _cleanup_free_ char *p = NULL;
+                                const char *subsys;
+
+                                if (sd_device_get_subsystem(device, &subsys) < 0)
+                                        continue;
+
+                                if (!streq(subsys, *prioritized_subsystem))
+                                        continue;
+
+                                devices[n++] = sd_device_ref(device);
+
+                                for (;;) {
+                                        _cleanup_free_ char *q = NULL;
+
+                                        r = path_extract_directory(p ?: syspath, &q);
+                                        if (r == -EADDRNOTAVAIL)
+                                                break;
+                                        if (r < 0)
+                                                goto failed;
+
+                                        device = hashmap_get(enumerator->devices_by_syspath, q);
+                                        if (device)
+                                                devices[n++] = sd_device_ref(device);
+
+                                        free_and_replace(p, q);
+                                }
+
+                                break;
+                        }
+
+                        /* We cannot remove multiple entries in the loop HASHMAP_FOREACH_KEY() above. */
+                        for (size_t i = m; i < n; i++) {
+                                r = sd_device_get_syspath(devices[i], &syspath);
+                                if (r < 0)
+                                        goto failed;
+
+                                assert_se(hashmap_remove(enumerator->devices_by_syspath, syspath) == devices[i]);
+                                sd_device_unref(devices[i]);
+                        }
+
+                        if (m == n)
+                                break;
+                }
+
+                typesafe_qsort(devices + n_sorted, n - n_sorted, device_compare);
+                n_sorted = n;
+        }
+
+        HASHMAP_FOREACH(device, enumerator->devices_by_syspath)
+                devices[n++] = sd_device_ref(device);
+
+        /* Move all devices back to the hashmap. Otherwise, devices added by
+         * udev_enumerate_add_syspath() -> device_enumerator_add_device() may not be listed. */
+        for (size_t i = 0; i < n_sorted; i++) {
+                const char *syspath;
+
+                r = sd_device_get_syspath(devices[i], &syspath);
+                if (r < 0)
+                        goto failed;
+
+                r = hashmap_put(enumerator->devices_by_syspath, syspath, devices[i]);
+                if (r < 0)
+                        goto failed;
+                assert(r > 0);
+
+                sd_device_ref(devices[i]);
+        }
+
+        typesafe_qsort(devices + n_sorted, n - n_sorted, device_compare);
+
+        device_unref_many(enumerator->devices, enumerator->n_devices);
+
+        enumerator->n_devices = n;
+        free_and_replace(enumerator->devices, devices);
+
+        enumerator->sorted = true;
+        return 0;
+
+failed:
+        device_unref_many(devices, n);
+        free(devices);
+        return r;
 }
 
 int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) {
+        const char *syspath;
+        int r;
+
         assert_return(enumerator, -EINVAL);
         assert_return(device, -EINVAL);
 
-        if (!GREEDY_REALLOC(enumerator->devices, enumerator->n_devices + 1))
-                return -ENOMEM;
+        r = sd_device_get_syspath(device, &syspath);
+        if (r < 0)
+                return r;
 
-        enumerator->devices[enumerator->n_devices++] = sd_device_ref(device);
+        r = hashmap_ensure_put(&enumerator->devices_by_syspath, &string_hash_ops, syspath, device);
+        if (IN_SET(r, -EEXIST, 0))
+                return 0;
+        if (r < 0)
+                return r;
 
-        return 0;
+        sd_device_ref(device);
+
+        enumerator->sorted = false;
+        return 1;
 }
 
 static bool match_property(sd_device_enumerator *enumerator, sd_device *device) {
@@ -344,10 +515,42 @@ static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname)
         return false;
 }
 
+static int match_initialized(sd_device_enumerator *enumerator, sd_device *device) {
+        int r;
+
+        assert(enumerator);
+        assert(device);
+
+        if (enumerator->match_initialized == MATCH_INITIALIZED_ALL)
+                return true;
+
+        r = sd_device_get_is_initialized(device);
+        if (r == -ENOENT) /* this is necessarily racey, so ignore missing devices */
+                return false;
+        if (r < 0)
+                return r;
+
+        if (enumerator->match_initialized == MATCH_INITIALIZED_COMPAT) {
+                /* only devices that have no devnode/ifindex or have a db entry are accepted. */
+                if (r > 0)
+                        return true;
+
+                if (sd_device_get_devnum(device, NULL) >= 0)
+                        return true;
+
+                if (sd_device_get_ifindex(device, NULL) >= 0)
+                        return true;
+
+                return false;
+        }
+
+        return (enumerator->match_initialized == MATCH_INITIALIZED_NO) == (r == 0);
+}
+
 static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) {
         _cleanup_closedir_ DIR *dir = NULL;
         char *path;
-        int r = 0;
+        int k, r = 0;
 
         assert(enumerator);
         assert(basedir);
@@ -368,7 +571,6 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator,
         FOREACH_DIRENT_ALL(de, dir, return -errno) {
                 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
                 char syspath[strlen(path) + 1 + strlen(de->d_name) + 1];
-                int initialized, k;
 
                 if (de->d_name[0] == '.')
                         continue;
@@ -387,31 +589,13 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator,
                         continue;
                 }
 
-                initialized = sd_device_get_is_initialized(device);
-                if (initialized < 0) {
-                        if (initialized != -ENOENT)
-                                /* this is necessarily racey, so ignore missing devices */
-                                r = initialized;
-
+                k = match_initialized(enumerator, device);
+                if (k <= 0) {
+                        if (k < 0)
+                                r = k;
                         continue;
                 }
 
-                /*
-                 * All devices with a device node or network interfaces
-                 * possibly need udev to adjust the device node permission
-                 * or context, or rename the interface before it can be
-                 * reliably used from other processes.
-                 *
-                 * For now, we can only check these types of devices, we
-                 * might not store a database, and have no way to find out
-                 * for all other types of devices.
-                 */
-                if (!enumerator->match_allow_uninitialized &&
-                    !initialized &&
-                    (sd_device_get_devnum(device, NULL) >= 0 ||
-                     sd_device_get_ifindex(device, NULL) >= 0))
-                        continue;
-
                 if (!device_match_parent(device, enumerator->match_parent, NULL))
                         continue;
 
@@ -670,57 +854,21 @@ static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) {
 }
 
 static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
-        int r = 0;
+        int k, r = 0;
 
         log_debug("sd-device-enumerator: Scan all dirs");
 
-        if (access("/sys/subsystem", F_OK) >= 0) {
-                /* we have /subsystem/, forget all the old stuff */
-                r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-device-enumerator: Failed to scan /sys/subsystem: %m");
-        } else {
-                int k;
+        k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
+        if (k < 0)
+                r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
 
-                k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
-                if (k < 0)
-                        r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
-
-                k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
-                if (k < 0)
-                        r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m");
-        }
+        k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
+        if (k < 0)
+                r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m");
 
         return r;
 }
 
-static void device_enumerator_dedup_devices(sd_device_enumerator *enumerator) {
-        sd_device **a, **b, **end;
-
-        assert(enumerator);
-
-        if (enumerator->n_devices <= 1)
-                return;
-
-        a = enumerator->devices + 1;
-        b = enumerator->devices;
-        end = enumerator->devices + enumerator->n_devices;
-
-        for (; a < end; a++) {
-                const char *devpath_a, *devpath_b;
-
-                assert_se(sd_device_get_devpath(*a, &devpath_a) >= 0);
-                assert_se(sd_device_get_devpath(*b, &devpath_b) >= 0);
-
-                if (path_equal(devpath_a, devpath_b))
-                        sd_device_unref(*a);
-                else
-                        *(++b) = *a;
-        }
-
-        enumerator->n_devices = b - enumerator->devices + 1;
-}
-
 int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
         int r = 0, k;
 
@@ -730,10 +878,7 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
             enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES)
                 return 0;
 
-        for (size_t i = 0; i < enumerator->n_devices; i++)
-                sd_device_unref(enumerator->devices[i]);
-
-        enumerator->n_devices = 0;
+        device_enumerator_unref_devices(enumerator);
 
         if (!set_isempty(enumerator->match_tag)) {
                 k = enumerator_scan_devices_tags(enumerator);
@@ -749,9 +894,6 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
                         r = k;
         }
 
-        typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
-        device_enumerator_dedup_devices(enumerator);
-
         enumerator->scan_uptodate = true;
         enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES;
 
@@ -759,12 +901,12 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {
 }
 
 _public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) {
-        int r;
-
         assert_return(enumerator, NULL);
 
-        r = device_enumerator_scan_devices(enumerator);
-        if (r < 0)
+        if (device_enumerator_scan_devices(enumerator) < 0)
+                return NULL;
+
+        if (enumerator_sort_devices(enumerator) < 0)
                 return NULL;
 
         enumerator->current_device_index = 0;
@@ -779,6 +921,7 @@ _public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *e
         assert_return(enumerator, NULL);
 
         if (!enumerator->scan_uptodate ||
+            !enumerator->sorted ||
             enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES ||
             enumerator->current_device_index + 1 >= enumerator->n_devices)
                 return NULL;
@@ -787,7 +930,6 @@ _public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *e
 }
 
 int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
-        const char *subsysdir;
         int r = 0, k;
 
         assert(enumerator);
@@ -796,10 +938,7 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
             enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS)
                 return 0;
 
-        for (size_t i = 0; i < enumerator->n_devices; i++)
-                sd_device_unref(enumerator->devices[i]);
-
-        enumerator->n_devices = 0;
+        device_enumerator_unref_devices(enumerator);
 
         /* modules */
         if (match_subsystem(enumerator, "module")) {
@@ -808,28 +947,20 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
                         r = log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m");
         }
 
-        if (access("/sys/subsystem", F_OK) >= 0)
-                subsysdir = "subsystem";
-        else
-                subsysdir = "bus";
-
         /* subsystems (only buses support coldplug) */
         if (match_subsystem(enumerator, "subsystem")) {
-                k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL);
+                k = enumerator_scan_dir_and_add_devices(enumerator, "bus", NULL, NULL);
                 if (k < 0)
                         r = log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m");
         }
 
         /* subsystem drivers */
         if (match_subsystem(enumerator, "drivers")) {
-                k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers");
+                k = enumerator_scan_dir(enumerator, "bus", "drivers", "drivers");
                 if (k < 0)
                         r = log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m");
         }
 
-        typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare);
-        device_enumerator_dedup_devices(enumerator);
-
         enumerator->scan_uptodate = true;
         enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS;
 
@@ -837,12 +968,12 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) {
 }
 
 _public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) {
-        int r;
-
         assert_return(enumerator, NULL);
 
-        r = device_enumerator_scan_subsystems(enumerator);
-        if (r < 0)
+        if (device_enumerator_scan_subsystems(enumerator) < 0)
+                return NULL;
+
+        if (enumerator_sort_devices(enumerator) < 0)
                 return NULL;
 
         enumerator->current_device_index = 0;
@@ -857,6 +988,7 @@ _public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator
         assert_return(enumerator, NULL);
 
         if (!enumerator->scan_uptodate ||
+            !enumerator->sorted ||
             enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS ||
             enumerator->current_device_index + 1 >= enumerator->n_devices)
                 return NULL;
@@ -864,12 +996,67 @@ _public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator
         return enumerator->devices[++enumerator->current_device_index];
 }
 
+int device_enumerator_scan_devices_and_subsystems(sd_device_enumerator *enumerator) {
+        int r = 0, k;
+
+        assert(enumerator);
+
+        if (enumerator->scan_uptodate &&
+            enumerator->type == DEVICE_ENUMERATION_TYPE_ALL)
+                return 0;
+
+        device_enumerator_unref_devices(enumerator);
+
+        if (!set_isempty(enumerator->match_tag)) {
+                k = enumerator_scan_devices_tags(enumerator);
+                if (k < 0)
+                        r = k;
+        } else if (enumerator->match_parent) {
+                k = enumerator_scan_devices_children(enumerator);
+                if (k < 0)
+                        r = k;
+        } else {
+                k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
+                if (k < 0)
+                        r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m");
+
+                k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
+                if (k < 0)
+                        r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
+
+                if (match_subsystem(enumerator, "module")) {
+                        k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL);
+                        if (k < 0)
+                                r = log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m");
+                }
+                if (match_subsystem(enumerator, "subsystem")) {
+                        k = enumerator_scan_dir_and_add_devices(enumerator, "bus", NULL, NULL);
+                        if (k < 0)
+                                r = log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m");
+                }
+
+                if (match_subsystem(enumerator, "drivers")) {
+                        k = enumerator_scan_dir(enumerator, "bus", "drivers", "drivers");
+                        if (k < 0)
+                                r = log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m");
+                }
+        }
+
+        enumerator->scan_uptodate = true;
+        enumerator->type = DEVICE_ENUMERATION_TYPE_ALL;
+
+        return r;
+}
+
 sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) {
         assert_return(enumerator, NULL);
 
         if (!enumerator->scan_uptodate)
                 return NULL;
 
+        if (enumerator_sort_devices(enumerator) < 0)
+                return NULL;
+
         enumerator->current_device_index = 0;
 
         if (enumerator->n_devices == 0)
@@ -882,6 +1069,7 @@ sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) {
         assert_return(enumerator, NULL);
 
         if (!enumerator->scan_uptodate ||
+            !enumerator->sorted ||
             enumerator->current_device_index + 1 >= enumerator->n_devices)
                 return NULL;
 
@@ -895,6 +1083,9 @@ sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size
         if (!enumerator->scan_uptodate)
                 return NULL;
 
+        if (enumerator_sort_devices(enumerator) < 0)
+                return NULL;
+
         *ret_n_devices = enumerator->n_devices;
         return enumerator->devices;
 }
index c383833740e622ed87d822617f9b23854373f39d..d08d81f53aa8dc581d5a2abdab7619d12a8bb7f9 100644 (file)
@@ -437,7 +437,6 @@ static int device_verify(sd_device *device) {
 
 int device_new_from_strv(sd_device **ret, char **strv) {
         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
-        char **key;
         const char *major = NULL, *minor = NULL;
         int r;
 
index 0e53406a9485ee235fd8bfb765adfe81b3597e90..8c24e7db635ffe9b836683a23115442a5079a3f1 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "event-source.h"
 #include "event-util.h"
+#include "fd-util.h"
 #include "log.h"
 #include "string-util.h"
 
@@ -121,3 +122,41 @@ int event_source_is_enabled(sd_event_source *s) {
 
         return sd_event_source_get_enabled(s, NULL);
 }
+
+int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata) {
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+        _cleanup_close_ int fd = -1;
+        int r;
+
+        assert(e);
+
+        /* Allocates an IO event source that gets woken up whenever the clock changes. Needs to be recreated on each event */
+
+        fd = time_change_fd();
+        if (fd < 0)
+                return fd;
+
+        r = sd_event_add_io(e, &s, fd, EPOLLIN, callback, userdata);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_io_fd_own(s, true);
+        if (r < 0)
+                return r;
+
+        TAKE_FD(fd);
+
+        r = sd_event_source_set_description(s, "time-change");
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = TAKE_PTR(s);
+        else {
+                r = sd_event_source_set_floating(s, true);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
index 64a41992449186cd920321427d33526b9950f4b3..abd043bdcc9341552ca85e7eb861b694cc7839ce 100644 (file)
@@ -29,3 +29,5 @@ int event_reset_time_relative(
                 bool force_reset);
 int event_source_disable(sd_event_source *s);
 int event_source_is_enabled(sd_event_source *s);
+
+int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata);
index 82056998bd64cce6e17c84cd61ee6d1368060321..38b2ef510b7c6de7197ea3d1d65aee4b9f2609f5 100644 (file)
@@ -1920,7 +1920,6 @@ static int event_make_inode_data(
 static uint32_t inode_data_determine_mask(struct inode_data *d) {
         bool excl_unlink = true;
         uint32_t combined = 0;
-        sd_event_source *s;
 
         assert(d);
 
@@ -3458,9 +3457,7 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
                         /* The queue overran, let's pass this event to all event sources connected to this inotify
                          * object */
 
-                        HASHMAP_FOREACH(inode_data, d->inodes) {
-                                sd_event_source *s;
-
+                        HASHMAP_FOREACH(inode_data, d->inodes)
                                 LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) {
 
                                         if (event_source_is_offline(s))
@@ -3470,10 +3467,8 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
                                         if (r < 0)
                                                 return r;
                                 }
-                        }
                 } else {
                         struct inode_data *inode_data;
-                        sd_event_source *s;
 
                         /* Find the inode object for this watch descriptor. If IN_IGNORED is set we also remove it from
                          * our watch descriptor table. */
@@ -3521,7 +3516,6 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
 }
 
 static int process_inotify(sd_event *e) {
-        struct inotify_data *d;
         int r, done = 0;
 
         assert(e);
@@ -3906,6 +3900,7 @@ static int epoll_wait_usec(
         int msec;
 #if 0
         static bool epoll_pwait2_absent = false;
+        int r;
 
         /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not.
          *
@@ -3914,12 +3909,10 @@ static int epoll_wait_usec(
          * https://github.com/systemd/systemd/issues/19052. */
 
         if (!epoll_pwait2_absent && timeout != USEC_INFINITY) {
-                struct timespec ts;
-
                 r = epoll_pwait2(fd,
                                  events,
                                  maxevents,
-                                 timespec_store(&ts, timeout),
+                                 TIMESPEC_STORE(timeout),
                                  NULL);
                 if (r >= 0)
                         return r;
index e2397f47e3a41e3bc974891122363d35b45fde3c..b988ee3bba4965b794b729ba664f7ed97194f992 100644 (file)
@@ -439,7 +439,6 @@ error:
 
 int catalog_update(const char* database, const char* root, const char* const* dirs) {
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         _cleanup_(strbuf_freep) struct strbuf *sb = NULL;
         _cleanup_ordered_hashmap_free_free_free_ OrderedHashmap *h = NULL;
         _cleanup_free_ CatalogItem *items = NULL;
@@ -706,7 +705,6 @@ int catalog_list(FILE *f, const char *database, bool oneline) {
 }
 
 int catalog_list_items(FILE *f, const char *database, bool oneline, char **items) {
-        char **item;
         int r = 0;
 
         STRV_FOREACH(item, items) {
index ac8ad145eda04e422dd17900f23d263d0ec3d4c4..27875fb9e71cc4a1976f51b77e9d180afc276581 100644 (file)
@@ -3336,6 +3336,9 @@ int journal_file_open(
         if (!IN_SET((flags & O_ACCMODE), O_RDONLY, O_RDWR))
                 return -EINVAL;
 
+        if ((flags & O_ACCMODE) == O_RDONLY && FLAGS_SET(flags, O_CREAT))
+                return -EINVAL;
+
         if (fname && (flags & O_CREAT) && !endswith(fname, ".journal"))
                 return -EINVAL;
 
@@ -3421,9 +3424,9 @@ int journal_file_open(
                  * or so, we likely fail quickly than block for long. For regular files O_NONBLOCK has no effect, hence
                  * it doesn't hurt in that case. */
 
-                f->fd = open(f->path, f->flags|O_CLOEXEC|O_NONBLOCK, f->mode);
+                f->fd = openat_report_new(AT_FDCWD, f->path, f->flags|O_CLOEXEC|O_NONBLOCK, f->mode, &newly_created);
                 if (f->fd < 0) {
-                        r = -errno;
+                        r = f->fd;
                         goto fail;
                 }
 
@@ -3433,6 +3436,19 @@ int journal_file_open(
                 r = fd_nonblock(f->fd, false);
                 if (r < 0)
                         goto fail;
+
+                if (!newly_created) {
+                        r = journal_file_fstat(f);
+                        if (r < 0)
+                                goto fail;
+                }
+        } else {
+                r = journal_file_fstat(f);
+                if (r < 0)
+                        goto fail;
+
+                /* If we just got the fd passed in, we don't really know if we created the file anew */
+                newly_created = f->last_stat.st_size == 0 && f->writable;
         }
 
         f->cache_fd = mmap_cache_add_fd(mmap_cache, f->fd, prot_from_flags(flags));
@@ -3441,12 +3457,7 @@ int journal_file_open(
                 goto fail;
         }
 
-        r = journal_file_fstat(f);
-        if (r < 0)
-                goto fail;
-
-        if (f->last_stat.st_size == 0 && f->writable) {
-
+        if (newly_created) {
                 (void) journal_file_warn_btrfs(f);
 
                 /* Let's attach the creation time to the journal file, so that the vacuuming code knows the age of this
@@ -3473,8 +3484,6 @@ int journal_file_open(
                 r = journal_file_fstat(f);
                 if (r < 0)
                         goto fail;
-
-                newly_created = true;
         }
 
         if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
index 124ee3f8c1b948f6c2ac04da01c8872cc4b695a9..82407f9396ebb58d0eadda7511825ef8d74dd15d 100644 (file)
@@ -86,7 +86,6 @@ MMapCache* mmap_cache_new(void) {
 }
 
 static void window_unlink(Window *w) {
-        Context *c;
 
         assert(w);
 
@@ -306,7 +305,7 @@ static int find_mmap(
                 size_t size,
                 void **ret) {
 
-        Window *w;
+        Window *found = NULL;
 
         assert(f);
         assert(f->cache);
@@ -318,16 +317,18 @@ static int find_mmap(
                 return -EIO;
 
         LIST_FOREACH(by_fd, w, f->windows)
-                if (window_matches(w, offset, size))
+                if (window_matches(w, offset, size)) {
+                        found = w;
                         break;
+                }
 
-        if (!w)
+        if (!found)
                 return 0;
 
-        context_attach_window(f->cache, c, w);
-        w->keep_always = w->keep_always || keep_always;
+        context_attach_window(f->cache, c, found);
+        found->keep_always = found->keep_always || keep_always;
 
-        *ret = (uint8_t*) w->ptr + (offset - w->offset);
+        *ret = (uint8_t*) found->ptr + (offset - found->offset);
         f->cache->n_window_list_hit++;
 
         return 1;
@@ -494,8 +495,6 @@ static void mmap_cache_process_sigbus(MMapCache *m) {
 
                 ours = false;
                 HASHMAP_FOREACH(f, m->fds) {
-                        Window *w;
-
                         LIST_FOREACH(by_fd, w, f->windows) {
                                 if ((uint8_t*) addr >= (uint8_t*) w->ptr &&
                                     (uint8_t*) addr < (uint8_t*) w->ptr + w->size) {
@@ -523,8 +522,6 @@ static void mmap_cache_process_sigbus(MMapCache *m) {
                 return;
 
         HASHMAP_FOREACH(f, m->fds) {
-                Window *w;
-
                 if (!f->sigbus)
                         continue;
 
index 399e33fa2b8dc9f1d305232af82755b487b59f93..62755ed9659986d764914c481178c1b4b4b62e65 100644 (file)
@@ -233,7 +233,7 @@ static Match *match_free_if_empty(Match *m) {
 }
 
 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
-        Match *l3, *l4, *add_here = NULL, *m = NULL;
+        Match *add_here = NULL, *m = NULL;
         uint64_t hash;
 
         assert_return(j, -EINVAL);
@@ -372,7 +372,6 @@ _public_ int sd_journal_add_disjunction(sd_journal *j) {
 
 static char *match_make_string(Match *m) {
         char *p = NULL, *r;
-        Match *i;
         bool enclose = false;
 
         if (!m)
@@ -518,7 +517,6 @@ static int next_for_match(
                 return journal_file_move_to_entry_by_offset_for_data(f, d, after_offset, direction, ret, offset);
 
         } else if (m->type == MATCH_OR_TERM) {
-                Match *i;
 
                 /* Find the earliest match beyond after_offset */
 
@@ -538,7 +536,7 @@ static int next_for_match(
                         return 0;
 
         } else if (m->type == MATCH_AND_TERM) {
-                Match *i, *last_moved;
+                Match *last_moved;
 
                 /* Always jump to the next matching entry and repeat
                  * this until we find an offset that matches for all
@@ -636,7 +634,6 @@ static int find_location_for_match(
         } else if (m->type == MATCH_OR_TERM) {
                 uint64_t np = 0;
                 Object *n;
-                Match *i;
 
                 /* Find the earliest match */
 
@@ -667,7 +664,6 @@ static int find_location_for_match(
                 return 1;
 
         } else {
-                Match *i;
                 uint64_t np = 0;
 
                 assert(m->type == MATCH_AND_TERM);
@@ -2035,7 +2031,6 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
 
 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
         _cleanup_(sd_journal_closep) sd_journal *j = NULL;
-        const char **path;
         int r;
 
         assert_return(ret, -EINVAL);
index 00129ff77dacb9d679893d7e47a69bdd73344981..f134f0a8c98aadf94de69f3fda475282690e41a9 100644 (file)
@@ -269,13 +269,11 @@ _public_ int sd_uid_get_state(uid_t uid, char**state) {
                 return r;
 
         r = parse_env_file(NULL, p, "STATE", &s);
-        if (r == -ENOENT) {
+        if (r == -ENOENT)
                 r = free_and_strdup(&s, "offline");
-                if (r < 0)
-                        return r;
-        } else if (r < 0)
+        if (r < 0)
                 return r;
-        else if (isempty(s))
+        if (isempty(s))
                 return -EIO;
 
         *state = TAKE_PTR(s);
index 30d159ec98f54a4717fae89dbd079ee166da8302..e846399a53c6548c9a759080e998ac1aec15ded6 100644 (file)
@@ -259,7 +259,6 @@ int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type,
 
 int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned short type, char * const *data) {
         size_t length, size;
-        char * const *p;
         int r;
 
         assert_return(m, -EINVAL);
index 4cd4e7340550e21ffcdb01dadc28143a3859a28d..5271c1492d42a75d1c3497934215ba4f3931b83b 100644 (file)
@@ -424,7 +424,6 @@ static int process_reply(sd_netlink *nl, sd_netlink_message *m) {
 }
 
 static int process_match(sd_netlink *nl, sd_netlink_message *m) {
-        struct match_callback *c;
         uint16_t type;
         uint8_t cmd;
         int r;
index ff1e0d5f8e464a29da5ef558820b112bb572cca5..8d9cfbc0afeae25134ed99ec6cd3ef8886e2626c 100644 (file)
@@ -669,7 +669,7 @@ _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***path
         if (!n)
                 return -ENOMEM;
 
-        char **i, **j = n;
+        char **j = n;
         STRV_FOREACH(i, l) {
                 *j = path_join(*i, suffix);
                 if (!*j)
index 2dc695bd03f9813c0b4b3d7e61525cc04b20fd05..d71a31c566a75397c004f44224c5e0a1819e8525 100644 (file)
@@ -365,7 +365,7 @@ _public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev
 
         assert_return(udev_enumerate, -EINVAL);
 
-        r = device_enumerator_add_match_is_initialized(udev_enumerate->enumerator);
+        r = device_enumerator_add_match_is_initialized(udev_enumerate->enumerator, MATCH_INITIALIZED_COMPAT);
         if (r < 0)
                 return r;
 
index 69efc1013c1e724336486391c34bce20ee4803b0..d3b63b1f8fba31dfb66e9ae6e8cd870c34ee2a67 100644 (file)
@@ -110,8 +110,6 @@ struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *
 }
 
 void udev_list_cleanup(struct udev_list *list) {
-        struct udev_list_entry *i, *n;
-
         if (!list)
                 return;
 
@@ -119,7 +117,7 @@ void udev_list_cleanup(struct udev_list *list) {
                 list->uptodate = false;
                 hashmap_clear_with_destructor(list->unique_entries, udev_list_entry_free);
         } else
-                LIST_FOREACH_SAFE(entries, i, n, list->entries)
+                LIST_FOREACH(entries, i, list->entries)
                         udev_list_entry_free(i);
 }
 
index 10d2ed7aece239295cd01ec72c6754bc38798646..9759f461631e8c6114e440a18d8ffca1b804dcc3 100644 (file)
@@ -157,7 +157,6 @@ int locale_read_data(Context *c, sd_bus_message *m) {
 int vconsole_read_data(Context *c, sd_bus_message *m) {
         struct stat st;
         usec_t t;
-        int r;
 
         /* Do not try to re-read the file within single bus operation. */
         if (m) {
@@ -185,13 +184,9 @@ int vconsole_read_data(Context *c, sd_bus_message *m) {
         c->vc_mtime = t;
         context_free_vconsole(c);
 
-        r = parse_env_file(NULL, "/etc/vconsole.conf",
-                           "KEYMAP",        &c->vc_keymap,
-                           "KEYMAP_TOGGLE", &c->vc_keymap_toggle);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return parse_env_file(NULL, "/etc/vconsole.conf",
+                              "KEYMAP",        &c->vc_keymap,
+                              "KEYMAP_TOGGLE", &c->vc_keymap_toggle);
 }
 
 int x11_read_data(Context *c, sd_bus_message *m) {
index b5624209dc155fbc952f8e1a76b43655de292e59..661d54c27dd054dd14c15c400b533477037857a2 100644 (file)
@@ -98,8 +98,6 @@ static void print_status_info(StatusInfo *i) {
         if (strv_isempty(i->locale))
                 puts("   System Locale: n/a");
         else {
-                char **j;
-
                 printf("   System Locale: %s\n", i->locale[0]);
                 STRV_FOREACH(j, i->locale + 1)
                         printf("                  %s\n", *j);
index c228385d0efd1fb1377c0522d314f063362eb7d0..5d95da15600b1aabb0a42f05253e1d42b0426d6b 100644 (file)
@@ -346,7 +346,6 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
         Context *c = userdata;
         bool modified = false;
         int interactive, r;
-        char **i;
         bool use_localegen;
 
         assert(m);
index 6af0ad2f3ccca54b82bcf4307e8f5b8127e7ad59..83f25135a2e68e1c9ef385ef748001a4ba38f456 100644 (file)
@@ -601,7 +601,6 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
                 printf("\t   State: %s\n", i.state);
 
         if (!strv_isempty(i.sessions)) {
-                char **l;
                 printf("\tSessions:");
 
                 STRV_FOREACH(l, i.sessions)
@@ -662,7 +661,6 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line)
         printf("%s\n", strna(i.id));
 
         if (!strv_isempty(i.sessions)) {
-                char **l;
                 printf("\tSessions:");
 
                 STRV_FOREACH(l, i.sessions) {
index 2ed4eeddfe67390cfeb7fb693a39e5bc3b4d8b48..a1c7d0a8836e407b25fb419eebfec5fb95acf333 100644 (file)
@@ -3813,8 +3813,8 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
                 if (streq_ptr(path, user->service_job)) {
                         user->service_job = mfree(user->service_job);
 
-                        LIST_FOREACH(sessions_by_user, session, user->sessions)
-                                (void) session_jobs_reply(session, id, unit, NULL /* don't propagate user service failures to the client */);
+                        LIST_FOREACH(sessions_by_user, s, user->sessions)
+                                (void) session_jobs_reply(s, id, unit, NULL /* don't propagate user service failures to the client */);
 
                         user_save(user);
                 }
@@ -3957,7 +3957,6 @@ int manager_start_scope(
                 char **job) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
-        char **i;
         int r;
 
         assert(manager);
index 982a7721945749eb7ed0a070c846c8cf8951a050..592ee3b9d65e9c3645fe716a34fcfac7f78b7250 100644 (file)
@@ -67,7 +67,6 @@ void device_free(Device *d) {
 }
 
 void device_attach(Device *d, Seat *s) {
-        Device *i;
         bool had_master;
 
         assert(d);
index f82991c5dab763e02a168b68c6fcf8bc418c3926..cd20693cbeab39dc742dfcd1af0c27af2b46871a 100644 (file)
@@ -56,7 +56,6 @@ static int property_get_sessions(
                 sd_bus_error *error) {
 
         Seat *s = userdata;
-        Session *session;
         int r;
 
         assert(bus);
index 58912b85b359dc279e8e4c772655b8ab8ec7b9b7..8e603f1f3834ae9082e00550ed66857f4a789f77 100644 (file)
@@ -122,8 +122,6 @@ int seat_save(Seat *s) {
         }
 
         if (s->sessions) {
-                Session *i;
-
                 fputs("SESSIONS=", f);
                 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
                         fprintf(f,
@@ -349,7 +347,7 @@ int seat_switch_to_previous(Seat *s) {
 }
 
 int seat_active_vt_changed(Seat *s, unsigned vtnr) {
-        Session *i, *new_active = NULL;
+        Session *new_active = NULL;
         int r;
 
         assert(s);
@@ -368,7 +366,7 @@ int seat_active_vt_changed(Seat *s, unsigned vtnr) {
                         break;
                 }
 
-        if (!new_active) {
+        if (!new_active)
                 /* no running one? then we can't decide which one is the
                  * active one, let the first one win */
                 LIST_FOREACH(sessions_by_seat, i, s->sessions)
@@ -376,7 +374,6 @@ int seat_active_vt_changed(Seat *s, unsigned vtnr) {
                                 new_active = i;
                                 break;
                         }
-        }
 
         r = seat_set_active(s, new_active);
         manager_spawn_autovt(s->manager, vtnr);
@@ -467,7 +464,6 @@ int seat_stop(Seat *s, bool force) {
 }
 
 int seat_stop_sessions(Seat *s, bool force) {
-        Session *session;
         int r = 0, k;
 
         assert(s);
@@ -482,7 +478,6 @@ int seat_stop_sessions(Seat *s, bool force) {
 }
 
 void seat_evict_position(Seat *s, Session *session) {
-        Session *iter;
         unsigned pos = session->position;
 
         session->position = 0;
@@ -496,12 +491,11 @@ void seat_evict_position(Seat *s, Session *session) {
                 /* There might be another session claiming the same
                  * position (eg., during gdm->session transition), so let's look
                  * for it and set it on the free slot. */
-                LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
+                LIST_FOREACH(sessions_by_seat, iter, s->sessions)
                         if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) {
                                 s->positions[pos] = iter;
                                 break;
                         }
-                }
         }
 }
 
@@ -599,7 +593,6 @@ bool seat_can_graphical(Seat *s) {
 }
 
 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
-        Session *session;
         bool idle_hint = true;
         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
 
index ab98a5055df017be37e70957845e32d228cbfbb2..eef48c252753bd2d5c8ed7b81421df59366b9f0d 100644 (file)
@@ -445,7 +445,6 @@ int session_load(Session *s) {
                            "ACTIVE",         &active,
                            "DEVICES",        &devices,
                            "IS_DISPLAY",     &is_display);
-
         if (r < 0)
                 return log_error_errno(r, "Failed to read %s: %m", s->state_file);
 
index 572a16a7af17295359169a75614a831bde5ddb59..67b6556d7d60881b2ef6b3690a90509678661e58 100644 (file)
@@ -106,7 +106,6 @@ static int property_get_sessions(
                 sd_bus_error *error) {
 
         User *u = userdata;
-        Session *session;
         int r;
 
         assert(bus);
index 8d6bafe45d347d74c3c09ccb967c9d4fc8ea46c9..af33bf7a4f77c0348e605456f1951e3f4c476218 100644 (file)
@@ -189,7 +189,6 @@ static int user_save_internal(User *u) {
                         u->last_session_timestamp);
 
         if (u->sessions) {
-                Session *i;
                 bool first;
 
                 fputs("SESSIONS=", f);
@@ -499,8 +498,8 @@ static void user_stop_service(User *u, bool force) {
 }
 
 int user_stop(User *u, bool force) {
-        Session *s;
         int r = 0;
+
         assert(u);
 
         /* This is called whenever we begin with tearing down a user record. It's called in two cases: explicit API
@@ -534,7 +533,6 @@ int user_stop(User *u, bool force) {
 }
 
 int user_finalize(User *u) {
-        Session *s;
         int r = 0, k;
 
         assert(u);
@@ -575,7 +573,6 @@ int user_finalize(User *u) {
 }
 
 int user_get_idle_hint(User *u, dual_timestamp *t) {
-        Session *s;
         bool idle_hint = true;
         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
 
@@ -721,8 +718,6 @@ void user_add_to_gc_queue(User *u) {
 }
 
 UserState user_get_state(User *u) {
-        Session *i;
-
         assert(u);
 
         if (u->stopping)
@@ -805,8 +800,6 @@ static int elect_display_compare(Session *s1, Session *s2) {
 }
 
 void user_elect_display(User *u) {
-        Session *s;
-
         assert(u);
 
         /* This elects a primary session for each user, which we call the "display". We try to keep the assignment
index 3b4de5b7991069eecea1fe364e626435051ea48e..0483902edd9bec52af99452c1b3dd0ea9ceeca07 100644 (file)
@@ -685,7 +685,7 @@ static int manager_connect_bus(Manager *m) {
 
 static int manager_vt_switch(sd_event_source *src, const struct signalfd_siginfo *si, void *data) {
         Manager *m = data;
-        Session *active, *iter;
+        Session *active;
 
         /*
          * We got a VT-switch signal and we have to acknowledge it immediately.
@@ -732,16 +732,14 @@ static int manager_vt_switch(sd_event_source *src, const struct signalfd_siginfo
                 return 0;
         }
 
-        if (active->vtfd >= 0) {
+        if (active->vtfd >= 0)
                 session_leave_vt(active);
-        } else {
-                LIST_FOREACH(sessions_by_seat, iter, m->seat0->sessions) {
+        else
+                LIST_FOREACH(sessions_by_seat, iter, m->seat0->sessions)
                         if (iter->vtnr == active->vtnr && iter->vtfd >= 0) {
                                 session_leave_vt(iter);
                                 break;
                         }
-                }
-        }
 
         return 0;
 }
index 5bd7efc3e87f5796d6da0ac740afa4da4d0fc886..2047dc18174beae1d7f312238c06fb5b4974a074 100644 (file)
@@ -533,7 +533,6 @@ static int pam_putenv_and_log(pam_handle_t *handle, const char *e, bool debug) {
 }
 
 static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool debug) {
-        char **i;
         int r;
 
         assert(handle);
index 80f73da94a8bb496d60ea9d0f0b6c9dd8e740046..b202220b87937f81322375231b09968935e2426f 100644 (file)
@@ -265,12 +265,10 @@ int machine_load(Machine *m) {
                            "REALTIME",  &realtime,
                            "MONOTONIC", &monotonic,
                            "NETIF",     &netif);
-        if (r < 0) {
-                if (r == -ENOENT)
-                        return 0;
-
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
                 return log_error_errno(r, "Failed to read %s: %m", m->state_file);
-        }
 
         if (id)
                 sd_id128_from_string(id, &m->id);
index eda847cb4a0292e93b15d4e8f7f357d7777ca8c8..9e20d59389e2f291420084139120455069a7c4ca 100644 (file)
@@ -194,7 +194,6 @@ static int run(int argc, char *argv[]) {
 
         } else {
                 _cleanup_strv_free_ char **files = NULL;
-                char **fn, **i;
 
                 STRV_FOREACH(i, arg_proc_cmdline_modules) {
                         k = module_load_and_warn(ctx, *i, true);
index 063ad08d80ba5d2a861d00b0cb5be03aaaba3ba6..a1b5511338c4c4a4726e52ebfa842bf9ad912a36 100644 (file)
@@ -1033,7 +1033,6 @@ int parse_cmdline_item(const char *key, const char *value, void *data) {
 
 int context_merge_networks(Context *context) {
         Network *all, *network;
-        Route *route;
         int r;
 
         assert(context);
@@ -1129,10 +1128,7 @@ static int route_dump(Route *route, FILE *f) {
 }
 
 void network_dump(Network *network, FILE *f) {
-        Address *address;
-        Route *route;
         const char *dhcp;
-        char **dns;
 
         assert(network);
         assert(f);
index 187b24c5582044d234d208baee88b33836222615..3d1a5e5b3107444ac8ea2a3332812b0652408749 100644 (file)
@@ -860,7 +860,6 @@ int netdev_load_one(Manager *manager, const char *filename) {
 
 int netdev_load(Manager *manager, bool reload) {
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         int r;
 
         assert(manager);
index 3bb496130b40cb9963605c910723b790dfff4a8b..9e98b93a5cf890cd7fe2b4d50175509999c6de4c 100644 (file)
@@ -147,7 +147,7 @@ cancel:
 }
 
 static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) {
-        WireguardIPmask *mask, *start;
+        WireguardIPmask *start, *last = NULL;
         uint16_t j = 0;
         int r;
 
@@ -196,8 +196,10 @@ static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, c
                 r = wireguard_set_ipmask_one(netdev, message, mask, ++j);
                 if (r < 0)
                         return r;
-                if (r == 0)
+                if (r == 0) {
+                        last = mask;
                         break;
+                }
         }
 
         r = sd_netlink_message_close_container(message);
@@ -208,8 +210,8 @@ static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, c
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
 
-        *mask_start = mask; /* Start next cycle from this mask. */
-        return !mask;
+        *mask_start = last; /* Start next cycle from this mask. */
+        return !last;
 
 cancel:
         r = sd_netlink_message_cancel_array(message);
@@ -222,7 +224,7 @@ cancel:
 static int wireguard_set_interface(NetDev *netdev) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
         WireguardIPmask *mask_start = NULL;
-        WireguardPeer *peer, *peer_start;
+        WireguardPeer *peer_start;
         bool sent_once = false;
         uint32_t serial;
         Wireguard *w;
@@ -267,14 +269,17 @@ static int wireguard_set_interface(NetDev *netdev) {
                 if (r < 0)
                         return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
 
+                WireguardPeer *peer_last = NULL;
                 LIST_FOREACH(peers, peer, peer_start) {
                         r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start);
                         if (r < 0)
                                 return r;
-                        if (r == 0)
+                        if (r == 0) {
+                                peer_last = peer;
                                 break;
+                        }
                 }
-                peer_start = peer; /* Start next cycle from this peer. */
+                peer_start = peer_last; /* Start next cycle from this peer. */
 
                 r = sd_netlink_message_close_container(message);
                 if (r < 0)
@@ -424,7 +429,6 @@ static int peer_resolve_endpoint(WireguardPeer *peer) {
 }
 
 static void wireguard_resolve_endpoints(NetDev *netdev) {
-        WireguardPeer *peer;
         Wireguard *w;
 
         assert(netdev);
@@ -1124,7 +1128,6 @@ static int wireguard_peer_verify(WireguardPeer *peer) {
 }
 
 static int wireguard_verify(NetDev *netdev, const char *filename) {
-        WireguardPeer *peer, *peer_next;
         Wireguard *w;
         int r;
 
@@ -1143,9 +1146,7 @@ static int wireguard_verify(NetDev *netdev, const char *filename) {
                                               "%s: Missing PrivateKey= or PrivateKeyFile=, "
                                               "Ignoring network device.", filename);
 
-        LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers) {
-                WireguardIPmask *ipmask;
-
+        LIST_FOREACH(peers, peer, w->peers) {
                 if (wireguard_peer_verify(peer) < 0) {
                         wireguard_peer_free(peer);
                         continue;
index 8fe9844c4f36b10d5437ad86c36de8db172cde48..d656027ec45e0133c136c419fb28798b8653221c 100644 (file)
@@ -525,7 +525,6 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b
                 if (!strv_fnmatch_full(patterns, str, 0, &pos) &&
                     !strv_fnmatch_full(patterns, name, 0, &pos)) {
                         bool match = false;
-                        char **p;
 
                         STRV_FOREACH(p, altnames)
                                 if (strv_fnmatch_full(patterns, *p, 0, &pos)) {
index 79ded2e99e7ae605c605b5357a3fc55c2c239c8e..5861ecb922874fe31926f8295ba183407c81b0c1 100644 (file)
@@ -217,8 +217,6 @@ static int link_push_uplink_to_dhcp_server(
                 break;
 
         case SD_DHCP_LEASE_NTP: {
-                char **i;
-
                 /* For NTP things are similar, but for NTP hostnames can be configured too, which we cannot
                  * propagate via DHCP. Hence let's only propagate those which are IP addresses. */
 
index fb1f50063ea0ff99e9859ca953c59caea965a009..dc9823c375631028742e22f65faae301ccfe6373 100644 (file)
@@ -699,7 +699,6 @@ static int server_build_json_one_string(const char *str, NetworkConfigSource s,
 static int ntp_build_json(Link *link, JsonVariant **ret) {
         JsonVariant **elements = NULL;
         size_t n = 0;
-        char **p;
         int r;
 
         assert(link);
@@ -878,7 +877,7 @@ static int domains_build_json(Link *link, bool is_route, JsonVariant **ret) {
         JsonVariant **elements = NULL;
         DHCPUseDomains use_domains;
         union in_addr_union s;
-        char **p, **domains;
+        char **domains;
         const char *domain;
         size_t n = 0;
         int r;
index 765733b38c4f9ab7843c9c2f560084b612737e48..4173eaa15fca0c4a637c7e0d876f6e37d074f9fe 100644 (file)
@@ -81,7 +81,6 @@ int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_
         _cleanup_strv_free_ char **ntp = NULL;
         Link *l = userdata;
         int r;
-        char **i;
 
         assert(message);
         assert(l);
@@ -484,7 +483,6 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
         _cleanup_strv_free_ char **ntas = NULL;
         Link *l = userdata;
         int r;
-        char **i;
 
         assert(message);
         assert(l);
index e925739d06e5f220c30b5d18408e9034b14ddae9..a122ea84fed915a8764133efb0312cc9464e8a7a 100644 (file)
@@ -910,8 +910,6 @@ static void link_drop_requests(Link *link) {
 }
 
 static Link *link_drop(Link *link) {
-        char **n;
-
         if (!link)
                 return NULL;
 
@@ -2251,7 +2249,6 @@ static int link_update_mtu(Link *link, sd_netlink_message *message) {
 
 static int link_update_alternative_names(Link *link, sd_netlink_message *message) {
         _cleanup_strv_free_ char **altnames = NULL;
-        char **n;
         int r;
 
         assert(link);
index 8cbc67f5429ba593716b3fc2a12ec19737eefa79..63e5ff6cc6657edd648fedf210962fa32be85c2d 100644 (file)
@@ -790,7 +790,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
         struct in6_addr router;
         uint32_t lifetime_sec;
         bool updated = false;
-        char **j;
         int r;
 
         assert(link);
index 683b04b5acd9be2219718cbb1dbf05c961f3f7b3..9acfc863f2b5384b97adab62d4fed1422d31e848 100644 (file)
@@ -580,7 +580,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
 
 int network_load(Manager *manager, OrderedHashmap **networks) {
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         int r;
 
         assert(manager);
index 97761cb6f0b913045be5d453da22d1bbbc975c3c..45ddb59f7613c8f0886c58d78c3a523ea6b02733 100644 (file)
@@ -200,7 +200,6 @@ int bind_user_prepare(
 
         _cleanup_(bind_user_context_freep) BindUserContext *c = NULL;
         uid_t current_uid = MAP_UID_MIN;
-        char **n;
         int r;
 
         assert(custom_mounts);
index 2890190eec917577e99f6df463594bddedbdc645..166383cce3bc6899b4a805778659ed5c1652e7c3 100644 (file)
 #include "util.h"
 
 int expose_port_parse(ExposePort **l, const char *s) {
-
         const char *split, *e;
         uint16_t container_port, host_port;
+        ExposePort *port;
         int protocol;
-        ExposePort *p;
         int r;
 
         assert(l);
@@ -59,17 +58,17 @@ int expose_port_parse(ExposePort **l, const char *s) {
                 if (p->protocol == protocol && p->host_port == host_port)
                         return -EEXIST;
 
-        p = new(ExposePort, 1);
-        if (!p)
+        port = new(ExposePort, 1);
+        if (!port)
                 return -ENOMEM;
 
-        *p = (ExposePort) {
+        *port = (ExposePort) {
                 .protocol = protocol,
                 .host_port = host_port,
                 .container_port = container_port,
         };
 
-        LIST_PREPEND(ports, *l, p);
+        LIST_PREPEND(ports, *l, port);
 
         return 0;
 }
@@ -84,7 +83,6 @@ void expose_port_free_all(ExposePort *p) {
 }
 
 int expose_port_flush(FirewallContext **fw_ctx, ExposePort* l, int af, union in_addr_union *exposed) {
-        ExposePort *p;
         int r;
 
         assert(exposed);
@@ -117,7 +115,6 @@ int expose_port_flush(FirewallContext **fw_ctx, ExposePort* l, int af, union in_
 int expose_port_execute(sd_netlink *rtnl, FirewallContext **fw_ctx, ExposePort *l, int af, union in_addr_union *exposed) {
         _cleanup_free_ struct local_address *addresses = NULL;
         union in_addr_union new_exposed;
-        ExposePort *p;
         bool add;
         int r;
 
index f2fad0f4625e5ed2cc04abe58d3bec67e653c7f5..7101a87694747f7c27f2b8f764b826178fe973e6 100644 (file)
@@ -166,8 +166,6 @@ int custom_mount_prepare_all(const char *dest, CustomMount *l, size_t n) {
                 }
 
                 if (m->type == CUSTOM_MOUNT_OVERLAY) {
-                        char **j;
-
                         STRV_FOREACH(j, m->lower) {
                                 char *s;
 
@@ -319,8 +317,6 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
                 if (!destination)
                         return -ENOMEM;
         } else {
-                char **i;
-
                 /* If more than two parameters are specified, the last one is the destination, the second to last one
                  * the "upper", and all before that the "lower" directories. */
 
index 023b1e7e1a81b96c476e71f8f543f3ba5a19200b..fab4eb9609a7f7c924592c4221f789b3f255ec42 100644 (file)
@@ -288,7 +288,6 @@ int setup_veth_extra(
 
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
         uint64_t idx = 0;
-        char **a, **b;
         int r;
 
         assert(machine_name);
@@ -495,7 +494,6 @@ int test_network_interface_initialized(const char *name) {
 
 int move_network_interfaces(int netns_fd, char **ifaces) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
-        char **i;
         int r;
 
         if (strv_isempty(ifaces))
@@ -532,7 +530,6 @@ int move_network_interfaces(int netns_fd, char **ifaces) {
 int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
         unsigned idx = 0;
-        char **i;
         int r;
 
         if (strv_isempty(ifaces))
@@ -619,7 +616,6 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
 
 int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
-        char **i;
         int r;
 
         if (strv_isempty(ifaces))
@@ -728,7 +724,6 @@ int veth_extra_parse(char ***l, const char *p) {
 
 int remove_veth_links(const char *primary, char **pairs) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
-        char **a, **b;
         int r;
 
         /* In some cases the kernel might pin the veth links between host and container even after the namespace
index a23358464ba7b135e065306c7d77b9baf61910ac..44564ba61959e2f25d0280cc916222ba97fb6032 100644 (file)
@@ -1892,7 +1892,6 @@ static int oci_seccomp_syscalls(const char *name, JsonVariant *v, JsonDispatchFl
                 struct syscall_rule rule = {
                         .action = UINT32_MAX,
                 };
-                char **i;
 
                 r = json_dispatch(e, table, oci_unexpected, flags, &rule);
                 if (r < 0)
index c94512ef30219c05dd90ea44cf48c072b7019cff..77f4c2ac88e2c42fb21c04daa21fe3808b8ac290 100644 (file)
@@ -140,7 +140,6 @@ static int add_syscall_filters(
         };
 
         _cleanup_strv_free_ char **added = NULL;
-        char **p;
         int r;
 
         for (size_t i = 0; i < ELEMENTSOF(allow_list); i++) {
index 6dbd6ba4c9e45983346ee5fdec46e3a2336eb62d..85c439815c6ddcb332f62fe98ef6841825b4ea7a 100644 (file)
@@ -142,10 +142,8 @@ int stub_pid1(sd_id128_t uuid) {
 
                 if (quit_usec == USEC_INFINITY)
                         r = sigwaitinfo(&waitmask, &si);
-                else {
-                        struct timespec ts;
-                        r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec));
-                }
+                else
+                        r = sigtimedwait(&waitmask, &si, TIMESPEC_STORE(quit_usec - current_usec));
                 if (r < 0) {
                         if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */
                                 continue;
index ff4ccde1f88a52fc5f056511ecd9ca8d40571cb4..7fab88b31a88fd755bddb6e4230008953b726b42 100644 (file)
@@ -3149,7 +3149,6 @@ static int patch_sysctl(void) {
         };
 
         unsigned long flags;
-        char **k, **v;
         int r;
 
         flags = effective_clone_ns_flags();
index 002e6925f957a4accb07403a4e5caf406d30c127..c69667d6607c3a376ba194929b4c402824e4e008 100644 (file)
@@ -217,7 +217,7 @@ int nss_pack_group_record(
                 char *buffer,
                 size_t buflen) {
 
-        char **array = NULL, *p, **m;
+        char **array = NULL, *p;
         size_t required, n = 0, i = 0;
 
         assert(g);
index 77718d9c9e3898cee9ab87e0da127a91ad9c1081..a135824c5368e5083ecbd17eaca36e0533677ff9 100644 (file)
@@ -192,6 +192,10 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) {
         if (!pids_killed)
                 return -ENOMEM;
 
+        r = increment_oomd_xattr(path, "user.oomd_ooms", 1);
+        if (r < 0)
+                log_debug_errno(r, "Failed to set user.oomd_ooms before kill: %m");
+
         if (recurse)
                 r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL);
         else
index 13d9e60f16c56c0a15550acb1c878bab15a45b4d..82a60ad8803e9fda856ad333e6584a2551822a65 100644 (file)
@@ -77,12 +77,16 @@ static void test_oomd_cgroup_kill(void) {
                         abort();
                 }
 
+                assert_se(cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_ooms", &v) >= 0);
+                assert_se(streq(v, i == 0 ? "1" : "2"));
+                v = mfree(v);
+
                 /* Wait a bit since processes may take some time to be cleaned up. */
                 sleep(2);
                 assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) == true);
 
                 assert_se(cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_kill", &v) >= 0);
-                assert_se(memcmp(v, i == 0 ? "2" : "4", 2) == 0);
+                assert_se(streq(v, i == 0 ? "2" : "4"));
         }
 }
 
index 52fd0436e357fc76e77245a83f0c055df7570bad..600c4f6e5b132c58fb17073ce81418d99a870c8d 100644 (file)
@@ -373,7 +373,6 @@ static int context_add_free_area(
 
 static bool context_drop_one_priority(Context *context) {
         int32_t priority = 0;
-        Partition *p;
         bool exists = false;
 
         LIST_FOREACH(partitions, p, context->partitions) {
@@ -561,8 +560,6 @@ static uint64_t charge_weight(uint64_t total, uint64_t amount) {
 }
 
 static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_free_area) {
-        Partition *p;
-
         assert(context);
 
         /* Sort free areas by size, putting smallest first */
@@ -613,7 +610,6 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
 
 static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
         uint64_t weight_sum = 0;
-        Partition *p;
 
         assert(context);
         assert(a);
@@ -677,7 +673,6 @@ static int context_grow_partitions_phase(
                 uint64_t *span,
                 uint64_t *weight_sum) {
 
-        Partition *p;
         int r;
 
         assert(context);
@@ -832,9 +827,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
 
         /* What? Even still some space left (maybe because there was no preceding partition, or it had a
          * size limit), then let's donate it to whoever wants it. */
-        if (span > 0) {
-                Partition *p;
-
+        if (span > 0)
                 LIST_FOREACH(partitions, p, context->partitions) {
                         uint64_t m, xsz;
 
@@ -857,7 +850,6 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
                         if (span == 0)
                                 break;
                 }
-        }
 
         /* Yuck, still no one? Then make it padding */
         if (span > 0 && a->after) {
@@ -869,7 +861,6 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
 }
 
 static int context_grow_partitions(Context *context) {
-        Partition *p;
         int r;
 
         assert(context);
@@ -903,7 +894,6 @@ static int context_grow_partitions(Context *context) {
 
 static void context_place_partitions(Context *context) {
         uint64_t partno = 0;
-        Partition *p;
 
         assert(context);
 
@@ -1404,7 +1394,6 @@ static int context_read_definitions(
 
         _cleanup_strv_free_ char **files = NULL;
         Partition *last = NULL;
-        char **f;
         int r;
 
         assert(context);
@@ -1729,7 +1718,7 @@ static int context_load_partition_table(
         n_partitions = fdisk_table_get_nents(t);
         for (size_t i = 0; i < n_partitions; i++)  {
                 _cleanup_free_ char *label_copy = NULL;
-                Partition *pp, *last = NULL;
+                Partition *last = NULL;
                 struct fdisk_partition *p;
                 struct fdisk_parttype *pt;
                 const char *pts, *ids, *label;
@@ -1910,11 +1899,9 @@ add_initial_free_area:
 }
 
 static void context_unload_partition_table(Context *context) {
-        Partition *p, *next;
-
         assert(context);
 
-        LIST_FOREACH_SAFE(partitions, p, next, context->partitions) {
+        LIST_FOREACH(partitions, p, context->partitions) {
 
                 /* Entirely remove partitions that have no configuration */
                 if (PARTITION_IS_FOREIGN(p)) {
@@ -1999,7 +1986,6 @@ static const char *partition_label(const Partition *p) {
 static int context_dump_partitions(Context *context, const char *node) {
         _cleanup_(table_unrefp) Table *t = NULL;
         uint64_t sum_padding = 0, sum_size = 0;
-        Partition *p;
         int r;
 
         if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) {
@@ -2189,7 +2175,7 @@ done:
 static int context_dump_partition_bar(Context *context, const char *node) {
         _cleanup_free_ Partition **bar = NULL;
         _cleanup_free_ size_t *start_array = NULL;
-        Partition *p, *last = NULL;
+        Partition *last = NULL;
         bool z = false;
         size_t c, j = 0;
 
@@ -2300,7 +2286,7 @@ static int context_dump_partition_bar(Context *context, const char *node) {
 }
 
 static bool context_changed(const Context *context) {
-        Partition *p;
+        assert(context);
 
         LIST_FOREACH(partitions, p, context->partitions) {
                 if (p->dropped)
@@ -2470,7 +2456,6 @@ static int context_discard_partition(Context *context, Partition *p) {
 
 static int context_discard_gap_after(Context *context, Partition *p) {
         uint64_t gap, next = UINT64_MAX;
-        Partition *q;
         int r;
 
         assert(context);
@@ -2528,7 +2513,6 @@ static int context_discard_gap_after(Context *context, Partition *p) {
 }
 
 static int context_wipe_and_discard(Context *context, bool from_scratch) {
-        Partition *p;
         int r;
 
         assert(context);
@@ -2746,7 +2730,6 @@ static int deactivate_luks(struct crypt_device *cd, const char *node) {
 }
 
 static int context_copy_blocks(Context *context) {
-        Partition *p;
         int whole_fd = -1, r;
 
         assert(context);
@@ -2832,7 +2815,6 @@ static int context_copy_blocks(Context *context) {
 }
 
 static int do_copy_files(Partition *p, const char *fs) {
-        char **source, **target;
         int r;
 
         assert(p);
@@ -2929,7 +2911,6 @@ static int do_copy_files(Partition *p, const char *fs) {
 }
 
 static int do_make_directories(Partition *p, const char *fs) {
-        char **d;
         int r;
 
         assert(p);
@@ -2996,7 +2977,6 @@ static int partition_populate(Partition *p, const char *node) {
 }
 
 static int context_mkfs(Context *context) {
-        Partition *p;
         int fd = -1, r;
 
         assert(context);
@@ -3114,7 +3094,6 @@ static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *re
         } result;
 
         uint64_t k = 0;
-        Partition *q;
         int r;
 
         assert(context);
@@ -3198,7 +3177,6 @@ static int partition_acquire_label(Context *context, Partition *p, char **ret) {
         for (;;) {
                 const char *ll = label ?: prefix;
                 bool retry = false;
-                Partition *q;
 
                 LIST_FOREACH(partitions, q, context->partitions) {
                         if (p == q)
@@ -3230,7 +3208,6 @@ static int partition_acquire_label(Context *context, Partition *p, char **ret) {
 }
 
 static int context_acquire_partition_uuids_and_labels(Context *context) {
-        Partition *p;
         int r;
 
         assert(context);
@@ -3337,7 +3314,6 @@ static uint64_t partition_merge_flags(Partition *p) {
 }
 
 static int context_mangle_partitions(Context *context) {
-        Partition *p;
         int r;
 
         assert(context);
@@ -3604,7 +3580,6 @@ static int context_read_seed(Context *context, const char *root) {
 }
 
 static int context_factory_reset(Context *context, bool from_scratch) {
-        Partition *p;
         size_t n = 0;
         int r;
 
@@ -3653,8 +3628,6 @@ static int context_factory_reset(Context *context, bool from_scratch) {
 }
 
 static int context_can_factory_reset(Context *context) {
-        Partition *p;
-
         assert(context);
 
         LIST_FOREACH(partitions, p, context->partitions)
@@ -3969,7 +3942,6 @@ static int context_open_copy_block_paths(
                 const char *root,
                 dev_t restrict_devno) {
 
-        Partition *p;
         int r;
 
         assert(context);
@@ -4797,7 +4769,6 @@ done:
 
 static int determine_auto_size(Context *c) {
         uint64_t sum;
-        Partition *p;
 
         assert(c);
 
index a54e091185c90bd8bb3b50be7d2cf300f723db60..a07a95a229f47882cb59307f1cdfc7ce5173b041 100644 (file)
@@ -58,7 +58,6 @@ static bool prefix_match(const char *unit, const char *prefix) {
 
 static bool unit_match(const char *unit, char **matches) {
         const char *dot;
-        char **i;
 
         dot = strrchr(unit, '.');
         if (!dot)
@@ -182,7 +181,6 @@ static int extract_now(
         _cleanup_close_ int os_release_fd = -1;
         _cleanup_free_ char *os_release_path = NULL;
         const char *os_release_id;
-        char **i;
         int r;
 
         /* Extracts the metadata from a directory tree 'where'. Extracts two kinds of information: the /etc/os-release
@@ -535,8 +533,6 @@ static int extract_image_and_extensions(
                 return r;
 
         if (!strv_isempty(extension_image_paths)) {
-                char **p;
-
                 extension_images = ordered_hashmap_new(&image_hash_ops);
                 if (!extension_images)
                         return -ENOMEM;
@@ -1261,8 +1257,6 @@ static int install_image_and_extensions_symlinks(
 }
 
 static bool prefix_matches_compatible(char **matches, char **valid_prefixes) {
-        char **m;
-
         /* Checks if all 'matches' are included in the list of 'valid_prefixes' */
 
         STRV_FOREACH(m, matches)
@@ -1380,7 +1374,6 @@ int portable_attach(
 
 static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths) {
         _cleanup_strv_free_ char **root_and_extensions = NULL;
-        char **image_name_or_path;
         const char *a;
         int r;
 
index 46e60310fc8dc16de4d3f44892040aeaba05c6db..9fb1cdffece192380f5c7e5b4020cf5f6ce75b90 100644 (file)
@@ -90,7 +90,6 @@ static int determine_image(const char *image, bool permit_non_existing, char **r
 }
 
 static int attach_extensions_to_message(sd_bus_message *m, char **extensions) {
-        char **p;
         int r;
 
         assert(m);
@@ -1174,7 +1173,6 @@ static int dump_profiles(void) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_strv_free_ char **l = NULL;
-        char **i;
         int r;
 
         r = acquire_bus(&bus);
index 5b3ceeff36a9ee90c5071074d8b9fec6a6d1c3dc..96a505f5e1f7c8088308a9d8bc95e78b4ef981f3 100644 (file)
@@ -705,7 +705,6 @@ invalid:
 
 static int verb_query(int argc, char **argv, void *userdata) {
         sd_bus *bus = userdata;
-        char **p;
         int q, r = 0;
 
         if (arg_type != 0)
@@ -975,7 +974,6 @@ static int resolve_openpgp(sd_bus *bus, const char *address) {
 
 static int verb_openpgp(int argc, char **argv, void *userdata) {
         sd_bus *bus = userdata;
-        char **p;
         int q, r = 0;
 
         STRV_FOREACH(p, argv + 1) {
@@ -1025,7 +1023,7 @@ static bool service_family_is_valid(const char *s) {
 
 static int verb_tlsa(int argc, char **argv, void *userdata) {
         sd_bus *bus = userdata;
-        char **p, **args = argv + 1;
+        char **args = argv + 1;
         const char *family = "tcp";
         int q, r = 0;
 
@@ -1389,7 +1387,6 @@ static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p)
                 printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal());
 
         size_t cols = columns(), position = pos2 - pos1 + 2;
-        char **i;
 
         STRV_FOREACH(i, p) {
                 size_t our_len = utf8_console_width(*i); /* This returns -1 on invalid utf-8 (which shouldn't happen).
@@ -2025,7 +2022,6 @@ static int verb_status(int argc, char **argv, void *userdata) {
         int r = 0;
 
         if (argc > 1) {
-                char **ifname;
                 bool empty_line = false;
 
                 STRV_FOREACH(ifname, argv + 1) {
@@ -2049,7 +2045,6 @@ static int verb_status(int argc, char **argv, void *userdata) {
 
 static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error, bool extended) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
-        char **p;
         int r;
 
         r = bus_message_new_method_call(bus, &req, locator, extended ? "SetLinkDNSEx" : "SetLinkDNS");
@@ -2157,7 +2152,6 @@ static int verb_dns(int argc, char **argv, void *userdata) {
 
 static int call_domain(sd_bus *bus, char **domain, const BusLocator *locator, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
-        char **p;
         int r;
 
         r = bus_message_new_method_call(bus, &req, locator, "SetLinkDomains");
@@ -2454,7 +2448,6 @@ static int call_nta(sd_bus *bus, char **nta, const BusLocator *locator,  sd_bus_
 static int verb_nta(int argc, char **argv, void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         sd_bus *bus = userdata;
-        char **p;
         int r;
         bool clear;
 
index 0dada39d6ee1a776251b1040f7f5d10e0aa3760d..4425a2f320883d62d43c4a32003e32bd3928a6b6 100644 (file)
@@ -863,7 +863,6 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
 static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) {
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
         _cleanup_free_ char *normalized = NULL;
-        DnsQuery *aux;
         int r;
 
         assert(q);
@@ -994,7 +993,6 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
 }
 
 static int append_txt(sd_bus_message *reply, DnsResourceRecord *rr) {
-        DnsTxtItem *i;
         int r;
 
         assert(reply);
@@ -1025,7 +1023,6 @@ static void resolve_service_all_complete(DnsQuery *query) {
         DnsQuestion *question;
         DnsResourceRecord *rr;
         unsigned added = 0;
-        DnsQuery *aux;
         int r;
 
         assert(q);
@@ -1448,7 +1445,6 @@ static int bus_property_get_dns_servers_internal(
                 bool extended) {
 
         Manager *m = userdata;
-        DnsServer *s;
         Link *l;
         int r;
 
@@ -1507,7 +1503,7 @@ static int bus_property_get_fallback_dns_servers_internal(
                 sd_bus_error *error,
                 bool extended) {
 
-        DnsServer *s, **f = userdata;
+        DnsServer **f = userdata;
         int r;
 
         assert(reply);
@@ -1600,7 +1596,6 @@ static int bus_property_get_domains(
                 sd_bus_error *error) {
 
         Manager *m = userdata;
-        DnsSearchDomain *d;
         Link *l;
         int r;
 
@@ -1658,7 +1653,6 @@ static int bus_property_get_cache_statistics(
 
         uint64_t size = 0, hit = 0, miss = 0;
         Manager *m = userdata;
-        DnsScope *s;
 
         assert(reply);
         assert(m);
@@ -1751,7 +1745,6 @@ static int bus_property_get_resolv_conf_mode(
 
 static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = userdata;
-        DnsScope *s;
 
         assert(message);
         assert(m);
index 57e0ac3acc60e471fc0249804e0e57c373a8df60..3dce32ed387953b67d477ce5b55a17ec9971cfc9 100644 (file)
@@ -117,7 +117,7 @@ static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
 }
 
 static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
-        DnsCacheItem *first, *i;
+        DnsCacheItem *first;
         int r;
 
         first = hashmap_get(c->by_key, rr->key);
@@ -135,7 +135,7 @@ static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
 }
 
 static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
-        DnsCacheItem *first, *i, *n;
+        DnsCacheItem *first;
 
         assert(c);
         assert(key);
@@ -144,7 +144,7 @@ static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
         if (!first)
                 return false;
 
-        LIST_FOREACH_SAFE(by_key, i, n, first) {
+        LIST_FOREACH(by_key, i, first) {
                 prioq_remove(c->by_expiry, i, &i->prioq_idx);
                 dns_cache_item_free(i);
         }
@@ -301,12 +301,10 @@ static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
 }
 
 static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
-        DnsCacheItem *i;
-
         assert(c);
         assert(rr);
 
-        LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key))
+        LIST_FOREACH(by_key, i, (DnsCacheItem*) hashmap_get(c->by_key, rr->key))
                 if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
                         return i;
 
@@ -987,7 +985,7 @@ int dns_cache_lookup(
         unsigned n = 0;
         int r;
         bool nxdomain = false;
-        DnsCacheItem *j, *first, *nsec = NULL;
+        DnsCacheItem *first, *nsec = NULL;
         bool have_authenticated = false, have_non_authenticated = false, have_confidential = false, have_non_confidential = false;
         usec_t current = 0;
         int found_rcode = -1;
@@ -1223,7 +1221,7 @@ miss:
 }
 
 int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
-        DnsCacheItem *i, *first;
+        DnsCacheItem *first;
         bool same_owner = true;
 
         assert(cache);
@@ -1266,9 +1264,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
         assert(cache);
         assert(p);
 
-        HASHMAP_FOREACH(i, cache->by_key) {
-                DnsCacheItem *j;
-
+        HASHMAP_FOREACH(i, cache->by_key)
                 LIST_FOREACH(by_key, j, i) {
                         if (!j->rr)
                                 continue;
@@ -1299,7 +1295,6 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
 
                         ancount++;
                 }
-        }
 
         DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
 
@@ -1315,9 +1310,7 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
         if (!f)
                 f = stdout;
 
-        HASHMAP_FOREACH(i, cache->by_key) {
-                DnsCacheItem *j;
-
+        HASHMAP_FOREACH(i, cache->by_key)
                 LIST_FOREACH(by_key, j, i) {
 
                         fputc('\t', f);
@@ -1341,7 +1334,6 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
                                 fputc('\n', f);
                         }
                 }
-        }
 }
 
 bool dns_cache_is_empty(DnsCache *cache) {
index d45f87ff5d1ce1b6f9b1ecb3825fbb55fa89c1fb..74396f14a870d11bfc6334d3e93ff2ba5f362a23 100644 (file)
@@ -941,15 +941,12 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
                         r = dns_packet_append_raw_string(p, NULL, 0, NULL);
                         if (r < 0)
                                 goto fail;
-                } else {
-                        DnsTxtItem *i;
-
+                } else
                         LIST_FOREACH(items, i, rr->txt.items) {
                                 r = dns_packet_append_raw_string(p, i->data, i->length, NULL);
                                 if (r < 0)
                                         goto fail;
                         }
-                }
 
                 r = 0;
                 break;
index c0bb40937a967c9b17e64ec3e503001a1b6f668d..b7cb21dd49d9f127c4f3debaeb6d4cc52b9ffdb3 100644 (file)
@@ -345,8 +345,6 @@ fail:
 }
 
 static void dns_query_stop(DnsQuery *q) {
-        DnsQueryCandidate *c;
-
         assert(q);
 
         event_source_disable(q->timeout_event_source);
@@ -718,8 +716,7 @@ static int dns_query_try_etc_hosts(DnsQuery *q) {
 
 int dns_query_go(DnsQuery *q) {
         DnsScopeMatch found = DNS_SCOPE_NO;
-        DnsScope *s, *first = NULL;
-        DnsQueryCandidate *c;
+        DnsScope *first = NULL;
         int r;
 
         assert(q);
@@ -938,8 +935,7 @@ fail:
 }
 
 void dns_query_ready(DnsQuery *q) {
-
-        DnsQueryCandidate *bad = NULL, *c;
+        DnsQueryCandidate *bad = NULL;
         bool pending = false;
 
         assert(q);
index 720b4c58d762eae88e79288aa832482d46aecee9..83140d79f87d06e91b1efebe816c3e1179bd8179 100644 (file)
@@ -784,7 +784,6 @@ static char *format_types(Bitmap *types) {
 }
 
 static char *format_txt(DnsTxtItem *first) {
-        DnsTxtItem *i;
         size_t c = 1;
         char *p, *s;
 
@@ -1358,8 +1357,6 @@ void dns_resource_record_hash_func(const DnsResourceRecord *rr, struct siphash *
 
         case DNS_TYPE_TXT:
         case DNS_TYPE_SPF: {
-                DnsTxtItem *j;
-
                 LIST_FOREACH(items, j, rr->txt.items) {
                         siphash24_compress_safe(j->data, j->length, state);
 
@@ -1813,7 +1810,7 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
 }
 
 DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) {
-        DnsTxtItem *i, *copy = NULL, *end = NULL;
+        DnsTxtItem *copy = NULL, *end = NULL;
 
         LIST_FOREACH(items, i, first) {
                 DnsTxtItem *j;
index 16c5e0094cdd0e0f3746558917ed2ce6efbe0d77..96978f8ac3b825ecdcdd470829cf2cef7538920d 100644 (file)
@@ -529,7 +529,6 @@ static DnsScopeMatch match_subnet_reverse_lookups(
                 bool exclude_own) {
 
         union in_addr_union ia;
-        LinkAddress *a;
         int f, r;
 
         assert(s);
@@ -587,7 +586,6 @@ DnsScopeMatch dns_scope_good_domain(
                 DnsQuery *q) {
 
         DnsQuestion *question;
-        DnsSearchDomain *d;
         const char *domain;
         uint64_t flags;
         int ifindex;
@@ -1082,7 +1080,7 @@ DnsTransaction *dns_scope_find_transaction(
                 DnsResourceKey *key,
                 uint64_t query_flags) {
 
-        DnsTransaction *first, *t;
+        DnsTransaction *first;
 
         assert(scope);
         assert(key);
@@ -1398,8 +1396,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         _cleanup_set_free_ Set *types = NULL;
-        DnsTransaction *t;
-        DnsZoneItem *z, *i;
+        DnsZoneItem *z;
         unsigned size = 0;
         char *service_type;
         int r;
@@ -1527,7 +1524,6 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
 
 int dns_scope_add_dnssd_services(DnsScope *scope) {
         DnssdService *service;
-        DnssdTxtData *txt_data;
         int r;
 
         assert(scope);
@@ -1561,7 +1557,6 @@ int dns_scope_add_dnssd_services(DnsScope *scope) {
 int dns_scope_remove_dnssd_services(DnsScope *scope) {
         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
         DnssdService *service;
-        DnssdTxtData *txt_data;
         int r;
 
         assert(scope);
@@ -1586,7 +1581,7 @@ int dns_scope_remove_dnssd_services(DnsScope *scope) {
 }
 
 static bool dns_scope_has_route_only_domains(DnsScope *scope) {
-        DnsSearchDomain *domain, *first;
+        DnsSearchDomain *first;
         bool route_only = false;
 
         assert(scope);
index c9f148a2b99dc54182b5659c54b7d095d132ef4b..0cc50d64783e5f5a4622fe61194d055c6cb44af2 100644 (file)
@@ -178,7 +178,6 @@ void dns_search_domain_mark_all(DnsSearchDomain *first) {
 }
 
 int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret) {
-        DnsSearchDomain *d;
         int r;
 
         assert(name);
index cd755b13d4c073bed91be0b57e30300801748d50..c2b608d383c25cd304b450f4dd4e518616c73acb 100644 (file)
@@ -815,8 +815,6 @@ void dns_server_mark_all(DnsServer *server) {
 }
 
 DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name) {
-        DnsServer *s;
-
         LIST_FOREACH(servers, s, first)
                 if (s->family == family &&
                     in_addr_equal(family, &s->address, in_addr) > 0 &&
@@ -992,8 +990,6 @@ void dns_server_reset_features(DnsServer *s) {
 }
 
 void dns_server_reset_features_all(DnsServer *s) {
-        DnsServer *i;
-
         LIST_FOREACH(servers, i, s)
                 dns_server_reset_features(i);
 }
index f937f9f7b594cde0de4a8dde8673fdb08c9330bc..52897649d159f0a783a114341ce2160681c0df38 100644 (file)
@@ -634,12 +634,9 @@ static int on_stream_complete(DnsStream *s, int error) {
                 }
         }
 
-        if (error != 0) {
-                DnsTransaction *t, *n;
-
-                LIST_FOREACH_SAFE(transactions_by_stream, t, n, s->transactions)
+        if (error != 0)
+                LIST_FOREACH(transactions_by_stream, t, s->transactions)
                         on_transaction_stream_error(t, error);
-        }
 
         return 0;
 }
@@ -1762,7 +1759,6 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
 static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         bool add_known_answers = false;
-        DnsTransaction *other;
         DnsResourceKey *tkey;
         _cleanup_set_free_ Set *keys = NULL;
         unsigned qdcount;
index b036aa402c4411fce7b9cb4b7814949bbddeee38..4a6c06d13d068e32b7e8cdcf92861d6fe6ae56fa 100644 (file)
@@ -409,7 +409,6 @@ static int dns_trust_anchor_load_files(
                 int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) {
 
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         int r;
 
         assert(d);
index 6b3f5f707d870686872a933eae36a7052aa930ff..f533f972fc955418177718f2c01264bd62638b7e 100644 (file)
@@ -78,12 +78,10 @@ void dns_zone_flush(DnsZone *z) {
 }
 
 DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
-        DnsZoneItem *i;
-
         assert(z);
         assert(rr);
 
-        LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key))
+        LIST_FOREACH(by_key, i, (DnsZoneItem*) hashmap_get(z->by_key, rr->key))
                 if (dns_resource_record_equal(i->rr, rr) > 0)
                         return i;
 
@@ -250,21 +248,15 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
                 return r;
 
         if (probe) {
-                DnsZoneItem *first, *j;
                 bool established = false;
 
                 /* Check if there's already an RR with the same name
                  * established. If so, it has been probed already, and
                  * we don't need to probe again. */
 
-                LIST_FIND_HEAD(by_name, i, first);
-                LIST_FOREACH(by_name, j, first) {
-                        if (i == j)
-                                continue;
-
+                LIST_FOREACH_OTHERS(by_name, j, i)
                         if (j->state == DNS_ZONE_ITEM_ESTABLISHED)
                                 established = true;
-                }
 
                 if (established)
                         i->state = DNS_ZONE_ITEM_ESTABLISHED;
@@ -306,7 +298,7 @@ static int dns_zone_add_authenticated_answer(DnsAnswer *a, DnsZoneItem *i, int i
 int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
         unsigned n_answer = 0;
-        DnsZoneItem *j, *first;
+        DnsZoneItem *first;
         bool tentative = true, need_soa = false;
         int r;
 
@@ -576,7 +568,7 @@ static int dns_zone_item_verify(DnsZoneItem *i) {
 }
 
 int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
-        DnsZoneItem *i, *first;
+        DnsZoneItem *first;
         int c = 0;
 
         assert(zone);
@@ -614,7 +606,7 @@ int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
 }
 
 int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) {
-        DnsZoneItem *i, *first;
+        DnsZoneItem *first;
         int c = 0;
 
         assert(zone);
@@ -639,12 +631,9 @@ void dns_zone_verify_all(DnsZone *zone) {
 
         assert(zone);
 
-        HASHMAP_FOREACH(i, zone->by_key) {
-                DnsZoneItem *j;
-
+        HASHMAP_FOREACH(i, zone->by_key)
                 LIST_FOREACH(by_key, j, i)
                         dns_zone_item_verify(j);
-        }
 }
 
 void dns_zone_dump(DnsZone *zone, FILE *f) {
@@ -656,9 +645,7 @@ void dns_zone_dump(DnsZone *zone, FILE *f) {
         if (!f)
                 f = stdout;
 
-        HASHMAP_FOREACH(i, zone->by_key) {
-                DnsZoneItem *j;
-
+        HASHMAP_FOREACH(i, zone->by_key)
                 LIST_FOREACH(by_key, j, i) {
                         const char *t;
 
@@ -672,7 +659,6 @@ void dns_zone_dump(DnsZone *zone, FILE *f) {
                         fputs(t, f);
                         fputc('\n', f);
                 }
-        }
 }
 
 bool dns_zone_is_empty(DnsZone *zone) {
@@ -683,7 +669,7 @@ bool dns_zone_is_empty(DnsZone *zone) {
 }
 
 bool dns_zone_contains_name(DnsZone *z, const char *name) {
-        DnsZoneItem *i, *first;
+        DnsZoneItem *first;
 
         first = hashmap_get(z->by_name, name);
         if (!first)
index d908cc64e6b5d9d06fcf57022da363b95568af7f..84a51ba2e610ba86f591323c7c3db2cdd4862a41 100644 (file)
@@ -12,7 +12,6 @@
 
 int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         DnssdService *s = userdata;
-        DnssdTxtData *txt_data;
         Manager *m;
         Link *l;
         int r;
index ab2773e4e4b693dfef05bda033101432bd4646a4..6d77aa817e41550dce9b9e40744d27b91e3c61ea 100644 (file)
@@ -186,7 +186,6 @@ int dnssd_render_instance_name(DnssdService *s, char **ret_name) {
 
 int dnssd_load(Manager *manager) {
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         int r;
 
         assert(manager);
@@ -211,7 +210,6 @@ int dnssd_update_rrs(DnssdService *s) {
         _cleanup_free_ char *n = NULL;
         _cleanup_free_ char *service_name = NULL;
         _cleanup_free_ char *full_name = NULL;
-        DnssdTxtData *txt_data;
         int r;
 
         assert(s);
index a8da6c3d8810c36efbe45f8ca435d44278044bda..72d6b4218953e516bdb7d8521f72d396edb08761 100644 (file)
@@ -192,7 +192,6 @@ static void strip_localhost(EtcHosts *hosts) {
 
         for (size_t j = 0; j < ELEMENTSOF(local_in_addrs); j++) {
                 bool all_localhost, in_order;
-                char **i;
 
                 item = hashmap_get(hosts->by_address, local_in_addrs + j);
                 if (!item)
@@ -392,8 +391,6 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
                 }
 
                 if (found_ptr) {
-                        char **n;
-
                         r = dns_answer_reserve(answer, strv_length(item->names));
                         if (r < 0)
                                 return r;
index 8d533d7ecf9ad1dc2f63f862a8872a402e94efcb..881b65bb2630b7ab528ac648d12acaedf5fccd97 100644 (file)
@@ -51,7 +51,6 @@ static int property_get_dns_internal(
                 bool extended) {
 
         Link *l = userdata;
-        DnsServer *s;
         int r;
 
         assert(reply);
@@ -144,7 +143,6 @@ static int property_get_domains(
                 sd_bus_error *error) {
 
         Link *l = userdata;
-        DnsSearchDomain *d;
         int r;
 
         assert(reply);
@@ -706,7 +704,6 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
         _cleanup_free_ char *j = NULL;
         Link *l = userdata;
         int r;
-        char **i;
 
         assert(message);
         assert(l);
index 9eb0ccefd121ce3c7f6ac9c23cbd193a3fdad2fa..8027eb6f91ff515d1ce31838b7c1b561f2b1a75a 100644 (file)
@@ -186,7 +186,6 @@ void link_allocate_scopes(Link *l) {
 }
 
 void link_add_rrs(Link *l, bool force_remove) {
-        LinkAddress *a;
         int r;
 
         LIST_FOREACH(addresses, a, l->addresses)
@@ -284,7 +283,6 @@ static int link_update_dns_server_one(Link *l, const char *str) {
 
 static int link_update_dns_servers(Link *l) {
         _cleanup_strv_free_ char **nameservers = NULL;
-        char **nameserver;
         int r;
 
         assert(l);
@@ -519,7 +517,6 @@ static int link_update_search_domain_one(Link *l, const char *name, bool route_o
 
 static int link_update_search_domains(Link *l) {
         _cleanup_strv_free_ char **sdomains = NULL, **rdomains = NULL;
-        char **i;
         int r, q;
 
         assert(l);
@@ -682,8 +679,6 @@ int link_update(Link *l) {
 }
 
 bool link_relevant(Link *l, int family, bool local_multicast) {
-        LinkAddress *a;
-
         assert(l);
 
         /* A link is relevant for local multicast traffic if it isn't a loopback device, has a link
@@ -717,8 +712,6 @@ bool link_relevant(Link *l, int family, bool local_multicast) {
 }
 
 LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
-        LinkAddress *a;
-
         assert(l);
 
         if (!IN_SET(family, AF_INET, AF_INET6))
@@ -1223,8 +1216,6 @@ int link_save_user(Link *l) {
                 fprintf(f, "DEFAULT_ROUTE=%s\n", yes_no(l->default_route));
 
         if (l->dns_servers) {
-                DnsServer *server;
-
                 fputs("SERVERS=", f);
                 LIST_FOREACH(servers, server, l->dns_servers) {
 
@@ -1243,8 +1234,6 @@ int link_save_user(Link *l) {
         }
 
         if (l->search_domains) {
-                DnsSearchDomain *domain;
-
                 fputs("DOMAINS=", f);
                 LIST_FOREACH(domains, domain, l->search_domains) {
 
index 615431ebb7e161f3fac4e8e8c561d8a70a636897..916ad3ce03b6fddabb8e5c943f47727290927066 100644 (file)
@@ -13,6 +13,7 @@
 #include "bus-polkit.h"
 #include "dirent-util.h"
 #include "dns-domain.h"
+#include "event-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "hostname-util.h"
@@ -338,28 +339,16 @@ static int on_clock_change(sd_event_source *source, int fd, uint32_t revents, vo
 }
 
 static int manager_clock_change_listen(Manager *m) {
-        _cleanup_close_ int fd = -1;
         int r;
 
         assert(m);
 
         m->clock_change_event_source = sd_event_source_disable_unref(m->clock_change_event_source);
 
-        fd = time_change_fd();
-        if (fd < 0)
-                return log_error_errno(fd, "Failed to allocate clock change timer fd: %m");
-
-        r = sd_event_add_io(m->event, &m->clock_change_event_source, fd, EPOLLIN, on_clock_change, m);
+        r = event_add_time_change(m->event, &m->clock_change_event_source, on_clock_change, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to create clock change event source: %m");
 
-        r = sd_event_source_set_io_fd_own(m->clock_change_event_source, true);
-        if (r < 0)
-                return log_error_errno(r, "Failed to pass ownership of clock fd to event source: %m");
-        TAKE_FD(fd);
-
-        (void) sd_event_source_set_description(m->clock_change_event_source, "clock-change");
-
         return 0;
 }
 
@@ -514,9 +503,7 @@ static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si
         _cleanup_free_ char *buffer = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         Manager *m = userdata;
-        DnsServer *server;
         size_t size = 0;
-        DnsScope *scope;
         Link *l;
 
         assert(s);
@@ -1316,8 +1303,6 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
 }
 
 void manager_verify_all(Manager *m) {
-        DnsScope *s;
-
         assert(m);
 
         LIST_FOREACH(scopes, s, m->dns_scopes)
@@ -1349,7 +1334,6 @@ int manager_is_own_hostname(Manager *m, const char *name) {
 }
 
 int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
-        DnsServer *s;
         Link *l;
         int r;
 
@@ -1400,7 +1384,6 @@ int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
  *   > 0 or true: return only domains which are for routing only
  */
 int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route) {
-        DnsSearchDomain *d;
         Link *l;
         int r;
 
@@ -1512,8 +1495,6 @@ bool manager_routable(Manager *m) {
 }
 
 void manager_flush_caches(Manager *m, int log_level) {
-        DnsScope *scope;
-
         assert(m);
 
         LIST_FOREACH(scopes, scope, m->dns_scopes)
index 07cd5e9a15e75ed5fe3981173dc15a0edef5abae..cf855a614cacffa1583aab393f3b3147eab17b15 100644 (file)
@@ -203,7 +203,6 @@ static bool mdns_should_reply_using_unicast(DnsPacket *p) {
 }
 
 static bool sender_on_local_subnet(DnsScope *s, DnsPacket *p) {
-        LinkAddress *a;
         int r;
 
         /* Check whether the sender is on a local subnet. */
@@ -359,7 +358,6 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
 
         if (dns_packet_validate_reply(p) > 0) {
                 DnsResourceRecord *rr;
-                DnsTransaction *t;
 
                 log_debug("Got mDNS reply packet");
 
index bca2f3b812298b6fec9afa830d316e5ccf7c96e7..656afa06ac8b4b551dba25271bc104b89cca6933 100644 (file)
@@ -188,17 +188,14 @@ static int load_state(Context *c, const struct rfkill_event *event) {
 }
 
 static void save_state_queue_remove(Context *c, int idx, const char *state_file) {
-        struct write_queue_item *item, *tmp;
-
         assert(c);
 
-        LIST_FOREACH_SAFE(queue, item, tmp, c->write_queue) {
+        LIST_FOREACH(queue, item, c->write_queue)
                 if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) {
                         log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file);
                         LIST_REMOVE(queue, c->write_queue, item);
                         write_queue_item_free(item);
                 }
-        }
 }
 
 static int save_state_queue(Context *c, const struct rfkill_event *event) {
index 1cf14e71fc57e266f458c24857c99db6181ba9f3..fb622097043b18a88c6045ae8db540e42ae3a745 100644 (file)
@@ -55,7 +55,6 @@ static int parse(const char *key, const char *value, void *data) {
 static int generate(void) {
         _cleanup_fclose_ FILE *f = NULL;
         const char *p;
-        char **c;
         int r;
 
         if (strv_isempty(arg_commands) && !arg_success_action)
index 3f286a888b5cd44adf65c938bacd1659c5bae7a4..2bdc529b80331ea1a0fd730333169b4446cf0767 100644 (file)
@@ -212,7 +212,6 @@ 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 = NULL;
-        char **entry;
         int r = -EINVAL;
         _cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL;
 
index a87cce4ea57e0e38e13036d6ba02f3965607bf62..53e079bdd70fb70a8e90198d9b4f77bd6a54bcb0 100644 (file)
@@ -1,40 +1,22 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <stdio.h>
-#include <linux/magic.h>
 #include <unistd.h>
 
-#include "sd-device.h"
-#include "sd-id128.h"
-
-#include "alloc-util.h"
-#include "blkid-util.h"
-#include "bootspec-fundamental.h"
 #include "bootspec.h"
+#include "bootspec-fundamental.h"
 #include "conf-files.h"
-#include "def.h"
-#include "device-nodes.h"
 #include "dirent-util.h"
-#include "efivars.h"
 #include "efi-loader.h"
 #include "env-file.h"
-#include "env-util.h"
-#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
-#include "gpt.h"
-#include "id128-util.h"
-#include "parse-util.h"
+#include "find-esp.h"
 #include "path-util.h"
 #include "pe-header.h"
 #include "sort-util.h"
 #include "stat-util.h"
-#include "string-table.h"
-#include "string-util.h"
 #include "strv.h"
 #include "unaligned.h"
-#include "util.h"
-#include "virt.h"
 
 static void boot_entry_free(BootEntry *entry) {
         assert(entry);
@@ -297,7 +279,6 @@ static int boot_entries_find(
                 size_t *n_entries) {
 
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         int r;
 
         assert(root);
@@ -872,8 +853,6 @@ int boot_entries_augment_from_loader(
                 "auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface",
         };
 
-        char **i;
-
         assert(config);
 
         /* Let's add the entries discovered by the boot loader to the end of our list, unless they are
@@ -882,7 +861,6 @@ int boot_entries_augment_from_loader(
         STRV_FOREACH(i, found_by_loader) {
                 BootEntry *existing;
                 _cleanup_free_ char *c = NULL, *t = NULL, *p = NULL;
-                char **a, **b;
 
                 existing = boot_config_find_entry(config, *i);
                 if (existing) {
@@ -923,696 +901,3 @@ int boot_entries_augment_from_loader(
 
         return 0;
 }
-
-/********************************************************************************/
-
-static int verify_esp_blkid(
-                dev_t devid,
-                bool searching,
-                uint32_t *ret_part,
-                uint64_t *ret_pstart,
-                uint64_t *ret_psize,
-                sd_id128_t *ret_uuid) {
-
-        sd_id128_t uuid = SD_ID128_NULL;
-        uint64_t pstart = 0, psize = 0;
-        uint32_t part = 0;
-
-#if HAVE_BLKID
-        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
-        _cleanup_free_ char *node = NULL;
-        const char *v;
-        int r;
-
-        r = device_path_make_major_minor(S_IFBLK, devid, &node);
-        if (r < 0)
-                return log_error_errno(r, "Failed to format major/minor device path: %m");
-
-        errno = 0;
-        b = blkid_new_probe_from_filename(node);
-        if (!b)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
-
-        blkid_probe_enable_superblocks(b, 1);
-        blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
-        blkid_probe_enable_partitions(b, 1);
-        blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
-
-        errno = 0;
-        r = blkid_do_safeprobe(b);
-        if (r == -2)
-                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
-        else if (r == 1)
-                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
-        else if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
-
-        r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
-        if (r != 0)
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "No filesystem found on \"%s\": %m", node);
-        if (!streq(v, "vfat"))
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "File system \"%s\" is not FAT.", node);
-
-        r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
-        if (r != 0)
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "File system \"%s\" is not located on a partitioned block device.", node);
-        if (!streq(v, "gpt"))
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "File system \"%s\" is not on a GPT partition table.", node);
-
-        errno = 0;
-        r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
-        if (r != 0)
-                return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
-        if (id128_equal_string(v, GPT_ESP) <= 0)
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                       "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
-
-        errno = 0;
-        r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
-        if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
-        r = sd_id128_from_string(v, &uuid);
-        if (r < 0)
-                return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
-
-        errno = 0;
-        r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
-        if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node);
-        r = safe_atou32(v, &part);
-        if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
-
-        errno = 0;
-        r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
-        if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
-        r = safe_atou64(v, &pstart);
-        if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
-
-        errno = 0;
-        r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
-        if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
-        r = safe_atou64(v, &psize);
-        if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
-#endif
-
-        if (ret_part)
-                *ret_part = part;
-        if (ret_pstart)
-                *ret_pstart = pstart;
-        if (ret_psize)
-                *ret_psize = psize;
-        if (ret_uuid)
-                *ret_uuid = uuid;
-
-        return 0;
-}
-
-static int verify_esp_udev(
-                dev_t devid,
-                bool searching,
-                uint32_t *ret_part,
-                uint64_t *ret_pstart,
-                uint64_t *ret_psize,
-                sd_id128_t *ret_uuid) {
-
-        _cleanup_(sd_device_unrefp) sd_device *d = NULL;
-        _cleanup_free_ char *node = NULL;
-        sd_id128_t uuid = SD_ID128_NULL;
-        uint64_t pstart = 0, psize = 0;
-        uint32_t part = 0;
-        const char *v;
-        int r;
-
-        r = device_path_make_major_minor(S_IFBLK, devid, &node);
-        if (r < 0)
-                return log_error_errno(r, "Failed to format major/minor device path: %m");
-
-        r = sd_device_new_from_devnum(&d, 'b', devid);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device from device number: %m");
-
-        r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
-        if (!streq(v, "vfat"))
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "File system \"%s\" is not FAT.", node );
-
-        r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
-        if (!streq(v, "gpt"))
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "File system \"%s\" is not on a GPT partition table.", node);
-
-        r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
-        if (id128_equal_string(v, GPT_ESP) <= 0)
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                       "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
-
-        r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
-        r = sd_id128_from_string(v, &uuid);
-        if (r < 0)
-                return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
-
-        r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
-        r = safe_atou32(v, &part);
-        if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
-
-        r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
-        r = safe_atou64(v, &pstart);
-        if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
-
-        r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
-        r = safe_atou64(v, &psize);
-        if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
-
-        if (ret_part)
-                *ret_part = part;
-        if (ret_pstart)
-                *ret_pstart = pstart;
-        if (ret_psize)
-                *ret_psize = psize;
-        if (ret_uuid)
-                *ret_uuid = uuid;
-
-        return 0;
-}
-
-static int verify_fsroot_dir(
-                const char *path,
-                bool searching,
-                bool unprivileged_mode,
-                dev_t *ret_dev) {
-
-        struct stat st, st2;
-        const char *t2, *trigger;
-        int r;
-
-        assert(path);
-        assert(ret_dev);
-
-        /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
-         * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
-         * before stat()ing */
-        trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
-        (void) access(trigger, F_OK);
-
-        if (stat(path, &st) < 0)
-                return log_full_errno((searching && errno == ENOENT) ||
-                                      (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
-                                      "Failed to determine block device node of \"%s\": %m", path);
-
-        if (major(st.st_dev) == 0)
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "Block device node of \"%s\" is invalid.", path);
-
-        if (path_equal(path, "/")) {
-                /* Let's assume that the root directory of the OS is always the root of its file system
-                 * (which technically doesn't have to be the case, but it's close enough, and it's not easy
-                 * to be fully correct for it, since we can't look further up than the root dir easily.) */
-                if (ret_dev)
-                        *ret_dev = st.st_dev;
-
-                return 0;
-        }
-
-        t2 = strjoina(path, "/..");
-        if (stat(t2, &st2) < 0) {
-                if (errno != EACCES)
-                        r = -errno;
-                else {
-                        _cleanup_free_ char *parent = NULL;
-
-                        /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
-                         * directly instead. It's not as good, due to symlinks and such, but we can't do
-                         * anything better here. */
-
-                        parent = dirname_malloc(path);
-                        if (!parent)
-                                return log_oom();
-
-                        r = RET_NERRNO(stat(parent, &st2));
-                }
-
-                if (r < 0)
-                        return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
-                                              "Failed to determine block device node of parent of \"%s\": %m", path);
-        }
-
-        if (st.st_dev == st2.st_dev)
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "Directory \"%s\" is not the root of the file system.", path);
-
-        if (ret_dev)
-                *ret_dev = st.st_dev;
-
-        return 0;
-}
-
-static int verify_esp(
-                const char *p,
-                bool searching,
-                bool unprivileged_mode,
-                uint32_t *ret_part,
-                uint64_t *ret_pstart,
-                uint64_t *ret_psize,
-                sd_id128_t *ret_uuid,
-                dev_t *ret_devid) {
-
-        bool relax_checks;
-        dev_t devid;
-        int r;
-
-        assert(p);
-
-        /* This logs about all errors, except:
-         *
-         *  -ENOENT        â†’ if 'searching' is set, and the dir doesn't exist
-         *  -EADDRNOTAVAIL â†’ if 'searching' is set, and the dir doesn't look like an ESP
-         *  -EACESS        â†’ if 'unprivileged_mode' is set, and we have trouble accessing the thing
-         */
-
-        relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
-
-        /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
-         * issues. Let's also, silence the error messages. */
-
-        if (!relax_checks) {
-                struct statfs sfs;
-
-                if (statfs(p, &sfs) < 0)
-                        /* If we are searching for the mount point, don't generate a log message if we can't find the path */
-                        return log_full_errno((searching && errno == ENOENT) ||
-                                              (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
-                                              "Failed to check file system type of \"%s\": %m", p);
-
-                if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
-                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                              SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                              "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
-        }
-
-        r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
-        if (r < 0)
-                return r;
-
-        /* In a container we don't have access to block devices, skip this part of the verification, we trust
-         * the container manager set everything up correctly on its own. */
-        if (detect_container() > 0 || relax_checks)
-                goto finish;
-
-        /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
-         * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
-         * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
-         * however blkid can't work if we have no privileges to access block devices directly, which is why
-         * we use udev in that case. */
-        if (unprivileged_mode)
-                r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
-        else
-                r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
-        if (r < 0)
-                return r;
-
-        if (ret_devid)
-                *ret_devid = devid;
-
-        return 0;
-
-finish:
-        if (ret_part)
-                *ret_part = 0;
-        if (ret_pstart)
-                *ret_pstart = 0;
-        if (ret_psize)
-                *ret_psize = 0;
-        if (ret_uuid)
-                *ret_uuid = SD_ID128_NULL;
-        if (ret_devid)
-                *ret_devid = 0;
-
-        return 0;
-}
-
-int find_esp_and_warn(
-                const char *path,
-                bool unprivileged_mode,
-                char **ret_path,
-                uint32_t *ret_part,
-                uint64_t *ret_pstart,
-                uint64_t *ret_psize,
-                sd_id128_t *ret_uuid,
-                dev_t *ret_devid) {
-
-        int r;
-
-        /* This logs about all errors except:
-         *
-         *    -ENOKEY â†’ when we can't find the partition
-         *   -EACCESS â†’ when unprivileged_mode is true, and we can't access something
-         */
-
-        if (path) {
-                r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
-                if (r < 0)
-                        return r;
-
-                goto found;
-        }
-
-        path = getenv("SYSTEMD_ESP_PATH");
-        if (path) {
-                struct stat st;
-
-                if (!path_is_valid(path) || !path_is_absolute(path))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
-                                               path);
-
-                /* Note: when the user explicitly configured things with an env var we won't validate the
-                 * path beyond checking it refers to a directory. After all we want this to be useful for
-                 * testing. */
-
-                if (stat(path, &st) < 0)
-                        return log_error_errno(errno, "Failed to stat '%s': %m", path);
-                if (!S_ISDIR(st.st_mode))
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path);
-
-                if (ret_part)
-                        *ret_part = 0;
-                if (ret_pstart)
-                        *ret_pstart = 0;
-                if (ret_psize)
-                        *ret_psize = 0;
-                if (ret_uuid)
-                        *ret_uuid = SD_ID128_NULL;
-                if (ret_devid)
-                        *ret_devid = st.st_dev;
-
-                goto found;
-        }
-
-        FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
-
-                r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
-                if (r >= 0)
-                        goto found;
-                if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
-                        return r;
-        }
-
-        /* No logging here */
-        return -ENOKEY;
-
-found:
-        if (ret_path) {
-                char *c;
-
-                c = strdup(path);
-                if (!c)
-                        return log_oom();
-
-                *ret_path = c;
-        }
-
-        return 0;
-}
-
-static int verify_xbootldr_blkid(
-                dev_t devid,
-                bool searching,
-                sd_id128_t *ret_uuid) {
-
-        sd_id128_t uuid = SD_ID128_NULL;
-
-#if HAVE_BLKID
-        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
-        _cleanup_free_ char *node = NULL;
-        const char *v;
-        int r;
-
-        r = device_path_make_major_minor(S_IFBLK, devid, &node);
-        if (r < 0)
-                return log_error_errno(r, "Failed to format major/minor device path: %m");
-        errno = 0;
-        b = blkid_new_probe_from_filename(node);
-        if (!b)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
-
-        blkid_probe_enable_partitions(b, 1);
-        blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
-
-        errno = 0;
-        r = blkid_do_safeprobe(b);
-        if (r == -2)
-                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
-        else if (r == 1)
-                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
-        else if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
-
-        errno = 0;
-        r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
-        if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
-        if (streq(v, "gpt")) {
-
-                errno = 0;
-                r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
-                if (r != 0)
-                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
-                if (id128_equal_string(v, GPT_XBOOTLDR) <= 0)
-                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                              searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
-                                              "File system \"%s\" has wrong type for extended boot loader partition.", node);
-
-                errno = 0;
-                r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
-                if (r != 0)
-                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
-                r = sd_id128_from_string(v, &uuid);
-                if (r < 0)
-                        return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
-
-        } else if (streq(v, "dos")) {
-
-                errno = 0;
-                r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
-                if (r != 0)
-                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
-                if (!streq(v, "0xea"))
-                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                              searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
-                                              "File system \"%s\" has wrong type for extended boot loader partition.", node);
-
-        } else
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
-                                      "File system \"%s\" is not on a GPT or DOS partition table.", node);
-#endif
-
-        if (ret_uuid)
-                *ret_uuid = uuid;
-
-        return 0;
-}
-
-static int verify_xbootldr_udev(
-                dev_t devid,
-                bool searching,
-                sd_id128_t *ret_uuid) {
-
-        _cleanup_(sd_device_unrefp) sd_device *d = NULL;
-        _cleanup_free_ char *node = NULL;
-        sd_id128_t uuid = SD_ID128_NULL;
-        const char *v;
-        int r;
-
-        r = device_path_make_major_minor(S_IFBLK, devid, &node);
-        if (r < 0)
-                return log_error_errno(r, "Failed to format major/minor device path: %m");
-
-        r = sd_device_new_from_devnum(&d, 'b', devid);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device from device number: %m");
-
-        r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
-
-        if (streq(v, "gpt")) {
-
-                r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to get device property: %m");
-                if (id128_equal_string(v, GPT_XBOOTLDR))
-                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                              searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
-                                              "File system \"%s\" has wrong type for extended boot loader partition.", node);
-
-                r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to get device property: %m");
-                r = sd_id128_from_string(v, &uuid);
-                if (r < 0)
-                        return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
-
-        } else if (streq(v, "dos")) {
-
-                r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to get device property: %m");
-                if (!streq(v, "0xea"))
-                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                              searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
-                                              "File system \"%s\" has wrong type for extended boot loader partition.", node);
-        } else
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
-                                      "File system \"%s\" is not on a GPT or DOS partition table.", node);
-
-        if (ret_uuid)
-                *ret_uuid = uuid;
-
-        return 0;
-}
-
-static int verify_xbootldr(
-                const char *p,
-                bool searching,
-                bool unprivileged_mode,
-                sd_id128_t *ret_uuid,
-                dev_t *ret_devid) {
-
-        bool relax_checks;
-        dev_t devid;
-        int r;
-
-        assert(p);
-
-        relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
-
-        r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
-        if (r < 0)
-                return r;
-
-        if (detect_container() > 0 || relax_checks)
-                goto finish;
-
-        if (unprivileged_mode)
-                r = verify_xbootldr_udev(devid, searching, ret_uuid);
-        else
-                r = verify_xbootldr_blkid(devid, searching, ret_uuid);
-        if (r < 0)
-                return r;
-
-        if (ret_devid)
-                *ret_devid = devid;
-
-        return 0;
-
-finish:
-        if (ret_uuid)
-                *ret_uuid = SD_ID128_NULL;
-        if (ret_devid)
-                *ret_devid = 0;
-
-        return 0;
-}
-
-int find_xbootldr_and_warn(
-                const char *path,
-                bool unprivileged_mode,
-                char **ret_path,
-                sd_id128_t *ret_uuid,
-                dev_t *ret_devid) {
-
-        int r;
-
-        /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
-
-        if (path) {
-                r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
-                if (r < 0)
-                        return r;
-
-                goto found;
-        }
-
-        path = getenv("SYSTEMD_XBOOTLDR_PATH");
-        if (path) {
-                struct stat st;
-
-                if (!path_is_valid(path) || !path_is_absolute(path))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
-                                               path);
-
-                if (stat(path, &st) < 0)
-                        return log_error_errno(errno, "Failed to stat '%s': %m", path);
-                if (!S_ISDIR(st.st_mode))
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path);
-
-                if (ret_uuid)
-                        *ret_uuid = SD_ID128_NULL;
-                if (ret_devid)
-                        *ret_devid = st.st_dev;
-
-                goto found;
-        }
-
-        r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid);
-        if (r >= 0) {
-                path = "/boot";
-                goto found;
-        }
-        if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
-                return r;
-
-        return -ENOKEY;
-
-found:
-        if (ret_path) {
-                char *c;
-
-                c = strdup(path);
-                if (!c)
-                        return log_oom();
-
-                *ret_path = c;
-        }
-
-        return 0;
-}
index 64052af593845bb8e6b9186d19c445d88d91e3bb..0f5e6c9ad50c21c3b096b32c4613906aeae79913 100644 (file)
@@ -2,12 +2,11 @@
 
 #pragma once
 
+#include <errno.h>
 #include <inttypes.h>
 #include <stdbool.h>
 #include <sys/types.h>
 
-#include "sd-id128.h"
-
 #include "string-util.h"
 
 typedef enum BootEntryType {
@@ -91,6 +90,3 @@ static inline const char* boot_entry_title(const BootEntry *entry) {
 
         return entry->show_title ?: entry->title ?: entry->id;
 }
-
-int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid, dev_t *ret_devid);
-int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid, dev_t *ret_devid);
index bbe04bea37f2a46e1cd8b6c73b09865ee935ad75..4d733a81737a09448bda7bcb6932d1960f648835 100644 (file)
@@ -36,7 +36,6 @@ static int bus_message_append_strv_key_value(
                 sd_bus_message *m,
                 const char **l) {
 
-        const char **k, **v;
         int r;
 
         assert(m);
index 87c0334fec5803d518e1b90b32adeb6ab4314ce7..3693fd61dc0dc9bbf4ff739df481952009f02f3b 100644 (file)
@@ -199,7 +199,7 @@ static int dump_processes(
         }
 
         if (cg->children) {
-                struct CGroupInfo **children, *child;
+                struct CGroupInfo **children;
                 size_t n = 0, i;
 
                 /* Order subcgroups by their name */
@@ -217,9 +217,7 @@ static int dump_processes(
                         const char *name, *special;
                         bool more;
 
-                        child = children[i];
-
-                        name = strrchr(child->cgroup_path, '/');
+                        name = strrchr(children[i]->cgroup_path, '/');
                         if (!name)
                                 return -EINVAL;
                         name++;
@@ -238,7 +236,7 @@ static int dump_processes(
                         if (!pp)
                                 return -ENOMEM;
 
-                        r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
+                        r = dump_processes(cgroups, children[i]->cgroup_path, pp, n_columns, flags);
                         if (r < 0)
                                 return r;
                 }
index c35dd286e60b6e9b589fe963c200c270f46c6ec9..95ef3230b4a9675f4b7f6116d54054b703aaa45b 100644 (file)
@@ -1675,7 +1675,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
 
         if (streq(field, "RootImageOptions")) {
                 _cleanup_strv_free_ char **l = NULL;
-                char **first = NULL, **second = NULL;
                 const char *p = eq;
 
                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
@@ -2024,7 +2023,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                         if (r < 0)
                                 return bus_log_create_error(r);
 
-                        char **source, **destination;
                         STRV_FOREACH_PAIR(source, destination, symlinks) {
                                 r = sd_bus_message_append(m, "(sst)", *source, *destination, 0);
                                 if (r < 0)
@@ -2648,7 +2646,6 @@ int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const cha
 }
 
 int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
-        char **i;
         int r;
 
         assert(m);
index 4a2b7684bce5bc4589bef13122ad4eed078e16c9..a907b67a70cc54aabb201a9f0f907139781582ef 100644 (file)
@@ -490,7 +490,6 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send
 
 int bus_track_add_name_many(sd_bus_track *t, char **l) {
         int r = 0;
-        char **i;
 
         assert(t);
 
@@ -562,7 +561,6 @@ int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *descri
 
 int bus_reply_pair_array(sd_bus_message *m, char **l) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        char **k, **v;
         int r;
 
         assert(m);
index 38a6755d178ce54a83624b4d5ce7ea5d8d29b346..a1fabc73c111e2bf6c88485b1d7b37fb225376c3 100644 (file)
@@ -22,7 +22,6 @@
 static int cg_any_controller_used_for_v1(void) {
         _cleanup_free_ char *buf = NULL;
         _cleanup_strv_free_ char **lines = NULL;
-        char **line;
         int r;
 
         r = read_full_virtual_file("/proc/cgroups", &buf, NULL);
index febea8ea85463c9340c61042363391f2f4f0f53f..eb6f12a2556b028d29a1d234e344bc7d8e026503 100644 (file)
@@ -134,9 +134,8 @@ int clock_reset_timewarp(void) {
 #define EPOCH_FILE "/usr/lib/clock-epoch"
 
 int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
-        struct stat st;
-        struct timespec ts;
         usec_t epoch_usec, now_usec;
+        struct stat st;
 
         /* NB: we update *ret_attempted_change in *all* cases, both
          * on success and failure, to indicate what we intended to do! */
@@ -161,7 +160,7 @@ int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
                 return 0;
         }
 
-        if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, epoch_usec)) < 0)
+        if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch_usec)) < 0)
                 return -errno;
 
         return 1;
index 68fbbf643a9197a2081f0a393e4831351b7a238d..0a4072c9bef58bb645657d40df19d1aae9d60aaa 100644 (file)
@@ -89,9 +89,7 @@ Condition* condition_free(Condition *c) {
 }
 
 Condition* condition_free_list_type(Condition *head, ConditionType type) {
-        Condition *c, *n;
-
-        LIST_FOREACH_SAFE(conditions, c, n, head)
+        LIST_FOREACH(conditions, c, head)
                 if (type < 0 || c->type == type) {
                         LIST_REMOVE(conditions, head, c);
                         condition_free(c);
@@ -829,7 +827,6 @@ static int condition_test_first_boot(Condition *c, char **env) {
 
 static int condition_test_environment(Condition *c, char **env) {
         bool equal;
-        char **i;
 
         assert(c);
         assert(c->parameter);
@@ -1171,7 +1168,6 @@ bool condition_test_list(
                 condition_test_logger_t logger,
                 void *userdata) {
 
-        Condition *c;
         int triggered = -1;
 
         assert(!!logger == !!to_string);
@@ -1234,8 +1230,6 @@ void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_stri
 }
 
 void condition_dump_list(Condition *first, FILE *f, const char *prefix, condition_to_string_t to_string) {
-        Condition *c;
-
         LIST_FOREACH(conditions, c, first)
                 condition_dump(c, f, prefix, to_string);
 }
index 0ae499814e29b55525bb22908ad8ddd0e612f2b8..23aed96b4825c9bfa7067b7b925520220f1d3752 100644 (file)
@@ -486,7 +486,6 @@ static int config_parse_many_files(
 
         _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL;
         struct stat st;
-        char **fn;
         int r;
 
         if (ret_stats_by_path) {
index 268d9102142ba67c0d7850d7f15cc31099510152..024ffb3248e181629d2adcaee16454927e7709c6 100644 (file)
@@ -632,7 +632,6 @@ int image_remove(Image *i) {
         _cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
         _cleanup_strv_free_ char **settings = NULL;
         _cleanup_free_ char *roothash = NULL;
-        char **j;
         int r;
 
         assert(i);
@@ -695,10 +694,9 @@ int image_remove(Image *i) {
                 return -EOPNOTSUPP;
         }
 
-        STRV_FOREACH(j, settings) {
+        STRV_FOREACH(j, settings)
                 if (unlink(*j) < 0 && errno != ENOENT)
                         log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j);
-        }
 
         if (unlink(roothash) < 0 && errno != ENOENT)
                 log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", roothash);
@@ -724,7 +722,6 @@ int image_rename(Image *i, const char *new_name) {
         _cleanup_free_ char *new_path = NULL, *nn = NULL, *roothash = NULL;
         _cleanup_strv_free_ char **settings = NULL;
         unsigned file_attr = 0;
-        char **j;
         int r;
 
         assert(i);
@@ -845,7 +842,6 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
         _cleanup_strv_free_ char **settings = NULL;
         _cleanup_free_ char *roothash = NULL;
         const char *new_path;
-        char **j;
         int r;
 
         assert(i);
index 14361a0e5a7704b6f5f4dbef3e771be5b5bebcff..8a3da1f650810c4e0a60c0ea736b79c689510c1c 100644 (file)
@@ -2208,7 +2208,6 @@ static int validate_signature_userspace(const VeritySettings *verity) {
         _cleanup_(BIO_freep) BIO *bio = NULL; /* 'bio' must be freed first, 's' second, hence keep this order
                                                * of declaration in place, please */
         const unsigned char *d;
-        char **i;
         int r;
 
         assert(verity);
@@ -3333,8 +3332,6 @@ MountOptions* mount_options_free_all(MountOptions *options) {
 }
 
 const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator) {
-        const MountOptions *m;
-
         LIST_FOREACH(mount_options, m, options)
                 if (designator == m->partition_designator && !isempty(m->options))
                         return m->options;
index eb016eb114604ba8d9190a69d1d1bc9ea3a50421..375a3ca600414e572cc2b1d7b4e93ba2c860c217 100644 (file)
@@ -232,7 +232,6 @@ int unit_file_find_dropin_paths(
 
         _cleanup_strv_free_ char **dirs = NULL;
         const char *n;
-        char **p;
         int r;
 
         assert(ret);
index c1da81e80930dcbbf91a5d12b0d0a68bc673d9f4..d1f50249d08a6f34bcb06472eb8d484f2df6500c 100644 (file)
@@ -91,7 +91,6 @@ static int do_execute(
 
         _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
         _cleanup_strv_free_ char **paths = NULL;
-        char **path, **e;
         int r;
         bool parallel_execution;
 
@@ -254,7 +253,7 @@ int execute_directories(
 }
 
 static int gather_environment_generate(int fd, void *arg) {
-        char ***env = arg, **x, **y;
+        char ***env = arg;
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_strv_free_ char **new = NULL;
         int r;
@@ -369,7 +368,6 @@ static int gather_environment_consume(int fd, void *arg) {
 
 int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags) {
         ExecCommandFlags ex_flag, ret_flags = 0;
-        char **opt;
 
         assert(flags);
 
diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c
new file mode 100644 (file)
index 0000000..1569b46
--- /dev/null
@@ -0,0 +1,709 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <linux/magic.h>
+
+#include "sd-device.h"
+
+#include "alloc-util.h"
+#include "blkid-util.h"
+#include "env-util.h"
+#include "errno-util.h"
+#include "find-esp.h"
+#include "gpt.h"
+#include "id128-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "virt.h"
+
+static int verify_esp_blkid(
+                dev_t devid,
+                bool searching,
+                uint32_t *ret_part,
+                uint64_t *ret_pstart,
+                uint64_t *ret_psize,
+                sd_id128_t *ret_uuid) {
+
+        sd_id128_t uuid = SD_ID128_NULL;
+        uint64_t pstart = 0, psize = 0;
+        uint32_t part = 0;
+
+#if HAVE_BLKID
+        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
+        _cleanup_free_ char *node = NULL;
+        const char *v;
+        int r;
+
+        r = device_path_make_major_minor(S_IFBLK, devid, &node);
+        if (r < 0)
+                return log_error_errno(r, "Failed to format major/minor device path: %m");
+
+        errno = 0;
+        b = blkid_new_probe_from_filename(node);
+        if (!b)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
+
+        blkid_probe_enable_superblocks(b, 1);
+        blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
+        blkid_probe_enable_partitions(b, 1);
+        blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+        errno = 0;
+        r = blkid_do_safeprobe(b);
+        if (r == -2)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
+        else if (r == 1)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
+        else if (r != 0)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
+
+        r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
+        if (r != 0)
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "No filesystem found on \"%s\": %m", node);
+        if (!streq(v, "vfat"))
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "File system \"%s\" is not FAT.", node);
+
+        r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
+        if (r != 0)
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "File system \"%s\" is not located on a partitioned block device.", node);
+        if (!streq(v, "gpt"))
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "File system \"%s\" is not on a GPT partition table.", node);
+
+        errno = 0;
+        r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
+        if (r != 0)
+                return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
+        if (id128_equal_string(v, GPT_ESP) <= 0)
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                       "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
+
+        errno = 0;
+        r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
+        if (r != 0)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
+        r = sd_id128_from_string(v, &uuid);
+        if (r < 0)
+                return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
+
+        errno = 0;
+        r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
+        if (r != 0)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node);
+        r = safe_atou32(v, &part);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
+
+        errno = 0;
+        r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
+        if (r != 0)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
+        r = safe_atou64(v, &pstart);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
+
+        errno = 0;
+        r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
+        if (r != 0)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
+        r = safe_atou64(v, &psize);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
+#endif
+
+        if (ret_part)
+                *ret_part = part;
+        if (ret_pstart)
+                *ret_pstart = pstart;
+        if (ret_psize)
+                *ret_psize = psize;
+        if (ret_uuid)
+                *ret_uuid = uuid;
+
+        return 0;
+}
+
+static int verify_esp_udev(
+                dev_t devid,
+                bool searching,
+                uint32_t *ret_part,
+                uint64_t *ret_pstart,
+                uint64_t *ret_psize,
+                sd_id128_t *ret_uuid) {
+
+        _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+        _cleanup_free_ char *node = NULL;
+        sd_id128_t uuid = SD_ID128_NULL;
+        uint64_t pstart = 0, psize = 0;
+        uint32_t part = 0;
+        const char *v;
+        int r;
+
+        r = device_path_make_major_minor(S_IFBLK, devid, &node);
+        if (r < 0)
+                return log_error_errno(r, "Failed to format major/minor device path: %m");
+
+        r = sd_device_new_from_devnum(&d, 'b', devid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device from device number: %m");
+
+        r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device property: %m");
+        if (!streq(v, "vfat"))
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "File system \"%s\" is not FAT.", node );
+
+        r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device property: %m");
+        if (!streq(v, "gpt"))
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "File system \"%s\" is not on a GPT partition table.", node);
+
+        r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device property: %m");
+        if (id128_equal_string(v, GPT_ESP) <= 0)
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                       "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
+
+        r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device property: %m");
+        r = sd_id128_from_string(v, &uuid);
+        if (r < 0)
+                return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
+
+        r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device property: %m");
+        r = safe_atou32(v, &part);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
+
+        r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device property: %m");
+        r = safe_atou64(v, &pstart);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
+
+        r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device property: %m");
+        r = safe_atou64(v, &psize);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
+
+        if (ret_part)
+                *ret_part = part;
+        if (ret_pstart)
+                *ret_pstart = pstart;
+        if (ret_psize)
+                *ret_psize = psize;
+        if (ret_uuid)
+                *ret_uuid = uuid;
+
+        return 0;
+}
+
+static int verify_fsroot_dir(
+                const char *path,
+                bool searching,
+                bool unprivileged_mode,
+                dev_t *ret_dev) {
+
+        struct stat st, st2;
+        const char *t2, *trigger;
+        int r;
+
+        assert(path);
+        assert(ret_dev);
+
+        /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
+         * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
+         * before stat()ing */
+        trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
+        (void) access(trigger, F_OK);
+
+        if (stat(path, &st) < 0)
+                return log_full_errno((searching && errno == ENOENT) ||
+                                      (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
+                                      "Failed to determine block device node of \"%s\": %m", path);
+
+        if (major(st.st_dev) == 0)
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "Block device node of \"%s\" is invalid.", path);
+
+        if (path_equal(path, "/")) {
+                /* Let's assume that the root directory of the OS is always the root of its file system
+                 * (which technically doesn't have to be the case, but it's close enough, and it's not easy
+                 * to be fully correct for it, since we can't look further up than the root dir easily.) */
+                if (ret_dev)
+                        *ret_dev = st.st_dev;
+
+                return 0;
+        }
+
+        t2 = strjoina(path, "/..");
+        if (stat(t2, &st2) < 0) {
+                if (errno != EACCES)
+                        r = -errno;
+                else {
+                        _cleanup_free_ char *parent = NULL;
+
+                        /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
+                         * directly instead. It's not as good, due to symlinks and such, but we can't do
+                         * anything better here. */
+
+                        parent = dirname_malloc(path);
+                        if (!parent)
+                                return log_oom();
+
+                        r = RET_NERRNO(stat(parent, &st2));
+                }
+
+                if (r < 0)
+                        return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
+                                              "Failed to determine block device node of parent of \"%s\": %m", path);
+        }
+
+        if (st.st_dev == st2.st_dev)
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                      "Directory \"%s\" is not the root of the file system.", path);
+
+        if (ret_dev)
+                *ret_dev = st.st_dev;
+
+        return 0;
+}
+
+static int verify_esp(
+                const char *p,
+                bool searching,
+                bool unprivileged_mode,
+                uint32_t *ret_part,
+                uint64_t *ret_pstart,
+                uint64_t *ret_psize,
+                sd_id128_t *ret_uuid,
+                dev_t *ret_devid) {
+
+        bool relax_checks;
+        dev_t devid;
+        int r;
+
+        assert(p);
+
+        /* This logs about all errors, except:
+         *
+         *  -ENOENT        â†’ if 'searching' is set, and the dir doesn't exist
+         *  -EADDRNOTAVAIL â†’ if 'searching' is set, and the dir doesn't look like an ESP
+         *  -EACESS        â†’ if 'unprivileged_mode' is set, and we have trouble accessing the thing
+         */
+
+        relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
+
+        /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
+         * issues. Let's also, silence the error messages. */
+
+        if (!relax_checks) {
+                struct statfs sfs;
+
+                if (statfs(p, &sfs) < 0)
+                        /* If we are searching for the mount point, don't generate a log message if we can't find the path */
+                        return log_full_errno((searching && errno == ENOENT) ||
+                                              (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
+                                              "Failed to check file system type of \"%s\": %m", p);
+
+                if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
+                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                              SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                              "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
+        }
+
+        r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
+        if (r < 0)
+                return r;
+
+        /* In a container we don't have access to block devices, skip this part of the verification, we trust
+         * the container manager set everything up correctly on its own. */
+        if (detect_container() > 0 || relax_checks)
+                goto finish;
+
+        /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
+         * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
+         * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
+         * however blkid can't work if we have no privileges to access block devices directly, which is why
+         * we use udev in that case. */
+        if (unprivileged_mode)
+                r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
+        else
+                r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
+        if (r < 0)
+                return r;
+
+        if (ret_devid)
+                *ret_devid = devid;
+
+        return 0;
+
+finish:
+        if (ret_part)
+                *ret_part = 0;
+        if (ret_pstart)
+                *ret_pstart = 0;
+        if (ret_psize)
+                *ret_psize = 0;
+        if (ret_uuid)
+                *ret_uuid = SD_ID128_NULL;
+        if (ret_devid)
+                *ret_devid = 0;
+
+        return 0;
+}
+
+int find_esp_and_warn(
+                const char *path,
+                bool unprivileged_mode,
+                char **ret_path,
+                uint32_t *ret_part,
+                uint64_t *ret_pstart,
+                uint64_t *ret_psize,
+                sd_id128_t *ret_uuid,
+                dev_t *ret_devid) {
+
+        int r;
+
+        /* This logs about all errors except:
+         *
+         *    -ENOKEY â†’ when we can't find the partition
+         *   -EACCESS â†’ when unprivileged_mode is true, and we can't access something
+         */
+
+        if (path) {
+                r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
+                if (r < 0)
+                        return r;
+
+                goto found;
+        }
+
+        path = getenv("SYSTEMD_ESP_PATH");
+        if (path) {
+                struct stat st;
+
+                if (!path_is_valid(path) || !path_is_absolute(path))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
+                                               path);
+
+                /* Note: when the user explicitly configured things with an env var we won't validate the
+                 * path beyond checking it refers to a directory. After all we want this to be useful for
+                 * testing. */
+
+                if (stat(path, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat '%s': %m", path);
+                if (!S_ISDIR(st.st_mode))
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path);
+
+                if (ret_part)
+                        *ret_part = 0;
+                if (ret_pstart)
+                        *ret_pstart = 0;
+                if (ret_psize)
+                        *ret_psize = 0;
+                if (ret_uuid)
+                        *ret_uuid = SD_ID128_NULL;
+                if (ret_devid)
+                        *ret_devid = st.st_dev;
+
+                goto found;
+        }
+
+        FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
+
+                r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
+                if (r >= 0)
+                        goto found;
+                if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
+                        return r;
+        }
+
+        /* No logging here */
+        return -ENOKEY;
+
+found:
+        if (ret_path) {
+                char *c;
+
+                c = strdup(path);
+                if (!c)
+                        return log_oom();
+
+                *ret_path = c;
+        }
+
+        return 0;
+}
+
+static int verify_xbootldr_blkid(
+                dev_t devid,
+                bool searching,
+                sd_id128_t *ret_uuid) {
+
+        sd_id128_t uuid = SD_ID128_NULL;
+
+#if HAVE_BLKID
+        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
+        _cleanup_free_ char *node = NULL;
+        const char *v;
+        int r;
+
+        r = device_path_make_major_minor(S_IFBLK, devid, &node);
+        if (r < 0)
+                return log_error_errno(r, "Failed to format major/minor device path: %m");
+        errno = 0;
+        b = blkid_new_probe_from_filename(node);
+        if (!b)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
+
+        blkid_probe_enable_partitions(b, 1);
+        blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+        errno = 0;
+        r = blkid_do_safeprobe(b);
+        if (r == -2)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
+        else if (r == 1)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
+        else if (r != 0)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
+
+        errno = 0;
+        r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
+        if (r != 0)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
+        if (streq(v, "gpt")) {
+
+                errno = 0;
+                r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
+                if (r != 0)
+                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
+                if (id128_equal_string(v, GPT_XBOOTLDR) <= 0)
+                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                              searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
+                                              "File system \"%s\" has wrong type for extended boot loader partition.", node);
+
+                errno = 0;
+                r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
+                if (r != 0)
+                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
+                r = sd_id128_from_string(v, &uuid);
+                if (r < 0)
+                        return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
+
+        } else if (streq(v, "dos")) {
+
+                errno = 0;
+                r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
+                if (r != 0)
+                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
+                if (!streq(v, "0xea"))
+                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                              searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
+                                              "File system \"%s\" has wrong type for extended boot loader partition.", node);
+
+        } else
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
+                                      "File system \"%s\" is not on a GPT or DOS partition table.", node);
+#endif
+
+        if (ret_uuid)
+                *ret_uuid = uuid;
+
+        return 0;
+}
+
+static int verify_xbootldr_udev(
+                dev_t devid,
+                bool searching,
+                sd_id128_t *ret_uuid) {
+
+        _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+        _cleanup_free_ char *node = NULL;
+        sd_id128_t uuid = SD_ID128_NULL;
+        const char *v;
+        int r;
+
+        r = device_path_make_major_minor(S_IFBLK, devid, &node);
+        if (r < 0)
+                return log_error_errno(r, "Failed to format major/minor device path: %m");
+
+        r = sd_device_new_from_devnum(&d, 'b', devid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device from device number: %m");
+
+        r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get device property: %m");
+
+        if (streq(v, "gpt")) {
+
+                r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get device property: %m");
+                if (id128_equal_string(v, GPT_XBOOTLDR))
+                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                              searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
+                                              "File system \"%s\" has wrong type for extended boot loader partition.", node);
+
+                r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get device property: %m");
+                r = sd_id128_from_string(v, &uuid);
+                if (r < 0)
+                        return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
+
+        } else if (streq(v, "dos")) {
+
+                r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get device property: %m");
+                if (!streq(v, "0xea"))
+                        return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                              searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
+                                              "File system \"%s\" has wrong type for extended boot loader partition.", node);
+        } else
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
+                                      "File system \"%s\" is not on a GPT or DOS partition table.", node);
+
+        if (ret_uuid)
+                *ret_uuid = uuid;
+
+        return 0;
+}
+
+static int verify_xbootldr(
+                const char *p,
+                bool searching,
+                bool unprivileged_mode,
+                sd_id128_t *ret_uuid,
+                dev_t *ret_devid) {
+
+        bool relax_checks;
+        dev_t devid;
+        int r;
+
+        assert(p);
+
+        relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
+
+        r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
+        if (r < 0)
+                return r;
+
+        if (detect_container() > 0 || relax_checks)
+                goto finish;
+
+        if (unprivileged_mode)
+                r = verify_xbootldr_udev(devid, searching, ret_uuid);
+        else
+                r = verify_xbootldr_blkid(devid, searching, ret_uuid);
+        if (r < 0)
+                return r;
+
+        if (ret_devid)
+                *ret_devid = devid;
+
+        return 0;
+
+finish:
+        if (ret_uuid)
+                *ret_uuid = SD_ID128_NULL;
+        if (ret_devid)
+                *ret_devid = 0;
+
+        return 0;
+}
+
+int find_xbootldr_and_warn(
+                const char *path,
+                bool unprivileged_mode,
+                char **ret_path,
+                sd_id128_t *ret_uuid,
+                dev_t *ret_devid) {
+
+        int r;
+
+        /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
+
+        if (path) {
+                r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
+                if (r < 0)
+                        return r;
+
+                goto found;
+        }
+
+        path = getenv("SYSTEMD_XBOOTLDR_PATH");
+        if (path) {
+                struct stat st;
+
+                if (!path_is_valid(path) || !path_is_absolute(path))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
+                                               path);
+
+                if (stat(path, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat '%s': %m", path);
+                if (!S_ISDIR(st.st_mode))
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path);
+
+                if (ret_uuid)
+                        *ret_uuid = SD_ID128_NULL;
+                if (ret_devid)
+                        *ret_devid = st.st_dev;
+
+                goto found;
+        }
+
+        r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid);
+        if (r >= 0) {
+                path = "/boot";
+                goto found;
+        }
+        if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
+                return r;
+
+        return -ENOKEY;
+
+found:
+        if (ret_path) {
+                char *c;
+
+                c = strdup(path);
+                if (!c)
+                        return log_oom();
+
+                *ret_path = c;
+        }
+
+        return 0;
+}
diff --git a/src/shared/find-esp.h b/src/shared/find-esp.h
new file mode 100644 (file)
index 0000000..e4e65ac
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "sd-id128.h"
+
+int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid, dev_t *ret_devid);
+int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path, sd_id128_t *ret_uuid, dev_t *ret_devid);
index 44b736d1fd6edb80edeeaff5854df718bbe9f240..d4e66cb6a4957bfacb3f3b6ad7a6a400fd204b57 100644 (file)
@@ -1364,7 +1364,6 @@ static char* format_strv_width(char **strv, size_t column_width) {
                 return NULL;
 
         size_t position = 0;
-        char **p;
         STRV_FOREACH(p, strv) {
                 size_t our_len = utf8_console_width(*p); /* This returns -1 on invalid utf-8 (which shouldn't happen).
                                                           * If that happens, we'll just print one item per line. */
index fe4785f3e5c9b13346e8687fadc26fd37f719d8e..f98d03f7669fd7c4ed0a2864d385d95ef9525c51 100644 (file)
@@ -434,7 +434,7 @@ static int trie_store(struct trie *trie, const char *filename, bool compat) {
 
 static int insert_data(struct trie *trie, char **match_list, char *line, const char *filename,
                        uint16_t file_priority, uint32_t line_number, bool compat) {
-        char *value, **entry;
+        char *value;
 
         assert(line[0] == ' ');
 
@@ -583,7 +583,6 @@ int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool co
         _cleanup_free_ char *hwdb_bin = NULL;
         _cleanup_(trie_freep) struct trie *trie = NULL;
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         uint16_t file_priority = 1;
         int r = 0, err;
 
index 4a2698d6a1d82bbdeaea78d30386fac5d0260f50..53b541880494c71e173f98c8c5e003674413604e 100644 (file)
@@ -864,7 +864,6 @@ static int find_symlinks_in_scope(
         bool same_name_link_runtime = false, same_name_link_config = false;
         bool enabled_in_runtime = false, enabled_at_all = false;
         bool ignore_same_name = false;
-        char **p;
         int r;
 
         assert(lp);
@@ -1421,7 +1420,6 @@ static int unit_file_search(
         _cleanup_free_ char *template = NULL;
         bool found_unit = false;
         int r, result;
-        char **p;
 
         assert(info);
         assert(lp);
@@ -1810,7 +1808,6 @@ static int install_info_symlink_alias(
                 UnitFileChange **changes,
                 size_t *n_changes) {
 
-        char **s;
         int r = 0, q;
 
         assert(i);
@@ -1854,7 +1851,6 @@ static int install_info_symlink_wants(
         _cleanup_free_ char *buf = NULL;
         UnitNameFlags valid_dst_type = UNIT_NAME_ANY;
         const char *n;
-        char **s;
         int r = 0, q;
 
         assert(i);
@@ -2158,7 +2154,6 @@ int unit_file_mask(
 
         _cleanup_(lookup_paths_free) LookupPaths lp = {};
         const char *config_path;
-        char **i;
         int r;
 
         assert(scope >= 0);
@@ -2207,7 +2202,6 @@ int unit_file_unmask(
         _cleanup_strv_free_ char **todo = NULL;
         const char *config_path;
         size_t n_todo = 0;
-        char **i;
         int r, q;
 
         assert(scope >= 0);
@@ -2299,7 +2293,6 @@ int unit_file_link(
         _cleanup_strv_free_ char **todo = NULL;
         const char *config_path;
         size_t n_todo = 0;
-        char **i;
         int r, q;
 
         assert(scope >= 0);
@@ -2399,7 +2392,6 @@ int unit_file_revert(
         _cleanup_(lookup_paths_free) LookupPaths lp = {};
         _cleanup_strv_free_ char **todo = NULL;
         size_t n_todo = 0;
-        char **i;
         int r, q;
 
         /* Puts a unit file back into vendor state. This means:
@@ -2419,7 +2411,6 @@ int unit_file_revert(
 
         STRV_FOREACH(i, files) {
                 bool has_vendor = false;
-                char **p;
 
                 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
                         return -EINVAL;
@@ -2503,7 +2494,6 @@ int unit_file_revert(
         STRV_FOREACH(i, todo) {
                 _cleanup_strv_free_ char **fs = NULL;
                 const char *rp;
-                char **j;
 
                 (void) get_files_in_directory(*i, &fs);
 
@@ -2556,7 +2546,6 @@ int unit_file_add_dependency(
         _cleanup_(install_context_done) InstallContext c = {};
         UnitFileInstallInfo *i, *target_info;
         const char *config_path;
-        char **f;
         int r;
 
         assert(scope >= 0);
@@ -2625,7 +2614,6 @@ int unit_file_enable(
         _cleanup_(install_context_done) InstallContext c = {};
         const char *config_path;
         UnitFileInstallInfo *i;
-        char **f;
         int r;
 
         assert(scope >= 0);
@@ -2668,7 +2656,6 @@ int unit_file_disable(
         _cleanup_(install_context_done) InstallContext c = {};
         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
         const char *config_path;
-        char **i;
         int r;
 
         assert(scope >= 0);
@@ -2986,7 +2973,6 @@ static int presets_find_config(UnitFileScope scope, const char *root_dir, char *
 static int read_presets(UnitFileScope scope, const char *root_dir, UnitFilePresets *presets) {
         _cleanup_(unit_file_presets_freep) UnitFilePresets ps = {};
         _cleanup_strv_free_ char **files = NULL;
-        char **p;
         int r;
 
         assert(scope >= 0);
@@ -3104,7 +3090,6 @@ static int pattern_match_multiple_instances(
         if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
                 _cleanup_strv_free_ char **out_strv = NULL;
 
-                char **iter;
                 STRV_FOREACH(iter, rule.instances) {
                         _cleanup_free_ char *name = NULL;
 
@@ -3151,11 +3136,10 @@ static int query_presets(const char *name, const UnitFilePresets *presets, char
                 log_debug("Preset files don't specify rule for %s. Enabling.", name);
                 return 1;
         case PRESET_ENABLE:
-                if (instance_name_list && *instance_name_list) {
-                        char **s;
+                if (instance_name_list && *instance_name_list)
                         STRV_FOREACH(s, *instance_name_list)
                                 log_debug("Preset files say enable %s.", *s);
-                else
+                else
                         log_debug("Preset files say enable %s.", name);
                 return 1;
         case PRESET_DISABLE:
@@ -3261,15 +3245,14 @@ static int preset_prepare_one(
                 return r;
 
         if (r > 0) {
-                if (instance_name_list) {
-                        char **s;
+                if (instance_name_list)
                         STRV_FOREACH(s, instance_name_list) {
                                 r = install_info_discover_and_check(scope, plus, lp, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
                                                                     &i, changes, n_changes);
                                 if (r < 0)
                                         return r;
                         }
-                else {
+                else {
                         r = install_info_discover_and_check(scope, plus, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
                                                             &i, changes, n_changes);
                         if (r < 0)
@@ -3296,7 +3279,6 @@ int unit_file_preset(
         _cleanup_(lookup_paths_free) LookupPaths lp = {};
         _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {};
         const char *config_path;
-        char **i;
         int r;
 
         assert(scope >= 0);
@@ -3336,7 +3318,6 @@ int unit_file_preset_all(
         _cleanup_(lookup_paths_free) LookupPaths lp = {};
         _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {};
         const char *config_path = NULL;
-        char **i;
         int r;
 
         assert(scope >= 0);
@@ -3408,7 +3389,6 @@ int unit_file_get_list(
                 char **patterns) {
 
         _cleanup_(lookup_paths_free) LookupPaths lp = {};
-        char **dirname;
         int r;
 
         assert(scope >= 0);
index 5b315413aab430a491ae327d32dbff5fa9ea9b33..0d72032f53f9a22f6af79e6dc9f7ea98461a150d 100644 (file)
@@ -197,7 +197,6 @@ int test_password_one(const char *hashed_password, const char *password) {
 }
 
 int test_password_many(char **hashed_password, const char *password) {
-        char **hpw;
         int r;
 
         STRV_FOREACH(hpw, hashed_password) {
index 87b88f04d65221de9d1056811ee99f3c09c1e71d..8c9fa88e321a6de867fefa168a9621d7392e9344 100644 (file)
@@ -313,8 +313,6 @@ static int fido2_use_hmac_hash_specific_token(
                 bool retry_with_up = false, retry_with_pin = false;
 
                 if (FLAGS_SET(required, FIDO2ENROLL_PIN)) {
-                        char **i;
-
                         /* OK, we need a pin, try with all pins in turn */
                         if (strv_isempty(pins))
                                 r = FIDO_ERR_PIN_REQUIRED;
@@ -683,7 +681,6 @@ int fido2_generate_hmac_hash(
 
                 for (;;) {
                         _cleanup_(strv_free_erasep) char **pin = NULL;
-                        char **i;
 
                         r = ask_password_auto("Please enter security token PIN:", askpw_icon_name, NULL, "fido2-pin", "fido2-pin", USEC_INFINITY, 0, &pin);
                         if (r < 0)
index fa636dfa2f405b2094e2df46152c13a76a262f60..668462f1731965b23c10673d88d93981a3bb51fe 100644 (file)
@@ -125,6 +125,8 @@ shared_sources = files(
         'fdset.h',
         'fileio-label.c',
         'fileio-label.h',
+        'find-esp.c',
+        'find-esp.h',
         'firewall-util-nft.c',
         'firewall-util-private.h',
         'firewall-util.c',
@@ -176,8 +178,8 @@ shared_sources = files(
         'json.h',
         'kbd-util.c',
         'kbd-util.h',
-        'keyring-util.h',
         'keyring-util.c',
+        'keyring-util.h',
         'killall.c',
         'killall.h',
         'label.c',
index 79179684975eb267cef0f96791bc611374a568e4..eb6dac32e198cf2f69746e05c6527cf241365a59 100644 (file)
@@ -247,8 +247,6 @@ static const char *join_with(const char *controller) {
                 NULL
         };
 
-        const char *const *x, *const *y;
-
         assert(controller);
 
         /* This will lookup which controller to mount another controller with. Input is a controller name, and output
@@ -436,7 +434,6 @@ static int relabel_cgroup_filesystems(void) {
 
 static int relabel_extra(void) {
         _cleanup_strv_free_ char **files = NULL;
-        char **file;
         int r, c = 0;
 
         /* Support for relabelling additional files or directories after loading the policy. For this, code in the
index ae1f2f0b809a1d8c63d688d1ac978c8eb8c97385..694c0383f56579c7a93c8c48479002217b8e7072 100644 (file)
@@ -274,7 +274,6 @@ int bind_remount_recursive_with_mountinfo(
                          * we shall operate on. */
                         if (!path_equal(path, prefix)) {
                                 bool deny_listed = false;
-                                char **i;
 
                                 STRV_FOREACH(i, deny_list) {
                                         if (path_equal(*i, prefix))
index b96cb60064979455d53a36e8b965733b9737c31d..15e3d94ded2c4ebae9c8ec6719af28fecd787132 100644 (file)
@@ -48,7 +48,6 @@ bool net_match_is_empty(const NetMatch *match) {
 }
 
 static bool net_condition_test_strv(char * const *patterns, const char *string) {
-        char * const *p;
         bool match = false, has_positive_rule = false;
 
         if (strv_isempty(patterns))
@@ -79,7 +78,6 @@ static bool net_condition_test_ifname(char * const *patterns, const char *ifname
         if (net_condition_test_strv(patterns, ifname))
                 return true;
 
-        char * const *p;
         STRV_FOREACH(p, alternative_names)
                 if (net_condition_test_strv(patterns, *p))
                         return true;
@@ -88,8 +86,6 @@ static bool net_condition_test_ifname(char * const *patterns, const char *ifname
 }
 
 static int net_condition_test_property(char * const *match_property, sd_device *device) {
-        char * const *p;
-
         if (strv_isempty(match_property))
                 return true;
 
index 06550306338581ebd7f312b342c3669351f572d2..95dfe24b2223cbdcec906f674aca46d6497ab758 100644 (file)
@@ -132,7 +132,6 @@ static int nscd_flush_cache_one(const char *database, usec_t end) {
 int nscd_flush_cache(char **databases) {
         usec_t end;
         int r = 0;
-        char **i;
 
         /* Tries to invalidate the specified database in nscd. We do this carefully, with a 5s timeout, so that we
          * don't block indefinitely on another service. */
index b01b9983c2b137c6e17d986246b1a15ba3e07e93..80feeb1fae8b1cf6418ed936a72e1837b87a0770 100644 (file)
@@ -276,7 +276,6 @@ int pkcs11_token_login(
         for (unsigned tries = 0; tries < 3; tries++) {
                 _cleanup_strv_free_erase_ char **passwords = NULL;
                 _cleanup_(erase_and_freep) char *envpin = NULL;
-                char **i;
 
                 r = getenv_steal_erase("PIN", &envpin);
                 if (r < 0)
index 26daec3450e689ceaef4e593b822e4df67992ea4..98619c25d4d7c49ae3b6f630d94b45217f6c6dd7 100644 (file)
@@ -168,7 +168,6 @@ static int cat_file(const char *filename, bool newline) {
 }
 
 int cat_files(const char *file, char **dropins, CatFlags flags) {
-        char **path;
         int r;
 
         if (file) {
@@ -284,10 +283,9 @@ static int guess_type(const char **name, char ***prefixes, bool *is_collection,
 int conf_files_cat(const char *root, const char *name) {
         _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
         _cleanup_free_ char *path = NULL;
-        char **prefix, **prefixes = NULL; /* explicit initialization to appease gcc */
+        char **prefixes = NULL; /* explicit initialization to appease gcc */
         bool is_collection;
         const char *extension;
-        char **t;
         int r;
 
         r = guess_type(&name, &prefixes, &is_collection, &extension);
index 32bd8aa73bd78f89b7595eaed2b2f6e44cc67cb4..e597a156cff730c611749e75003f4f600cbbcdc9 100644 (file)
@@ -1837,7 +1837,6 @@ int seccomp_restrict_archs(Set *archs) {
 
 int parse_syscall_archs(char **l, Set **ret_archs) {
         _cleanup_set_free_ Set *archs = NULL;
-        char **s;
         int r;
 
         assert(l);
index 47996b9ead64bb6cbc3c06a3cdb417f28eb27fd7..cd48286355715af6a845ceca08a56ac63b2001d4 100644 (file)
@@ -117,7 +117,6 @@ int serialize_dual_timestamp(FILE *f, const char *name, const dual_timestamp *t)
 
 int serialize_strv(FILE *f, const char *key, char **l) {
         int ret = 0, r;
-        char **i;
 
         /* Returns the first error, or positive if anything was serialized, 0 otherwise. */
 
index b00006b41a24a690f9de9a6549d326d26722e3cd..e0a1409e3a4fadcdbec04ccf539542b0c11be812 100644 (file)
@@ -49,7 +49,6 @@ static void load_testdata_env(void) {
         _cleanup_free_ char *s = NULL;
         _cleanup_free_ char *envpath = NULL;
         _cleanup_strv_free_ char **pairs = NULL;
-        char **k, **v;
 
         if (called)
                 return;
index 7c2751f3a7b4619f5f6a80f64af326274df0a8fc..95895a8e458dc2b645dab62c761fcda2f172e3e1 100644 (file)
@@ -143,7 +143,6 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
                         break;
                 }
                 bool has_valid_passwords = false;
-                char **p;
                 STRV_FOREACH(p, hr->hashed_password)
                         if (!hashed_password_is_locked_or_invalid(*p)) {
                                 has_valid_passwords = true;
@@ -240,15 +239,12 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
         if (hr->preferred_language)
                 printf("    Language: %s\n", hr->preferred_language);
 
-        if (!strv_isempty(hr->environment)) {
-                char **i;
-
+        if (!strv_isempty(hr->environment))
                 STRV_FOREACH(i, hr->environment) {
                         printf(i == hr->environment ?
                                " Environment: %s\n" :
                                "              %s\n", *i);
                 }
-        }
 
         if (hr->locked >= 0)
                 printf("      Locked: %s\n", yes_no(hr->locked));
@@ -478,14 +474,11 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
         if (!strv_isempty(hr->ssh_authorized_keys))
                 printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys));
 
-        if (!strv_isempty(hr->pkcs11_token_uri)) {
-                char **i;
-
+        if (!strv_isempty(hr->pkcs11_token_uri))
                 STRV_FOREACH(i, hr->pkcs11_token_uri)
                         printf(i == hr->pkcs11_token_uri ?
                                "PKCS11 Token: %s\n" :
                                "              %s\n", *i);
-        }
 
         if (hr->n_fido2_hmac_credential > 0)
                 printf(" FIDO2 Token: %zu\n", hr->n_fido2_hmac_credential);
@@ -558,7 +551,6 @@ void group_record_show(GroupRecord *gr, bool show_full_user_info) {
                 }
         } else {
                 const char *prefix = "     Members:";
-                char **i;
 
                 STRV_FOREACH(i, gr->members) {
                         printf("%s %s\n", prefix, *i);
@@ -568,7 +560,6 @@ void group_record_show(GroupRecord *gr, bool show_full_user_info) {
 
         if (!strv_isempty(gr->administrators)) {
                 const char *prefix = "      Admins:";
-                char **i;
 
                 STRV_FOREACH(i, gr->administrators) {
                         printf("%s %s\n", prefix, *i);
index 04e69a601e603708da537094d659ef7796a7f551..bae2031d0c5f7fba05cfd937b2ea38c78db3826e 100644 (file)
@@ -2347,7 +2347,6 @@ int varlink_server_shutdown(VarlinkServer *s) {
 }
 
 int varlink_server_attach_event(VarlinkServer *s, sd_event *e, int64_t priority) {
-        VarlinkServerSocket *ss;
         int r;
 
         assert_return(s, -EINVAL);
@@ -2382,8 +2381,6 @@ fail:
 }
 
 int varlink_server_detach_event(VarlinkServer *s) {
-        VarlinkServerSocket *ss;
-
         assert_return(s, -EINVAL);
 
         LIST_FOREACH(sockets, ss, s->sockets)
index 56fcff05b165ad7f6154084f19aaf3af2e48a90d..c92105b62b4a1e7ec86c3f48b295b626a3fabb62 100644 (file)
@@ -12,7 +12,6 @@
 static void test_mount_points_list_one(const char *fname) {
         _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
         _cleanup_free_ char *testdata_fname = NULL;
-        MountPoint *m;
 
         log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/self/mountinfo");
 
@@ -43,7 +42,6 @@ TEST(mount_points_list) {
 static void test_swap_list_one(const char *fname) {
         _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
         _cleanup_free_ char *testdata_fname = NULL;
-        MountPoint *m;
         int r;
 
         log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/swaps");
index ed45ea9edb7271bbd73dfe8b8c23d213dc2d25a9..3e9e241499a5f0a552b3321e31e5d92198653f28 100644 (file)
@@ -589,7 +589,6 @@ static int umount_with_timeout(MountPoint *m, int umount_log_level) {
 /* This includes remounting readonly, which changes the kernel mount options.  Therefore the list passed to
  * this function is invalidated, and should not be reused. */
 static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) {
-        MountPoint *m;
         int n_failed = 0;
 
         assert(head);
@@ -635,13 +634,12 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, int umount
 }
 
 static int swap_points_list_off(MountPoint **head, bool *changed) {
-        MountPoint *m, *n;
         int n_failed = 0;
 
         assert(head);
         assert(changed);
 
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+        LIST_FOREACH(mount_point, m, *head) {
                 log_info("Deactivating swap %s.", m->path);
                 if (swapoff(m->path) < 0) {
                         log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path);
@@ -657,7 +655,6 @@ static int swap_points_list_off(MountPoint **head, bool *changed) {
 }
 
 static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
-        MountPoint *m, *n;
         int n_failed = 0, r;
         dev_t rootdev = 0;
 
@@ -666,7 +663,7 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed, int umo
 
         (void) get_block_device("/", &rootdev);
 
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+        LIST_FOREACH(mount_point, m, *head) {
                 if (major(rootdev) != 0 && rootdev == m->devnum) {
                         n_failed++;
                         continue;
@@ -689,7 +686,6 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed, int umo
 }
 
 static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
-        MountPoint *m, *n;
         int n_failed = 0, r;
         dev_t rootdev = 0;
 
@@ -698,7 +694,7 @@ static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_lo
 
         (void) get_block_device("/", &rootdev);
 
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+        LIST_FOREACH(mount_point, m, *head) {
                 if (major(rootdev) != 0 && rootdev == m->devnum) {
                         n_failed ++;
                         continue;
@@ -720,7 +716,6 @@ static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_lo
 }
 
 static int md_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
-        MountPoint *m, *n;
         int n_failed = 0, r;
         dev_t rootdev = 0;
 
@@ -729,7 +724,7 @@ static int md_points_list_detach(MountPoint **head, bool *changed, int umount_lo
 
         (void) get_block_device("/", &rootdev);
 
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+        LIST_FOREACH(mount_point, m, *head) {
                 if (major(rootdev) != 0 && rootdev == m->devnum) {
                         n_failed ++;
                         continue;
index 7064f3a90594bce963a8eab0721369a57d855744..f108529bbdfe49f005f0e2640e01a5e9e4b76c9b 100644 (file)
@@ -85,7 +85,6 @@ static int write_hibernate_location_info(const HibernateLocation *hibernate_loca
 
 static int write_mode(char **modes) {
         int r = 0;
-        char **mode;
 
         STRV_FOREACH(mode, modes) {
                 int k;
@@ -103,7 +102,6 @@ static int write_mode(char **modes) {
 }
 
 static int write_state(FILE **f, char **states) {
-        char **state;
         int r = 0;
 
         assert(f);
index 408ac3b8bec8d004f36500bbfb7741e27e31778b..24c8baab03f266b556aa442173b78effc7d6ea29 100644 (file)
@@ -51,8 +51,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Option*, option_free);
 DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(option_hash_ops, char, string_hash_func, string_compare_func, Option, option_free);
 
 static bool test_prefix(const char *p) {
-        char **i;
-
         if (strv_isempty(arg_prefixes))
                 return true;
 
@@ -131,7 +129,6 @@ static int apply_all(OrderedHashmap *sysctl_options) {
                 if (string_is_glob(option->key)) {
                         _cleanup_strv_free_ char **paths = NULL;
                         _cleanup_free_ char *pattern = NULL;
-                        char **s;
 
                         pattern = path_join("/proc/sys", option->key);
                         if (!pattern)
@@ -403,7 +400,6 @@ static int run(int argc, char *argv[]) {
                 }
         } else {
                 _cleanup_strv_free_ char **files = NULL;
-                char **f;
 
                 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("sysctl.d"));
                 if (r < 0)
index 60789e0f2c19d725061be923477ea71efa4a6709..6d4df0afd27f75495716f638794317b2f3558ac6 100644 (file)
@@ -124,7 +124,6 @@ static int unmerge_hierarchy(const char *p) {
 
 static int unmerge(void) {
         int r, ret = 0;
-        char **p;
 
         STRV_FOREACH(p, arg_hierarchies) {
                 _cleanup_free_ char *resolved = NULL;
@@ -161,7 +160,6 @@ static int verb_unmerge(int argc, char **argv, void *userdata) {
 static int verb_status(int argc, char **argv, void *userdata) {
         _cleanup_(table_unrefp) Table *t = NULL;
         int r, ret = 0;
-        char **p;
 
         t = table_new("hierarchy", "extensions", "since");
         if (!t)
@@ -245,7 +243,6 @@ static int mount_overlayfs(
 
         _cleanup_free_ char *options = NULL;
         bool separator = false;
-        char **l;
         int r;
 
         assert(where);
@@ -285,7 +282,6 @@ static int merge_hierarchy(
         _cleanup_free_ char *resolved_hierarchy = NULL, *f = NULL, *buf = NULL;
         _cleanup_strv_free_ char **layers = NULL;
         struct stat st;
-        char **p;
         int r;
 
         assert(hierarchy);
@@ -453,7 +449,6 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
         size_t n_extensions = 0;
         unsigned n_ignored = 0;
         Image *img;
-        char **h;
         int r;
 
         /* Mark the whole of /run as MS_SLAVE, so that we can mount stuff below it that doesn't show up on
@@ -764,7 +759,6 @@ static int image_discover_and_read_metadata(Hashmap **ret_images) {
 
 static int verb_merge(int argc, char **argv, void *userdata) {
         _cleanup_(hashmap_freep) Hashmap *images = NULL;
-        char **p;
         int r;
 
         if (!have_effective_cap(CAP_SYS_ADMIN))
index 76976010ad37c90222e0ca961c407f930d4b8e5f..3cf136946379d361c133f457127e673c50ef425c 100644 (file)
@@ -10,7 +10,6 @@
 
 int verb_cancel(int argc, char *argv[], void *userdata) {
         sd_bus *bus;
-        char **name;
         int r;
 
         if (argc <= 1) /* Shortcut to trivial_method() if no argument is given */
index 7ed41e8e0af021cedd0aecfe9512322203232577..5c15a9fba0749b0b74c1de7c90fef07bc0730e86 100644 (file)
@@ -11,7 +11,6 @@ int verb_clean_or_freeze(int argc, char *argv[], void *userdata) {
         _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL;
         _cleanup_strv_free_ char **names = NULL;
         int r, ret = EXIT_SUCCESS;
-        char **name;
         const char *method;
         sd_bus *bus;
 
index f5123e80c12c6919ea9966c789f9bd2a81060b5b..e18a32190f939ec88b3c4952b3d52239c224fab4 100644 (file)
@@ -26,7 +26,6 @@ int verb_cat(int argc, char *argv[], void *userdata) {
         _cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
         _cleanup_(lookup_paths_free) LookupPaths lp = {};
         _cleanup_strv_free_ char **names = NULL;
-        char **name;
         sd_bus *bus;
         bool first = true;
         int r, rc = 0;
@@ -145,7 +144,6 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
         } else if (original_unit_paths) {
                 _cleanup_free_ char *new_contents = NULL;
                 _cleanup_fclose_ FILE *f = NULL;
-                char **path;
 
                 r = mac_selinux_create_file_prepare(new_path, S_IFREG);
                 if (r < 0)
@@ -318,7 +316,7 @@ static int run_editor(char **paths) {
         if (r < 0)
                 return r;
         if (r == 0) {
-                char **editor_args = NULL, **tmp_path, **original_path;
+                char **editor_args = NULL;
                 size_t n_editor_args = 0, i = 1, argc;
                 const char **args, *editor, *p;
 
@@ -379,7 +377,6 @@ static int run_editor(char **paths) {
 static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
         _cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
         _cleanup_(lookup_paths_free) LookupPaths lp = {};
-        char **name;
         int r;
 
         assert(names);
@@ -501,7 +498,6 @@ int verb_edit(int argc, char *argv[], void *userdata) {
         _cleanup_(lookup_paths_free) LookupPaths lp = {};
         _cleanup_strv_free_ char **names = NULL;
         _cleanup_strv_free_ char **paths = NULL;
-        char **original, **tmp;
         sd_bus *bus;
         int r;
 
index 9fb935cc5ecb075f5eca50da083874fff9c6915e..b18c516b260967f1f3d9985e0fcf8bb7873784ba 100644 (file)
@@ -12,7 +12,6 @@
 #include "systemctl.h"
 
 static int normalize_filenames(char **names) {
-        char **u;
         int r;
 
         STRV_FOREACH(u, names)
@@ -40,7 +39,6 @@ static int normalize_filenames(char **names) {
 }
 
 static int normalize_names(char **names) {
-        char **u;
         bool was_path = false;
 
         STRV_FOREACH(u, names) {
@@ -141,7 +139,6 @@ int verb_enable(int argc, char *argv[], void *userdata) {
                 sd_bus *bus;
 
                 if (STR_IN_SET(verb, "mask", "unmask")) {
-                        char **name;
                         _cleanup_(lookup_paths_free) LookupPaths lp = {};
 
                         r = lookup_paths_init(&lp, arg_scope, 0, arg_root);
index e1acf79702c5dc54b017acbad2e81e5672d73fa9..7218e900154f78b2edcf7bbda375d2087f6d2056 100644 (file)
@@ -13,7 +13,6 @@ static int check_unit_generic(int code, const UnitActiveState good_states[], int
         _cleanup_strv_free_ char **names = NULL;
         UnitActiveState active_state;
         sd_bus *bus;
-        char **name;
         int r;
         bool found = false;
 
index 3b4fbd9c1f36c28eac5896782842dfd4bfac7c36..c280d55d69cf7a64abbde9a7221f6c5921e827fa 100644 (file)
@@ -59,7 +59,6 @@ static int show_installation_targets(sd_bus *bus, const char *name) {
 int verb_is_enabled(int argc, char *argv[], void *userdata) {
         _cleanup_strv_free_ char **names = NULL;
         bool enabled;
-        char **name;
         int r;
 
         r = mangle_names("to check", strv_skip(argv, 1), &names);
index 0346be98a6aa1acf9ad8ea4b329f1d3a05feb7ed..1a8b4a3eda840db9cdbd829dbbab88b4890c95a9 100644 (file)
@@ -8,7 +8,7 @@
 
 int verb_kill(int argc, char *argv[], void *userdata) {
         _cleanup_strv_free_ char **names = NULL;
-        char *kill_who = NULL, **name;
+        char *kill_who = NULL;
         sd_bus *bus;
         int r, q;
 
index 0a45a017e68f37bb9ba6c842597ebbe87d2fd359..86d1c5b7c299d41c3a3cfbce6112005ea3fca2b5 100644 (file)
@@ -63,7 +63,6 @@ static int list_dependencies_one(
                 unsigned branches) {
 
         _cleanup_strv_free_ char **deps = NULL;
-        char **c;
         int r;
 
         assert(bus);
@@ -138,7 +137,7 @@ static int list_dependencies_one(
 
 int verb_list_dependencies(int argc, char *argv[], void *userdata) {
         _cleanup_strv_free_ char **units = NULL, **done = NULL;
-        char **u, **patterns;
+        char **patterns;
         sd_bus *bus;
         int r;
 
index 1ebfb019ec596ab392fdc1f847dfb052ab05a9a1..0abafa7bf0e5ba802daebdbcdc73ef6292f9cdf7 100644 (file)
@@ -93,7 +93,6 @@ static int get_machine_list(
         struct machine_info *machine_infos = NULL;
         _cleanup_strv_free_ char **m = NULL;
         _cleanup_free_ char *hn = NULL;
-        char **i;
         int c = 0, r;
 
         hn = gethostname_malloc();
index fdf524385f42957dabde054da81d96a6db9e5310..4f3d534bc80da0377ae963c408200e9b4c26f281 100644 (file)
@@ -49,7 +49,6 @@ static int get_unit_list_recursive(
 
         if (arg_recursive) {
                 _cleanup_strv_free_ char **machines = NULL;
-                char **i;
 
                 r = sd_get_machine_names(&machines);
                 if (r < 0)
index 897d936abaad899bc3fac07b6f723cf1b99bb849..40521b85954fa5e267e2af2dc15abfcccd71f4ae 100644 (file)
@@ -112,7 +112,6 @@ int logind_check_inhibitors(enum action a) {
         uint32_t uid, pid;
         sd_bus *bus;
         unsigned c = 0;
-        char **s;
         int r;
 
         if (arg_check_inhibitors == 0 || arg_force > 0)
@@ -399,7 +398,6 @@ int help_boot_loader_entry(void) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_strv_free_ char **l = NULL;
         sd_bus *bus;
-        char **i;
         int r;
 
         r = acquire_bus(BUS_FULL, &bus);
index abc8aadf63d5f3b87d3fcaf9df8999a274f80616..1ca05339dc595337e640b6e16dc6450aff8dca5b 100644 (file)
@@ -10,7 +10,6 @@
 int verb_reset_failed(int argc, char *argv[], void *userdata) {
         _cleanup_strv_free_ char **names = NULL;
         sd_bus *bus;
-        char **name;
         int r, q;
 
         if (argc <= 1) /* Shortcut to trivial_method() if no argument is given */
index 61540e03e23280bc3f1b8b902edce99f50185c9a..55d1160510f6a93ff406d497d85254de74c153c2 100644 (file)
@@ -171,7 +171,6 @@ int verb_import_environment(int argc, char *argv[], void *userdata) {
 
                 strv_env_clean_with_callback(copy, invalid_callback, NULL);
 
-                char **e;
                 STRV_FOREACH(e, copy)
                         if (string_has_cc(*e, NULL))
                                 log_notice("Environment variable $%.*s contains control characters, importing anyway.",
@@ -180,8 +179,6 @@ int verb_import_environment(int argc, char *argv[], void *userdata) {
                 r = sd_bus_message_append_strv(m, copy);
 
         } else {
-                char **a, **b;
-
                 r = sd_bus_message_open_container(m, 'a', "s");
                 if (r < 0)
                         return bus_log_create_error(r);
index 24182d4fff2e53e67e956582eb256484fe80cc1e..b8d702ac0e8d53e9c4addb0eb8317cd75ef12861 100644 (file)
@@ -46,7 +46,6 @@ static int set_property_one(sd_bus *bus, const char *name, char **properties) {
 int verb_set_property(int argc, char *argv[], void *userdata) {
         sd_bus *bus;
         _cleanup_strv_free_ char **names = NULL;
-        char **name;
         int r, k;
 
         r = acquire_bus(BUS_MANAGER, &bus);
index c1298ce75831e1a46ad7d0f3b58385c9bf122e21..0f3099b7b5510ee206a11d9bf36af85d3b30102f 100644 (file)
@@ -305,10 +305,8 @@ static void print_status_info(
 
         const char *active_on, *active_off, *on, *off, *ss, *fs;
         _cleanup_free_ char *formatted_path = NULL;
-        ExecStatusInfo *p;
         usec_t timestamp;
         const char *path;
-        char **t, **t2;
         int r;
 
         assert(i);
@@ -367,7 +365,6 @@ static void print_status_info(
         if (!strv_isempty(i->dropin_paths)) {
                 _cleanup_free_ char *dir = NULL;
                 bool last = false;
-                char ** dropin;
 
                 STRV_FOREACH(dropin, i->dropin_paths) {
                         _cleanup_free_ char *dropin_formatted = NULL;
@@ -488,7 +485,6 @@ static void print_status_info(
         }
 
         if (!i->condition_result && i->condition_timestamp > 0) {
-                UnitCondition *c;
                 int n = 0;
 
                 printf("  Condition: start %scondition failed%s at %s; %s\n",
@@ -774,8 +770,6 @@ static void print_status_info(
 }
 
 static void show_unit_help(UnitStatusInfo *i) {
-        char **p;
-
         assert(i);
 
         if (!i->documentation) {
@@ -1078,7 +1072,6 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
 
                         if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) || allow_list || !strv_isempty(l)) {
                                 bool first = true;
-                                char **i;
 
                                 if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE)) {
                                         fputs(name, stdout);
@@ -1970,7 +1963,6 @@ static int show_one(
                 .io_read_bytes = UINT64_MAX,
                 .io_write_bytes = UINT64_MAX,
         };
-        char **pp;
         int r;
 
         assert(path);
@@ -2194,7 +2186,6 @@ int verb_show(int argc, char *argv[], void *userdata) {
                         ret = show_all(bus, &new_line, &ellipsized);
         } else {
                 _cleanup_free_ char **patterns = NULL;
-                char **name;
 
                 STRV_FOREACH(name, strv_skip(argv, 1)) {
                         _cleanup_free_ char *path = NULL, *unit = NULL;
index 590ee171943017fc1c580a3b579b6f31b762966b..9b215c2e8acf1b911477df7dfdb8a99a7387965b 100644 (file)
@@ -199,16 +199,13 @@ static int enqueue_marked_jobs(
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        if (w) {
-                char **path;
-
+        if (w)
                 STRV_FOREACH(path, paths) {
                         log_debug("Adding %s to the set", *path);
                         r = bus_wait_for_jobs_add(w, *path);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to watch job %s: %m", *path);
                 }
-        }
 
         return 0;
 }
@@ -269,7 +266,6 @@ int verb_start(int argc, char *argv[], void *userdata) {
         _cleanup_strv_free_ char **names = NULL;
         int r, ret = EXIT_SUCCESS;
         sd_bus *bus;
-        char **name;
 
         if (arg_wait && !STR_IN_SET(argv[0], "start", "restart"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
index 8afd4b222ac427dc583bbd3280ccb0303d619711..0334b8c1fc78530995e78c4a4573fa5513cc7517 100644 (file)
@@ -234,7 +234,6 @@ int get_unit_list(
 
 int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret, bool *ret_expanded) {
         _cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
-        char **name;
         int r;
 
         assert(bus);
@@ -294,7 +293,6 @@ int check_triggering_units(sd_bus *bus, const char *unit) {
         _cleanup_strv_free_ char **triggered_by = NULL;
         bool print_warning_label = true;
         UnitActiveState active_state;
-        char **i;
         int r;
 
         r = unit_name_mangle(unit, 0, &n);
@@ -386,8 +384,6 @@ void warn_unit_file_changed(const char *unit) {
 }
 
 int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **ret_unit_path) {
-        char **p;
-
         assert(lp);
         assert(unit_name);
 
@@ -666,7 +662,6 @@ int unit_exists(LookupPaths *lp, const char *unit) {
 
 int append_unit_dependencies(sd_bus *bus, char **names, char ***ret) {
         _cleanup_strv_free_ char **with_deps = NULL;
-        char **name;
 
         assert(bus);
         assert(ret);
@@ -860,7 +855,7 @@ UnitFileFlags unit_file_flags_from_args(void) {
 
 int mangle_names(const char *operation, char **original_names, char ***ret_mangled_names) {
         _cleanup_strv_free_ char **l = NULL;
-        char **i, **name;
+        char **i;
         int r;
 
         assert(ret_mangled_names);
index b9445cf0a9dfbf01922e61f4c9fc36e2f6551336..f3cca758a0c01901a827933978da0c1c4d138a99 100644 (file)
@@ -200,6 +200,9 @@ _SD_BEGIN_DECLARATIONS;
 #define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR \
                                           SD_ID128_MAKE_STR(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
 
+#define SD_MESSAGE_TIME_SYNC              SD_ID128_MAKE(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
+#define SD_MESSAGE_TIME_SYNC_STR          SD_ID128_MAKE_STR(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
+
 _SD_END_DECLARATIONS;
 
 #endif
diff --git a/src/sysupdate/meson.build b/src/sysupdate/meson.build
new file mode 100644 (file)
index 0000000..2b1a256
--- /dev/null
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+systemd_sysupdate_sources = files('''
+        sysupdate-instance.c
+        sysupdate-instance.h
+        sysupdate-partition.c
+        sysupdate-partition.h
+        sysupdate-pattern.c
+        sysupdate-pattern.h
+        sysupdate-resource.c
+        sysupdate-resource.h
+        sysupdate-transfer.c
+        sysupdate-transfer.h
+        sysupdate-update-set.c
+        sysupdate-update-set.h
+        sysupdate-util.c
+        sysupdate-util.h
+        sysupdate-cache.c
+        sysupdate-cache.h
+        sysupdate.c
+        sysupdate.h
+'''.split())
diff --git a/src/sysupdate/sysupdate-cache.c b/src/sysupdate/sysupdate-cache.c
new file mode 100644 (file)
index 0000000..8dad3ee
--- /dev/null
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "memory-util.h"
+#include "sysupdate-cache.h"
+
+#define WEB_CACHE_ENTRIES_MAX 64U
+#define WEB_CACHE_ITEM_SIZE_MAX (64U*1024U*1024U)
+
+static WebCacheItem* web_cache_item_free(WebCacheItem *i) {
+        if (!i)
+                return NULL;
+
+        free(i->url);
+        return mfree(i);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(WebCacheItem*, web_cache_item_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(web_cache_hash_ops, char, string_hash_func, string_compare_func, WebCacheItem, web_cache_item_free);
+
+int web_cache_add_item(
+                Hashmap **web_cache,
+                const char *url,
+                bool verified,
+                const void *data,
+                size_t size) {
+
+        _cleanup_(web_cache_item_freep) WebCacheItem *item = NULL;
+        _cleanup_free_ char *u = NULL;
+        int r;
+
+        assert(web_cache);
+        assert(url);
+        assert(data || size == 0);
+
+        if (size > WEB_CACHE_ITEM_SIZE_MAX)
+                return -E2BIG;
+
+        item = web_cache_get_item(*web_cache, url, verified);
+        if (item && memcmp_nn(item->data, item->size, data, size) == 0)
+                return 0;
+
+        if (hashmap_size(*web_cache) >= (size_t) (WEB_CACHE_ENTRIES_MAX + !!hashmap_get(*web_cache, url)))
+                return -ENOSPC;
+
+        r = hashmap_ensure_allocated(web_cache, &web_cache_hash_ops);
+        if (r < 0)
+                return r;
+
+        u = strdup(url);
+        if (!u)
+                return -ENOMEM;
+
+        item = malloc(offsetof(WebCacheItem, data) + size + 1);
+        if (!item)
+                return -ENOMEM;
+
+        *item = (WebCacheItem) {
+                .url = TAKE_PTR(u),
+                .size = size,
+                .verified = verified,
+        };
+
+        /* Just to be extra paranoid, let's NUL terminate the downloaded buffer */
+        *(uint8_t*) mempcpy(item->data, data, size) = 0;
+
+        web_cache_item_free(hashmap_remove(*web_cache, url));
+
+        r = hashmap_put(*web_cache, item->url, item);
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(item);
+        return 1;
+}
+
+WebCacheItem* web_cache_get_item(Hashmap *web_cache, const char *url, bool verified) {
+        WebCacheItem *i;
+
+        i = hashmap_get(web_cache, url);
+        if (!i)
+                return NULL;
+
+        if (i->verified != verified)
+                return NULL;
+
+        return i;
+}
diff --git a/src/sysupdate/sysupdate-cache.h b/src/sysupdate/sysupdate-cache.h
new file mode 100644 (file)
index 0000000..d6a7897
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "hashmap.h"
+
+typedef struct WebCacheItem {
+        char *url;
+        bool verified;
+        size_t size;
+        uint8_t data[];
+} WebCacheItem;
+
+/* A simple in-memory cache for downloaded manifests. Very likely multiple transfers will use the same
+ * manifest URLs, hence let's make sure we only download them once within each sysupdate invocation. */
+
+int web_cache_add_item(Hashmap **cache, const char *url, bool verified, const void *data, size_t size);
+
+WebCacheItem* web_cache_get_item(Hashmap *cache, const char *url, bool verified);
diff --git a/src/sysupdate/sysupdate-instance.c b/src/sysupdate/sysupdate-instance.c
new file mode 100644 (file)
index 0000000..16bfab9
--- /dev/null
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "sysupdate-instance.h"
+
+void instance_metadata_destroy(InstanceMetadata *m) {
+        assert(m);
+        free(m->version);
+}
+
+int instance_new(
+                Resource *rr,
+                const char *path,
+                const InstanceMetadata *f,
+                Instance **ret) {
+
+        _cleanup_(instance_freep) Instance *i = NULL;
+        _cleanup_free_ char *p = NULL, *v = NULL;
+
+        assert(rr);
+        assert(path);
+        assert(f);
+        assert(f->version);
+        assert(ret);
+
+        p = strdup(path);
+        if (!p)
+                return log_oom();
+
+        v = strdup(f->version);
+        if (!v)
+                return log_oom();
+
+        i = new(Instance, 1);
+        if (!i)
+                return log_oom();
+
+        *i = (Instance) {
+                .resource = rr,
+                .metadata = *f,
+                .path = TAKE_PTR(p),
+                .partition_info = PARTITION_INFO_NULL,
+        };
+
+        i->metadata.version = TAKE_PTR(v);
+
+        *ret = TAKE_PTR(i);
+        return 0;
+}
+
+Instance *instance_free(Instance *i) {
+        if (!i)
+                return NULL;
+
+        instance_metadata_destroy(&i->metadata);
+
+        free(i->path);
+        partition_info_destroy(&i->partition_info);
+
+        return mfree(i);
+}
diff --git a/src/sysupdate/sysupdate-instance.h b/src/sysupdate/sysupdate-instance.h
new file mode 100644 (file)
index 0000000..2860d29
--- /dev/null
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "sd-id128.h"
+
+#include "fs-util.h"
+#include "time-util.h"
+
+typedef struct InstanceMetadata InstanceMetadata;
+typedef struct Instance Instance;
+
+#include "sysupdate-resource.h"
+#include "sysupdate-partition.h"
+
+struct InstanceMetadata {
+        /* Various bits of metadata for each instance, that is either derived from the filename/GPT label or
+         * from metadata of the file/partition itself */
+        char *version;
+        sd_id128_t partition_uuid;
+        bool partition_uuid_set;
+        uint64_t partition_flags;          /* GPT partition flags */
+        bool partition_flags_set;
+        usec_t mtime;
+        mode_t mode;
+        uint64_t size;                     /* uncompressed size of the file */
+        uint64_t tries_done, tries_left;   /* for boot assessment counters */
+        int no_auto;
+        int read_only;
+        int growfs;
+        uint8_t sha256sum[32];             /* SHA256 sum of the download (i.e. compressed) file */
+        bool sha256sum_set;
+};
+
+#define INSTANCE_METADATA_NULL                  \
+        {                                       \
+                .mtime = USEC_INFINITY,         \
+                .mode = MODE_INVALID,           \
+                .size = UINT64_MAX,             \
+                .tries_done = UINT64_MAX,       \
+                .tries_left = UINT64_MAX,       \
+                .no_auto = -1,                  \
+                .read_only = -1,                \
+                .growfs = -1,                   \
+        }
+
+struct Instance {
+        /* A pointer back to the resource this belongs to */
+        Resource *resource;
+
+        /* Metadata of this version */
+        InstanceMetadata metadata;
+
+        /* Where we found the instance */
+        char *path;
+        PartitionInfo partition_info;
+};
+
+void instance_metadata_destroy(InstanceMetadata *m);
+
+int instance_new(Resource *rr, const char *path, const InstanceMetadata *f, Instance **ret);
+Instance *instance_free(Instance *i);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Instance*, instance_free);
diff --git a/src/sysupdate/sysupdate-partition.c b/src/sysupdate/sysupdate-partition.c
new file mode 100644 (file)
index 0000000..f3e2100
--- /dev/null
@@ -0,0 +1,379 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/file.h>
+
+#include "alloc-util.h"
+#include "extract-word.h"
+#include "gpt.h"
+#include "id128-util.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "sysupdate-partition.h"
+#include "util.h"
+
+void partition_info_destroy(PartitionInfo *p) {
+        assert(p);
+
+        p->label = mfree(p->label);
+        p->device = mfree(p->device);
+}
+
+static int fdisk_partition_get_attrs_as_uint64(
+                struct fdisk_partition *pa,
+                uint64_t *ret) {
+
+        uint64_t flags = 0;
+        const char *a;
+        int r;
+
+        assert(pa);
+        assert(ret);
+
+        /* Retrieve current flags as uint64_t mask */
+
+        a = fdisk_partition_get_attrs(pa);
+        if (!a) {
+                *ret = 0;
+                return 0;
+        }
+
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
+
+                r = extract_first_word(&a, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (streq(word, "RequiredPartition"))
+                        flags |= GPT_FLAG_REQUIRED_PARTITION;
+                else if (streq(word, "NoBlockIOProtocol"))
+                        flags |= GPT_FLAG_NO_BLOCK_IO_PROTOCOL;
+                else if (streq(word, "LegacyBIOSBootable"))
+                        flags |= GPT_FLAG_LEGACY_BIOS_BOOTABLE;
+                else {
+                        const char *e;
+                        unsigned u;
+
+                        /* Drop "GUID" prefix if specified */
+                        e = startswith(word, "GUID:") ?: word;
+
+                        if (safe_atou(e, &u) < 0) {
+                                log_debug("Unknown partition flag '%s', ignoring.", word);
+                                continue;
+                        }
+
+                        if (u >= sizeof(flags)*8) { /* partition flags on GPT are 64bit. Let's ignore any further
+                                                       bits should libfdisk report them */
+                                log_debug("Partition flag above bit 63 (%s), ignoring.", word);
+                                continue;
+                        }
+
+                        flags |= UINT64_C(1) << u;
+                }
+        }
+
+        *ret = flags;
+        return 0;
+}
+
+static int fdisk_partition_set_attrs_as_uint64(
+                struct fdisk_partition *pa,
+                uint64_t flags) {
+
+        _cleanup_free_ char *attrs = NULL;
+        int r;
+
+        assert(pa);
+
+        for (unsigned i = 0; i < sizeof(flags) * 8; i++) {
+                if (!FLAGS_SET(flags, UINT64_C(1) << i))
+                        continue;
+
+                r = strextendf_with_separator(&attrs, ",", "%u", i);
+                if (r < 0)
+                        return r;
+        }
+
+        return fdisk_partition_set_attrs(pa, strempty(attrs));
+}
+
+int read_partition_info(
+                struct fdisk_context *c,
+                struct fdisk_table *t,
+                size_t i,
+                PartitionInfo *ret) {
+
+        _cleanup_free_ char *label_copy = NULL, *device = NULL;
+        const char *pts, *ids, *label;
+        struct fdisk_partition *p;
+        struct fdisk_parttype *pt;
+        uint64_t start, size, flags;
+        sd_id128_t ptid, id;
+        size_t partno;
+        int r;
+
+        assert(c);
+        assert(t);
+        assert(ret);
+
+        p = fdisk_table_get_partition(t, i);
+        if (!p)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
+
+        if (fdisk_partition_is_used(p) <= 0) {
+                *ret = (PartitionInfo) PARTITION_INFO_NULL;
+                return 0; /* not found! */
+        }
+
+        if (fdisk_partition_has_partno(p) <= 0 ||
+            fdisk_partition_has_start(p) <= 0 ||
+            fdisk_partition_has_size(p) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a number, position or size.");
+
+        partno = fdisk_partition_get_partno(p);
+
+        start = fdisk_partition_get_start(p);
+        assert(start <= UINT64_MAX / 512U);
+        start *= 512U;
+
+        size = fdisk_partition_get_size(p);
+        assert(size <= UINT64_MAX / 512U);
+        size *= 512U;
+
+        label = fdisk_partition_get_name(p);
+        if (!label)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a label.");
+
+        pt = fdisk_partition_get_type(p);
+        if (!pt)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition: %m");
+
+        pts = fdisk_parttype_get_string(pt);
+        if (!pts)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition as string: %m");
+
+        r = sd_id128_from_string(pts, &ptid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse partition type UUID %s: %m", pts);
+
+        ids = fdisk_partition_get_uuid(p);
+        if (!ids)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a UUID.");
+
+        r = sd_id128_from_string(ids, &id);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse partition UUID %s: %m", ids);
+
+        r = fdisk_partition_get_attrs_as_uint64(p, &flags);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get partition flags: %m");
+
+        r = fdisk_partition_to_string(p, c, FDISK_FIELD_DEVICE, &device);
+        if (r != 0)
+                return log_error_errno(r, "Failed to get partition device name: %m");
+
+        label_copy = strdup(label);
+        if (!label_copy)
+                return log_oom();
+
+        *ret = (PartitionInfo) {
+                .partno = partno,
+                .start = start,
+                .size = size,
+                .flags = flags,
+                .type = ptid,
+                .uuid = id,
+                .label = TAKE_PTR(label_copy),
+                .device = TAKE_PTR(device),
+                .no_auto = FLAGS_SET(flags, GPT_FLAG_NO_AUTO) && gpt_partition_type_knows_no_auto(ptid),
+                .read_only = FLAGS_SET(flags, GPT_FLAG_READ_ONLY) && gpt_partition_type_knows_read_only(ptid),
+                .growfs = FLAGS_SET(flags, GPT_FLAG_GROWFS) && gpt_partition_type_knows_growfs(ptid),
+        };
+
+        return 1; /* found! */
+}
+
+int find_suitable_partition(
+                const char *device,
+                uint64_t space,
+                sd_id128_t *partition_type,
+                PartitionInfo *ret) {
+
+        _cleanup_(partition_info_destroy) PartitionInfo smallest = PARTITION_INFO_NULL;
+        _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+        _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
+        size_t n_partitions;
+        int r;
+
+        assert(device);
+        assert(ret);
+
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
+
+        r = fdisk_assign_device(c, device, /* readonly= */ true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open device '%s': %m", device);
+
+        if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
+                return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not suitable.", device);
+
+        r = fdisk_get_partitions(c, &t);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire partition table: %m");
+
+        n_partitions = fdisk_table_get_nents(t);
+        for (size_t i = 0; i < n_partitions; i++)  {
+                _cleanup_(partition_info_destroy) PartitionInfo pinfo = PARTITION_INFO_NULL;
+
+                r = read_partition_info(c, t, i, &pinfo);
+                if (r < 0)
+                        return r;
+                if (r == 0) /* not assigned */
+                        continue;
+
+                /* Filter out non-matching partition types */
+                if (partition_type && !sd_id128_equal(pinfo.type, *partition_type))
+                        continue;
+
+                if (!streq_ptr(pinfo.label, "_empty")) /* used */
+                        continue;
+
+                if (space != UINT64_MAX && pinfo.size < space) /* too small */
+                        continue;
+
+                if (smallest.partno != SIZE_MAX && smallest.size <= pinfo.size) /* already found smaller */
+                        continue;
+
+                smallest = pinfo;
+                pinfo = (PartitionInfo) PARTITION_INFO_NULL;
+        }
+
+        if (smallest.partno == SIZE_MAX)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSPC), "No available partition of a suitable size found.");
+
+        *ret = smallest;
+        smallest = (PartitionInfo) PARTITION_INFO_NULL;
+
+        return 0;
+}
+
+int patch_partition(
+                const char *device,
+                const PartitionInfo *info,
+                PartitionChange change) {
+
+        _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *pa = NULL;
+        _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+        bool tweak_no_auto, tweak_read_only, tweak_growfs;
+        int r, fd;
+
+        assert(device);
+        assert(info);
+        assert(change <= _PARTITION_CHANGE_MAX);
+
+        if (change == 0) /* Nothing to do */
+                return 0;
+
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
+
+        r = fdisk_assign_device(c, device, /* readonly= */ false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open device '%s': %m", device);
+
+        assert_se((fd = fdisk_get_devfd(c)) >= 0);
+
+        /* Make sure udev doesn't read the device while we make changes (this lock is released automatically
+         * by the kernel when the fd is closed, i.e. when the fdisk context is freed, hence no explicit
+         * unlock by us here anywhere.) */
+        if (flock(fd, LOCK_EX) < 0)
+                return log_error_errno(errno, "Failed to lock block device '%s': %m", device);
+
+        if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
+                return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not suitable.", device);
+
+        r = fdisk_get_partition(c, info->partno, &pa);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read partition %zu of GPT label of '%s': %m", info->partno, device);
+
+        if (change & PARTITION_LABEL) {
+                r = fdisk_partition_set_name(pa, info->label);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to update partition label: %m");
+        }
+
+        if (change & PARTITION_UUID) {
+                r = fdisk_partition_set_uuid(pa, SD_ID128_TO_UUID_STRING(info->uuid));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to update partition UUID: %m");
+        }
+
+        /* Tweak the read-only flag, but only if supported by the partition type */
+        tweak_no_auto =
+                FLAGS_SET(change, PARTITION_NO_AUTO) &&
+                gpt_partition_type_knows_no_auto(info->type);
+        tweak_read_only =
+                FLAGS_SET(change, PARTITION_READ_ONLY) &&
+                gpt_partition_type_knows_read_only(info->type);
+        tweak_growfs =
+                FLAGS_SET(change, PARTITION_GROWFS) &&
+                gpt_partition_type_knows_growfs(info->type);
+
+        if (change & PARTITION_FLAGS) {
+                uint64_t flags;
+
+                /* Update the full flags parameter, and import the read-only flag into it */
+
+                flags = info->flags;
+                if (tweak_no_auto)
+                        SET_FLAG(flags, GPT_FLAG_NO_AUTO, info->no_auto);
+                if (tweak_read_only)
+                        SET_FLAG(flags, GPT_FLAG_READ_ONLY, info->read_only);
+                if (tweak_growfs)
+                        SET_FLAG(flags, GPT_FLAG_GROWFS, info->growfs);
+
+                r = fdisk_partition_set_attrs_as_uint64(pa, flags);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to update partition flags: %m");
+
+        } else if (tweak_no_auto || tweak_read_only || tweak_growfs) {
+                uint64_t old_flags, new_flags;
+
+                /* So we aren't supposed to update the full flags parameter, but we are supposed to update
+                 * the RO flag of it. */
+
+                r = fdisk_partition_get_attrs_as_uint64(pa, &old_flags);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get old partition flags: %m");
+
+                new_flags = old_flags;
+                if (tweak_no_auto)
+                        SET_FLAG(new_flags, GPT_FLAG_NO_AUTO, info->no_auto);
+                if (tweak_read_only)
+                        SET_FLAG(new_flags, GPT_FLAG_READ_ONLY, info->read_only);
+                if (tweak_growfs)
+                        SET_FLAG(new_flags, GPT_FLAG_GROWFS, info->growfs);
+
+                if (new_flags != old_flags) {
+                        r = fdisk_partition_set_attrs_as_uint64(pa, new_flags);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to update partition flags: %m");
+                }
+        }
+
+        r = fdisk_set_partition(c, info->partno, pa);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update partition: %m");
+
+        r = fdisk_write_disklabel(c);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write updated partition table: %m");
+
+        return 0;
+}
diff --git a/src/sysupdate/sysupdate-partition.h b/src/sysupdate/sysupdate-partition.h
new file mode 100644 (file)
index 0000000..672eb93
--- /dev/null
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "sd-id128.h"
+
+#include "fdisk-util.h"
+#include "macro.h"
+
+typedef struct PartitionInfo PartitionInfo;
+
+typedef enum PartitionChange {
+        PARTITION_FLAGS           = 1 << 0,
+        PARTITION_NO_AUTO         = 1 << 1,
+        PARTITION_READ_ONLY       = 1 << 2,
+        PARTITION_GROWFS          = 1 << 3,
+        PARTITION_UUID            = 1 << 4,
+        PARTITION_LABEL           = 1 << 5,
+        _PARTITION_CHANGE_MAX     = (1 << 6) - 1, /* all of the above */
+        _PARTITION_CHANGE_INVALID = -EINVAL,
+} PartitionChange;
+
+struct PartitionInfo {
+        size_t partno;
+        uint64_t start, size;
+        uint64_t flags;
+        sd_id128_t type, uuid;
+        char *label;
+        char *device; /* Note that this might point to some non-existing path in case we operate on a loopback file */
+        bool no_auto:1;
+        bool read_only:1;
+        bool growfs:1;
+};
+
+#define PARTITION_INFO_NULL                     \
+        {                                       \
+                .partno = SIZE_MAX,             \
+                .start = UINT64_MAX,            \
+                .size = UINT64_MAX,             \
+        }
+
+void partition_info_destroy(PartitionInfo *p);
+
+int read_partition_info(struct fdisk_context *c, struct fdisk_table *t, size_t i, PartitionInfo *ret);
+
+int find_suitable_partition(const char *device, uint64_t space, sd_id128_t *partition_type, PartitionInfo *ret);
+int patch_partition(const char *device, const PartitionInfo *info, PartitionChange change);
diff --git a/src/sysupdate/sysupdate-pattern.c b/src/sysupdate/sysupdate-pattern.c
new file mode 100644 (file)
index 0000000..c922868
--- /dev/null
@@ -0,0 +1,602 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "hexdecoct.h"
+#include "list.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "sysupdate-pattern.h"
+#include "sysupdate-util.h"
+
+typedef enum PatternElementType {
+        PATTERN_LITERAL,
+        PATTERN_VERSION,
+        PATTERN_PARTITION_UUID,
+        PATTERN_PARTITION_FLAGS,
+        PATTERN_MTIME,
+        PATTERN_MODE,
+        PATTERN_SIZE,
+        PATTERN_TRIES_DONE,
+        PATTERN_TRIES_LEFT,
+        PATTERN_NO_AUTO,
+        PATTERN_READ_ONLY,
+        PATTERN_GROWFS,
+        PATTERN_SHA256SUM,
+        _PATTERN_ELEMENT_TYPE_MAX,
+        _PATTERN_ELEMENT_TYPE_INVALID = -EINVAL,
+} PatternElementType;
+
+typedef struct PatternElement PatternElement;
+
+struct PatternElement {
+        PatternElementType type;
+        LIST_FIELDS(PatternElement, elements);
+        char literal[];
+};
+
+static PatternElement *pattern_element_free_all(PatternElement *e) {
+        PatternElement *p;
+
+        while ((p = LIST_POP(elements, e)))
+                free(p);
+
+        return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(PatternElement*, pattern_element_free_all);
+
+static PatternElementType pattern_element_type_from_char(char c) {
+        switch (c) {
+        case 'v':
+                return PATTERN_VERSION;
+        case 'u':
+                return PATTERN_PARTITION_UUID;
+        case 'f':
+                return PATTERN_PARTITION_FLAGS;
+        case 't':
+                return PATTERN_MTIME;
+        case 'm':
+                return PATTERN_MODE;
+        case 's':
+                return PATTERN_SIZE;
+        case 'd':
+                return PATTERN_TRIES_DONE;
+        case 'l':
+                return PATTERN_TRIES_LEFT;
+        case 'a':
+                return PATTERN_NO_AUTO;
+        case 'r':
+                return PATTERN_READ_ONLY;
+        case 'g':
+                return PATTERN_GROWFS;
+        case 'h':
+                return PATTERN_SHA256SUM;
+        default:
+                return _PATTERN_ELEMENT_TYPE_INVALID;
+        }
+}
+
+static bool valid_char(char x) {
+
+        /* Let's refuse control characters here, and let's reserve some characters typically used in pattern
+         * languages so that we can use them later, possibly. */
+
+        if ((unsigned) x < ' ' || x >= 127)
+                return false;
+
+        return !IN_SET(x, '$', '*', '?', '[', ']', '!', '\\', '/', '|');
+}
+
+static int pattern_split(
+                const char *pattern,
+                PatternElement **ret) {
+
+        _cleanup_(pattern_element_free_allp) PatternElement *first = NULL;
+        bool at = false, last_literal = true;
+        PatternElement *last = NULL;
+        uint64_t mask_found = 0;
+        size_t l, k = 0;
+
+        assert(pattern);
+
+        l = strlen(pattern);
+
+        for (const char *e = pattern; *e != 0; e++) {
+                if (*e == '@') {
+                        if (!at) {
+                                at = true;
+                                continue;
+                        }
+
+                        /* Two at signs in a sequence, write out one */
+                        at = false;
+
+                } else if (at) {
+                        PatternElementType t;
+                        uint64_t bit;
+
+                        t = pattern_element_type_from_char(*e);
+                        if (t < 0)
+                                return log_debug_errno(t, "Unknown pattern field marker '@%c'.", *e);
+
+                        bit = UINT64_C(1) << t;
+                        if (mask_found & bit)
+                                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Pattern field marker '@%c' appears twice in pattern.", *e);
+
+                        /* We insist that two pattern field markers are separated by some literal string that
+                         * we can use to separate the fields when parsing. */
+                        if (!last_literal)
+                                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Found two pattern field markers without separating literal.");
+
+                        if (ret) {
+                                PatternElement *z;
+
+                                z = malloc(offsetof(PatternElement, literal));
+                                if (!z)
+                                        return -ENOMEM;
+
+                                z->type = t;
+                                LIST_INSERT_AFTER(elements, first, last, z);
+                                last = z;
+                        }
+
+                        mask_found |= bit;
+                        last_literal = at = false;
+                        continue;
+                }
+
+                if (!valid_char(*e))
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADRQC), "Invalid character 0x%0x in pattern, refusing.", *e);
+
+                last_literal = true;
+
+                if (!ret)
+                        continue;
+
+                if (!last || last->type != PATTERN_LITERAL) {
+                        PatternElement *z;
+
+                        z = malloc0(offsetof(PatternElement, literal) + l + 1); /* l is an upper bound to all literal elements */
+                        if (!z)
+                                return -ENOMEM;
+
+                        z->type = PATTERN_LITERAL;
+                        k = 0;
+
+                        LIST_INSERT_AFTER(elements, first, last, z);
+                        last = z;
+                }
+
+                assert(last);
+                assert(last->type == PATTERN_LITERAL);
+
+                last->literal[k++] = *e;
+        }
+
+        if (at)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Trailing @ character found, refusing.");
+        if (!(mask_found & (UINT64_C(1) << PATTERN_VERSION)))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Version field marker '@v' not specified in pattern, refusing.");
+
+        if (ret)
+                *ret = TAKE_PTR(first);
+
+        return 0;
+}
+
+int pattern_match(const char *pattern, const char *s, InstanceMetadata *ret) {
+        _cleanup_(instance_metadata_destroy) InstanceMetadata found = INSTANCE_METADATA_NULL;
+        _cleanup_(pattern_element_free_allp) PatternElement *elements = NULL;
+        const char *p;
+        int r;
+
+        assert(pattern);
+        assert(s);
+
+        r = pattern_split(pattern, &elements);
+        if (r < 0)
+                return r;
+
+        p = s;
+        LIST_FOREACH(elements, e, elements) {
+                _cleanup_free_ char *t = NULL;
+                const char *n;
+
+                if (e->type == PATTERN_LITERAL) {
+                        const char *k;
+
+                        /* Skip literal fields */
+                        k = startswith(p, e->literal);
+                        if (!k)
+                                goto nope;
+
+                        p = k;
+                        continue;
+                }
+
+                if (e->elements_next) {
+                        /* The next element must be literal, as we use it to determine where to split */
+                        assert(e->elements_next->type == PATTERN_LITERAL);
+
+                        n = strstr(p, e->elements_next->literal);
+                        if (!n)
+                                goto nope;
+
+                } else
+                        /* End of the string */
+                        assert_se(n = strchr(p, 0));
+                t = strndup(p, n - p);
+                if (!t)
+                        return -ENOMEM;
+
+                switch (e->type) {
+
+                case PATTERN_VERSION:
+                        if (!version_is_valid(t)) {
+                                log_debug("Version string is not valid, refusing: %s", t);
+                                goto nope;
+                        }
+
+                        assert(!found.version);
+                        found.version = TAKE_PTR(t);
+                        break;
+
+                case PATTERN_PARTITION_UUID: {
+                        sd_id128_t id;
+
+                        if (sd_id128_from_string(t, &id) < 0)
+                                goto nope;
+
+                        assert(!found.partition_uuid_set);
+                        found.partition_uuid = id;
+                        found.partition_uuid_set = true;
+                        break;
+                }
+
+                case PATTERN_PARTITION_FLAGS: {
+                        uint64_t f;
+
+                        if (safe_atoux64(t, &f) < 0)
+                                goto nope;
+
+                        if (found.partition_flags_set && found.partition_flags != f)
+                                goto nope;
+
+                        assert(!found.partition_flags_set);
+                        found.partition_flags = f;
+                        found.partition_flags_set = true;
+                        break;
+                }
+
+                case PATTERN_MTIME: {
+                        uint64_t v;
+
+                        if (safe_atou64(t, &v) < 0)
+                                goto nope;
+                        if (v == USEC_INFINITY) /* Don't permit our internal special infinity value */
+                                goto nope;
+                        if (v / 1000000U > TIME_T_MAX) /* Make sure this fits in a timespec structure */
+                                goto nope;
+
+                        assert(found.mtime == USEC_INFINITY);
+                        found.mtime = v;
+                        break;
+                }
+
+                case PATTERN_MODE: {
+                        mode_t m;
+
+                        r = parse_mode(t, &m);
+                        if (r < 0)
+                                goto nope;
+                        if (m & ~0775) /* Don't allow world-writable files or suid files to be generated this way */
+                                goto nope;
+
+                        assert(found.mode == MODE_INVALID);
+                        found.mode = m;
+                        break;
+                }
+
+                case PATTERN_SIZE: {
+                        uint64_t u;
+
+                        r = safe_atou64(t, &u);
+                        if (r < 0)
+                                goto nope;
+                        if (u == UINT64_MAX)
+                                goto nope;
+
+                        assert(found.size == UINT64_MAX);
+                        found.size = u;
+                        break;
+                }
+
+                case PATTERN_TRIES_DONE: {
+                        uint64_t u;
+
+                        r = safe_atou64(t, &u);
+                        if (r < 0)
+                                goto nope;
+                        if (u == UINT64_MAX)
+                                goto nope;
+
+                        assert(found.tries_done == UINT64_MAX);
+                        found.tries_done = u;
+                        break;
+                }
+
+                case PATTERN_TRIES_LEFT: {
+                        uint64_t u;
+
+                        r = safe_atou64(t, &u);
+                        if (r < 0)
+                                goto nope;
+                        if (u == UINT64_MAX)
+                                goto nope;
+
+                        assert(found.tries_left == UINT64_MAX);
+                        found.tries_left = u;
+                        break;
+                }
+
+                case PATTERN_NO_AUTO:
+                        r = parse_boolean(t);
+                        if (r < 0)
+                                goto nope;
+
+                        assert(found.no_auto < 0);
+                        found.no_auto = r;
+                        break;
+
+                case PATTERN_READ_ONLY:
+                        r = parse_boolean(t);
+                        if (r < 0)
+                                goto nope;
+
+                        assert(found.read_only < 0);
+                        found.read_only = r;
+                        break;
+
+                case PATTERN_GROWFS:
+                        r = parse_boolean(t);
+                        if (r < 0)
+                                goto nope;
+
+                        assert(found.growfs < 0);
+                        found.growfs = r;
+                        break;
+
+                case PATTERN_SHA256SUM: {
+                        _cleanup_free_ void *d = NULL;
+                        size_t l;
+
+                        if (strlen(t) != sizeof(found.sha256sum) * 2)
+                                goto nope;
+
+                        r = unhexmem(t, sizeof(found.sha256sum) * 2, &d, &l);
+                        if (r == -ENOMEM)
+                                return r;
+                        if (r < 0)
+                                goto nope;
+
+                        assert(!found.sha256sum_set);
+                        assert(l == sizeof(found.sha256sum));
+                        memcpy(found.sha256sum, d, l);
+                        found.sha256sum_set = true;
+                        break;
+                }
+
+                default:
+                        assert_se("unexpected pattern element");
+                }
+
+                p = n;
+        }
+
+        if (ret) {
+                *ret = found;
+                found = (InstanceMetadata) INSTANCE_METADATA_NULL;
+        }
+
+        return true;
+
+nope:
+        if (ret)
+                *ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
+
+        return false;
+}
+
+int pattern_match_many(char **patterns, const char *s, InstanceMetadata *ret) {
+        _cleanup_(instance_metadata_destroy) InstanceMetadata found = INSTANCE_METADATA_NULL;
+        int r;
+
+        STRV_FOREACH(p, patterns) {
+                r = pattern_match(*p, s, &found);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        if (ret) {
+                                *ret = found;
+                                found = (InstanceMetadata) INSTANCE_METADATA_NULL;
+                        }
+
+                        return true;
+                }
+        }
+
+        if (ret)
+                *ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
+
+        return false;
+}
+
+int pattern_valid(const char *pattern) {
+        int r;
+
+        r = pattern_split(pattern, NULL);
+        if (r == -EINVAL)
+                return false;
+        if (r < 0)
+                return r;
+
+        return true;
+}
+
+int pattern_format(
+                const char *pattern,
+                const InstanceMetadata *fields,
+                char **ret) {
+
+        _cleanup_(pattern_element_free_allp) PatternElement *elements = NULL;
+        _cleanup_free_ char *j = NULL;
+        int r;
+
+        assert(pattern);
+        assert(fields);
+        assert(ret);
+
+        r = pattern_split(pattern, &elements);
+        if (r < 0)
+                return r;
+
+        LIST_FOREACH(elements, e, elements) {
+
+                switch (e->type) {
+
+                case PATTERN_LITERAL:
+                        if (!strextend(&j, e->literal))
+                                return -ENOMEM;
+
+                        break;
+
+                case PATTERN_VERSION:
+                        if (!fields->version)
+                                return -ENXIO;
+
+                        if (!strextend(&j, fields->version))
+                                return -ENOMEM;
+                        break;
+
+                case PATTERN_PARTITION_UUID: {
+                        char formatted[SD_ID128_STRING_MAX];
+
+                        if (!fields->partition_uuid_set)
+                                return -ENXIO;
+
+                        if (!strextend(&j, sd_id128_to_string(fields->partition_uuid, formatted)))
+                                return -ENOMEM;
+
+                        break;
+                }
+
+                case PATTERN_PARTITION_FLAGS:
+                        if (!fields->partition_flags_set)
+                                return -ENXIO;
+
+                        r = strextendf(&j, "%" PRIx64, fields->partition_flags);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case PATTERN_MTIME:
+                        if (fields->mtime == USEC_INFINITY)
+                                return -ENXIO;
+
+                        r = strextendf(&j, "%" PRIu64, fields->mtime);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case PATTERN_MODE:
+                        if (fields->mode == MODE_INVALID)
+                                return -ENXIO;
+
+                        r = strextendf(&j, "%03o", fields->mode);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case PATTERN_SIZE:
+                        if (fields->size == UINT64_MAX)
+                                return -ENXIO;
+
+                        r = strextendf(&j, "%" PRIu64, fields->size);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case PATTERN_TRIES_DONE:
+                        if (fields->tries_done == UINT64_MAX)
+                                return -ENXIO;
+
+                        r = strextendf(&j, "%" PRIu64, fields->tries_done);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case PATTERN_TRIES_LEFT:
+                        if (fields->tries_left == UINT64_MAX)
+                                return -ENXIO;
+
+                        r = strextendf(&j, "%" PRIu64, fields->tries_left);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case PATTERN_NO_AUTO:
+                        if (fields->no_auto < 0)
+                                return -ENXIO;
+
+                        if (!strextend(&j, one_zero(fields->no_auto)))
+                                return -ENOMEM;
+
+                        break;
+
+                case PATTERN_READ_ONLY:
+                        if (fields->read_only < 0)
+                                return -ENXIO;
+
+                        if (!strextend(&j, one_zero(fields->read_only)))
+                                return -ENOMEM;
+
+                        break;
+
+                case PATTERN_GROWFS:
+                        if (fields->growfs < 0)
+                                return -ENXIO;
+
+                        if (!strextend(&j, one_zero(fields->growfs)))
+                                return -ENOMEM;
+
+                        break;
+
+                case PATTERN_SHA256SUM: {
+                        _cleanup_free_ char *h = NULL;
+
+                        if (!fields->sha256sum_set)
+                                return -ENXIO;
+
+                        h = hexmem(fields->sha256sum, sizeof(fields->sha256sum));
+                        if (!h)
+                                return -ENOMEM;
+
+                        if (!strextend(&j, h))
+                                return -ENOMEM;
+
+                        break;
+                }
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        *ret = TAKE_PTR(j);
+        return 0;
+}
diff --git a/src/sysupdate/sysupdate-pattern.h b/src/sysupdate/sysupdate-pattern.h
new file mode 100644 (file)
index 0000000..1c60fa0
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sysupdate-instance.h"
+#include "time-util.h"
+
+int pattern_match(const char *pattern, const char *s, InstanceMetadata *ret);
+int pattern_match_many(char **patterns, const char *s, InstanceMetadata *ret);
+int pattern_valid(const char *pattern);
+int pattern_format(const char *pattern, const InstanceMetadata *fields, char **ret);
diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c
new file mode 100644 (file)
index 0000000..3df34cf
--- /dev/null
@@ -0,0 +1,633 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "blockdev-util.h"
+#include "chase-symlinks.h"
+#include "dirent-util.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "glyph-util.h"
+#include "gpt.h"
+#include "hexdecoct.h"
+#include "import-util.h"
+#include "macro.h"
+#include "process-util.h"
+#include "sort-util.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "sysupdate-cache.h"
+#include "sysupdate-instance.h"
+#include "sysupdate-pattern.h"
+#include "sysupdate-resource.h"
+#include "sysupdate.h"
+#include "utf8.h"
+
+void resource_destroy(Resource *rr) {
+        assert(rr);
+
+        free(rr->path);
+        strv_free(rr->patterns);
+
+        for (size_t i = 0; i < rr->n_instances; i++)
+                instance_free(rr->instances[i]);
+        free(rr->instances);
+}
+
+static int resource_add_instance(
+                Resource *rr,
+                const char *path,
+                const InstanceMetadata *f,
+                Instance **ret) {
+
+        Instance *i;
+        int r;
+
+        assert(rr);
+        assert(path);
+        assert(f);
+        assert(f->version);
+
+        if (!GREEDY_REALLOC(rr->instances, rr->n_instances + 1))
+                return log_oom();
+
+        r = instance_new(rr, path, f, &i);
+        if (r < 0)
+                return r;
+
+        rr->instances[rr->n_instances++] = i;
+
+        if (ret)
+                *ret = i;
+
+        return 0;
+}
+
+static int resource_load_from_directory(
+                Resource *rr,
+                mode_t m) {
+
+        _cleanup_(closedirp) DIR *d = NULL;
+        int r;
+
+        assert(rr);
+        assert(IN_SET(rr->type, RESOURCE_TAR, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME));
+        assert(IN_SET(m, S_IFREG, S_IFDIR));
+
+        d = opendir(rr->path);
+        if (!d) {
+                if (errno == ENOENT) {
+                        log_debug("Directory %s does not exist, not loading any resources.", rr->path);
+                        return 0;
+                }
+
+                return log_error_errno(errno, "Failed to open directory '%s': %m", rr->path);
+        }
+
+        for (;;) {
+                _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
+                _cleanup_free_ char *joined = NULL;
+                Instance *instance;
+                struct dirent *de;
+                struct stat st;
+
+                errno = 0;
+                de = readdir_no_dot(d);
+                if (!de) {
+                        if (errno != 0)
+                                return log_error_errno(errno, "Failed to read directory '%s': %m", rr->path);
+                        break;
+                }
+
+                switch (de->d_type) {
+
+                case DT_UNKNOWN:
+                        break;
+
+                case DT_DIR:
+                        if (m != S_IFDIR)
+                                continue;
+
+                        break;
+
+                case DT_REG:
+                        if (m != S_IFREG)
+                                continue;
+                        break;
+
+                default:
+                        continue;
+                }
+
+                if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT) < 0) {
+                        if (errno == ENOENT) /* Gone by now? */
+                                continue;
+
+                        return log_error_errno(errno, "Failed to stat %s/%s: %m", rr->path, de->d_name);
+                }
+
+                if ((st.st_mode & S_IFMT) != m)
+                        continue;
+
+                r = pattern_match_many(rr->patterns, de->d_name, &extracted_fields);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to match pattern: %m");
+                if (r == 0)
+                        continue;
+
+                joined = path_join(rr->path, de->d_name);
+                if (!joined)
+                        return log_oom();
+
+                r = resource_add_instance(rr, joined, &extracted_fields, &instance);
+                if (r < 0)
+                        return r;
+
+                /* Inherit these from the source, if not explicitly overwritten */
+                if (instance->metadata.mtime == USEC_INFINITY)
+                        instance->metadata.mtime = timespec_load(&st.st_mtim) ?: USEC_INFINITY;
+
+                if (instance->metadata.mode == MODE_INVALID)
+                        instance->metadata.mode = st.st_mode & 0775; /* mask out world-writability and suid and stuff, for safety */
+        }
+
+        return 0;
+}
+
+static int resource_load_from_blockdev(Resource *rr) {
+        _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+        _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
+        size_t n_partitions;
+        int r;
+
+        assert(rr);
+
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
+
+        r = fdisk_assign_device(c, rr->path, /* readonly= */ true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open device '%s': %m", rr->path);
+
+        if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
+                return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not suitable.", rr->path);
+
+        r = fdisk_get_partitions(c, &t);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire partition table: %m");
+
+        n_partitions = fdisk_table_get_nents(t);
+        for (size_t i = 0; i < n_partitions; i++)  {
+                _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
+                _cleanup_(partition_info_destroy) PartitionInfo pinfo = PARTITION_INFO_NULL;
+                Instance *instance;
+
+                r = read_partition_info(c, t, i, &pinfo);
+                if (r < 0)
+                        return r;
+                if (r == 0) /* not assigned */
+                        continue;
+
+                /* Check if partition type matches */
+                if (rr->partition_type_set && !sd_id128_equal(pinfo.type, rr->partition_type))
+                        continue;
+
+                /* A label of "_empty" means "not used so far" for us */
+                if (streq_ptr(pinfo.label, "_empty")) {
+                        rr->n_empty++;
+                        continue;
+                }
+
+                r = pattern_match_many(rr->patterns, pinfo.label, &extracted_fields);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to match pattern: %m");
+                if (r == 0)
+                        continue;
+
+                r = resource_add_instance(rr, pinfo.device, &extracted_fields, &instance);
+                if (r < 0)
+                        return r;
+
+                instance->partition_info = pinfo;
+                pinfo = (PartitionInfo) PARTITION_INFO_NULL;
+
+                /* Inherit data from source if not configured explicitly */
+                if (!instance->metadata.partition_uuid_set) {
+                        instance->metadata.partition_uuid = instance->partition_info.uuid;
+                        instance->metadata.partition_uuid_set = true;
+                }
+
+                if (!instance->metadata.partition_flags_set) {
+                        instance->metadata.partition_flags = instance->partition_info.flags;
+                        instance->metadata.partition_flags_set = true;
+                }
+
+                if (instance->metadata.read_only < 0)
+                        instance->metadata.read_only = instance->partition_info.read_only;
+        }
+
+        return 0;
+}
+
+static int download_manifest(
+                const char *url,
+                bool verify_signature,
+                char **ret_buffer,
+                size_t *ret_size) {
+
+        _cleanup_free_ char *buffer = NULL, *suffixed_url = NULL;
+        _cleanup_(close_pairp) int pfd[2] = { -1, -1 };
+        _cleanup_fclose_ FILE *manifest = NULL;
+        size_t size = 0;
+        pid_t pid;
+        int r;
+
+        assert(url);
+        assert(ret_buffer);
+        assert(ret_size);
+
+        /* Download a SHA256SUMS file as manifest */
+
+        r = import_url_append_component(url, "SHA256SUMS", &suffixed_url);
+        if (r < 0)
+                return log_error_errno(r, "Failed to append SHA256SUMS to URL: %m");
+
+        if (pipe2(pfd, O_CLOEXEC) < 0)
+                return log_error_errno(errno, "Failed to allocate pipe: %m");
+
+        log_info("%s Acquiring manifest file %s…", special_glyph(SPECIAL_GLYPH_DOWNLOAD), suffixed_url);
+
+        r = safe_fork("(sd-pull)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child */
+
+                const char *cmdline[] = {
+                        "systemd-pull",
+                        "raw",
+                        "--direct",                        /* just download the specified URL, don't download anything else */
+                        "--verify", verify_signature ? "signature" : "no", /* verify the manifest file */
+                        suffixed_url,
+                        "-",                               /* write to stdout */
+                        NULL
+                };
+
+                pfd[0] = safe_close(pfd[0]);
+
+                r = rearrange_stdio(-1, pfd[1], STDERR_FILENO);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to rearrange stdin/stdout: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                (void) unsetenv("NOTIFY_SOCKET");
+                execv(pull_binary_path(), (char *const*) cmdline);
+                log_error_errno(errno, "Failed to execute %s tool: %m", pull_binary_path());
+                _exit(EXIT_FAILURE);
+        };
+
+        pfd[1] = safe_close(pfd[1]);
+
+        /* We'll first load the entire manifest into memory before parsing it. That's because the
+         * systemd-pull tool can validate the download only after its completion, but still pass the data to
+         * us as it runs. We thus need to check the return value of the process *before* parsing, to be
+         * reasonably safe. */
+
+        manifest = fdopen(pfd[0], "r");
+        if (!manifest)
+                return log_error_errno(errno, "Failed allocate FILE object for manifest file: %m");
+
+        TAKE_FD(pfd[0]);
+
+        r = read_full_stream(manifest, &buffer, &size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read manifest file from child: %m");
+
+        manifest = safe_fclose(manifest);
+
+        r = wait_for_terminate_and_check("(sd-pull)", pid, WAIT_LOG);
+        if (r < 0)
+                return r;
+        if (r != 0)
+                return -EPROTO;
+
+        *ret_buffer = TAKE_PTR(buffer);
+        *ret_size = size;
+
+        return 0;
+}
+
+static int resource_load_from_web(
+                Resource *rr,
+                bool verify,
+                Hashmap **web_cache) {
+
+        size_t manifest_size = 0, left = 0;
+        _cleanup_free_ char *buf = NULL;
+        const char *manifest, *p;
+        size_t line_nr = 1;
+        WebCacheItem *ci;
+        int r;
+
+        assert(rr);
+
+        ci = web_cache ? web_cache_get_item(*web_cache, rr->path, verify) : NULL;
+        if (ci) {
+                log_debug("Manifest web cache hit for %s.", rr->path);
+
+                manifest = (char*) ci->data;
+                manifest_size = ci->size;
+        } else {
+                log_debug("Manifest web cache miss for %s.", rr->path);
+
+                r = download_manifest(rr->path, verify, &buf, &manifest_size);
+                if (r < 0)
+                        return r;
+
+                manifest = buf;
+        }
+
+        if (memchr(manifest, 0, manifest_size))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Manifest file has embedded NUL byte, refusing.");
+        if (!utf8_is_valid_n(manifest, manifest_size))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Manifest file is not valid UTF-8, refusing.");
+
+        p = manifest;
+        left = manifest_size;
+
+        while (left > 0) {
+                _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
+                _cleanup_free_ char *fn = NULL;
+                _cleanup_free_ void *h = NULL;
+                Instance *instance;
+                const char *e;
+                size_t hlen;
+
+                /* 64 character hash + separator + filename + newline */
+                if (left < 67)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Corrupt manifest at line %zu, refusing.", line_nr);
+
+                if (p[0] == '\\')
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "File names with escapes not supported in manifest at line %zu, refusing.", line_nr);
+
+                r = unhexmem(p, 64, &h, &hlen);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse digest at manifest line %zu, refusing.", line_nr);
+
+                p += 64, left -= 64;
+
+                if (*p != ' ')
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing space separator at manifest line %zu, refusing.", line_nr);
+                p++, left--;
+
+                if (!IN_SET(*p, '*', ' '))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing binary/text input marker at manifest line %zu, refusing.", line_nr);
+                p++, left--;
+
+                e = memchr(p, '\n', left);
+                if (!e)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Truncated manifest file at line %zu, refusing.", line_nr);
+                if (e == p)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty filename specified at manifest line %zu, refusing.", line_nr);
+
+                fn = strndup(p, e - p);
+                if (!fn)
+                        return log_oom();
+
+                if (!filename_is_valid(fn))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid filename specified at manifest line %zu, refusing.", line_nr);
+                if (string_has_cc(fn, NULL))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Filename contains control characters at manifest line %zu, refusing.", line_nr);
+
+                r = pattern_match_many(rr->patterns, fn, &extracted_fields);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to match pattern: %m");
+                if (r > 0) {
+                        _cleanup_free_ char *path = NULL;
+
+                        r = import_url_append_component(rr->path, fn, &path);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to build instance URL: %m");
+
+                        r = resource_add_instance(rr, path, &extracted_fields, &instance);
+                        if (r < 0)
+                                return r;
+
+                        assert(hlen == sizeof(instance->metadata.sha256sum));
+
+                        if (instance->metadata.sha256sum_set) {
+                                if (memcmp(instance->metadata.sha256sum, h, hlen) != 0)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "SHA256 sum parsed from filename and manifest don't match at line %zu, refusing.", line_nr);
+                        } else {
+                                memcpy(instance->metadata.sha256sum, h, hlen);
+                                instance->metadata.sha256sum_set = true;
+                        }
+                }
+
+                left -= (e - p) + 1;
+                p = e + 1;
+
+                line_nr++;
+        }
+
+        if (!ci && web_cache) {
+                r = web_cache_add_item(web_cache, rr->path, verify, manifest, manifest_size);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to add manifest '%s' to cache, ignoring: %m", rr->path);
+                else
+                        log_debug("Added manifest '%s' to cache.", rr->path);
+        }
+
+        return 0;
+}
+
+static int instance_cmp(Instance *const*a, Instance *const*b) {
+        int r;
+
+        assert(a);
+        assert(b);
+        assert(*a);
+        assert(*b);
+        assert((*a)->metadata.version);
+        assert((*b)->metadata.version);
+
+        /* Newest version at the beginning */
+        r = strverscmp_improved((*a)->metadata.version, (*b)->metadata.version);
+        if (r != 0)
+                return -r;
+
+        /* Instances don't have to be uniquely named (uniqueness on partition tables is not enforced at all,
+         * and since we allow multiple matching patterns not even in directories they are unique). Hence
+         * let's order by path as secondary ordering key. */
+        return path_compare((*a)->path, (*b)->path);
+}
+
+int resource_load_instances(Resource *rr, bool verify, Hashmap **web_cache) {
+        int r;
+
+        assert(rr);
+
+        switch (rr->type) {
+
+        case RESOURCE_TAR:
+        case RESOURCE_REGULAR_FILE:
+                r = resource_load_from_directory(rr, S_IFREG);
+                break;
+
+        case RESOURCE_DIRECTORY:
+        case RESOURCE_SUBVOLUME:
+                r = resource_load_from_directory(rr, S_IFDIR);
+                break;
+
+        case RESOURCE_PARTITION:
+                r = resource_load_from_blockdev(rr);
+                break;
+
+        case RESOURCE_URL_FILE:
+        case RESOURCE_URL_TAR:
+                r = resource_load_from_web(rr, verify, web_cache);
+                break;
+
+        default:
+                assert_not_reached();
+        }
+        if (r < 0)
+                return r;
+
+        typesafe_qsort(rr->instances, rr->n_instances, instance_cmp);
+        return 0;
+}
+
+Instance* resource_find_instance(Resource *rr, const char *version) {
+        Instance key = {
+                .metadata.version = (char*) version,
+        }, *k = &key;
+
+        return typesafe_bsearch(&k, rr->instances, rr->n_instances, instance_cmp);
+}
+
+int resource_resolve_path(
+                Resource *rr,
+                const char *root,
+                const char *node) {
+
+        _cleanup_free_ char *p = NULL;
+        dev_t d;
+        int r;
+
+        assert(rr);
+
+        if (rr->path_auto) {
+
+                /* NB: we don't actually check the backing device of the root fs "/", but of "/usr", in order
+                 * to support environments where the root fs is a tmpfs, and the OS itself placed exclusively
+                 * in /usr/. */
+
+                if (rr->type != RESOURCE_PARTITION)
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                               "Automatic root path discovery only supported for partition resources.");
+
+                if (node) { /* If --image= is specified, directly use the loopback device */
+                        r = free_and_strdup_warn(&rr->path, node);
+                        if (r < 0)
+                                return r;
+
+                        return 0;
+                }
+
+                if (root)
+                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+                                               "Block device is not allowed when using --root= mode.");
+
+                r = get_block_device_harder("/usr/", &d);
+
+        } else if (rr->type == RESOURCE_PARTITION) {
+                _cleanup_close_ int fd = -1, real_fd = -1;
+                _cleanup_free_ char *resolved = NULL;
+                struct stat st;
+
+                r = chase_symlinks(rr->path, root, CHASE_PREFIX_ROOT, &resolved, &fd);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to resolve '%s': %m", rr->path);
+
+                if (fstat(fd, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat '%s': %m", resolved);
+
+                if (S_ISBLK(st.st_mode) && root)
+                        return log_error_errno(SYNTHETIC_ERRNO(EPERM), "When using --root= or --image= access to device nodes is prohibited.");
+
+                if (S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)) {
+                        /* Not a directory, hence no need to find backing block device for the path */
+                        free_and_replace(rr->path, resolved);
+                        return 0;
+                }
+
+                if (!S_ISDIR(st.st_mode))
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "Target path '%s' does not refer to regular file, directory or block device, refusing.",  rr->path);
+
+                if (node) { /* If --image= is specified all file systems are backed by the same loopback device, hence shortcut things. */
+                        r = free_and_strdup_warn(&rr->path, node);
+                        if (r < 0)
+                                return r;
+
+                        return 0;
+                }
+
+                real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+                if (real_fd < 0)
+                        return log_error_errno(real_fd, "Failed to convert O_PATH file descriptor for %s to regular file descriptor: %m", rr->path);
+
+                r = get_block_device_harder_fd(fd, &d);
+
+        } else if (RESOURCE_IS_FILESYSTEM(rr->type) && root) {
+                _cleanup_free_ char *resolved = NULL;
+
+                r = chase_symlinks(rr->path, root, CHASE_PREFIX_ROOT, &resolved, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to resolve '%s': %m", rr->path);
+
+                free_and_replace(rr->path, resolved);
+                return 0;
+        } else
+                return 0; /* Otherwise assume there's nothing to resolve */
+
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine block device of file system: %m");
+
+        r = block_get_whole_disk(d, &d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to find whole disk device for partition backing file system: %m");
+        if (r == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "File system is not placed on a partition block device, cannot determine whole block device backing root file system.");
+
+        r = device_path_make_canonical(S_IFBLK, d, &p);
+        if (r < 0)
+                return r;
+
+        if (rr->path)
+                log_info("Automatically discovered block device '%s' from '%s'.", p, rr->path);
+        else
+                log_info("Automatically discovered root block device '%s'.", p);
+
+        free_and_replace(rr->path, p);
+        return 1;
+}
+
+static const char *resource_type_table[_RESOURCE_TYPE_MAX] = {
+        [RESOURCE_URL_FILE]     = "url-file",
+        [RESOURCE_URL_TAR]      = "url-tar",
+        [RESOURCE_TAR]          = "tar",
+        [RESOURCE_PARTITION]    = "partition",
+        [RESOURCE_REGULAR_FILE] = "regular-file",
+        [RESOURCE_DIRECTORY]    = "directory",
+        [RESOURCE_SUBVOLUME]    = "subvolume",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(resource_type, ResourceType);
diff --git a/src/sysupdate/sysupdate-resource.h b/src/sysupdate/sysupdate-resource.h
new file mode 100644 (file)
index 0000000..86be0d3
--- /dev/null
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "sd-id128.h"
+
+#include "hashmap.h"
+#include "macro.h"
+
+/* Forward declare this type so that the headers below can use it */
+typedef struct Resource Resource;
+
+#include "sysupdate-instance.h"
+
+typedef enum ResourceType {
+        RESOURCE_URL_FILE,
+        RESOURCE_URL_TAR,
+        RESOURCE_TAR,
+        RESOURCE_PARTITION,
+        RESOURCE_REGULAR_FILE,
+        RESOURCE_DIRECTORY,
+        RESOURCE_SUBVOLUME,
+        _RESOURCE_TYPE_MAX,
+        _RESOURCE_TYPE_INVALID = -EINVAL,
+} ResourceType;
+
+static inline bool RESOURCE_IS_SOURCE(ResourceType t) {
+        return IN_SET(t,
+                      RESOURCE_URL_FILE,
+                      RESOURCE_URL_TAR,
+                      RESOURCE_TAR,
+                      RESOURCE_REGULAR_FILE,
+                      RESOURCE_DIRECTORY,
+                      RESOURCE_SUBVOLUME);
+}
+
+static inline bool RESOURCE_IS_TARGET(ResourceType t) {
+        return IN_SET(t,
+                      RESOURCE_PARTITION,
+                      RESOURCE_REGULAR_FILE,
+                      RESOURCE_DIRECTORY,
+                      RESOURCE_SUBVOLUME);
+}
+
+/* Returns true for all resources that deal with file system objects, i.e. where we operate on top of the
+ * file system layer, instead of below. */
+static inline bool RESOURCE_IS_FILESYSTEM(ResourceType t) {
+        return IN_SET(t,
+                      RESOURCE_TAR,
+                      RESOURCE_REGULAR_FILE,
+                      RESOURCE_DIRECTORY,
+                      RESOURCE_SUBVOLUME);
+}
+
+static inline bool RESOURCE_IS_TAR(ResourceType t) {
+        return IN_SET(t,
+                      RESOURCE_TAR,
+                      RESOURCE_URL_TAR);
+}
+
+static inline bool RESOURCE_IS_URL(ResourceType t) {
+        return IN_SET(t,
+                      RESOURCE_URL_TAR,
+                      RESOURCE_URL_FILE);
+}
+
+struct Resource {
+        ResourceType type;
+
+        /* Where to look for instances, and what to match precisely */
+        char *path;
+        bool path_auto; /* automatically find root path (only available if target resource, not source resource) */
+        char **patterns;
+        sd_id128_t partition_type;
+        bool partition_type_set;
+
+        /* All instances of this resource we found */
+        Instance **instances;
+        size_t n_instances;
+
+        /* If this is a partition resource (RESOURCE_PARTITION), then how many partition slots are currently unassigned, that we can use */
+        size_t n_empty;
+};
+
+void resource_destroy(Resource *rr);
+
+int resource_load_instances(Resource *rr, bool verify, Hashmap **web_cache);
+
+Instance* resource_find_instance(Resource *rr, const char *version);
+
+int resource_resolve_path(Resource *rr, const char *root, const char *node);
+
+ResourceType resource_type_from_string(const char *s) _pure_;
+const char *resource_type_to_string(ResourceType t) _const_;
diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c
new file mode 100644 (file)
index 0000000..a9fceed
--- /dev/null
@@ -0,0 +1,1247 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "blockdev-util.h"
+#include "chase-symlinks.h"
+#include "conf-parser.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "glyph-util.h"
+#include "gpt.h"
+#include "hexdecoct.h"
+#include "install-file.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "rm-rf.h"
+#include "specifier.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "sync-util.h"
+#include "sysupdate-pattern.h"
+#include "sysupdate-resource.h"
+#include "sysupdate-transfer.h"
+#include "sysupdate-util.h"
+#include "sysupdate.h"
+#include "tmpfile-util.h"
+#include "web-util.h"
+
+/* Default value for InstancesMax= for fs object targets */
+#define DEFAULT_FILE_INSTANCES_MAX 3
+
+Transfer *transfer_free(Transfer *t) {
+        if (!t)
+                return NULL;
+
+        t->temporary_path = rm_rf_subvolume_and_free(t->temporary_path);
+
+        free(t->definition_path);
+        free(t->min_version);
+        strv_free(t->protected_versions);
+        free(t->current_symlink);
+        free(t->final_path);
+
+        partition_info_destroy(&t->partition_info);
+
+        resource_destroy(&t->source);
+        resource_destroy(&t->target);
+
+        return mfree(t);
+}
+
+Transfer *transfer_new(void) {
+        Transfer *t;
+
+        t = new(Transfer, 1);
+        if (!t)
+                return NULL;
+
+        *t = (Transfer) {
+                .source.type = _RESOURCE_TYPE_INVALID,
+                .target.type = _RESOURCE_TYPE_INVALID,
+                .remove_temporary = true,
+                .mode = MODE_INVALID,
+                .tries_left = UINT64_MAX,
+                .tries_done = UINT64_MAX,
+                .verify = true,
+
+                /* the three flags, as configured by the user */
+                .no_auto = -1,
+                .read_only = -1,
+                .growfs = -1,
+
+                /* the read only flag, as ultimately determined */
+                .install_read_only = -1,
+
+                .partition_info = PARTITION_INFO_NULL,
+        };
+
+        return t;
+}
+
+static const Specifier specifier_table[] = {
+        COMMON_SYSTEM_SPECIFIERS,
+        COMMON_TMP_SPECIFIERS,
+        {}
+};
+
+static int config_parse_protect_version(
+                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;
+        char ***protected_versions = data;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to expand specifiers in ProtectVersion=, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (!version_is_valid(resolved))  {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "ProtectVersion= string is not valid, ignoring: %s", resolved);
+                return 0;
+        }
+
+        r = strv_extend(protected_versions, resolved);
+        if (r < 0)
+                return log_oom();
+
+        return 0;
+}
+
+static int config_parse_min_version(
+                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;
+        char **version = data;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to expand specifiers in MinVersion=, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (!version_is_valid(rvalue)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "MinVersion= string is not valid, ignoring: %s", resolved);
+                return 0;
+        }
+
+        return free_and_replace(*version, resolved);
+}
+
+static int config_parse_current_symlink(
+                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;
+        char **current_symlink = data;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to expand specifiers in CurrentSymlink=, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
+        if (r < 0)
+                return 0;
+
+        return free_and_replace(*current_symlink, resolved);
+}
+
+static int config_parse_instances_max(
+                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 *instances_max = data, i;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *instances_max = 0; /* Revert to default logic, see transfer_read_definition() */
+                return 0;
+        }
+
+        r = safe_atou64(rvalue, &i);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse InstancesMax= value, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (i < 2) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "InstancesMax= value must be at least 2, bumping: %s", rvalue);
+                *instances_max = 2;
+        } else
+                *instances_max = i;
+
+        return 0;
+}
+
+static int config_parse_resource_pattern(
+                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) {
+
+        char ***patterns = data;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *patterns = strv_free(*patterns);
+                return 0;
+        }
+
+        for (;;) {
+                _cleanup_free_ char *word = NULL, *resolved = NULL;
+
+                r = extract_first_word(&rvalue, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to extract first pattern from MatchPattern=, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        break;
+
+                r = specifier_printf(word, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to expand specifiers in MatchPattern=, ignoring: %s", rvalue);
+                        return 0;
+                }
+
+                if (!pattern_valid(resolved))
+                        return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
+                                          "MatchPattern= string is not valid, refusing: %s", resolved);
+
+                r = strv_consume(patterns, TAKE_PTR(resolved));
+                if (r < 0)
+                        return log_oom();
+        }
+
+        strv_uniq(*patterns);
+        return 0;
+}
+
+static int config_parse_resource_path(
+                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;
+        Resource *rr = data;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        if (streq(rvalue, "auto")) {
+                rr->path_auto = true;
+                rr->path = mfree(rr->path);
+                return 0;
+        }
+
+        r = specifier_printf(rvalue, PATH_MAX-1, specifier_table, arg_root, NULL, &resolved);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to expand specifiers in Path=, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        /* Note that we don't validate the path as being absolute or normalized. We'll do that in
+         * transfer_read_definition() as we might not know yet whether Path refers to an URL or a file system
+         * path. */
+
+        rr->path_auto = false;
+        return free_and_replace(rr->path, resolved);
+}
+
+static DEFINE_CONFIG_PARSE_ENUM(config_parse_resource_type, resource_type, ResourceType, "Invalid resource type");
+
+static int config_parse_resource_ptype(
+                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) {
+
+        Resource *rr = data;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        r = gpt_partition_type_uuid_from_string(rvalue, &rr->partition_type);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed parse partition type, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        rr->partition_type_set = true;
+        return 0;
+}
+
+static int config_parse_partition_uuid(
+                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) {
+
+        Transfer *t = data;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        r = sd_id128_from_string(rvalue, &t->partition_uuid);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed parse partition UUID, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        t->partition_uuid_set = true;
+        return 0;
+}
+
+static int config_parse_partition_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) {
+
+        Transfer *t = data;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        r = safe_atou64(rvalue, &t->partition_flags);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed parse partition flags, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        t->partition_flags_set = true;
+        return 0;
+}
+
+int transfer_read_definition(Transfer *t, const char *path) {
+        int r;
+
+        assert(t);
+        assert(path);
+
+        ConfigTableItem table[] = {
+                { "Transfer",    "MinVersion",              config_parse_min_version,          0, &t->min_version        },
+                { "Transfer",    "ProtectVersion",          config_parse_protect_version,      0, &t->protected_versions },
+                { "Transfer",    "Verify",                  config_parse_bool,                 0, &t->verify             },
+                { "Source",      "Type",                    config_parse_resource_type,        0, &t->source.type        },
+                { "Source",      "Path",                    config_parse_resource_path,        0, &t->source             },
+                { "Source",      "MatchPattern",            config_parse_resource_pattern,     0, &t->source.patterns    },
+                { "Target",      "Type",                    config_parse_resource_type,        0, &t->target.type        },
+                { "Target",      "Path",                    config_parse_resource_path,        0, &t->target             },
+                { "Target",      "MatchPattern",            config_parse_resource_pattern,     0, &t->target.patterns    },
+                { "Target",      "MatchPartitionType",      config_parse_resource_ptype,       0, &t->target             },
+                { "Target",      "PartitionUUID",           config_parse_partition_uuid,       0, t                      },
+                { "Target",      "PartitionFlags",          config_parse_partition_flags,      0, t                      },
+                { "Target",      "PartitionNoAuto",         config_parse_tristate,             0, &t->no_auto            },
+                { "Target",      "PartitionGrowFileSystem", config_parse_tristate,             0, &t->growfs             },
+                { "Target",      "ReadOnly",                config_parse_tristate,             0, &t->read_only          },
+                { "Target",      "Mode",                    config_parse_mode,                 0, &t->mode               },
+                { "Target",      "TriesLeft",               config_parse_uint64,               0, &t->tries_left         },
+                { "Target",      "TriesDone",               config_parse_uint64,               0, &t->tries_done         },
+                { "Target",      "InstancesMax",            config_parse_instances_max,        0, &t->instances_max      },
+                { "Target",      "RemoveTemporary",         config_parse_bool,                 0, &t->remove_temporary   },
+                { "Target",      "CurrentSymlink",          config_parse_current_symlink,      0, &t->current_symlink    },
+                {}
+        };
+
+        r = config_parse(NULL, path, NULL,
+                         "Transfer\0"
+                         "Source\0"
+                         "Target\0",
+                         config_item_table_lookup, table,
+                         CONFIG_PARSE_WARN,
+                         t,
+                         NULL);
+        if (r < 0)
+                return r;
+
+        if (!RESOURCE_IS_SOURCE(t->source.type))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Source Type= must be one of url-file, url-tar, tar, regular-file, directory, subvolume.");
+
+        if (t->target.type < 0) {
+                switch (t->source.type) {
+
+                case RESOURCE_URL_FILE:
+                case RESOURCE_REGULAR_FILE:
+                        t->target.type =
+                                t->target.path && path_startswith(t->target.path, "/dev/") ?
+                                RESOURCE_PARTITION : RESOURCE_REGULAR_FILE;
+                        break;
+
+                case RESOURCE_URL_TAR:
+                case RESOURCE_TAR:
+                case RESOURCE_DIRECTORY:
+                        t->target.type = RESOURCE_DIRECTORY;
+                        break;
+
+                case RESOURCE_SUBVOLUME:
+                        t->target.type = RESOURCE_SUBVOLUME;
+                        break;
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        if (!RESOURCE_IS_TARGET(t->target.type))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Target Type= must be one of partition, regular-file, directory, subvolume.");
+
+        if ((IN_SET(t->source.type, RESOURCE_URL_FILE, RESOURCE_PARTITION, RESOURCE_REGULAR_FILE) &&
+             !IN_SET(t->target.type, RESOURCE_PARTITION, RESOURCE_REGULAR_FILE)) ||
+            (IN_SET(t->source.type, RESOURCE_URL_TAR, RESOURCE_TAR, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME) &&
+             !IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME)))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Target type '%s' is incompatible with source type '%s', refusing.",
+                                  resource_type_to_string(t->source.type), resource_type_to_string(t->target.type));
+
+        if (!t->source.path && !t->source.path_auto)
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Source specification lacks Path=.");
+
+        if (t->source.path) {
+                if (RESOURCE_IS_FILESYSTEM(t->source.type) || t->source.type == RESOURCE_PARTITION)
+                        if (!path_is_absolute(t->source.path) || !path_is_normalized(t->source.path))
+                                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                                  "Source path is not a normalized, absolute path: %s", t->source.path);
+
+                /* We unofficially support file:// in addition to http:// and https:// for url
+                 * sources. That's mostly for testing, since it relieves us from having to set up a HTTP
+                 * server, and CURL abstracts this away from us thankfully. */
+                if (RESOURCE_IS_URL(t->source.type))
+                        if (!http_url_is_valid(t->source.path) && !file_url_is_valid(t->source.path))
+                                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                                  "Source path is not a valid HTTP or HTTPS URL: %s", t->source.path);
+        }
+
+        if (strv_isempty(t->source.patterns))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Source specification lacks MatchPattern=.");
+
+        if (!t->target.path && !t->target.path_auto)
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Target specification lacks Path= field.");
+
+        if (t->target.path &&
+            (!path_is_absolute(t->target.path) || !path_is_normalized(t->target.path)))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Target path is not a normalized, absolute path: %s", t->target.path);
+
+        if (strv_isempty(t->target.patterns)) {
+                strv_free(t->target.patterns);
+                t->target.patterns = strv_copy(t->source.patterns);
+                if (!t->target.patterns)
+                        return log_oom();
+        }
+
+        if (t->current_symlink && !RESOURCE_IS_FILESYSTEM(t->target.type) && !path_is_absolute(t->current_symlink))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Current symlink must be absolute path if target is partition: %s", t->current_symlink);
+
+        /* When no instance limit is set, use all available partition slots in case of partitions, or 3 in case of fs objects */
+        if (t->instances_max == 0)
+                t->instances_max = t->target.type == RESOURCE_PARTITION ? UINT64_MAX : DEFAULT_FILE_INSTANCES_MAX;
+
+        return 0;
+}
+
+int transfer_resolve_paths(
+                Transfer *t,
+                const char *root,
+                const char *node) {
+
+        int r;
+
+        /* If Path=auto is used in [Source] or [Target] sections, let's automatically detect the path of the
+         * block device to use. Moreover, if this path points to a directory but we need a block device,
+         * automatically determine the backing block device, so that users can reference block devices by
+         * mount point. */
+
+        assert(t);
+
+        r = resource_resolve_path(&t->source, root, node);
+        if (r < 0)
+                return r;
+
+        r = resource_resolve_path(&t->target, root, node);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static void transfer_remove_temporary(Transfer *t) {
+        _cleanup_(closedirp) DIR *d = NULL;
+        int r;
+
+        assert(t);
+
+        if (!t->remove_temporary)
+                return;
+
+        if (!IN_SET(t->target.type, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME))
+                return;
+
+        /* Removes all temporary files/dirs from previous runs in the target directory, i.e. all those starting with '.#' */
+
+        d = opendir(t->target.path);
+        if (!d) {
+                if (errno == ENOENT)
+                        return;
+
+                log_debug_errno(errno, "Failed to open target directory '%s', ignoring: %m", t->target.path);
+                return;
+        }
+
+        for (;;) {
+                struct dirent *de;
+
+                errno = 0;
+                de = readdir_no_dot(d);
+                if (!de) {
+                        if (errno != 0)
+                                log_debug_errno(errno, "Failed to read target directory '%s', ignoring: %m", t->target.path);
+                        break;
+                }
+
+                if (!startswith(de->d_name, ".#"))
+                        continue;
+
+                r = rm_rf_child(dirfd(d), de->d_name, REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD);
+                if (r == -ENOENT)
+                        continue;
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to remove temporary resource instance '%s/%s', ignoring: %m", t->target.path, de->d_name);
+                        continue;
+                }
+
+                log_debug("Removed temporary resource instance '%s/%s'.", t->target.path, de->d_name);
+        }
+}
+
+int transfer_vacuum(
+                Transfer *t,
+                uint64_t space,
+                const char *extra_protected_version) {
+
+        uint64_t instances_max, limit;
+        int r, count = 0;
+
+        assert(t);
+
+        transfer_remove_temporary(t);
+
+        /* First, calculate how many instances to keep, based on the instance limit â€” but keep at least one */
+
+        instances_max = arg_instances_max != UINT64_MAX ? arg_instances_max : t->instances_max;
+        assert(instances_max >= 1);
+        if (instances_max == UINT64_MAX) /* Keep infinite instances? */
+                limit = UINT64_MAX;
+        else if (space > instances_max)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
+                                       "Asked to delete more instances than total maximum allowed number of instances, refusing.");
+        else if (space == instances_max)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
+                                       "Asked to delete all possible instances, can't allow that. One instance must always remain.");
+        else
+                limit = instances_max - space;
+
+        if (t->target.type == RESOURCE_PARTITION) {
+                uint64_t rm, remain;
+
+                /* If we are looking at a partition table, we also have to take into account how many
+                 * partition slots of the right type are available */
+
+                if (t->target.n_empty + t->target.n_instances < 2)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
+                                               "Partition table has less than two partition slots of the right type " SD_ID128_UUID_FORMAT_STR " (%s), refusing.",
+                                               SD_ID128_FORMAT_VAL(t->target.partition_type),
+                                               gpt_partition_type_uuid_to_string(t->target.partition_type));
+                if (space > t->target.n_empty + t->target.n_instances)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
+                                               "Partition table does not have enough partition slots of right type " SD_ID128_UUID_FORMAT_STR " (%s) for operation.",
+                                               SD_ID128_FORMAT_VAL(t->target.partition_type),
+                                               gpt_partition_type_uuid_to_string(t->target.partition_type));
+                if (space == t->target.n_empty + t->target.n_instances)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
+                                               "Asked to empty all partition table slots of the right type " SD_ID128_UUID_FORMAT_STR " (%s), can't allow that. One instance must always remain.",
+                                               SD_ID128_FORMAT_VAL(t->target.partition_type),
+                                               gpt_partition_type_uuid_to_string(t->target.partition_type));
+
+                rm = LESS_BY(space, t->target.n_empty);
+                remain = LESS_BY(t->target.n_instances, rm);
+                limit = MIN(limit, remain);
+        }
+
+        while (t->target.n_instances > limit) {
+                Instance *oldest;
+                size_t p = t->target.n_instances - 1;
+
+                for (;;) {
+                        oldest = t->target.instances[p];
+                        assert(oldest);
+
+                        /* If this is listed among the protected versions, then let's not remove it */
+                        if (!strv_contains(t->protected_versions, oldest->metadata.version) &&
+                            (!extra_protected_version || !streq(extra_protected_version, oldest->metadata.version)))
+                                break;
+
+                        log_debug("Version '%s' is protected, not removing.", oldest->metadata.version);
+                        if (p == 0) {
+                                oldest = NULL;
+                                break;
+                        }
+
+                        p--;
+                }
+
+                if (!oldest) /* Nothing more to remove */
+                        break;
+
+                assert(oldest->resource);
+
+                log_info("%s Removing old '%s' (%s).", special_glyph(SPECIAL_GLYPH_RECYCLING), oldest->path, resource_type_to_string(oldest->resource->type));
+
+                switch (t->target.type) {
+
+                case RESOURCE_REGULAR_FILE:
+                case RESOURCE_DIRECTORY:
+                case RESOURCE_SUBVOLUME:
+                        r = rm_rf(oldest->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_MISSING_OK|REMOVE_CHMOD);
+                        if (r < 0 && r != -ENOENT)
+                                return log_error_errno(r, "Failed to make room, deleting '%s' failed: %m", oldest->path);
+
+                        break;
+
+                case RESOURCE_PARTITION: {
+                        PartitionInfo pinfo = oldest->partition_info;
+
+                        /* label "_empty" means "no contents" for our purposes */
+                        pinfo.label = (char*) "_empty";
+
+                        r = patch_partition(t->target.path, &pinfo, PARTITION_LABEL);
+                        if (r < 0)
+                                return r;
+
+                        t->target.n_empty++;
+                        break;
+                }
+
+                default:
+                        assert_not_reached();
+                        break;
+                }
+
+                instance_free(oldest);
+                memmove(t->target.instances + p, t->target.instances + p + 1, (t->target.n_instances - p - 1) * sizeof(Instance*));
+                t->target.n_instances--;
+
+                count++;
+        }
+
+        return count;
+}
+
+static void compile_pattern_fields(
+                const Transfer *t,
+                const Instance *i,
+                InstanceMetadata *ret) {
+
+        assert(t);
+        assert(i);
+        assert(ret);
+
+        *ret = (InstanceMetadata) {
+                .version = i->metadata.version,
+
+                /* We generally prefer explicitly configured values for the transfer over those automatically
+                 * derived from the source instance. Also, if the source is a tar archive, then let's not
+                 * patch mtime/mode and use the one embedded in the tar file */
+                .partition_uuid = t->partition_uuid_set ? t->partition_uuid : i->metadata.partition_uuid,
+                .partition_uuid_set = t->partition_uuid_set || i->metadata.partition_uuid_set,
+                .partition_flags = t->partition_flags_set ? t->partition_flags : i->metadata.partition_flags,
+                .partition_flags_set = t->partition_flags_set || i->metadata.partition_flags_set,
+                .mtime = RESOURCE_IS_TAR(i->resource->type) ? USEC_INFINITY : i->metadata.mtime,
+                .mode = t->mode != MODE_INVALID ? t->mode : (RESOURCE_IS_TAR(i->resource->type) ? MODE_INVALID : i->metadata.mode),
+                .size = i->metadata.size,
+                .tries_done = t->tries_done != UINT64_MAX ? t->tries_done :
+                              i->metadata.tries_done != UINT64_MAX ? i->metadata.tries_done : 0,
+                .tries_left = t->tries_left != UINT64_MAX ? t->tries_left :
+                              i->metadata.tries_left != UINT64_MAX ? i->metadata.tries_left : 3,
+                .no_auto = t->no_auto >= 0 ? t->no_auto : i->metadata.no_auto,
+                .read_only = t->read_only >= 0 ? t->read_only : i->metadata.read_only,
+                .growfs = t->growfs >= 0 ? t->growfs : i->metadata.growfs,
+                .sha256sum_set = i->metadata.sha256sum_set,
+        };
+
+        memcpy(ret->sha256sum, i->metadata.sha256sum, sizeof(ret->sha256sum));
+}
+
+static int run_helper(
+                const char *name,
+                const char *path,
+                const char * const cmdline[]) {
+
+        int r;
+
+        assert(name);
+        assert(path);
+        assert(cmdline);
+
+        r = safe_fork(name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child */
+
+                (void) unsetenv("NOTIFY_SOCKET");
+                execv(path, (char *const*) cmdline);
+                log_error_errno(errno, "Failed to execute %s tool: %m", path);
+                _exit(EXIT_FAILURE);
+        }
+
+        return 0;
+}
+
+int transfer_acquire_instance(Transfer *t, Instance *i) {
+        _cleanup_free_ char *formatted_pattern = NULL, *digest = NULL;
+        char offset[DECIMAL_STR_MAX(uint64_t)+1], max_size[DECIMAL_STR_MAX(uint64_t)+1];
+        const char *where = NULL;
+        InstanceMetadata f;
+        Instance *existing;
+        int r;
+
+        assert(t);
+        assert(i);
+        assert(i->resource);
+        assert(t == container_of(i->resource, Transfer, source));
+
+        /* Does this instance already exist in the target? Then we don't need to acquire anything */
+        existing = resource_find_instance(&t->target, i->metadata.version);
+        if (existing) {
+                log_info("No need to acquire '%s', already installed.", i->path);
+                return 0;
+        }
+
+        assert(!t->final_path);
+        assert(!t->temporary_path);
+        assert(!strv_isempty(t->target.patterns));
+
+        /* Format the target name using the first pattern specified */
+        compile_pattern_fields(t, i, &f);
+        r = pattern_format(t->target.patterns[0], &f, &formatted_pattern);
+        if (r < 0)
+                return log_error_errno(r, "Failed to format target pattern: %m");
+
+        if (RESOURCE_IS_FILESYSTEM(t->target.type)) {
+
+                if (!filename_is_valid(formatted_pattern))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as file name, refusing: %s", formatted_pattern);
+
+                t->final_path = path_join(t->target.path, formatted_pattern);
+                if (!t->final_path)
+                        return log_oom();
+
+                r = tempfn_random(t->final_path, "sysupdate", &t->temporary_path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to generate temporary target path: %m");
+
+                where = t->final_path;
+        }
+
+        if (t->target.type == RESOURCE_PARTITION) {
+                r = gpt_partition_label_valid(formatted_pattern);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine if formatted pattern is suitable as GPT partition label: %s", formatted_pattern);
+                if (!r)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_pattern);
+
+                r = find_suitable_partition(
+                                t->target.path,
+                                i->metadata.size,
+                                t->target.partition_type_set ? &t->target.partition_type : NULL,
+                                &t->partition_info);
+                if (r < 0)
+                        return r;
+
+                xsprintf(offset, "%" PRIu64, t->partition_info.start);
+                xsprintf(max_size, "%" PRIu64, t->partition_info.size);
+
+                where = t->partition_info.device;
+        }
+
+        assert(where);
+
+        log_info("%s Acquiring %s %s %s...", special_glyph(SPECIAL_GLYPH_DOWNLOAD), i->path, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), where);
+
+        if (RESOURCE_IS_URL(i->resource->type)) {
+                /* For URL sources we require the SHA256 sum to be known so that we can validate the
+                 * download. */
+
+                if (!i->metadata.sha256sum_set)
+                        return log_error_errno(r, "SHA256 checksum not known for download '%s', refusing.", i->path);
+
+                digest = hexmem(i->metadata.sha256sum, sizeof(i->metadata.sha256sum));
+                if (!digest)
+                        return log_oom();
+        }
+
+        switch (i->resource->type) { /* Source */
+
+        case RESOURCE_REGULAR_FILE:
+
+                switch (t->target.type) { /* Target */
+
+                case RESOURCE_REGULAR_FILE:
+
+                        /* regular file â†’ regular file (why fork off systemd-import for such a simple file
+                         * copy case? implicit decompression mostly, and thus also sandboxing. Also, the
+                         * importer has some tricks up its sleeve, such as sparse file generation, which we
+                         * want to take benefit of, too.) */
+
+                        r = run_helper("(sd-import-raw)",
+                                       import_binary_path(),
+                                       (const char* const[]) {
+                                               "systemd-import",
+                                               "raw",
+                                               "--direct",          /* just copy/unpack the specified file, don't do anything else */
+                                               arg_sync ? "--sync=yes" : "--sync=no",
+                                               i->path,
+                                               t->temporary_path,
+                                               NULL
+                                       });
+                        break;
+
+                case RESOURCE_PARTITION:
+
+                        /* regular file â†’ partition */
+
+                        r = run_helper("(sd-import-raw)",
+                                       import_binary_path(),
+                                       (const char* const[]) {
+                                               "systemd-import",
+                                               "raw",
+                                               "--direct",          /* just copy/unpack the specified file, don't do anything else */
+                                               "--offset", offset,
+                                               "--size-max", max_size,
+                                               arg_sync ? "--sync=yes" : "--sync=no",
+                                               i->path,
+                                               t->target.path,
+                                               NULL
+                                       });
+                        break;
+
+                default:
+                        assert_not_reached();
+                }
+
+                break;
+
+        case RESOURCE_DIRECTORY:
+        case RESOURCE_SUBVOLUME:
+                assert(IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME));
+
+                /* directory/subvolume â†’ directory/subvolume */
+
+                r = run_helper("(sd-import-fs)",
+                               import_fs_binary_path(),
+                               (const char* const[]) {
+                                       "systemd-import-fs",
+                                       "run",
+                                       "--direct",          /* just untar the specified file, don't do anything else */
+                                       arg_sync ? "--sync=yes" : "--sync=no",
+                                       t->target.type == RESOURCE_SUBVOLUME ? "--btrfs-subvol=yes" : "--btrfs-subvol=no",
+                                       i->path,
+                                       t->temporary_path,
+                                       NULL
+                               });
+                break;
+
+        case RESOURCE_TAR:
+                assert(IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME));
+
+                /* tar â†’ directory/subvolume */
+
+                r = run_helper("(sd-import-tar)",
+                               import_binary_path(),
+                               (const char* const[]) {
+                                       "systemd-import",
+                                       "tar",
+                                       "--direct",          /* just untar the specified file, don't do anything else */
+                                       arg_sync ? "--sync=yes" : "--sync=no",
+                                       t->target.type == RESOURCE_SUBVOLUME ? "--btrfs-subvol=yes" : "--btrfs-subvol=no",
+                                       i->path,
+                                       t->temporary_path,
+                                       NULL
+                               });
+                break;
+
+        case RESOURCE_URL_FILE:
+
+                switch (t->target.type) {
+
+                case RESOURCE_REGULAR_FILE:
+
+                        /* url file â†’ regular file */
+
+                        r = run_helper("(sd-pull-raw)",
+                                       pull_binary_path(),
+                                       (const char* const[]) {
+                                               "systemd-pull",
+                                               "raw",
+                                               "--direct",          /* just download the specified URL, don't download anything else */
+                                               "--verify", digest,  /* validate by explicit SHA256 sum */
+                                               arg_sync ? "--sync=yes" : "--sync=no",
+                                               i->path,
+                                               t->temporary_path,
+                                               NULL
+                                       });
+                        break;
+
+                case RESOURCE_PARTITION:
+
+                        /* url file â†’ partition */
+
+                        r = run_helper("(sd-pull-raw)",
+                                       pull_binary_path(),
+                                       (const char* const[]) {
+                                               "systemd-pull",
+                                               "raw",
+                                               "--direct",              /* just download the specified URL, don't download anything else */
+                                               "--verify", digest,      /* validate by explicit SHA256 sum */
+                                               "--offset", offset,
+                                               "--size-max", max_size,
+                                               arg_sync ? "--sync=yes" : "--sync=no",
+                                               i->path,
+                                               t->target.path,
+                                               NULL
+                                       });
+                        break;
+
+                default:
+                        assert_not_reached();
+                }
+
+                break;
+
+        case RESOURCE_URL_TAR:
+                assert(IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME));
+
+                r = run_helper("(sd-pull-tar)",
+                               pull_binary_path(),
+                               (const char*const[]) {
+                                       "systemd-pull",
+                                       "tar",
+                                       "--direct",          /* just download the specified URL, don't download anything else */
+                                       "--verify", digest,  /* validate by explicit SHA256 sum */
+                                       t->target.type == RESOURCE_SUBVOLUME ? "--btrfs-subvol=yes" : "--btrfs-subvol=no",
+                                       arg_sync ? "--sync=yes" : "--sync=no",
+                                       i->path,
+                                       t->temporary_path,
+                                       NULL
+                               });
+                break;
+
+        default:
+                assert_not_reached();
+        }
+        if (r < 0)
+                return r;
+
+        if (RESOURCE_IS_FILESYSTEM(t->target.type)) {
+                bool need_sync = false;
+                assert(t->temporary_path);
+
+                /* Apply file attributes if set */
+                if (f.mtime != USEC_INFINITY) {
+                        struct timespec ts;
+
+                        timespec_store(&ts, f.mtime);
+
+                        if (utimensat(AT_FDCWD, t->temporary_path, (struct timespec[2]) { ts, ts }, AT_SYMLINK_NOFOLLOW) < 0)
+                                return log_error_errno(errno, "Failed to adjust mtime of '%s': %m", t->temporary_path);
+
+                        need_sync = true;
+                }
+
+                if (f.mode != MODE_INVALID) {
+                        /* Try with AT_SYMLINK_NOFOLLOW first, because it's the safe thing to do. Older
+                         * kernels don't support that however, in that case we fall back to chmod(). Not as
+                         * safe, but shouldn't be a problem, given that we don't create symlinks here. */
+                        if (fchmodat(AT_FDCWD, t->temporary_path, f.mode, AT_SYMLINK_NOFOLLOW) < 0 &&
+                            (!ERRNO_IS_NOT_SUPPORTED(errno) || chmod(t->temporary_path, f.mode) < 0))
+                                return log_error_errno(errno, "Failed to adjust mode of '%s': %m", t->temporary_path);
+
+                        need_sync = true;
+                }
+
+                /* Synchronize */
+                if (arg_sync && need_sync) {
+                        if (t->target.type == RESOURCE_REGULAR_FILE)
+                                r = fsync_path_and_parent_at(AT_FDCWD, t->temporary_path);
+                        else {
+                                assert(IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME));
+                                r = syncfs_path(AT_FDCWD, t->temporary_path);
+                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to synchronize file system backing '%s': %m", t->temporary_path);
+                }
+
+                t->install_read_only = f.read_only;
+        }
+
+        if (t->target.type == RESOURCE_PARTITION) {
+                free_and_replace(t->partition_info.label, formatted_pattern);
+                t->partition_change = PARTITION_LABEL;
+
+                if (f.partition_uuid_set) {
+                        t->partition_info.uuid = f.partition_uuid;
+                        t->partition_change |= PARTITION_UUID;
+                }
+
+                if (f.partition_flags_set) {
+                        t->partition_info.flags = f.partition_flags;
+                        t->partition_change |= PARTITION_FLAGS;
+                }
+
+                if (f.no_auto >= 0) {
+                        t->partition_info.no_auto = f.no_auto;
+                        t->partition_change |= PARTITION_NO_AUTO;
+                }
+
+                if (f.read_only >= 0) {
+                        t->partition_info.read_only = f.read_only;
+                        t->partition_change |= PARTITION_READ_ONLY;
+                }
+
+                if (f.growfs >= 0) {
+                        t->partition_info.growfs = f.growfs;
+                        t->partition_change |= PARTITION_GROWFS;
+                }
+        }
+
+        /* For regular file cases the only step left is to install the file in place, which install_file()
+         * will do via rename(). For partition cases the only step left is to update the partition table,
+         * which is done at the same place. */
+
+        log_info("Successfully acquired '%s'.", i->path);
+        return 0;
+}
+
+int transfer_install_instance(
+                Transfer *t,
+                Instance *i,
+                const char *root) {
+
+        int r;
+
+        assert(t);
+        assert(i);
+        assert(i->resource);
+        assert(t == container_of(i->resource, Transfer, source));
+
+        if (t->temporary_path) {
+                assert(RESOURCE_IS_FILESYSTEM(t->target.type));
+                assert(t->final_path);
+
+                r = install_file(AT_FDCWD, t->temporary_path,
+                                 AT_FDCWD, t->final_path,
+                                 INSTALL_REPLACE|
+                                 (t->install_read_only > 0 ? INSTALL_READ_ONLY : 0)|
+                                 (t->target.type == RESOURCE_REGULAR_FILE ? INSTALL_FSYNC_FULL : INSTALL_SYNCFS));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to move '%s' into place: %m", t->final_path);
+
+                log_info("Successfully installed '%s' (%s) as '%s' (%s).",
+                         i->path,
+                         resource_type_to_string(i->resource->type),
+                         t->final_path,
+                         resource_type_to_string(t->target.type));
+
+                t->temporary_path = mfree(t->temporary_path);
+        }
+
+        if (t->partition_change != 0) {
+                assert(t->target.type == RESOURCE_PARTITION);
+
+                r = patch_partition(
+                                t->target.path,
+                                &t->partition_info,
+                                t->partition_change);
+                if (r < 0)
+                        return r;
+
+                log_info("Successfully installed '%s' (%s) as '%s' (%s).",
+                         i->path,
+                         resource_type_to_string(i->resource->type),
+                         t->partition_info.device,
+                         resource_type_to_string(t->target.type));
+        }
+
+        if (t->current_symlink) {
+                _cleanup_free_ char *buf = NULL, *parent = NULL, *relative = NULL, *resolved = NULL;
+                const char *link_path, *link_target;
+                bool resolve_link_path = false;
+
+                if (RESOURCE_IS_FILESYSTEM(t->target.type)) {
+
+                        assert(t->target.path);
+
+                        if (path_is_absolute(t->current_symlink)) {
+                                link_path = t->current_symlink;
+                                resolve_link_path = true;
+                        } else {
+                                buf = path_make_absolute(t->current_symlink, t->target.path);
+                                if (!buf)
+                                        return log_oom();
+
+                                link_path = buf;
+                        }
+
+                        link_target = t->final_path;
+
+                } else if (t->target.type == RESOURCE_PARTITION) {
+
+                        assert(path_is_absolute(t->current_symlink));
+
+                        link_path = t->current_symlink;
+                        link_target = t->partition_info.device;
+
+                        resolve_link_path = true;
+                } else
+                        assert_not_reached();
+
+                if (resolve_link_path && root) {
+                        r = chase_symlinks(link_path, root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to resolve current symlink path '%s': %m", link_path);
+
+                        link_path = resolved;
+                }
+
+                if (link_target) {
+                        r = path_extract_directory(link_path, &parent);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to extract directory of target path '%s': %m", link_path);
+
+                        r = path_make_relative(parent, link_target, &relative);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to make symlink path '%s' relative to '%s': %m", link_target, parent);
+
+                        r = symlink_atomic(relative, link_path);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to update current symlink '%s' â†’ '%s': %m", link_path, relative);
+
+                        log_info("Updated symlink '%s' â†’ '%s'.", link_path, relative);
+                }
+        }
+
+        return 0;
+}
diff --git a/src/sysupdate/sysupdate-transfer.h b/src/sysupdate/sysupdate-transfer.h
new file mode 100644 (file)
index 0000000..b0c2a6e
--- /dev/null
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "sd-id128.h"
+
+/* Forward declare this type so that the headers below can use it */
+typedef struct Transfer Transfer;
+
+#include "sysupdate-partition.h"
+#include "sysupdate-resource.h"
+
+struct Transfer {
+        char *definition_path;
+        char *min_version;
+        char **protected_versions;
+        char *current_symlink;
+        bool verify;
+
+        Resource source, target;
+
+        uint64_t instances_max;
+        bool remove_temporary;
+
+        /* When creating a new partition/file, optionally override these attributes explicitly */
+        sd_id128_t partition_uuid;
+        bool partition_uuid_set;
+        uint64_t partition_flags;
+        bool partition_flags_set;
+        mode_t mode;
+        uint64_t tries_left, tries_done;
+        int no_auto;
+        int read_only;
+        int growfs;
+
+        /* If we create a new file/dir/subvol in the fs, the temporary and final path we create it under, as well as the read-only flag for it */
+        char *temporary_path;
+        char *final_path;
+        int install_read_only;
+
+        /* If we write to a partition in a partition table, the metrics of it */
+        PartitionInfo partition_info;
+        PartitionChange partition_change;
+};
+
+Transfer *transfer_new(void);
+
+Transfer *transfer_free(Transfer *t);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_free);
+
+int transfer_read_definition(Transfer *t, const char *path);
+
+int transfer_resolve_paths(Transfer *t, const char *root, const char *node);
+
+int transfer_vacuum(Transfer *t, uint64_t space, const char *extra_protected_version);
+
+int transfer_acquire_instance(Transfer *t, Instance *i);
+
+int transfer_install_instance(Transfer *t, Instance *i, const char *root);
diff --git a/src/sysupdate/sysupdate-update-set.c b/src/sysupdate/sysupdate-update-set.c
new file mode 100644 (file)
index 0000000..6d6051d
--- /dev/null
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "glyph-util.h"
+#include "string-util.h"
+#include "sysupdate-update-set.h"
+#include "terminal-util.h"
+
+UpdateSet *update_set_free(UpdateSet *us) {
+        if (!us)
+                return NULL;
+
+        free(us->version);
+        free(us->instances); /* The objects referenced by this array are freed via resource_free(), not us */
+
+        return mfree(us);
+}
+
+int update_set_cmp(UpdateSet *const*a, UpdateSet *const*b) {
+        assert(a);
+        assert(b);
+        assert(*a);
+        assert(*b);
+        assert((*a)->version);
+        assert((*b)->version);
+
+        /* Newest version at the beginning */
+        return -strverscmp_improved((*a)->version, (*b)->version);
+}
+
+const char *update_set_flags_to_color(UpdateSetFlags flags) {
+
+        if (flags == 0 || (flags & UPDATE_OBSOLETE))
+                return (flags & UPDATE_NEWEST) ? ansi_highlight_grey() : ansi_grey();
+
+        if (FLAGS_SET(flags, UPDATE_INSTALLED|UPDATE_NEWEST))
+                return ansi_highlight();
+
+        if (FLAGS_SET(flags, UPDATE_INSTALLED|UPDATE_PROTECTED))
+                return ansi_highlight_magenta();
+
+        if ((flags & (UPDATE_AVAILABLE|UPDATE_INSTALLED|UPDATE_NEWEST|UPDATE_OBSOLETE)) == (UPDATE_AVAILABLE|UPDATE_NEWEST))
+                return ansi_highlight_green();
+
+        return NULL;
+}
+
+const char *update_set_flags_to_glyph(UpdateSetFlags flags) {
+
+        if (flags == 0 || (flags & UPDATE_OBSOLETE))
+                return special_glyph(SPECIAL_GLYPH_MULTIPLICATION_SIGN);
+
+        if (FLAGS_SET(flags, UPDATE_INSTALLED|UPDATE_NEWEST))
+                return special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE);
+
+        if (FLAGS_SET(flags, UPDATE_INSTALLED|UPDATE_PROTECTED))
+                return special_glyph(SPECIAL_GLYPH_WHITE_CIRCLE);
+
+        if ((flags & (UPDATE_AVAILABLE|UPDATE_INSTALLED|UPDATE_NEWEST|UPDATE_OBSOLETE)) == (UPDATE_AVAILABLE|UPDATE_NEWEST))
+                return special_glyph(SPECIAL_GLYPH_CIRCLE_ARROW);
+
+        return " ";
+}
diff --git a/src/sysupdate/sysupdate-update-set.h b/src/sysupdate/sysupdate-update-set.h
new file mode 100644 (file)
index 0000000..5dd94bc
--- /dev/null
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+typedef struct UpdateSet UpdateSet;
+
+#include "sysupdate-instance.h"
+
+typedef enum UpdateSetFlags {
+        UPDATE_NEWEST    = 1 << 0,
+        UPDATE_AVAILABLE = 1 << 1,
+        UPDATE_INSTALLED = 1 << 2,
+        UPDATE_OBSOLETE  = 1 << 3,
+        UPDATE_PROTECTED = 1 << 4,
+} UpdateSetFlags;
+
+struct UpdateSet {
+        UpdateSetFlags flags;
+        char *version;
+        Instance **instances;
+        size_t n_instances;
+};
+
+UpdateSet *update_set_free(UpdateSet *us);
+
+int update_set_cmp(UpdateSet *const*a, UpdateSet *const*b);
+
+const char *update_set_flags_to_color(UpdateSetFlags flags);
+const char *update_set_flags_to_glyph(UpdateSetFlags flags);
diff --git a/src/sysupdate/sysupdate-util.c b/src/sysupdate/sysupdate-util.c
new file mode 100644 (file)
index 0000000..c7a2301
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "path-util.h"
+#include "sysupdate-util.h"
+
+bool version_is_valid(const char *s) {
+        if (isempty(s))
+                return false;
+
+        if (!filename_is_valid(s))
+                return false;
+
+        if (!in_charset(s, ALPHANUMERICAL ".,_-+"))
+                return false;
+
+        return true;
+}
diff --git a/src/sysupdate/sysupdate-util.h b/src/sysupdate/sysupdate-util.h
new file mode 100644 (file)
index 0000000..afa3a9d
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+bool version_is_valid(const char *s);
diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c
new file mode 100644 (file)
index 0000000..76c5bae
--- /dev/null
@@ -0,0 +1,1411 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "bus-error.h"
+#include "bus-locator.h"
+#include "chase-symlinks.h"
+#include "conf-files.h"
+#include "def.h"
+#include "dirent-util.h"
+#include "dissect-image.h"
+#include "fd-util.h"
+#include "format-table.h"
+#include "glyph-util.h"
+#include "hexdecoct.h"
+#include "login-util.h"
+#include "main-func.h"
+#include "mount-util.h"
+#include "os-util.h"
+#include "pager.h"
+#include "parse-argument.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "set.h"
+#include "sort-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "sysupdate-transfer.h"
+#include "sysupdate-update-set.h"
+#include "sysupdate.h"
+#include "terminal-util.h"
+#include "utf8.h"
+#include "verbs.h"
+
+static char *arg_definitions = NULL;
+bool arg_sync = true;
+uint64_t arg_instances_max = UINT64_MAX;
+static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
+static PagerFlags arg_pager_flags = 0;
+static bool arg_legend = true;
+char *arg_root = NULL;
+static char *arg_image = NULL;
+static bool arg_reboot = false;
+static char *arg_component = NULL;
+static int arg_verify = -1;
+
+STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_component, freep);
+
+typedef struct Context {
+        Transfer **transfers;
+        size_t n_transfers;
+
+        UpdateSet **update_sets;
+        size_t n_update_sets;
+
+        UpdateSet *newest_installed, *candidate;
+
+        Hashmap *web_cache; /* Cache for downloaded resources, keyed by URL */
+} Context;
+
+static Context *context_free(Context *c) {
+        if (!c)
+                return NULL;
+
+        for (size_t i = 0; i < c->n_transfers; i++)
+                transfer_free(c->transfers[i]);
+        free(c->transfers);
+
+        for (size_t i = 0; i < c->n_update_sets; i++)
+                update_set_free(c->update_sets[i]);
+        free(c->update_sets);
+
+        hashmap_free(c->web_cache);
+
+        return mfree(c);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Context*, context_free);
+
+static Context *context_new(void) {
+        /* For now, no fields to initialize non-zero */
+        return new0(Context, 1);
+}
+
+static int context_read_definitions(
+                Context *c,
+                const char *directory,
+                const char *component,
+                const char *root,
+                const char *node) {
+
+        _cleanup_strv_free_ char **files = NULL;
+        int r;
+
+        assert(c);
+
+        if (directory)
+                r = conf_files_list_strv(&files, ".conf", NULL, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) STRV_MAKE(directory));
+        else if (component) {
+                _cleanup_strv_free_ char **n = NULL;
+                char **l = CONF_PATHS_STRV("");
+                size_t k = 0;
+
+                n = new0(char*, strv_length(l) + 1);
+                if (!n)
+                        return log_oom();
+
+                STRV_FOREACH(i, l) {
+                        char *j;
+
+                        j = strjoin(*i, "sysupdate.", component, ".d");
+                        if (!j)
+                                return log_oom();
+
+                        n[k++] = j;
+                }
+
+                r = conf_files_list_strv(&files, ".conf", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) n);
+        } else
+                r = conf_files_list_strv(&files, ".conf", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) CONF_PATHS_STRV("sysupdate.d"));
+        if (r < 0)
+                return log_error_errno(r, "Failed to enumerate *.conf files: %m");
+
+        STRV_FOREACH(f, files) {
+                _cleanup_(transfer_freep) Transfer *t = NULL;
+
+                if (!GREEDY_REALLOC(c->transfers, c->n_transfers + 1))
+                        return log_oom();
+
+                t = transfer_new();
+                if (!t)
+                        return log_oom();
+
+                t->definition_path = strdup(*f);
+                if (!t->definition_path)
+                        return log_oom();
+
+                r = transfer_read_definition(t, *f);
+                if (r < 0)
+                        return r;
+
+                c->transfers[c->n_transfers++] = TAKE_PTR(t);
+        }
+
+        if (c->n_transfers == 0) {
+                if (arg_component)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+                                               "No transfer definitions for component '%s' found.", arg_component);
+
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+                                       "No transfer definitions found.");
+        }
+
+        for (size_t i = 0; i < c->n_transfers; i++) {
+                r = transfer_resolve_paths(c->transfers[i], root, node);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int context_load_installed_instances(Context *c) {
+        int r;
+
+        assert(c);
+
+        log_info("Discovering installed instances…");
+
+        for (size_t i = 0; i < c->n_transfers; i++) {
+                r = resource_load_instances(
+                                &c->transfers[i]->target,
+                                arg_verify >= 0 ? arg_verify : c->transfers[i]->verify,
+                                &c->web_cache);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int context_load_available_instances(Context *c) {
+        int r;
+
+        assert(c);
+
+        log_info("Discovering available instances…");
+
+        for (size_t i = 0; i < c->n_transfers; i++) {
+                assert(c->transfers[i]);
+
+                r = resource_load_instances(
+                                &c->transfers[i]->source,
+                                arg_verify >= 0 ? arg_verify : c->transfers[i]->verify,
+                                &c->web_cache);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int context_discover_update_sets_by_flag(Context *c, UpdateSetFlags flags) {
+        _cleanup_free_ Instance **cursor_instances = NULL;
+        _cleanup_free_ char *boundary = NULL;
+        bool newest_found = false;
+        int r;
+
+        assert(c);
+        assert(IN_SET(flags, UPDATE_AVAILABLE, UPDATE_INSTALLED));
+
+        for (;;) {
+                bool incomplete = false, exists = false;
+                UpdateSetFlags extra_flags = 0;
+                _cleanup_free_ char *cursor = NULL;
+                UpdateSet *us = NULL;
+
+                for (size_t k = 0; k < c->n_transfers; k++) {
+                        Transfer *t = c->transfers[k];
+                        bool cursor_found = false;
+                        Resource *rr;
+
+                        assert(t);
+
+                        if (flags == UPDATE_AVAILABLE)
+                                rr = &t->source;
+                        else {
+                                assert(flags == UPDATE_INSTALLED);
+                                rr = &t->target;
+                        }
+
+                        for (size_t j = 0; j < rr->n_instances; j++) {
+                                Instance *i = rr->instances[j];
+
+                                assert(i);
+
+                                /* Is the instance we are looking at equal or newer than the boundary? If so, we
+                                 * already checked this version, and it wasn't complete, let's ignore it. */
+                                if (boundary && strverscmp_improved(i->metadata.version, boundary) >= 0)
+                                        continue;
+
+                                if (cursor) {
+                                        if (strverscmp_improved(i->metadata.version, cursor) != 0)
+                                                continue;
+                                } else {
+                                        cursor = strdup(i->metadata.version);
+                                        if (!cursor)
+                                                return log_oom();
+                                }
+
+                                cursor_found = true;
+
+                                if (!cursor_instances) {
+                                        cursor_instances = new(Instance*, c->n_transfers);
+                                        if (!cursor_instances)
+                                                return -ENOMEM;
+                                }
+                                cursor_instances[k] = i;
+                                break;
+                        }
+
+                        if (!cursor) /* No suitable instance beyond the boundary found? Then we are done! */
+                                break;
+
+                        if (!cursor_found) {
+                                /* Hmm, we didn't find the version indicated by 'cursor' among the instances
+                                 * of this transfer, let's skip it. */
+                                incomplete = true;
+                                break;
+                        }
+
+                        if (t->min_version && strverscmp_improved(t->min_version, cursor) > 0)
+                                extra_flags |= UPDATE_OBSOLETE;
+
+                        if (strv_contains(t->protected_versions, cursor))
+                                extra_flags |= UPDATE_PROTECTED;
+                }
+
+                if (!cursor) /* EOL */
+                        break;
+
+                r = free_and_strdup_warn(&boundary, cursor);
+                if (r < 0)
+                        return r;
+
+                if (incomplete) /* One transfer was missing this version, ignore the whole thing */
+                        continue;
+
+                /* See if we already have this update set in our table */
+                for (size_t i = 0; i < c->n_update_sets; i++) {
+                        if (strverscmp_improved(c->update_sets[i]->version, cursor) != 0)
+                                continue;
+
+                        /* We only store the instances we found first, but we remember we also found it again */
+                        c->update_sets[i]->flags |= flags | extra_flags;
+                        exists = true;
+                        newest_found = true;
+                        break;
+                }
+
+                if (exists)
+                        continue;
+
+                /* Doesn't exist yet, let's add it */
+                if (!GREEDY_REALLOC(c->update_sets, c->n_update_sets + 1))
+                        return log_oom();
+
+                us = new(UpdateSet, 1);
+                if (!us)
+                        return log_oom();
+
+                *us = (UpdateSet) {
+                        .flags = flags | (newest_found ? 0 : UPDATE_NEWEST) | extra_flags,
+                        .version = TAKE_PTR(cursor),
+                        .instances = TAKE_PTR(cursor_instances),
+                        .n_instances = c->n_transfers,
+                };
+
+                c->update_sets[c->n_update_sets++] = us;
+
+                newest_found = true;
+
+                /* Remember which one is the newest installed */
+                if ((us->flags & (UPDATE_NEWEST|UPDATE_INSTALLED)) == (UPDATE_NEWEST|UPDATE_INSTALLED))
+                        c->newest_installed = us;
+
+                /* Remember which is the newest non-obsolete, available (and not installed) version, which we declare the "candidate" */
+                if ((us->flags & (UPDATE_NEWEST|UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE)) == (UPDATE_NEWEST|UPDATE_AVAILABLE))
+                        c->candidate = us;
+        }
+
+        /* Newest installed is newer than or equal to candidate? Then suppress the candidate */
+        if (c->newest_installed && c->candidate && strverscmp_improved(c->newest_installed->version, c->candidate->version) >= 0)
+                c->candidate = NULL;
+
+        return 0;
+}
+
+static int context_discover_update_sets(Context *c) {
+        int r;
+
+        assert(c);
+
+        log_info("Determining installed update sets…");
+
+        r = context_discover_update_sets_by_flag(c, UPDATE_INSTALLED);
+        if (r < 0)
+                return r;
+
+        log_info("Determining available update sets…");
+
+        r = context_discover_update_sets_by_flag(c, UPDATE_AVAILABLE);
+        if (r < 0)
+                return r;
+
+        typesafe_qsort(c->update_sets, c->n_update_sets, update_set_cmp);
+        return 0;
+}
+
+static const char *update_set_flags_to_string(UpdateSetFlags flags) {
+
+        switch ((unsigned) flags) {
+
+        case 0:
+                return "n/a";
+
+        case UPDATE_INSTALLED|UPDATE_NEWEST:
+        case UPDATE_INSTALLED|UPDATE_NEWEST|UPDATE_PROTECTED:
+        case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_NEWEST:
+        case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_NEWEST|UPDATE_PROTECTED:
+                return "current";
+
+        case UPDATE_AVAILABLE|UPDATE_NEWEST:
+        case UPDATE_AVAILABLE|UPDATE_NEWEST|UPDATE_PROTECTED:
+                return "candidate";
+
+        case UPDATE_INSTALLED:
+        case UPDATE_INSTALLED|UPDATE_AVAILABLE:
+                return "installed";
+
+        case UPDATE_INSTALLED|UPDATE_PROTECTED:
+        case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_PROTECTED:
+                return "protected";
+
+        case UPDATE_AVAILABLE:
+        case UPDATE_AVAILABLE|UPDATE_PROTECTED:
+                return "available";
+
+        case UPDATE_INSTALLED|UPDATE_OBSOLETE|UPDATE_NEWEST:
+        case UPDATE_INSTALLED|UPDATE_OBSOLETE|UPDATE_NEWEST|UPDATE_PROTECTED:
+        case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_NEWEST:
+        case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_NEWEST|UPDATE_PROTECTED:
+                return "current+obsolete";
+
+        case UPDATE_INSTALLED|UPDATE_OBSOLETE:
+        case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE:
+                return "installed+obsolete";
+
+        case UPDATE_INSTALLED|UPDATE_OBSOLETE|UPDATE_PROTECTED:
+        case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_PROTECTED:
+                return "protected+obsolete";
+
+        case UPDATE_AVAILABLE|UPDATE_OBSOLETE:
+        case UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_PROTECTED:
+        case UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_NEWEST:
+        case UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_NEWEST|UPDATE_PROTECTED:
+                return "available+obsolete";
+
+        default:
+                assert_not_reached();
+        }
+}
+
+
+static int context_show_table(Context *c) {
+        _cleanup_(table_unrefp) Table *t = NULL;
+        int r;
+
+        assert(c);
+
+        t = table_new("", "version", "installed", "available", "assessment");
+        if (!t)
+                return log_oom();
+
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 0), 100);
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 2), 50);
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 3), 50);
+
+        for (size_t i = 0; i < c->n_update_sets; i++) {
+                UpdateSet *us = c->update_sets[i];
+                const char *color;
+
+                color = update_set_flags_to_color(us->flags);
+
+                r = table_add_many(t,
+                                   TABLE_STRING,    update_set_flags_to_glyph(us->flags),
+                                   TABLE_SET_COLOR, color,
+                                   TABLE_STRING,    us->version,
+                                   TABLE_SET_COLOR, color,
+                                   TABLE_STRING,    special_glyph_check_mark_space(FLAGS_SET(us->flags, UPDATE_INSTALLED)),
+                                   TABLE_SET_COLOR, color,
+                                   TABLE_STRING,    special_glyph_check_mark_space(FLAGS_SET(us->flags, UPDATE_AVAILABLE)),
+                                   TABLE_SET_COLOR, color,
+                                   TABLE_STRING,    update_set_flags_to_string(us->flags),
+                                   TABLE_SET_COLOR, color);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
+        return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
+}
+
+static UpdateSet *context_update_set_by_version(Context *c, const char *version) {
+        assert(c);
+        assert(version);
+
+        for (size_t i = 0; i < c->n_update_sets; i++)
+                if (streq(c->update_sets[i]->version, version))
+                        return c->update_sets[i];
+
+        return NULL;
+}
+
+static int context_show_version(Context *c, const char *version) {
+        bool show_fs_columns = false, show_partition_columns = false,
+                have_fs_attributes = false, have_partition_attributes = false,
+                have_size = false, have_tries = false, have_no_auto = false,
+                have_read_only = false, have_growfs = false, have_sha256 = false;
+        _cleanup_(table_unrefp) Table *t = NULL;
+        UpdateSet *us;
+        int r;
+
+        assert(c);
+        assert(version);
+
+        us = context_update_set_by_version(c, version);
+        if (!us)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Update '%s' not found.", version);
+
+        if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
+                (void) pager_open(arg_pager_flags);
+
+        if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
+                printf("%s%s%s Version: %s\n"
+                       "    State: %s%s%s\n"
+                       "Installed: %s%s\n"
+                       "Available: %s%s\n"
+                       "Protected: %s%s%s\n"
+                       " Obsolete: %s%s%s\n\n",
+                       strempty(update_set_flags_to_color(us->flags)), update_set_flags_to_glyph(us->flags), ansi_normal(), us->version,
+                       strempty(update_set_flags_to_color(us->flags)), update_set_flags_to_string(us->flags), ansi_normal(),
+                       yes_no(us->flags & UPDATE_INSTALLED), FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_NEWEST) ? " (newest)" : "",
+                       yes_no(us->flags & UPDATE_AVAILABLE), (us->flags & (UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_NEWEST)) == (UPDATE_AVAILABLE|UPDATE_NEWEST) ? " (newest)" : "",
+                       FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_PROTECTED) ? ansi_highlight() : "", yes_no(FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_PROTECTED)), ansi_normal(),
+                       us->flags & UPDATE_OBSOLETE ? ansi_highlight_red() : "", yes_no(us->flags & UPDATE_OBSOLETE), ansi_normal());
+
+
+        t = table_new("type", "path", "ptuuid", "ptflags", "mtime", "mode", "size", "tries-done", "tries-left", "noauto", "ro", "growfs", "sha256");
+        if (!t)
+                return log_oom();
+
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 3), 100);
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 4), 100);
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 6), 100);
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100);
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 8), 100);
+        (void) table_set_empty_string(t, "-");
+
+        /* Determine if the target will make use of partition/fs attributes for any of the transfers */
+        for (size_t n = 0; n < c->n_transfers; n++) {
+                Transfer *tr = c->transfers[n];
+
+                if (tr->target.type == RESOURCE_PARTITION)
+                        show_partition_columns = true;
+                if (RESOURCE_IS_FILESYSTEM(tr->target.type))
+                        show_fs_columns = true;
+        }
+
+        for (size_t n = 0; n < us->n_instances; n++) {
+                Instance *i = us->instances[n];
+
+                r = table_add_many(t,
+                                   TABLE_STRING, resource_type_to_string(i->resource->type),
+                                   TABLE_PATH, i->path);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.partition_uuid_set) {
+                        have_partition_attributes = true;
+                        r = table_add_cell(t, NULL, TABLE_UUID, &i->metadata.partition_uuid);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.partition_flags_set) {
+                        have_partition_attributes = true;
+                        r = table_add_cell(t, NULL, TABLE_UINT64_HEX, &i->metadata.partition_flags);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.mtime != USEC_INFINITY) {
+                        have_fs_attributes = true;
+                        r = table_add_cell(t, NULL, TABLE_TIMESTAMP, &i->metadata.mtime);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.mode != MODE_INVALID) {
+                        have_fs_attributes = true;
+                        r = table_add_cell(t, NULL, TABLE_MODE, &i->metadata.mode);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.size != UINT64_MAX) {
+                        have_size = true;
+                        r = table_add_cell(t, NULL, TABLE_SIZE, &i->metadata.size);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.tries_done != UINT64_MAX) {
+                        have_tries = true;
+                        r = table_add_cell(t, NULL, TABLE_UINT64, &i->metadata.tries_done);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.tries_left != UINT64_MAX) {
+                        have_tries = true;
+                        r = table_add_cell(t, NULL, TABLE_UINT64, &i->metadata.tries_left);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.no_auto >= 0) {
+                        bool b;
+
+                        have_no_auto = true;
+                        b = i->metadata.no_auto;
+                        r = table_add_cell(t, NULL, TABLE_BOOLEAN, &b);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+                if (i->metadata.read_only >= 0) {
+                        bool b;
+
+                        have_read_only = true;
+                        b = i->metadata.read_only;
+                        r = table_add_cell(t, NULL, TABLE_BOOLEAN, &b);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.growfs >= 0) {
+                        bool b;
+
+                        have_growfs = true;
+                        b = i->metadata.growfs;
+                        r = table_add_cell(t, NULL, TABLE_BOOLEAN, &b);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (i->metadata.sha256sum_set) {
+                        _cleanup_free_ char *formatted = NULL;
+
+                        have_sha256 = true;
+
+                        formatted = hexmem(i->metadata.sha256sum, sizeof(i->metadata.sha256sum));
+                        if (!formatted)
+                                return log_oom();
+
+                        r = table_add_cell(t, NULL, TABLE_STRING, formatted);
+                } else
+                        r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
+        /* Hide the fs/partition columns if we don't have any data to show there */
+        if (!have_fs_attributes)
+                show_fs_columns = false;
+        if (!have_partition_attributes)
+                show_partition_columns = false;
+
+        if (!show_partition_columns)
+                (void) table_hide_column_from_display(t, 2, 3);
+        if (!show_fs_columns)
+                (void) table_hide_column_from_display(t, 4, 5);
+        if (!have_size)
+                (void) table_hide_column_from_display(t, 6);
+        if (!have_tries)
+                (void) table_hide_column_from_display(t, 7, 8);
+        if (!have_no_auto)
+                (void) table_hide_column_from_display(t, 9);
+        if (!have_read_only)
+                (void) table_hide_column_from_display(t, 10);
+        if (!have_growfs)
+                (void) table_hide_column_from_display(t, 11);
+        if (!have_sha256)
+                (void) table_hide_column_from_display(t, 12);
+
+        return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
+}
+
+static int context_vacuum(
+                Context *c,
+                uint64_t space,
+                const char *extra_protected_version) {
+
+        int r, count = 0;
+
+        assert(c);
+
+        if (space == 0)
+                log_info("Making room…");
+        else
+                log_info("Making room for %" PRIu64 " updates…", space);
+
+        for (size_t i = 0; i < c->n_transfers; i++) {
+                r = transfer_vacuum(c->transfers[i], space, extra_protected_version);
+                if (r < 0)
+                        return r;
+
+                count = MAX(count, r);
+        }
+
+        if (count > 0)
+                log_info("Removed %i instances.", count);
+        else
+                log_info("Removed no instances.");
+
+        return 0;
+}
+
+static int context_make_offline(Context **ret, const char *node) {
+        _cleanup_(context_freep) Context* context = NULL;
+        int r;
+
+        assert(ret);
+
+        /* Allocates a context object and initializes everything we can initialize offline, i.e. without
+         * checking on the update source (i.e. the Internet) what versions are available */
+
+        context = context_new();
+        if (!context)
+                return log_oom();
+
+        r = context_read_definitions(context, arg_definitions, arg_component, arg_root, node);
+        if (r < 0)
+                return r;
+
+        r = context_load_installed_instances(context);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(context);
+        return 0;
+}
+
+static int context_make_online(Context **ret, const char *node) {
+        _cleanup_(context_freep) Context* context = NULL;
+        int r;
+
+        assert(ret);
+
+        /* Like context_make_offline(), but also communicates with the update source looking for new
+         * versions. */
+
+        r = context_make_offline(&context, node);
+        if (r < 0)
+                return r;
+
+        r = context_load_available_instances(context);
+        if (r < 0)
+                return r;
+
+        r = context_discover_update_sets(context);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(context);
+        return 0;
+}
+
+static int context_apply(
+                Context *c,
+                const char *version,
+                UpdateSet **ret_applied) {
+
+        UpdateSet *us = NULL;
+        int r;
+
+        assert(c);
+
+        if (version) {
+                us = context_update_set_by_version(c, version);
+                if (!us)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Update '%s' not found.", version);
+        } else {
+                if (!c->candidate) {
+                        log_info("No update needed.");
+
+                        if (ret_applied)
+                                *ret_applied = NULL;
+
+                        return 0;
+                }
+
+                us = c->candidate;
+        }
+
+        if (FLAGS_SET(us->flags, UPDATE_INSTALLED)) {
+                log_info("Selected update '%s' is already installed. Skipping update.", us->version);
+
+                if (ret_applied)
+                        *ret_applied = NULL;
+
+                return 0;
+        }
+        if (!FLAGS_SET(us->flags, UPDATE_AVAILABLE))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected update '%s' is not available, refusing.", us->version);
+        if (FLAGS_SET(us->flags, UPDATE_OBSOLETE))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected update '%s' is obsolete, refusing.", us->version);
+
+        assert((us->flags & (UPDATE_AVAILABLE|UPDATE_INSTALLED|UPDATE_OBSOLETE)) == UPDATE_AVAILABLE);
+
+        if (!FLAGS_SET(us->flags, UPDATE_NEWEST))
+                log_notice("Selected update '%s' is not the newest, proceeding anyway.", us->version);
+        if (c->newest_installed && strverscmp_improved(c->newest_installed->version, us->version) > 0)
+                log_notice("Selected update '%s' is older than newest installed version, proceeding anyway.", us->version);
+
+        log_info("Selected update '%s' for install.", us->version);
+
+        (void) sd_notifyf(false,
+                          "STATUS=Making room for '%s'.", us->version);
+
+        /* Let's make some room. We make sure for each transfer we have one free space to fill. While
+         * removing stuff we'll protect the version we are trying to acquire. Why that? Maybe an earlier
+         * download succeeded already, in which case we shouldn't remove it just to acquire it again */
+        r = context_vacuum(
+                        c,
+                        /* space = */ 1,
+                        /* extra_protected_version = */ us->version);
+        if (r < 0)
+                return r;
+
+        if (arg_sync)
+                sync();
+
+        (void) sd_notifyf(false,
+                          "STATUS=Updating to '%s'.\n", us->version);
+
+        /* There should now be one instance picked for each transfer, and the order is the same */
+        assert(us->n_instances == c->n_transfers);
+
+        for (size_t i = 0; i < c->n_transfers; i++) {
+                r = transfer_acquire_instance(c->transfers[i], us->instances[i]);
+                if (r < 0)
+                        return r;
+        }
+
+        if (arg_sync)
+                sync();
+
+        for (size_t i = 0; i < c->n_transfers; i++) {
+                r = transfer_install_instance(c->transfers[i], us->instances[i], arg_root);
+                if (r < 0)
+                        return r;
+        }
+
+        log_info("%s Successfully installed update '%s'.", special_glyph(SPECIAL_GLYPH_SPARKLES), us->version);
+
+        if (ret_applied)
+                *ret_applied = us;
+
+        return 1;
+}
+
+static int reboot_now(void) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
+        int r;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open bus connection: %m");
+
+        r = bus_call_method(bus, bus_login_mgr, "RebootWithFlags", &error, NULL, "t",
+                            (uint64_t) SD_LOGIND_ROOT_CHECK_INHIBITORS);
+        if (r < 0)
+                return log_error_errno(r, "Failed to issue reboot request: %s", bus_error_message(&error, r));
+
+        return 0;
+}
+
+static int process_image(
+                bool ro,
+                char **ret_mounted_dir,
+                LoopDevice **ret_loop_device,
+                DecryptedImage **ret_decrypted_image) {
+
+        _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;
+        int r;
+
+        assert(ret_mounted_dir);
+        assert(ret_loop_device);
+        assert(ret_decrypted_image);
+
+        if (!arg_image)
+                return 0;
+
+        assert(!arg_root);
+
+        r = mount_image_privately_interactively(
+                        arg_image,
+                        (ro ? DISSECT_IMAGE_READ_ONLY : 0) |
+                        DISSECT_IMAGE_FSCK |
+                        DISSECT_IMAGE_MKDIR |
+                        DISSECT_IMAGE_GROWFS |
+                        DISSECT_IMAGE_RELAX_VAR_CHECK |
+                        DISSECT_IMAGE_USR_NO_ROOT |
+                        DISSECT_IMAGE_GENERIC_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();
+
+        *ret_mounted_dir = TAKE_PTR(mounted_dir);
+        *ret_loop_device = TAKE_PTR(loop_device);
+        *ret_decrypted_image = TAKE_PTR(decrypted_image);
+
+        return 0;
+}
+
+static int verb_list(int argc, char **argv, void *userdata) {
+        _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;
+        const char *version;
+        int r;
+
+        assert(argc <= 2);
+        version = argc >= 2 ? argv[1] : NULL;
+
+        r = process_image(/* ro= */ true, &mounted_dir, &loop_device, &decrypted_image);
+        if (r < 0)
+                return r;
+
+        r = context_make_online(&context, loop_device ? loop_device->node : NULL);
+        if (r < 0)
+                return r;
+
+        if (version)
+                return context_show_version(context, version);
+        else
+                return context_show_table(context);
+}
+
+static int verb_check_new(int argc, char **argv, void *userdata) {
+        _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;
+        int r;
+
+        assert(argc <= 1);
+
+        r = process_image(/* ro= */ true, &mounted_dir, &loop_device, &decrypted_image);
+        if (r < 0)
+                return r;
+
+        r = context_make_online(&context, loop_device ? loop_device->node : NULL);
+        if (r < 0)
+                return r;
+
+        if (!context->candidate) {
+                log_debug("No candidate found.");
+                return EXIT_FAILURE;
+        }
+
+        puts(context->candidate->version);
+        return EXIT_SUCCESS;
+}
+
+static int verb_vacuum(int argc, char **argv, void *userdata) {
+        _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;
+        int r;
+
+        assert(argc <= 1);
+
+        r = process_image(/* ro= */ false, &mounted_dir, &loop_device, &decrypted_image);
+        if (r < 0)
+                return r;
+
+        r = context_make_offline(&context, loop_device ? loop_device->node : NULL);
+        if (r < 0)
+                return r;
+
+        return context_vacuum(context, 0, NULL);
+}
+
+static int verb_update(int argc, char **argv, void *userdata) {
+        _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 *booted_version = NULL;
+        UpdateSet *applied = NULL;
+        const char *version;
+        int r;
+
+        assert(argc <= 2);
+        version = argc >= 2 ? argv[1] : NULL;
+
+        if (arg_reboot) {
+                /* If automatic reboot on completion is requested, let's first determine the currently booted image */
+
+                r = parse_os_release(arg_root, "IMAGE_VERSION", &booted_version);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse /etc/os-release: %m");
+                if (!booted_version)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "/etc/os-release lacks IMAGE_VERSION field.");
+        }
+
+        r = process_image(/* ro= */ false, &mounted_dir, &loop_device, &decrypted_image);
+        if (r < 0)
+                return r;
+
+        r = context_make_online(&context, loop_device ? loop_device->node : NULL);
+        if (r < 0)
+                return r;
+
+        r = context_apply(context, version, &applied);
+        if (r < 0)
+                return r;
+
+        if (r > 0 && arg_reboot) {
+                assert(applied);
+                assert(booted_version);
+
+                if (strverscmp_improved(applied->version, booted_version) > 0) {
+                        log_notice("Newly installed version is newer than booted version, rebooting.");
+                        return reboot_now();
+                }
+
+                log_info("Booted version is newer or identical to newly installed version, not rebooting.");
+        }
+
+        return 0;
+}
+
+static int verb_pending_or_reboot(int argc, char **argv, void *userdata) {
+        _cleanup_(context_freep) Context* context = NULL;
+        _cleanup_free_ char *booted_version = NULL;
+        int r;
+
+        assert(argc == 1);
+
+        if (arg_image || arg_root)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "The --root=/--image switches may not be combined with the '%s' operation.", argv[0]);
+
+        r = context_make_offline(&context, NULL);
+        if (r < 0)
+                return r;
+
+        log_info("Determining installed update sets…");
+
+        r = context_discover_update_sets_by_flag(context, UPDATE_INSTALLED);
+        if (r < 0)
+                return r;
+        if (!context->newest_installed)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "Couldn't find any suitable installed versions.");
+
+        r = parse_os_release(arg_root, "IMAGE_VERSION", &booted_version);
+        if (r < 0) /* yes, arg_root is NULL here, but we have to pass something, and it's a lot more readable
+                    * if we see what the first argument is about */
+                return log_error_errno(r, "Failed to parse /etc/os-release: %m");
+        if (!booted_version)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "/etc/os-release lacks IMAGE_VERSION= field.");
+
+        r = strverscmp_improved(context->newest_installed->version, booted_version);
+        if (r > 0) {
+                log_notice("Newest installed version '%s' is newer than booted version '%s'.%s",
+                           context->newest_installed->version, booted_version,
+                           streq(argv[0], "pending") ? " Reboot recommended." : "");
+
+                if (streq(argv[0], "reboot"))
+                        return reboot_now();
+
+                return EXIT_SUCCESS;
+        } else if (r == 0)
+                log_info("Newest installed version '%s' matches booted version '%s'.",
+                         context->newest_installed->version, booted_version);
+        else
+                log_warning("Newest installed version '%s' is older than booted version '%s'.",
+                            context->newest_installed->version, booted_version);
+
+        if (streq(argv[0], "pending")) /* When called as 'pending' tell the caller via failure exit code that there's nothing newer installed */
+                return EXIT_FAILURE;
+
+        return EXIT_SUCCESS;
+}
+
+static int component_name_valid(const char *c) {
+        _cleanup_free_ char *j = NULL;
+
+        /* See if the specified string enclosed in the directory prefix+suffix would be a valid file name */
+
+        if (isempty(c))
+                return false;
+
+        if (string_has_cc(c, NULL))
+                return false;
+
+        if (!utf8_is_valid(c))
+                return false;
+
+        j = strjoin("sysupdate.", c, ".d");
+        if (!j)
+                return -ENOMEM;
+
+        return filename_is_valid(j);
+}
+
+static int verb_components(int argc, char **argv, void *userdata) {
+        _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_(set_freep) Set *names = NULL;
+        _cleanup_free_ char **z = NULL; /* We use simple free() rather than strv_free() here, since set_free() will free the strings for us */
+        char **l = CONF_PATHS_STRV("");
+        bool has_default_component = false;
+        int r;
+
+        assert(argc <= 1);
+
+        r = process_image(/* ro= */ false, &mounted_dir, &loop_device, &decrypted_image);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, l) {
+                _cleanup_closedir_ DIR *d = NULL;
+                _cleanup_free_ char *p = NULL;
+
+                r = chase_symlinks_and_opendir(*i, arg_root, CHASE_PREFIX_ROOT, &p, &d);
+                if (r == -ENOENT)
+                        continue;
+                if (r < 0)
+                        return log_error_errno(r, "Failed to open directory '%s': %m", *i);
+
+                for (;;) {
+                        _cleanup_free_ char *n = NULL;
+                        struct dirent *de;
+                        const char *e, *a;
+
+                        de = readdir_ensure_type(d);
+                        if (!de) {
+                                if (errno != 0)
+                                        return log_error_errno(errno, "Failed to enumerate directory '%s': %m", p);
+
+                                break;
+                        }
+
+                        if (de->d_type != DT_DIR)
+                                continue;
+
+                        if (dot_or_dot_dot(de->d_name))
+                                continue;
+
+                        if (streq(de->d_name, "sysupdate.d")) {
+                                has_default_component = true;
+                                continue;
+                        }
+
+                        e = startswith(de->d_name, "sysupdate.");
+                        if (!e)
+                                continue;
+
+                        a = endswith(e, ".d");
+                        if (!a)
+                                continue;
+
+                        n = strndup(e, a - e);
+                        if (!n)
+                                return log_oom();
+
+                        r = component_name_valid(n);
+                        if (r < 0)
+                                return log_error_errno(r, "Unable to validate component name: %m");
+                        if (r == 0)
+                                continue;
+
+                        r = set_ensure_consume(&names, &string_hash_ops_free, TAKE_PTR(n));
+                        if (r < 0 && r != -EEXIST)
+                                return log_error_errno(r, "Failed to add component to set: %m");
+                }
+        }
+
+        if (!has_default_component && set_isempty(names)) {
+                log_info("No components defined.");
+                return 0;
+        }
+
+        z = set_get_strv(names);
+        if (!z)
+                return log_oom();
+
+        strv_sort(z);
+
+        if (has_default_component)
+                printf("%s<default>%s\n",
+                       ansi_highlight(), ansi_normal());
+
+        STRV_FOREACH(i, z)
+                puts(*i);
+
+        return 0;
+}
+
+static int verb_help(int argc, char **argv, void *userdata) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("systemd-sysupdate", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s [OPTIONS...] [VERSION]\n"
+               "\n%5$sUpdate OS images.%6$s\n"
+               "\n%3$sCommands:%4$s\n"
+               "  list [VERSION]          Show installed and available versions\n"
+               "  check-new               Check if there's a new version available\n"
+               "  update [VERSION]        Install new version now\n"
+               "  vacuum                  Make room, by deleting old versions\n"
+               "  pending                 Report whether a newer version is installed than\n"
+               "                          currently booted\n"
+               "  reboot                  Reboot if a newer version is installed than booted\n"
+               "  components              Show list of components\n"
+               "  -h --help               Show this help\n"
+               "     --version            Show package version\n"
+               "\n%3$sOptions:%4$s\n"
+               "  -C --component=NAME     Select component to update\n"
+               "     --definitions=DIR    Find transfer definitions in specified directory\n"
+               "     --root=PATH          Operate relative to root path\n"
+               "     --image=PATH         Operate relative to image file\n"
+               "  -m --instances-max=INT  How many instances to maintain\n"
+               "     --sync=BOOL          Controls whether to sync data to disk\n"
+               "     --verify=BOOL        Force signature verification on or off\n"
+               "     --reboot             Reboot after updating to newer version\n"
+               "     --no-pager           Do not pipe output into a pager\n"
+               "     --no-legend          Do not show the headers and footers\n"
+               "     --json=pretty|short|off\n"
+               "                          Generate JSON output\n"
+               "\nSee the %2$s for details.\n"
+               , program_invocation_short_name
+               , link
+               , ansi_underline(), ansi_normal()
+               , ansi_highlight(), ansi_normal()
+        );
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_NO_PAGER,
+                ARG_NO_LEGEND,
+                ARG_SYNC,
+                ARG_DEFINITIONS,
+                ARG_JSON,
+                ARG_ROOT,
+                ARG_IMAGE,
+                ARG_REBOOT,
+                ARG_VERIFY,
+        };
+
+        static const struct option options[] = {
+                { "help",              no_argument,       NULL, 'h'                   },
+                { "version",           no_argument,       NULL, ARG_VERSION           },
+                { "no-pager",          no_argument,       NULL, ARG_NO_PAGER          },
+                { "no-legend",         no_argument,       NULL, ARG_NO_LEGEND         },
+                { "definitions",       required_argument, NULL, ARG_DEFINITIONS       },
+                { "instances-max",     required_argument, NULL, 'm'                   },
+                { "sync",              required_argument, NULL, ARG_SYNC              },
+                { "json",              required_argument, NULL, ARG_JSON              },
+                { "root",              required_argument, NULL, ARG_ROOT              },
+                { "image",             required_argument, NULL, ARG_IMAGE             },
+                { "reboot",            no_argument,       NULL, ARG_REBOOT            },
+                { "component",         required_argument, NULL, 'C'                   },
+                { "verify",            required_argument, NULL, ARG_VERIFY            },
+                {}
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "hm:C:", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        return verb_help(0, NULL, NULL);
+
+                case ARG_VERSION:
+                        return version();
+
+                case ARG_NO_PAGER:
+                        arg_pager_flags |= PAGER_DISABLE;
+                        break;
+
+                case ARG_NO_LEGEND:
+                        arg_legend = false;
+                        break;
+
+                case 'm':
+                        r = safe_atou64(optarg, &arg_instances_max);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --instances-max= parameter: %s", optarg);
+
+                        break;
+
+                case ARG_SYNC:
+                        r = parse_boolean_argument("--sync=", optarg, &arg_sync);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_DEFINITIONS:
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_definitions);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_JSON:
+                        r = parse_json_argument(optarg, &arg_json_format_flags);
+                        if (r <= 0)
+                                return r;
+
+                        break;
+
+                case 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;
+
+                case ARG_REBOOT:
+                        arg_reboot = true;
+                        break;
+
+                case 'C':
+                        if (isempty(optarg)) {
+                                arg_component = mfree(arg_component);
+                                break;
+                        }
+
+                        r = component_name_valid(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to determine if component name is valid: %m");
+                        if (r == 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Component name invalid: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_component, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case ARG_VERIFY: {
+                        bool b;
+
+                        r = parse_boolean_argument("--verify=", optarg, &b);
+                        if (r < 0)
+                                return r;
+
+                        arg_verify = b;
+                        break;
+                }
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        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.");
+
+        if ((arg_image || arg_root) && arg_reboot)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --reboot switch may not be combined with --root= or --image=.");
+
+        if (arg_definitions && arg_component)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --definitions= and --component= switches may not be combined.");
+
+        return 1;
+}
+
+static int sysupdate_main(int argc, char *argv[]) {
+
+        static const Verb verbs[] = {
+                { "list",       VERB_ANY, 2, VERB_DEFAULT, verb_list              },
+                { "components", VERB_ANY, 1, 0,            verb_components        },
+                { "check-new",  VERB_ANY, 1, 0,            verb_check_new         },
+                { "update",     VERB_ANY, 2, 0,            verb_update               },
+                { "vacuum",     VERB_ANY, 1, 0,            verb_vacuum            },
+                { "reboot",     1,        1, 0,            verb_pending_or_reboot },
+                { "pending",    1,        1, 0,            verb_pending_or_reboot },
+                { "help",       VERB_ANY, 1, 0,            verb_help              },
+                {}
+        };
+
+        return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+static int run(int argc, char *argv[]) {
+        int r;
+
+        log_setup();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        return sysupdate_main(argc, argv);
+}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/sysupdate/sysupdate.h b/src/sysupdate/sysupdate.h
new file mode 100644 (file)
index 0000000..6d387b7
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+extern bool arg_sync;
+extern uint64_t arg_instances_max;
+extern char *arg_root;
+
+static inline const char* import_binary_path(void) {
+        return secure_getenv("SYSTEMD_IMPORT_PATH") ?: SYSTEMD_IMPORT_PATH;
+}
+
+static inline const char* import_fs_binary_path(void) {
+        return secure_getenv("SYSTEMD_IMPORT_FS_PATH") ?: SYSTEMD_IMPORT_FS_PATH;
+}
+
+static inline const char *pull_binary_path(void) {
+        return secure_getenv("SYSTEMD_PULL_PATH") ?: SYSTEMD_PULL_PATH;
+}
index 4962bf7233392d24a6fec4319f686452fba3f2a9..59b76c8620aa8e693d2284fc2be57f75bd89db6d 100644 (file)
@@ -312,7 +312,6 @@ static int putgrent_with_members(const struct group *gr, FILE *group) {
         if (a) {
                 _cleanup_strv_free_ char **l = NULL;
                 bool added = false;
-                char **i;
 
                 l = strv_copy(gr->gr_mem);
                 if (!l)
@@ -357,7 +356,6 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
         if (a) {
                 _cleanup_strv_free_ char **l = NULL;
                 bool added = false;
-                char **i;
 
                 l = strv_copy(sg->sg_mem);
                 if (!l)
@@ -1406,8 +1404,6 @@ static int add_implicit(void) {
 
         /* Implicitly create additional users and groups, if they were listed in "m" lines */
         ORDERED_HASHMAP_FOREACH_KEY(l, g, members) {
-                char **m;
-
                 STRV_FOREACH(m, l)
                         if (!ordered_hashmap_get(users, *m)) {
                                 _cleanup_(item_freep) Item *j = NULL;
@@ -1977,7 +1973,6 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 static int parse_arguments(char **args) {
-        char **arg;
         unsigned pos = 1;
         int r;
 
@@ -1999,7 +1994,6 @@ static int parse_arguments(char **args) {
 static int read_config_files(char **args) {
         _cleanup_strv_free_ char **files = NULL;
         _cleanup_free_ char *p = NULL;
-        char **f;
         int r;
 
         r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, &p);
index e9976540b5a45a08206df8f143819201273cdbb4..a81df6600ba843a28f8cebd4e700c15ac843f6f3 100644 (file)
@@ -103,7 +103,6 @@ static int generate_unit_file(SysvStub *s) {
         _cleanup_free_ char *path_escaped = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         const char *unit;
-        char **p;
         int r;
 
         assert(s);
@@ -707,7 +706,6 @@ static int acquire_search_path(const char *def, const char *envvar, char ***ret)
 
 static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
         _cleanup_strv_free_ char **sysvinit_path = NULL;
-        char **path;
         int r;
 
         assert(lp);
@@ -791,7 +789,6 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
         Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
         _cleanup_strv_free_ char **sysvrcnd_path = NULL;
         SysvStub *service;
-        char **p;
         int r;
 
         assert(lp);
index 1de502db8bf0e7dea9c1ed85dc6503a800118ddb..297a65d9afc54f2f743b4bed4044b64cc138debb 100644 (file)
@@ -613,7 +613,7 @@ tests += [
                'nss-test-util.h'),
          [],
          [libdl],
-         [], 'ENABLE_NSS'],
+         [], 'ENABLE_NSS', 'timeout=120'],
 
         [files('test-nss-users.c',
                'nss-test-util.c',
index d73f487ff65dca1225331061715f379b11a62a80..377019f19397da3777920f8dd1c4271891543ac6 100644 (file)
@@ -133,8 +133,6 @@ static int bpf_foreign_test_to_string(enum bpf_attach_type attach_type, const ch
 }
 
 static char **unlink_paths_and_free(char **paths) {
-        char **i;
-
         STRV_FOREACH(i, paths)
                 (void) unlink(*i);
 
index 60d1789a0a2d3987a6873dc006fcc2cb5eba98a4..480865d0bc3e09d1d42fc1c1d12c829954d47863 100644 (file)
@@ -16,7 +16,6 @@ static int test_restrict_filesystems(Manager *m, const char *unit_name, const ch
         _cleanup_free_ char *exec_start = NULL;
         _cleanup_(unit_freep) Unit *u = NULL;
         ExecContext *ec = NULL;
-        char **allow_filesystem;
         int cld_code, r;
 
         assert_se(u = unit_new(m, sizeof(Service)));
index 2c17c9d83daf4537cd008016a0515675db8bf640..71836a307e44a64b9252bb893f7b517577386d0e 100644 (file)
@@ -83,7 +83,6 @@ TEST(copy_tree) {
         char **hardlinks = STRV_MAKE("hlink", "file",
                                      "hlink2", "dir1/file");
         const char *unixsockp;
-        char **p, **ll;
         struct stat st;
         int xattr_worked = -1; /* xattr support is optional in temporary directories, hence use it if we can,
                                 * but don't fail if we can't */
index 99ed0d2a050f962a5e34437593ca61d5d726b96a..cc37d96327ba6fd4b5b3daf7c46f1a5e66faddf5 100644 (file)
@@ -412,7 +412,6 @@ TEST(getenv_steal_erase) {
         r = safe_fork("(sd-getenvstealerase)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
         if (r == 0) {
                 _cleanup_strv_free_ char **l = NULL;
-                char **e;
 
                 /* child */
 
index d6a9de32ad28a39961239b4ccccdc6e18ffb4ece..e8b04e879f5937b84f8943cfbcd9640b2be8c787 100644 (file)
@@ -212,7 +212,7 @@ static int gather_stdout_one(int fd, void *arg) {
         return 0;
 }
 static int gather_stdout_two(int fd, void *arg) {
-        char ***s = arg, **t;
+        char ***s = arg;
 
         STRV_FOREACH(t, *s)
                 assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
@@ -285,7 +285,6 @@ TEST(stdout_gathering) {
 TEST(environment_gathering) {
         _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
         const char *name, *name2, *name3, *old;
-        char **p;
         int r;
 
         char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
index 8e18bbdb013911c857875ffbaa9c8c31d9ad057e..a2758b3f8418e2d1ecda0538b45359373e4ad49c 100644 (file)
@@ -610,7 +610,6 @@ static int find_libraries(const char *exec, char ***ret) {
         _cleanup_strv_free_ char **v = NULL;
         assert_se(strv_split_newlines_full(&v, result, 0) >= 0);
 
-        char **q;
         STRV_FOREACH(q, v) {
                 _cleanup_free_ char *word = NULL;
                 const char *p = *q;
@@ -678,7 +677,6 @@ static void test_exec_mount_apivfs(Manager *m) {
         assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n"));
         assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n"));
 
-        char **p;
         STRV_FOREACH(p, libraries)
                 assert_se(strextend(&data, "BindReadOnlyPaths=", *p, "\n"));
 
index 087d76f7609bc4b8b5b5a026dd35211aee015ddd..b23aadadcc654ea54c02b776609ad21c39d6e197 100644 (file)
@@ -35,7 +35,6 @@ TEST(parse_env_file) {
                         *six = NULL, *seven = NULL, *eight = NULL, *nine = NULL, *ten = NULL,
                         *eleven = NULL, *twelve = NULL, *thirteen = NULL;
         _cleanup_strv_free_ char **a = NULL, **b = NULL;
-        char **i;
         unsigned k;
         int r;
 
@@ -172,7 +171,6 @@ TEST(parse_multiline_env_file) {
                 p[] = "/tmp/test-fileio-out-XXXXXX";
         FILE *f;
         _cleanup_strv_free_ char **a = NULL, **b = NULL;
-        char **i;
         int r;
 
         assert_se(fmkostemp_safe(t, "w", &f) == 0);
@@ -222,7 +220,6 @@ TEST(merge_env_file) {
         _cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_strv_free_ char **a = NULL;
-        char **i;
         int r;
 
         assert_se(fmkostemp_safe(t, "w", &f) == 0);
@@ -286,7 +283,6 @@ TEST(merge_env_file_invalid) {
         _cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_strv_free_ char **a = NULL;
-        char **i;
         int r;
 
         assert_se(fmkostemp_safe(t, "w", &f) == 0);
@@ -487,7 +483,6 @@ TEST(load_env_file_pairs) {
         int fd, r;
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_strv_free_ char **l = NULL;
-        char **k, **v;
 
         fd = mkostemp_safe(fn);
         assert_se(fd >= 0);
index f53a3ebf59c3a1ac08ea5223fd9cc3aa43c3d2a3..f24e97b4ccc956479ee94d21ffa4fc4e499ae931 100644 (file)
@@ -692,7 +692,6 @@ TEST(rename_noreplace) {
 
         _cleanup_(rm_rf_physical_and_freep) char *z = NULL;
         const char *j = NULL;
-        char **a, **b;
 
         if (arg_test_dir)
                 j = strjoina(arg_test_dir, "/testXXXXXX");
@@ -971,6 +970,74 @@ TEST(open_mkdir_at) {
         assert_se(subsubdir_fd >= 0);
 }
 
+TEST(openat_report_new) {
+        _cleanup_free_ char *j = NULL;
+        _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
+        _cleanup_close_ int fd = -1;
+        bool b;
+
+        assert_se(mkdtemp_malloc(NULL, &d) >= 0);
+
+        j = path_join(d, "test");
+        assert_se(j);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
+        assert_se(fd >= 0);
+        fd = safe_close(fd);
+        assert_se(b);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
+        assert_se(fd >= 0);
+        fd = safe_close(fd);
+        assert_se(!b);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
+        assert_se(fd >= 0);
+        fd = safe_close(fd);
+        assert_se(!b);
+
+        assert_se(unlink(j) >= 0);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
+        assert_se(fd >= 0);
+        fd = safe_close(fd);
+        assert_se(b);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
+        assert_se(fd >= 0);
+        fd = safe_close(fd);
+        assert_se(!b);
+
+        assert_se(unlink(j) >= 0);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, NULL);
+        assert_se(fd >= 0);
+        fd = safe_close(fd);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b);
+        assert_se(fd >= 0);
+        fd = safe_close(fd);
+        assert_se(!b);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR, 0666, &b);
+        assert_se(fd >= 0);
+        fd = safe_close(fd);
+        assert_se(!b);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT|O_EXCL, 0666, &b);
+        assert_se(fd == -EEXIST);
+
+        assert_se(unlink(j) >= 0);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR, 0666, &b);
+        assert_se(fd == -ENOENT);
+
+        fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT|O_EXCL, 0666, &b);
+        assert_se(fd >= 0);
+        fd = safe_close(fd);
+        assert_se(b);
+}
+
 static int intro(void) {
         arg_test_dir = saved_argv[1];
         return EXIT_SUCCESS;
index f15cff4794021d288655f097a2dc6f9ded5f8899..0a166c6e1f71642fd4292e3dd84b55fd4df8e8e6 100644 (file)
@@ -7,7 +7,6 @@
 
 int main(int argc, char *argv[]) {
         _cleanup_strv_free_ char **maps = NULL;
-        char **m;
         int r;
 
         log_show_color(true);
index 25cc55a998de8ed5fde60708d2bd1db83280eb61..78bbad8e4e001f4c11cfba41a24dcc2ff3c2febf 100644 (file)
@@ -11,7 +11,6 @@ int main(int argc, const char *argv[]) {
         LIST_HEAD(list_item, head);
         LIST_HEAD(list_item, head2);
         list_item items[4];
-        list_item *cursor;
 
         LIST_HEAD_INIT(head);
         LIST_HEAD_INIT(head2);
@@ -57,6 +56,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[2].item_prev == &items[3]);
         assert_se(items[3].item_prev == NULL);
 
+        list_item *cursor;
         LIST_FIND_HEAD(item, &items[0], cursor);
         assert_se(cursor == &items[3]);
 
index 9bbc5917a1eadc604e79b34f9d027de32b411974..6ec3f7f00bdc0a73e248856ff4ceb0f7e959efff 100644 (file)
@@ -10,7 +10,6 @@
 
 TEST(get_locales) {
         _cleanup_strv_free_ char **locales = NULL;
-        char **p;
         int r;
 
         r = get_locales(&locales);
@@ -58,7 +57,6 @@ TEST(locale_is_installed) {
 
 TEST(keymaps) {
         _cleanup_strv_free_ char **kmaps = NULL;
-        char **p;
         int r;
 
         assert_se(!keymap_is_valid(""));
index 3ae38acb17e72a3c02e41cc0ea20fc5de30843c1..69225da8b92ff04b0411579da811c77adda6e429 100644 (file)
@@ -67,8 +67,6 @@ static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) {
 }
 
 static void print_struct_hostent(struct hostent *host, const char *canon) {
-        char **s;
-
         log_info("        \"%s\"", host->h_name);
         STRV_FOREACH(s, host->h_aliases)
                 log_info("        alias \"%s\"", *s);
@@ -376,7 +374,6 @@ static int test_one_module(const char *dir,
         if (!handle)
                 return -EINVAL;
 
-        char **name;
         STRV_FOREACH(name, names)
                 test_byname(handle, module, *name);
 
@@ -424,7 +421,6 @@ static int parse_argv(int argc, char **argv,
         assert_se(modules);
 
         if (argc > 2) {
-                char **name;
                 int family;
                 union in_addr_union address;
 
@@ -463,7 +459,6 @@ static int run(int argc, char **argv) {
         _cleanup_strv_free_ char **modules = NULL, **names = NULL;
         _cleanup_free_ struct local_address *addresses = NULL;
         int n_addresses = 0;
-        char **module;
         int r;
 
         test_setup_logging(LOG_INFO);
index 88e2764e99d5aa5be81cf4f651ae12ae507ad5ff..5178779d54a4767a52b74a5031430acb65c1696c 100644 (file)
@@ -170,7 +170,6 @@ static int test_one_module(const char *dir,
         if (!handle)
                 return -EINVAL;
 
-        char **name;
         STRV_FOREACH(name, names)
                 test_byname(handle, module, *name);
 
@@ -235,7 +234,6 @@ static int parse_argv(int argc, char **argv,
 static int run(int argc, char **argv) {
         _cleanup_free_ char *dir = NULL;
         _cleanup_strv_free_ char **modules = NULL, **names = NULL;
-        char **module;
         int r;
 
         test_setup_logging(LOG_INFO);
index a1ae2362393db54f12d09608b7b204666ccf49f6..dfb755054388c17cc47217ca58b12e590b977a9b 100644 (file)
@@ -41,7 +41,7 @@ TEST(paths) {
 
 TEST(user_and_global_paths) {
         _cleanup_(lookup_paths_free) LookupPaths lp_global = {}, lp_user = {};
-        char **u, **g, **p;
+        char **u, **g;
         unsigned k = 0;
 
         assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0);
@@ -78,7 +78,6 @@ static void test_generator_binary_paths_one(UnitFileScope scope) {
         _cleanup_strv_free_ char **env_gp_with_env = NULL;
         char *systemd_generator_path = NULL;
         char *systemd_env_generator_path = NULL;
-        char **dir;
 
         assert_se(mkdtemp_malloc("/tmp/test-path-lookup.XXXXXXX", &tmp) >= 0);
 
index b9c4ef41267ec5b7e3429fb5e84157dd0b02a433..d40febef5f726ad0eab083601bdc28cf91454f5e 100644 (file)
@@ -481,7 +481,6 @@ TEST(path_strv_resolve) {
         char tmp_dir[] = "/tmp/test-path-util-XXXXXX";
         _cleanup_strv_free_ char **search_dirs = NULL;
         _cleanup_strv_free_ char **absolute_dirs = NULL;
-        char **d;
 
         assert_se(mkdtemp(tmp_dir) != NULL);
 
index 529487d1ad84a164373335ed030cca6e86a60456..2690dc0aa4fe3e3de14a06c8d4effa9cf83adb18 100644 (file)
@@ -24,7 +24,6 @@ typedef void (*test_function_t)(Manager *m);
 static int setup_test(Manager **m) {
         char **tests_path = STRV_MAKE("exists", "existsglobFOOBAR", "changed", "modified", "unit",
                                       "directorynotempty", "makedirectory");
-        char **test_path;
         Manager *tmp = NULL;
         int r;
 
index 10a8a4a63f44caebd52c39574c5e216476c6a11f..4f23e3bb695b9c0d77b6b7368815b98d7fc36c0f 100644 (file)
@@ -32,7 +32,6 @@ TEST(sd_path_lookup) {
 TEST(sd_path_lookup_strv) {
         for (uint64_t i = 0; i < _SD_PATH_MAX; i++) {
                 _cleanup_strv_free_ char **t = NULL, **s = NULL;
-                char **item;
                 int r;
 
                 r = sd_path_lookup_strv(i, NULL, &t);
index f4197aaf3c02469696165c94e7931853ff1b0d87..62300c4ae43913a6f50a40a53e6f1dd3e8ba39a9 100644 (file)
@@ -13,7 +13,7 @@
 #include "virt.h"
 
 static int find_netcat_executable(char **ret_path) {
-        char **candidates = STRV_MAKE("ncat", "nc", "netcat"), **c;
+        char **candidates = STRV_MAKE("ncat", "nc", "netcat");
         int r = 0;
 
         STRV_FOREACH(c, candidates) {
@@ -34,9 +34,7 @@ static int test_socket_bind(
                 char **deny_rules) {
         _cleanup_free_ char *exec_start = NULL;
         _cleanup_(unit_freep) Unit *u = NULL;
-        CGroupSocketBindItem *bi;
         CGroupContext *cc = NULL;
-        char **rule;
         int cld_code, r;
 
         assert_se(u = unit_new(m, sizeof(Service)));
index 071b391361839d0dfc327d1ffcaaa117beabe0c9..93b674baabfee24b227457816cd9f6af14f15074 100644 (file)
@@ -865,7 +865,6 @@ TEST(strverscmp_improved) {
                 "124",
                 NULL,
         };
-        const char * const *p, * const *q;
 
         STRV_FOREACH(p, versions)
                 STRV_FOREACH(q, p + 1)
index 94581fc8320294d641e42da9304f846b4bf3df59..edb782eb0d74a612a99d355525c2de1cd2006aa8 100644 (file)
@@ -204,7 +204,6 @@ static void test_strv_unquote_one(const char *quoted, char **list) {
         _cleanup_strv_free_ char **s;
         _cleanup_free_ char *j;
         unsigned i = 0;
-        char **t;
         int r;
 
         log_info("/* %s */", __func__);
@@ -446,7 +445,6 @@ TEST(strv_split_colon_pairs) {
 
 TEST(strv_split_newlines) {
         unsigned i = 0;
-        char **s;
         _cleanup_strv_free_ char **l = NULL;
         const char str[] = "one\ntwo\nthree";
 
@@ -619,7 +617,6 @@ TEST(strv_extendf) {
 TEST(strv_foreach) {
         _cleanup_strv_free_ char **a;
         unsigned i = 0;
-        char **check;
 
         a = strv_new("one", "two", "three");
         assert_se(a);
@@ -631,7 +628,6 @@ TEST(strv_foreach) {
 TEST(strv_foreach_backwards) {
         _cleanup_strv_free_ char **a;
         unsigned i = 2;
-        char **check;
 
         a = strv_new("one", "two", "three");
 
@@ -643,13 +639,17 @@ TEST(strv_foreach_backwards) {
         STRV_FOREACH_BACKWARDS(check, (char**) NULL)
                 assert_not_reached();
 
-        STRV_FOREACH_BACKWARDS(check, (char**) { NULL })
+        STRV_FOREACH_BACKWARDS(check, STRV_MAKE_EMPTY)
                 assert_not_reached();
+
+        unsigned count = 0;
+        STRV_FOREACH_BACKWARDS(check, STRV_MAKE("ONE"))
+                count++;
+        assert_se(count == 1);
 }
 
 TEST(strv_foreach_pair) {
         _cleanup_strv_free_ char **a = NULL;
-        char **x, **y;
 
         a = strv_new("pair_one",   "pair_one",
                      "pair_two",   "pair_two",
index 8bd3c26152f4a85b257bf1c2038ba286a48ff1a4..02180dc4be8c67b4c6e4bf91be50fd8e4f4cedea 100644 (file)
@@ -27,7 +27,6 @@ static const char* const cases[] = {
 };
 
 TEST(sysctl_normalize) {
-        const char **s, **expected;
         STRV_FOREACH_PAIR(s, expected, (const char**) cases) {
                 _cleanup_free_ char *t;
 
index 799d271a443c8ae13074f571befbacefa4d7181d..15f4a0c169b04933af47d6bf656390cebaa7a19d 100644 (file)
@@ -258,7 +258,6 @@ TEST(timezone_is_valid) {
 TEST(get_timezones) {
         _cleanup_strv_free_ char **zones = NULL;
         int r;
-        char **zone;
 
         r = get_timezones(&zones);
         assert_se(r == 0);
index cc08a4ae4ba90aeb7de93664d1bbb939a6dd1c97..9b05a191e6305ff4f5bba4e137b6dbc85db1a882 100644 (file)
@@ -55,7 +55,6 @@ TEST(unit_file_build_name_map) {
         if (r == 0)
                 log_debug("Cache rebuild skipped based on mtime.");
 
-        char **id;
         STRV_FOREACH(id, ids) {
                  const char *fragment, *name;
                  _cleanup_set_free_free_ Set *names = NULL;
index 1ac3dc102d2335f4f9b464cf48c3b71b66504d00..c28615cf5e634826160af70b16deb528c2f80c0d 100644 (file)
@@ -133,7 +133,6 @@ static void context_clear(Context *c) {
 
 static int context_add_ntp_service(Context *c, const char *s, const char *source) {
         _cleanup_(unit_status_info_freep) UnitStatusInfo *unit = NULL;
-        UnitStatusInfo *u;
 
         assert(c);
         assert(s);
@@ -197,7 +196,6 @@ static int context_parse_ntp_services_from_environment(Context *c) {
 
 static int context_parse_ntp_services_from_disk(Context *c) {
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         int r;
 
         r = conf_files_list_strv(&files, ".list", NULL, CONF_FILES_FILTER_MASKED, UNIT_LIST_DIRS);
@@ -251,7 +249,6 @@ static int context_parse_ntp_services(Context *c) {
 }
 
 static int context_ntp_service_is_active(Context *c) {
-        UnitStatusInfo *info;
         int count = 0;
 
         assert(c);
@@ -265,7 +262,6 @@ static int context_ntp_service_is_active(Context *c) {
 }
 
 static int context_ntp_service_exists(Context *c) {
-        UnitStatusInfo *info;
         int count = 0;
 
         assert(c);
@@ -410,7 +406,6 @@ static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m)
                 { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
                 {}
         };
-        UnitStatusInfo *u;
         int r;
 
         assert(c);
@@ -453,7 +448,6 @@ static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m)
 
 static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         Context *c = userdata;
-        UnitStatusInfo *u;
         const char *path;
         unsigned n = 0;
         int r;
@@ -925,7 +919,6 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
         _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
         sd_bus *bus = sd_bus_message_get_bus(m);
         Context *c = userdata;
-        UnitStatusInfo *u;
         const UnitStatusInfo *selected = NULL;
         int enable, interactive, q, r;
 
index 1a14564e04624b6660499b62acd9ea96e1d67256..b738dfd3cc2a38fc0ab2310143c9874a34879e40 100644 (file)
@@ -23,7 +23,7 @@ static int property_get_servers(
                 void *userdata,
                 sd_bus_error *error) {
 
-        ServerName *p, **s = userdata;
+        ServerName **s = userdata;
         int r;
 
         assert(s);
index 8f34441e156eff5a68cbb2ccc8d1a111f0a2a3b3..21fe7daec43c466bde0d1d4da648ceff1dab04cd 100644 (file)
@@ -24,7 +24,6 @@ int manager_parse_server_string(Manager *m, ServerType type, const char *string)
         for (;;) {
                 _cleanup_free_ char *word = NULL;
                 bool found = false;
-                ServerName *n;
 
                 r = extract_first_word(&string, &word, NULL, 0);
                 if (r < 0)
index 918da195d811083cbeaf0c2dbeefaba84a22ccd8..7392edbe27b2b6af669a510929d0b4be5afbabcb 100644 (file)
 #include <sys/types.h>
 
 #include "sd-daemon.h"
+#include "sd-messages.h"
 
 #include "alloc-util.h"
 #include "dns-domain.h"
+#include "event-util.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "fs-util.h"
@@ -28,6 +30,7 @@
 #include "time-util.h"
 #include "timesyncd-conf.h"
 #include "timesyncd-manager.h"
+#include "user-util.h"
 #include "util.h"
 
 #ifndef ADJ_SETOFFSET
@@ -59,7 +62,7 @@ static int manager_arm_timer(Manager *m, usec_t next);
 static int manager_clock_watch_setup(Manager *m);
 static int manager_listen_setup(Manager *m);
 static void manager_listen_stop(Manager *m);
-static int manager_save_time_and_rearm(Manager *m);
+static int manager_save_time_and_rearm(Manager *m, usec_t t);
 
 static double ntp_ts_short_to_d(const struct ntp_ts_short *ts) {
         return be16toh(ts->sec) + (be16toh(ts->frac) / 65536.0);
@@ -229,14 +232,9 @@ static int manager_clock_watch_setup(Manager *m) {
 
         assert(m);
 
-        m->event_clock_watch = sd_event_source_unref(m->event_clock_watch);
-        safe_close(m->clock_watch_fd);
+        m->event_clock_watch = sd_event_source_disable_unref(m->event_clock_watch);
 
-        m->clock_watch_fd = time_change_fd();
-        if (m->clock_watch_fd < 0)
-                return log_error_errno(m->clock_watch_fd, "Failed to create timerfd: %m");
-
-        r = sd_event_add_io(m->event, &m->event_clock_watch, m->clock_watch_fd, EPOLLIN, manager_clock_watch, m);
+        r = event_add_time_change(m->event, &m->event_clock_watch, manager_clock_watch, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to create clock watch event source: %m");
 
@@ -244,31 +242,29 @@ static int manager_clock_watch_setup(Manager *m) {
 }
 
 static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
-        struct timex tmx = {};
-        int r;
+        struct timex tmx;
 
         assert(m);
 
-        /*
-         * For small deltas, tell the kernel to gradually adjust the system
-         * clock to the NTP time, larger deltas are just directly set.
-         */
+        /* For small deltas, tell the kernel to gradually adjust the system clock to the NTP time, larger
+         * deltas are just directly set. */
         if (fabs(offset) < NTP_MAX_ADJUST) {
-                tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR;
-                tmx.status = STA_PLL;
-                tmx.offset = offset * NSEC_PER_SEC;
-                tmx.constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4;
-                tmx.maxerror = 0;
-                tmx.esterror = 0;
+                tmx = (struct timex) {
+                        .modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR,
+                        .status = STA_PLL,
+                        .offset = offset * NSEC_PER_SEC,
+                        .constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4,
+                };
+
                 log_debug("  adjust (slew): %+.3f sec", offset);
         } else {
-                tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET | ADJ_MAXERROR | ADJ_ESTERROR;
+                tmx = (struct timex) {
+                        .modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET | ADJ_MAXERROR | ADJ_ESTERROR,
 
-                /* ADJ_NANO uses nanoseconds in the microseconds field */
-                tmx.time.tv_sec = (long)offset;
-                tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC;
-                tmx.maxerror = 0;
-                tmx.esterror = 0;
+                        /* ADJ_NANO uses nanoseconds in the microseconds field */
+                        .time.tv_sec = (long)offset,
+                        .time.tv_usec = (offset - (double) (long) offset) * NSEC_PER_SEC,
+                };
 
                 /* the kernel expects -0.3s as {-1, 7000.000.000} */
                 if (tmx.time.tv_usec < 0) {
@@ -280,14 +276,11 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
                 log_debug("  adjust (jump): %+.3f sec", offset);
         }
 
-        /*
-         * An unset STA_UNSYNC will enable the kernel's 11-minute mode,
-         * which syncs the system time periodically to the RTC.
+        /* An unset STA_UNSYNC will enable the kernel's 11-minute mode, which syncs the system time
+         * periodically to the RTC.
          *
-         * In case the RTC runs in local time, never touch the RTC,
-         * we have no way to properly handle daylight saving changes and
-         * mobile devices moving between time zones.
-         */
+         * In case the RTC runs in local time, never touch the RTC, we have no way to properly handle
+         * daylight saving changes and mobile devices moving between time zones. */
         if (m->rtc_local_time)
                 tmx.status |= STA_UNSYNC;
 
@@ -300,17 +293,9 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
                 break;
         }
 
-        r = clock_adjtime(CLOCK_REALTIME, &tmx);
-        if (r < 0)
+        if (clock_adjtime(CLOCK_REALTIME, &tmx) < 0)
                 return -errno;
 
-        r = manager_save_time_and_rearm(m);
-        if (r < 0)
-                return r;
-
-        /* If touch fails, there isn't much we can do. Maybe it'll work next time. */
-        (void) touch("/run/systemd/timesync/synchronized");
-
         m->drift_freq = tmx.freq;
 
         log_debug("  status       : %04i %s\n"
@@ -427,15 +412,12 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                 .msg_name = &server_addr,
                 .msg_namelen = sizeof(server_addr),
         };
-        struct cmsghdr *cmsg;
         struct timespec *recv_time = NULL;
+        triple_timestamp dts;
         ssize_t len;
-        double origin, receive, trans, dest;
-        double delay, offset;
-        double root_distance;
+        double origin, receive, trans, dest, delay, offset, root_distance;
         bool spike;
-        int leap_sec;
-        int r;
+        int leap_sec, r;
 
         assert(source);
         assert(m);
@@ -466,21 +448,9 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                 return 0;
         }
 
-        CMSG_FOREACH(cmsg, &msghdr) {
-                if (cmsg->cmsg_level != SOL_SOCKET)
-                        continue;
-
-                switch (cmsg->cmsg_type) {
-                case SCM_TIMESTAMPNS:
-                        assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct timespec)));
-
-                        recv_time = (struct timespec *) CMSG_DATA(cmsg);
-                        break;
-                }
-        }
+        recv_time = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_TIMESTAMPNS, struct timespec);
         if (!recv_time)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Invalid packet timestamp.");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Packet timestamp missing.");
 
         if (!m->pending) {
                 log_debug("Unexpected reply. Ignoring.");
@@ -597,10 +567,23 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                   m->samples_jitter, spike ? " spike" : "",
                   m->poll_interval_usec / USEC_PER_SEC);
 
+        /* Get current monotonic/realtime clocks immediately before adjusting the latter */
+        triple_timestamp_get(&dts);
+
         if (!spike) {
+                /* Fix up our idea of the time. */
+                dts.realtime = (usec_t) (dts.realtime + offset * USEC_PER_SEC);
+
                 r = manager_adjust_clock(m, offset, leap_sec);
                 if (r < 0)
                         log_error_errno(r, "Failed to call clock_adjtime(): %m");
+
+                (void) manager_save_time_and_rearm(m, dts.realtime);
+
+                /* If touch fails, there isn't much we can do. Maybe it'll work next time. */
+                r = touch("/run/systemd/timesync/synchronized");
+                if (r < 0)
+                        log_debug_errno(r, "Failed to touch /run/systemd/timesync/synchronized, ignoring: %m");
         }
 
         /* Save NTP response */
@@ -621,15 +604,26 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                                 "NTPMessage",
                                 NULL);
 
-        if (!m->good) {
+        if (!m->talking) {
                 _cleanup_free_ char *pretty = NULL;
 
-                m->good = true;
+                m->talking = true;
+
+                (void) server_address_pretty(m->current_server_address, &pretty);
+
+                log_info("Contacted time server %s (%s).", strna(pretty), m->current_server_name->string);
+                (void) sd_notifyf(false, "STATUS=Contacted time server %s (%s).", strna(pretty), m->current_server_name->string);
+        }
+
+        if (!spike && !m->synchronized) {
+                m->synchronized = true;
 
-                server_address_pretty(m->current_server_address, &pretty);
-                /* "Initial", as further successful syncs will not be logged. */
-                log_info("Initial synchronization to time server %s (%s).", strna(pretty), m->current_server_name->string);
-                sd_notifyf(false, "STATUS=Initial synchronization to time server %s (%s).", strna(pretty), m->current_server_name->string);
+                log_struct(LOG_INFO,
+                           LOG_MESSAGE("Initial clock synchronization to %s.", FORMAT_TIMESTAMP_STYLE(dts.realtime, TIMESTAMP_US)),
+                           "MESSAGE_ID=" SD_MESSAGE_TIME_SYNC_STR,
+                           "MONOTONIC_USEC=" USEC_FMT, dts.monotonic,
+                           "REALTIME_USEC=" USEC_FMT, dts.realtime,
+                           "BOOTIME_USEC=" USEC_FMT, dts.boottime);
         }
 
         r = manager_arm_timer(m, m->poll_interval_usec);
@@ -686,14 +680,14 @@ static int manager_begin(Manager *m) {
         assert_return(m->current_server_name, -EHOSTUNREACH);
         assert_return(m->current_server_address, -EHOSTUNREACH);
 
-        m->good = false;
+        m->talking = false;
         m->missed_replies = NTP_MAX_MISSED_REPLIES;
         if (m->poll_interval_usec == 0)
                 m->poll_interval_usec = m->poll_interval_min_usec;
 
         server_address_pretty(m->current_server_address, &pretty);
         log_debug("Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string);
-        sd_notifyf(false, "STATUS=Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string);
+        (void) sd_notifyf(false, "STATUS=Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string);
 
         r = manager_clock_watch_setup(m);
         if (r < 0)
@@ -818,7 +812,7 @@ int manager_connect(Manager *m) {
         if (m->current_server_address && m->current_server_address->addresses_next)
                 manager_set_server_address(m, m->current_server_address->addresses_next);
         else {
-                struct addrinfo hints = {
+                static const struct addrinfo hints = {
                         .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG,
                         .ai_socktype = SOCK_DGRAM,
                 };
@@ -910,12 +904,11 @@ void manager_disconnect(Manager *m) {
 
         manager_listen_stop(m);
 
-        m->event_clock_watch = sd_event_source_unref(m->event_clock_watch);
-        m->clock_watch_fd = safe_close(m->clock_watch_fd);
+        m->event_clock_watch = sd_event_source_disable_unref(m->event_clock_watch);
 
         m->event_timeout = sd_event_source_unref(m->event_timeout);
 
-        sd_notify(false, "STATUS=Idle.");
+        (void) sd_notify(false, "STATUS=Idle.");
 }
 
 void manager_flush_server_names(Manager  *m, ServerType t) {
@@ -960,8 +953,6 @@ Manager* manager_free(Manager *m) {
 
 static int manager_network_read_link_servers(Manager *m) {
         _cleanup_strv_free_ char **ntp = NULL;
-        ServerName *n, *nx;
-        char **i;
         bool changed = false;
         int r;
 
@@ -1009,7 +1000,7 @@ static int manager_network_read_link_servers(Manager *m) {
                 }
         }
 
-        LIST_FOREACH_SAFE(names, n, nx, m->link_servers)
+        LIST_FOREACH(names, n, m->link_servers)
                 if (n->marked) {
                         server_name_free(n);
                         changed = true;
@@ -1098,21 +1089,26 @@ int manager_new(Manager **ret) {
 
         assert(ret);
 
-        m = new0(Manager, 1);
+        m = new(Manager, 1);
         if (!m)
                 return -ENOMEM;
 
-        m->root_distance_max_usec = NTP_ROOT_DISTANCE_MAX_USEC;
-        m->poll_interval_min_usec = NTP_POLL_INTERVAL_MIN_USEC;
-        m->poll_interval_max_usec = NTP_POLL_INTERVAL_MAX_USEC;
+        *m = (Manager) {
+                .root_distance_max_usec = NTP_ROOT_DISTANCE_MAX_USEC,
+                .poll_interval_min_usec = NTP_POLL_INTERVAL_MIN_USEC,
+                .poll_interval_max_usec = NTP_POLL_INTERVAL_MAX_USEC,
 
-        m->connection_retry_usec = DEFAULT_CONNECTION_RETRY_USEC;
+                .connection_retry_usec = DEFAULT_CONNECTION_RETRY_USEC,
 
-        m->server_socket = m->clock_watch_fd = -1;
+                .server_socket = -1,
 
-        m->ratelimit = (RateLimit) { RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST };
+                .ratelimit = (RateLimit) {
+                        RATELIMIT_INTERVAL_USEC,
+                        RATELIMIT_BURST
+                },
 
-        m->save_time_interval_usec = DEFAULT_SAVE_TIME_INTERVAL_USEC;
+                .save_time_interval_usec = DEFAULT_SAVE_TIME_INTERVAL_USEC,
+        };
 
         r = sd_event_default(&m->event);
         if (r < 0)
@@ -1123,6 +1119,12 @@ int manager_new(Manager **ret) {
 
         (void) sd_event_set_watchdog(m->event, true);
 
+        /* Load previous synchronization state */
+        r = access("/run/systemd/timesync/synchronized", F_OK);
+        if (r < 0 && errno != ENOENT)
+                log_debug_errno(errno, "Failed to determine whether /run/systemd/timesync/synchronized exists, ignoring: %m");
+        m->synchronized = r >= 0;
+
         r = sd_resolve_default(&m->resolve);
         if (r < 0)
                 return r;
@@ -1147,7 +1149,7 @@ static int manager_save_time_handler(sd_event_source *s, uint64_t usec, void *us
 
         assert(m);
 
-        (void) manager_save_time_and_rearm(m);
+        (void) manager_save_time_and_rearm(m, USEC_INFINITY);
         return 0;
 }
 
@@ -1175,12 +1177,16 @@ int manager_setup_save_time_event(Manager *m) {
         return 0;
 }
 
-static int manager_save_time_and_rearm(Manager *m) {
+static int manager_save_time_and_rearm(Manager *m, usec_t t) {
         int r;
 
         assert(m);
 
-        r = touch(CLOCK_FILE);
+        /* Updates the timestamp file to the specified time. If 't' is USEC_INFINITY uses the current system
+         * clock, but otherwise uses the specified timestamp. Note that whenever we acquire an NTP sync the
+         * specified timestamp value might be more accurate than the system clock, since the latter is
+         * subject to slow adjustments. */
+        r = touch_file(CLOCK_FILE, false, t, UID_INVALID, GID_INVALID, MODE_INVALID);
         if (r < 0)
                 log_debug_errno(r, "Failed to update " CLOCK_FILE ", ignoring: %m");
 
index aceb0098cef37cc26cb5e7218d0af538de44075f..74917aa0ee409bbf6b6dc4c793bb8d31271ab2e4 100644 (file)
@@ -61,7 +61,7 @@ struct Manager {
         int missed_replies;
         uint64_t packet_count;
         sd_event_source *event_timeout;
-        bool good;
+        bool talking;
 
         /* last sent packet */
         struct timespec trans_time_mon;
@@ -92,7 +92,6 @@ struct Manager {
 
         /* watch for time changes */
         sd_event_source *event_clock_watch;
-        int clock_watch_fd;
 
         /* Retry connections */
         sd_event_source *event_retry;
@@ -105,6 +104,9 @@ struct Manager {
         struct timespec origin_time, dest_time;
         bool spike;
 
+        /* Indicates whether we ever managed to set the local clock from NTP */
+        bool synchronized;
+
         /* save time event */
         sd_event_source *event_save_time;
         usec_t save_time_interval_usec;
index 79dfd47266965193d04795bca9f52634b1ac5f00..dd168917341ef43399221831c848304bed660f66 100644 (file)
@@ -16,16 +16,19 @@ int server_address_new(
         assert(socklen >= offsetof(struct sockaddr, sa_data));
         assert(socklen <= sizeof(union sockaddr_union));
 
-        a = new0(ServerAddress, 1);
+        a = new(ServerAddress, 1);
         if (!a)
                 return -ENOMEM;
 
+        *a = (ServerAddress) {
+                .name = n,
+                .socklen = socklen,
+        };
+
         memcpy(&a->sockaddr, sockaddr, socklen);
-        a->socklen = socklen;
 
         LIST_FIND_TAIL(addresses, n->addresses, tail);
         LIST_INSERT_AFTER(addresses, n->addresses, tail, a);
-        a->name = n;
 
         if (ret)
                 *ret = a;
@@ -58,12 +61,16 @@ int server_name_new(
         assert(m);
         assert(string);
 
-        n = new0(ServerName, 1);
+        n = new(ServerName, 1);
         if (!n)
                 return -ENOMEM;
 
-        n->type = type;
-        n->string = strdup(string);
+        *n = (ServerName) {
+                .manager = m,
+                .type = type,
+                .string = strdup(string),
+        };
+
         if (!n->string) {
                 free(n);
                 return -ENOMEM;
@@ -81,8 +88,6 @@ int server_name_new(
         } else
                 assert_not_reached();
 
-        n->manager = m;
-
         if (type != SERVER_FALLBACK &&
             m->current_server_name &&
             m->current_server_name->type == SERVER_FALLBACK)
index 6f316746f5889453c496b63845f7895167ae7516..999d1d385237834d1a99fdd5e08d7e7accc8429e 100644 (file)
@@ -22,9 +22,8 @@
 #include "user-util.h"
 
 static int load_clock_timestamp(uid_t uid, gid_t gid) {
+        usec_t min = TIME_EPOCH * USEC_PER_SEC, ct;
         _cleanup_close_ int fd = -1;
-        usec_t min = TIME_EPOCH * USEC_PER_SEC;
-        usec_t ct;
         int r;
 
         /* Let's try to make sure that the clock is always
@@ -40,8 +39,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) {
                 usec_t stamp;
 
                 /* check if the recorded time is later than the compiled-in one */
-                r = fstat(fd, &st);
-                if (r >= 0) {
+                if (fstat(fd, &st) >= 0) {
                         stamp = timespec_load(&st.st_mtim);
                         if (stamp > min)
                                 min = stamp;
@@ -64,7 +62,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) {
                 }
 
                 /* create stamp file with the compiled-in date */
-                r = touch_file(CLOCK_FILE, false, min, uid, gid, 0644);
+                r = touch_file(CLOCK_FILE, /* parents= */ false, min, uid, gid, 0644);
                 if (r < 0)
                         log_debug_errno(r, "Failed to create %s, ignoring: %m", CLOCK_FILE);
         }
@@ -72,13 +70,12 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) {
 settime:
         ct = now(CLOCK_REALTIME);
         if (ct < min) {
-                struct timespec ts;
                 char date[FORMAT_TIMESTAMP_MAX];
 
                 log_info("System clock time unset or jumped backwards, restoring from recorded timestamp: %s",
                          format_timestamp(date, sizeof(date), min));
 
-                if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, min)) < 0)
+                if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(min)) < 0)
                         log_error_errno(errno, "Failed to restore system clock, ignoring: %m");
         }
 
index fcab51c2081d0943864853fb4dd1d681309dd92a..f0190f9b4cd5f7f7b9fd54ebcef6f7cb746d86ed 100644 (file)
@@ -1044,8 +1044,6 @@ static int parse_xattrs_from_arg(Item *i) {
 }
 
 static int fd_set_xattrs(Item *i, int fd, const char *path, const struct stat *st) {
-        char **name, **value;
-
         assert(i);
         assert(fd >= 0);
         assert(path);
@@ -1964,7 +1962,6 @@ static int glob_item(Item *i, action_t action) {
                 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
         };
         int r = 0, k;
-        char **fn;
 
         k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
         if (k < 0 && k != -ENOENT)
@@ -1984,7 +1981,6 @@ static int glob_item_recursively(Item *i, fdaction_t action) {
                 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
         };
         int r = 0, k;
-        char **fn;
 
         k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
         if (k < 0 && k != -ENOENT)
@@ -2720,8 +2716,6 @@ static bool item_compatible(Item *a, Item *b) {
 }
 
 static bool should_include_path(const char *path) {
-        char **prefix;
-
         STRV_FOREACH(prefix, arg_exclude_prefixes)
                 if (path_startswith(path, *prefix)) {
                         log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
@@ -2771,8 +2765,7 @@ static int specifier_expansion_from_arg(Item *i) {
                 return free_and_replace(i->argument, resolved);
         }
         case SET_XATTR:
-        case RECURSIVE_SET_XATTR: {
-                char **xattr;
+        case RECURSIVE_SET_XATTR:
                 STRV_FOREACH(xattr, i->xattrs) {
                         _cleanup_free_ char *resolved = NULL;
 
@@ -2783,7 +2776,7 @@ static int specifier_expansion_from_arg(Item *i) {
                         free_and_replace(*xattr, resolved);
                 }
                 return 0;
-        }
+
         default:
                 return 0;
         }
@@ -3612,7 +3605,6 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe
 }
 
 static int parse_arguments(char **config_dirs, char **args, bool *invalid_config) {
-        char **arg;
         int r;
 
         STRV_FOREACH(arg, args) {
@@ -3627,7 +3619,6 @@ static int parse_arguments(char **config_dirs, char **args, bool *invalid_config
 static int read_config_files(char **config_dirs, char **args, bool *invalid_config) {
         _cleanup_strv_free_ char **files = NULL;
         _cleanup_free_ char *p = NULL;
-        char **f;
         int r;
 
         r = conf_files_list_with_replacement(arg_root, config_dirs, arg_replace, &files, &p);
@@ -3734,7 +3725,6 @@ static int run(int argc, char *argv[]) {
 
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *t = NULL;
-                char **i;
 
                 STRV_FOREACH(i, config_dirs) {
                         _cleanup_free_ char *j = NULL;
index e98b38510ebc43c227440e1250a919b186a11724..7fc0ed7c8d799507526b15109f7300120cb78600 100644 (file)
@@ -59,7 +59,7 @@ static int send_passwords(const char *socket_name, char **passwords) {
         union sockaddr_union sa;
         socklen_t sa_len;
         size_t packet_length = 1;
-        char **p, *d;
+        char *d;
         ssize_t n;
         int r;
 
@@ -554,8 +554,6 @@ static int ask_on_this_console(const char *tty, pid_t *ret_pid, char **arguments
         if (r < 0)
                 return r;
         if (r == 0) {
-                char **i;
-
                 assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
 
                 STRV_FOREACH(i, arguments) {
@@ -581,8 +579,6 @@ static int ask_on_this_console(const char *tty, pid_t *ret_pid, char **arguments
 }
 
 static void terminate_agents(Set *pids) {
-        struct timespec ts;
-        siginfo_t status = {};
         sigset_t set;
         void *p;
         int r, signum;
@@ -599,11 +595,10 @@ static void terminate_agents(Set *pids) {
          */
         assert_se(sigemptyset(&set) >= 0);
         assert_se(sigaddset(&set, SIGCHLD) >= 0);
-        timespec_store(&ts, 50 * USEC_PER_MSEC);
 
         while (!set_isempty(pids)) {
+                siginfo_t status = {};
 
-                zero(status);
                 r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
                 if (r < 0 && errno == EINTR)
                         continue;
@@ -613,7 +608,7 @@ static void terminate_agents(Set *pids) {
                         continue;
                 }
 
-                signum = sigtimedwait(&set, NULL, &ts);
+                signum = sigtimedwait(&set, NULL, TIMESPEC_STORE(50 * USEC_PER_MSEC));
                 if (signum < 0) {
                         if (errno != EAGAIN)
                                 log_error_errno(errno, "sigtimedwait() failed: %m");
@@ -635,7 +630,6 @@ static int ask_on_consoles(char *argv[]) {
         _cleanup_set_free_ Set *pids = NULL;
         _cleanup_strv_free_ char **consoles = NULL, **arguments = NULL;
         siginfo_t status = {};
-        char **tty;
         pid_t pid;
         int r;
 
index 64691113e0f2808d53f19b5879608520ada10ac8..3c0e887ada81cbe624793d3426ad0925914a3f58 100644 (file)
@@ -69,12 +69,10 @@ static LinkConfig* link_config_free(LinkConfig *config) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(LinkConfig*, link_config_free);
 
 static void link_configs_free(LinkConfigContext *ctx) {
-        LinkConfig *config, *config_next;
-
         if (!ctx)
                 return;
 
-        LIST_FOREACH_SAFE(configs, config, config_next, ctx->configs)
+        LIST_FOREACH(configs, config, ctx->configs)
                 link_config_free(config);
 }
 
@@ -326,7 +324,6 @@ static int device_unsigned_attribute(sd_device *device, const char *attr, unsign
 
 int link_config_load(LinkConfigContext *ctx) {
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         int r;
 
         link_configs_free(ctx);
@@ -423,7 +420,6 @@ int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link
 }
 
 int link_get_config(LinkConfigContext *ctx, Link *link) {
-        LinkConfig *config;
         int r;
 
         assert(ctx);
@@ -833,7 +829,6 @@ static int link_apply_alternative_names(Link *link, sd_netlink **rtnl) {
         if (r < 0)
                 log_link_debug_errno(link, r, "Failed to get alternative names, ignoring: %m");
 
-        char **p;
         STRV_FOREACH(p, current_altnames)
                 strv_remove(altnames, *p);
 
index 85f89fadd8dafb03db11327a174e64c2c65adc24..76fbbc1954fd67a213f1af8030740b12bbb39ab5 100644 (file)
@@ -611,7 +611,6 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd
         /* Log output only if we watch stderr. */
         if (l > 0 && spawn->fd_stderr >= 0) {
                 _cleanup_strv_free_ char **v = NULL;
-                char **q;
 
                 r = strv_split_newlines_full(&v, p, EXTRACT_RETAIN_ESCAPE);
                 if (r < 0)
index ea4d548f60de7764bdff6eb94fc365fb3af868ec..d245c2c7b39624fa520acc1fb7ed927f99cf481c 100644 (file)
@@ -268,11 +268,9 @@ static void udev_rule_token_free(UdevRuleToken *token) {
 }
 
 static void udev_rule_line_clear_tokens(UdevRuleLine *rule_line) {
-        UdevRuleToken *i, *next;
-
         assert(rule_line);
 
-        LIST_FOREACH_SAFE(tokens, i, next, rule_line->tokens)
+        LIST_FOREACH(tokens, i, rule_line->tokens)
                 udev_rule_token_free(i);
 
         rule_line->tokens = NULL;
@@ -298,12 +296,10 @@ static UdevRuleLine* udev_rule_line_free(UdevRuleLine *rule_line) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRuleLine*, udev_rule_line_free);
 
 static void udev_rule_file_free(UdevRuleFile *rule_file) {
-        UdevRuleLine *i, *next;
-
         if (!rule_file)
                 return;
 
-        LIST_FOREACH_SAFE(rule_lines, i, next, rule_file->rule_lines)
+        LIST_FOREACH(rule_lines, i, rule_file->rule_lines)
                 udev_rule_line_free(i);
 
         free(rule_file->filename);
@@ -311,12 +307,10 @@ static void udev_rule_file_free(UdevRuleFile *rule_file) {
 }
 
 UdevRules *udev_rules_free(UdevRules *rules) {
-        UdevRuleFile *i, *next;
-
         if (!rules)
                 return NULL;
 
-        LIST_FOREACH_SAFE(rule_files, i, next, rules->rule_files)
+        LIST_FOREACH(rule_files, i, rules->rule_files)
                 udev_rule_file_free(i);
 
         hashmap_free_free_key(rules->known_users);
@@ -1070,7 +1064,7 @@ static void sort_tokens(UdevRuleLine *rule_line) {
         rule_line->current_token = NULL;
 
         while (!LIST_IS_EMPTY(head_old)) {
-                UdevRuleToken *t, *min_token = NULL;
+                UdevRuleToken *min_token = NULL;
 
                 LIST_FOREACH(tokens, t, head_old)
                         if (!min_token || min_token->type > t->type)
@@ -1146,12 +1140,10 @@ static int rule_add_line(UdevRules *rules, const char *line_str, unsigned line_n
 }
 
 static void rule_resolve_goto(UdevRuleFile *rule_file) {
-        UdevRuleLine *line, *line_next, *i;
-
         assert(rule_file);
 
         /* link GOTOs to LABEL rules in this file to be able to fast-forward */
-        LIST_FOREACH_SAFE(rule_lines, line, line_next, rule_file->rule_lines) {
+        LIST_FOREACH(rule_lines, line, rule_file->rule_lines) {
                 if (!FLAGS_SET(line->type, LINE_HAS_GOTO))
                         continue;
 
@@ -1300,7 +1292,6 @@ UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing) {
 int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) {
         _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
         _cleanup_strv_free_ char **files = NULL;
-        char **f;
         int r;
 
         rules = udev_rules_new(resolve_name_timing);
@@ -1814,7 +1805,7 @@ static int udev_rule_apply_token_to_event(
         }
         case TK_M_IMPORT_PROGRAM: {
                 _cleanup_strv_free_ char **lines = NULL;
-                char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE], **line;
+                char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE];
                 bool truncated;
 
                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
@@ -2438,9 +2429,12 @@ static int udev_rule_apply_parent_token_to_event(
         head = rules->current_file->current_line->current_token;
         event->dev_parent = event->dev;
         for (;;) {
-                LIST_FOREACH(tokens, line->current_token, head) {
-                        if (!token_is_for_parents(line->current_token))
+                line->current_token = NULL;
+                LIST_FOREACH(tokens, token, head) {
+                        if (!token_is_for_parents(token))
                                 return true; /* All parent tokens match. */
+
+                        line->current_token = token;
                         r = udev_rule_apply_token_to_event(rules, event->dev_parent, event, 0, timeout_signal, NULL);
                         if (r < 0)
                                 return r;
@@ -2468,7 +2462,6 @@ static int udev_rule_apply_line_to_event(
 
         UdevRuleLine *line = rules->current_file->current_line;
         UdevRuleLineType mask = LINE_HAS_GOTO | LINE_UPDATE_SOMETHING;
-        UdevRuleToken *token, *next_token;
         bool parents_done = false;
         sd_device_action_t action;
         int r;
@@ -2492,7 +2485,7 @@ static int udev_rule_apply_line_to_event(
 
         DEVICE_TRACE_POINT(rules_apply_line, event->dev, line->rule_file->filename, line->line_number);
 
-        LIST_FOREACH_SAFE(tokens, token, next_token, line->tokens) {
+        LIST_FOREACH(tokens, token, line->tokens) {
                 line->current_token = token;
 
                 if (token_is_for_parents(token)) {
@@ -2525,8 +2518,6 @@ int udev_rules_apply_to_event(
                 int timeout_signal,
                 Hashmap *properties_list) {
 
-        UdevRuleFile *file;
-        UdevRuleLine *next_line;
         int r;
 
         assert(rules);
@@ -2534,7 +2525,8 @@ int udev_rules_apply_to_event(
 
         LIST_FOREACH(rule_files, file, rules->rule_files) {
                 rules->current_file = file;
-                LIST_FOREACH_SAFE(rule_lines, file->current_line, next_line, file->rule_lines) {
+                LIST_FOREACH_WITH_NEXT(rule_lines, line, next_line, file->rule_lines) {
+                        file->current_line = line;
                         r = udev_rule_apply_line_to_event(rules, event, timeout_usec, timeout_signal, properties_list, &next_line);
                         if (r < 0)
                                 return r;
@@ -2548,7 +2540,6 @@ static int apply_static_dev_perms(const char *devnode, uid_t uid, gid_t gid, mod
         char device_node[UDEV_PATH_SIZE], tags_dir[UDEV_PATH_SIZE], tag_symlink[UDEV_PATH_SIZE];
         _cleanup_free_ char *unescaped_filename = NULL;
         struct stat stats;
-        char **t;
         int r;
 
         assert(devnode);
@@ -2612,7 +2603,6 @@ static int apply_static_dev_perms(const char *devnode, uid_t uid, gid_t gid, mod
 }
 
 static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) {
-        UdevRuleToken *token;
         _cleanup_strv_free_ char **tags = NULL;
         uid_t uid = UID_INVALID;
         gid_t gid = GID_INVALID;
@@ -2645,8 +2635,6 @@ static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) {
 }
 
 int udev_rules_apply_static_dev_perms(UdevRules *rules) {
-        UdevRuleFile *file;
-        UdevRuleLine *line;
         int r;
 
         assert(rules);
index 992764b80ce64d56c0116cac5777f93f75553ce0..66ddf3a779db57a0e4aeea1f8dd021a71434c77d 100644 (file)
@@ -584,7 +584,6 @@ int info_main(int argc, char *argv[], void *userdata) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "-x/--export or -P/--export-prefix cannot be used with --value");
 
-        char **p;
         STRV_FOREACH(p, devices) {
                 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
 
index a88823cc21a4218093e6577e8d5b77e664056e7c..56921e2cc62ecfcdedc5fcd47b3faf8c6f3d70eb 100644 (file)
@@ -11,6 +11,7 @@
 #include "device-util.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
 #include "set.h"
@@ -226,10 +227,14 @@ static int help(void) {
                "  -y --sysname-match=NAME           Trigger devices with this /sys path\n"
                "     --name-match=NAME              Trigger devices with this /dev name\n"
                "  -b --parent-match=NAME            Trigger devices with that parent device\n"
+               "     --initialized-match            Trigger devices that are already initialized\n"
+               "     --initialized-nomatch          Trigger devices that are not initialized yet\n"
                "  -w --settle                       Wait for the triggered events to complete\n"
                "     --wait-daemon[=SECONDS]        Wait for udevd daemon to be initialized\n"
                "                                    before triggering uevents\n"
-               "     --uuid                         Print synthetic uevent UUID\n",
+               "     --uuid                         Print synthetic uevent UUID\n"
+               "     --prioritized-subsystem=SUBSYSTEM[,SUBSYSTEM…]\n"
+               "                                    Trigger devices from a matching subsystem first\n",
                program_invocation_short_name);
 
         return 0;
@@ -240,33 +245,40 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                 ARG_NAME = 0x100,
                 ARG_PING,
                 ARG_UUID,
+                ARG_PRIORITIZED_SUBSYSTEM,
+                ARG_INITIALIZED_MATCH,
+                ARG_INITIALIZED_NOMATCH,
         };
 
         static const struct option options[] = {
-                { "verbose",           no_argument,       NULL, 'v'      },
-                { "dry-run",           no_argument,       NULL, 'n'      },
-                { "quiet",             no_argument,       NULL, 'q'      },
-                { "type",              required_argument, NULL, 't'      },
-                { "action",            required_argument, NULL, 'c'      },
-                { "subsystem-match",   required_argument, NULL, 's'      },
-                { "subsystem-nomatch", required_argument, NULL, 'S'      },
-                { "attr-match",        required_argument, NULL, 'a'      },
-                { "attr-nomatch",      required_argument, NULL, 'A'      },
-                { "property-match",    required_argument, NULL, 'p'      },
-                { "tag-match",         required_argument, NULL, 'g'      },
-                { "sysname-match",     required_argument, NULL, 'y'      },
-                { "name-match",        required_argument, NULL, ARG_NAME },
-                { "parent-match",      required_argument, NULL, 'b'      },
-                { "settle",            no_argument,       NULL, 'w'      },
-                { "wait-daemon",       optional_argument, NULL, ARG_PING },
-                { "version",           no_argument,       NULL, 'V'      },
-                { "help",              no_argument,       NULL, 'h'      },
-                { "uuid",              no_argument,       NULL, ARG_UUID },
+                { "verbose",               no_argument,       NULL, 'v'                       },
+                { "dry-run",               no_argument,       NULL, 'n'                       },
+                { "quiet",                 no_argument,       NULL, 'q'                       },
+                { "type",                  required_argument, NULL, 't'                       },
+                { "action",                required_argument, NULL, 'c'                       },
+                { "subsystem-match",       required_argument, NULL, 's'                       },
+                { "subsystem-nomatch",     required_argument, NULL, 'S'                       },
+                { "attr-match",            required_argument, NULL, 'a'                       },
+                { "attr-nomatch",          required_argument, NULL, 'A'                       },
+                { "property-match",        required_argument, NULL, 'p'                       },
+                { "tag-match",             required_argument, NULL, 'g'                       },
+                { "sysname-match",         required_argument, NULL, 'y'                       },
+                { "name-match",            required_argument, NULL, ARG_NAME                  },
+                { "parent-match",          required_argument, NULL, 'b'                       },
+                { "initialized-match",     no_argument,       NULL, ARG_INITIALIZED_MATCH     },
+                { "initialized-nomatch",   no_argument,       NULL, ARG_INITIALIZED_NOMATCH   },
+                { "settle",                no_argument,       NULL, 'w'                       },
+                { "wait-daemon",           optional_argument, NULL, ARG_PING                  },
+                { "version",               no_argument,       NULL, 'V'                       },
+                { "help",                  no_argument,       NULL, 'h'                       },
+                { "uuid",                  no_argument,       NULL, ARG_UUID                  },
+                { "prioritized-subsystem", required_argument, NULL, ARG_PRIORITIZED_SUBSYSTEM },
                 {}
         };
         enum {
                 TYPE_DEVICES,
                 TYPE_SUBSYSTEMS,
+                TYPE_ALL,
         } device_type = TYPE_DEVICES;
         sd_device_action_t action = SD_DEVICE_CHANGE;
         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
@@ -309,6 +321,8 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                                 device_type = TYPE_DEVICES;
                         else if (streq(optarg, "subsystems"))
                                 device_type = TYPE_SUBSYSTEMS;
+                        else if (streq(optarg, "all"))
+                                device_type = TYPE_ALL;
                         else
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg);
                         break;
@@ -405,6 +419,26 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                         arg_uuid = true;
                         break;
 
+                case ARG_PRIORITIZED_SUBSYSTEM: {
+                        _cleanup_strv_free_ char **subsystems = NULL;
+
+                        subsystems = strv_split(optarg, ",");
+                        if (!subsystems)
+                                return log_error_errno(r, "Failed to parse prioritized subsystem '%s': %m", optarg);
+
+                        STRV_FOREACH(p, subsystems) {
+                                r = device_enumerator_add_prioritized_subsystem(e, *p);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to add prioritized subsystem '%s': %m", *p);
+                        }
+                        break;
+                }
+                case ARG_INITIALIZED_MATCH:
+                case ARG_INITIALIZED_NOMATCH:
+                        r = device_enumerator_add_match_is_initialized(e, c == ARG_INITIALIZED_MATCH ? MATCH_INITIALIZED_YES : MATCH_INITIALIZED_NO);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set initialized filter: %m");
+                        break;
                 case 'V':
                         return print_version();
                 case 'h':
@@ -477,6 +511,11 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to scan devices: %m");
                 break;
+        case TYPE_ALL:
+                r = device_enumerator_scan_devices_and_subsystems(e);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to scan devices and subsystems: %m");
+                break;
         default:
                 assert_not_reached();
         }
index 6137d805f407ec8282730b8ac5b62eaf88ea95c6..41d0ec1e137c192a4089d2e238c7834f543c8841 100644 (file)
@@ -181,9 +181,7 @@ static Event *event_free(Event *event) {
 }
 
 static void event_queue_cleanup(Manager *manager, EventState match_state) {
-        Event *event, *tmp;
-
-        LIST_FOREACH_SAFE(event, event, tmp, manager->events) {
+        LIST_FOREACH(event, event, manager->events) {
                 if (match_state != EVENT_UNDEF && match_state != event->state)
                         continue;
 
@@ -781,7 +779,7 @@ static int event_run(Event *event) {
 static int event_is_blocked(Event *event) {
         const char *subsystem, *devpath, *devpath_old = NULL;
         dev_t devnum = makedev(0, 0);
-        Event *loop_event;
+        Event *loop_event = NULL;
         size_t devpath_len;
         int r, ifindex = 0;
         bool is_block;
@@ -796,7 +794,9 @@ static int event_is_blocked(Event *event) {
                 /* we have checked previously and no blocker found */
                 return false;
 
-        LIST_FOREACH(event, loop_event, event->manager->events) {
+        LIST_FOREACH(event, e, event->manager->events) {
+                loop_event = e;
+
                 /* we already found a later event, earlier cannot block us, no need to check again */
                 if (loop_event->seqnum < event->blocker_seqnum)
                         continue;
@@ -842,10 +842,12 @@ static int event_is_blocked(Event *event) {
                 return r;
 
         /* check if queue contains events we depend on */
-        LIST_FOREACH(event, loop_event, loop_event) {
+        LIST_FOREACH(event, e, loop_event) {
                 size_t loop_devpath_len, common;
                 const char *loop_devpath;
 
+                loop_event = e;
+
                 /* found ourself, no later event can block us */
                 if (loop_event->seqnum >= event->seqnum)
                         goto no_blocker;
@@ -915,7 +917,6 @@ no_blocker:
 }
 
 static int event_queue_start(Manager *manager) {
-        Event *event, *event_next;
         usec_t usec;
         int r;
 
@@ -948,7 +949,7 @@ static int event_queue_start(Manager *manager) {
                         return log_warning_errno(r, "Failed to read udev rules: %m");
         }
 
-        LIST_FOREACH_SAFE(event, event, event_next, manager->events) {
+        LIST_FOREACH(event, event, manager->events) {
                 if (event->state != EVENT_QUEUED)
                         continue;
 
index 0b65ae92102dba9324a885490f5482eb089cd0ce..c3d61f00593f1beab8d9a36e8ee7bebd39318abe 100644 (file)
@@ -257,9 +257,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
                 (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 5, (size_t) 6, (size_t) 7);
         }
 
-        if (argc > 1) {
-                char **i;
-
+        if (argc > 1)
                 STRV_FOREACH(i, argv + 1) {
                         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
                         uid_t uid;
@@ -289,7 +287,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
                                 draw_separator = true;
                         }
                 }
-        else {
+        else {
                 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
 
                 r = userdb_all(arg_userdb_flags, &iterator);
@@ -496,9 +494,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
                 (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4);
         }
 
-        if (argc > 1) {
-                char **i;
-
+        if (argc > 1)
                 STRV_FOREACH(i, argv + 1) {
                         _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
                         gid_t gid;
@@ -528,8 +524,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
                                 draw_separator = true;
                         }
                 }
-
-        } else {
+        else {
                 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
 
                 r = groupdb_all(arg_userdb_flags, &iterator);
@@ -654,9 +649,7 @@ static int display_memberships(int argc, char *argv[], void *userdata) {
                 (void) table_set_sort(table, (size_t) 0, (size_t) 1);
         }
 
-        if (argc > 1) {
-                char **i;
-
+        if (argc > 1)
                 STRV_FOREACH(i, argv + 1) {
                         _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
 
@@ -687,7 +680,7 @@ static int display_memberships(int argc, char *argv[], void *userdata) {
                                         return r;
                         }
                 }
-        else {
+        else {
                 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
 
                 r = membershipdb_all(arg_userdb_flags, &iterator);
@@ -849,12 +842,9 @@ static int ssh_authorized_keys(int argc, char *argv[], void *userdata) {
         else {
                 if (strv_isempty(ur->ssh_authorized_keys))
                         log_debug("User record for %s has no public SSH keys.", argv[1]);
-                else {
-                        char **i;
-
+                else
                         STRV_FOREACH(i, ur->ssh_authorized_keys)
                                 printf("%s\n", *i);
-                }
 
                 if (ur->incomplete) {
                         fflush(stdout);
index 0564840dbe8db1ba4fb64dae18fdb8aaa826031d..aabf8070d2a58366075c517e40a9a54e7dbe10ed 100644 (file)
@@ -251,7 +251,6 @@ static int start_workers(Manager *m, bool explicit_request) {
 }
 
 int manager_startup(Manager *m) {
-        struct timeval ts;
         int n, r;
 
         assert(m);
@@ -300,7 +299,7 @@ int manager_startup(Manager *m) {
 
         /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
          * GC'ed on idle */
-        if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, timeval_store(&ts, LISTEN_TIMEOUT_USEC), sizeof(ts)) < 0)
+        if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
                 return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
 
         return start_workers(m, false);
index c4485cf6256fb2a96632b4fdb2abd8919e8104f9..9ceea61547bafc25120f19221115d512f1a52cda 100644 (file)
@@ -13,7 +13,6 @@
 static int run(int argc, char *argv[]) {
         _cleanup_strv_free_ char **only_show_in = NULL, **not_show_in = NULL, **desktops = NULL;
         const char *xdg_current_desktop;
-        char **d;
 
         if (argc != 3)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
index 8bc16831e730839814a139c4464ca5d47a22853a..91dda27d43f3b398dfa485d8c3a9ce9ab36fd0b7 100644 (file)
@@ -24,7 +24,6 @@ static int enumerate_xdg_autostart(Hashmap *all_services) {
         _cleanup_strv_free_ char **config_dirs = NULL;
         _unused_ _cleanup_strv_free_ char **data_dirs = NULL;
         _cleanup_free_ char *user_config_autostart_dir = NULL;
-        char **path;
         int r;
 
         r = xdg_user_config_dir(&user_config_autostart_dir, "/autostart");
index dd7b8998b5b65d05bca97802111f4ae2188bc61e..c7f78fe3c9b8115a9eff68bda221eff7c8e0c691 100755 (executable)
@@ -13,18 +13,23 @@ _ORIG_NSPAWN="${SYSTEMD_NSPAWN:?}"
 SYSTEMD_NSPAWN="${STATEDIR:?}/run-nspawn"
 
 setup_nspawn_root_hook() {
-    cat > "${STATEDIR:?}"/run-nspawn <<-EOF
-       #!/bin/bash
-       exec "${TEST_BASE_DIR:?}"/test-shutdown.py -- "$_ORIG_NSPAWN" "\$@"
-       exit 1
-       EOF
+    cat >"${STATEDIR:?}/run-nspawn" <<EOF
+#!/bin/bash
+exec "${TEST_BASE_DIR:?}/test-shutdown.py" -v -- "$_ORIG_NSPAWN" "\$@"
+exit 1
+EOF
     chmod 755 "${STATEDIR:?}"/run-nspawn
 }
 
 test_append_files() {
     local workspace="${1:?}"
     # prevent shutdown in test suite, the expect script does that manually.
-    rm "${workspace:?}/usr/lib/systemd/tests/testdata/units/end.service"
+    mkdir -p "${workspace:?}/etc/systemd/system/end.service.d"
+    cat >"$workspace/etc/systemd/system/end.service.d/99-override.conf" <<EOF
+[Service]
+ExecStart=
+ExecStart=/bin/true
+EOF
     inst /usr/bin/screen
     echo "PS1='screen\$WINDOW # '" >>"$workspace/root/.bashrc"
     echo 'startup_message off' >"$workspace/etc/screenrc"
diff --git a/test/TEST-72-SYSUPDATE/Makefile b/test/TEST-72-SYSUPDATE/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-72-SYSUPDATE/test.sh b/test/TEST-72-SYSUPDATE/test.sh
new file mode 100755 (executable)
index 0000000..471b02e
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+
+TEST_DESCRIPTION="test sysupdate"
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+test_append_files() {
+    inst_binary sha256sum
+}
+
+do_test "$@"
index 3b2b81edc8dd2946f177c6b30bda2216fb1a3505..7aa0664b85f6ddfc82e41b61ec66493c784a01f6 100644 (file)
@@ -4,4 +4,4 @@ Description=TEST-69-SHUTDOWN
 
 [Service]
 Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+ExecStart=/bin/true
diff --git a/test/units/testsuite-72.service b/test/units/testsuite-72.service
new file mode 100644 (file)
index 0000000..1640350
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-72-SYSUPDATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-72.sh b/test/units/testsuite-72.sh
new file mode 100755 (executable)
index 0000000..9effc98
--- /dev/null
@@ -0,0 +1,170 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+SYSUPDATE=/lib/systemd/systemd-sysupdate
+
+if ! test -x "$SYSUPDATE"; then
+    echo "no systemd-sysupdate" >/skipped
+    exit 0
+fi
+
+export SYSTEMD_PAGER=cat
+export SYSTEMD_LOG_LEVEL=debug
+
+rm -f /var/tmp/72-joined.raw
+truncate -s 10M /var/tmp/72-joined.raw
+
+sfdisk /var/tmp/72-joined.raw <<EOF
+label: gpt
+unit: sectors
+sector-size: 512
+
+size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
+size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
+size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
+size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
+EOF
+
+rm -rf /var/tmp/72-dirs
+
+rm -rf /var/tmp/72-defs
+mkdir -p /var/tmp/72-defs
+
+cat >/var/tmp/72-defs/01-first.conf <<"EOF"
+[Source]
+Type=regular-file
+Path=/var/tmp/72-source
+MatchPattern=part1-@v.raw
+
+[Target]
+Type=partition
+Path=/var/tmp/72-joined.raw
+MatchPattern=part1-@v
+MatchPartitionType=root-x86-64
+EOF
+
+cat >/var/tmp/72-defs/02-second.conf <<"EOF"
+[Source]
+Type=regular-file
+Path=/var/tmp/72-source
+MatchPattern=part2-@v.raw.gz
+
+[Target]
+Type=partition
+Path=/var/tmp/72-joined.raw
+MatchPattern=part2-@v
+MatchPartitionType=root-x86-64-verity
+EOF
+
+cat >/var/tmp/72-defs/03-third.conf <<"EOF"
+[Source]
+Type=directory
+Path=/var/tmp/72-source
+MatchPattern=dir-@v
+
+[Target]
+Type=directory
+Path=/var/tmp/72-dirs
+CurrentSymlink=/var/tmp/72-dirs/current
+MatchPattern=dir-@v
+InstancesMax=3
+EOF
+
+rm -rf /var/tmp/72-source
+mkdir -p /var/tmp/72-source
+
+new_version() {
+    # Create a pair of random partition payloads, and compress one
+    dd if=/dev/urandom of="/var/tmp/72-source/part1-$1.raw" bs=1024 count=1024
+    dd if=/dev/urandom of="/var/tmp/72-source/part2-$1.raw" bs=1024 count=1024
+    gzip -k -f "/var/tmp/72-source/part2-$1.raw"
+
+    mkdir -p "/var/tmp/72-source/dir-$1"
+    echo $RANDOM >"/var/tmp/72-source/dir-$1/foo.txt"
+    echo $RANDOM >"/var/tmp/72-source/dir-$1/bar.txt"
+
+    tar --numeric-owner -C "/var/tmp/72-source/dir-$1/" -czf "/var/tmp/72-source/dir-$1.tar.gz" .
+
+    ( cd /var/tmp/72-source/ && sha256sum part* dir-*.tar.gz >SHA256SUMS )
+}
+
+update_now() {
+    # Update to newest version. First there should be an update ready, then we
+    # do the update, and then there should not be any ready anymore
+
+    "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new
+    "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no update
+    ( ! "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new )
+}
+
+verify_version() {
+    # Expects: version ID + sector offset of both partitions to compare
+    dd if=/var/tmp/72-joined.raw bs=1024 skip="$2" count=1024 | cmp "/var/tmp/72-source/part1-$1.raw"
+    dd if=/var/tmp/72-joined.raw bs=1024 skip="$3" count=1024 | cmp "/var/tmp/72-source/part2-$1.raw"
+    cmp "/var/tmp/72-source/dir-$1/foo.txt" /var/tmp/72-dirs/current/foo.txt
+    cmp "/var/tmp/72-source/dir-$1/bar.txt" /var/tmp/72-dirs/current/bar.txt
+}
+
+# Install initial version and verify
+new_version v1
+update_now
+verify_version v1 1024 3072
+
+# Create second version, update and verify that it is added
+new_version v2
+update_now
+verify_version v2 2048 4096
+
+# Create third version, update and verify it replaced the first version
+new_version v3
+update_now
+verify_version v3 1024 3072
+
+# Create fourth version, and update through a file:// URL. This should be
+# almost as good as testing HTTP, but is simpler for us to set up. file:// is
+# abstracted in curl for us, and since our main goal is to test our own code
+# (and not curl) this test should be quite good even if not comprehensive. This
+# will test the SHA256SUMS logic at least (we turn off GPG validation though,
+# see above)
+new_version v4
+
+cat >/var/tmp/72-defs/02-second.conf <<"EOF"
+[Source]
+Type=url-file
+Path=file:///var/tmp/72-source
+MatchPattern=part2-@v.raw.gz
+
+[Target]
+Type=partition
+Path=/var/tmp/72-joined.raw
+MatchPattern=part2-@v
+MatchPartitionType=root-x86-64-verity
+EOF
+
+cat >/var/tmp/72-defs/03-third.conf <<"EOF"
+[Source]
+Type=url-tar
+Path=file:///var/tmp/72-source
+MatchPattern=dir-@v.tar.gz
+
+[Target]
+Type=directory
+Path=/var/tmp/72-dirs
+CurrentSymlink=/var/tmp/72-dirs/current
+MatchPattern=dir-@v
+InstancesMax=3
+EOF
+
+update_now
+verify_version v4 2048 4096
+
+rm  /var/tmp/72-joined.raw
+rm -r /var/tmp/72-dirs /var/tmp/72-defs /var/tmp/72-source
+
+echo OK >/testok
+
+exit 0
index 4da8b82af483d5406a90ee3bdc869d6a84bfe349..f94f261e0797149ac91e46bd5ea420c4b972b648 100755 (executable)
@@ -4,58 +4,39 @@ from argparse import ArgumentParser
 from pathlib import Path
 from subprocess import run, PIPE
 
-
 def extract_interfaces_xml(output_dir, executable):
-    list_interfaces_process = run(
+    proc = run(
         args=[executable.absolute(), '--bus-introspect', 'list'],
         stdout=PIPE,
         check=True,
-        universal_newlines=True,
-    )
-
-    interfaces_lines = list_interfaces_process.stdout.splitlines()
+        universal_newlines=True)
 
-    interface_names = [x.split()[1] for x in interfaces_lines]
+    interface_names = (x.split()[1] for x in proc.stdout.splitlines())
 
     for interface_name in interface_names:
-        interface_introspection_run = run(
+        proc = run(
             args=[executable.absolute(), '--bus-introspect', interface_name],
             stdout=PIPE,
             check=True,
-            universal_newlines=True,
-        )
+            universal_newlines=True)
 
         interface_file_name = output_dir / (interface_name + '.xml')
-        with open(interface_file_name, mode='w') as f:
-            f.write(interface_introspection_run.stdout)
+        interface_file_name.write_text(proc.stdout)
         interface_file_name.chmod(0o644)
 
-
-def iterate_executables(output_dir, executables):
-    output_dir.mkdir(mode=0o755, exist_ok=True)
-
-    for exe in executables:
-        extract_interfaces_xml(output_dir, exe)
-
-
 def main():
     parser = ArgumentParser()
-
-    parser.add_argument(
-        'output',
-        type=Path,
-    )
-
-    parser.add_argument(
-        'executables',
-        type=Path,
-        nargs='+',
-    )
+    parser.add_argument('output',
+                        type=Path)
+    parser.add_argument('executables',
+                        nargs='+',
+                        type=Path)
 
     args = parser.parse_args()
 
-    iterate_executables(args.output, args.executables)
-
+    args.output.mkdir(exist_ok=True)
+    for exe in args.executables:
+        extract_interfaces_xml(args.output, exe)
 
 if __name__ == '__main__':
     main()
index 31ed91c432edd5790ca86dd03c7a99bf15b67bcd..3a8c31dc358ab241c9a4a4c98ac6cc985fccf254 100755 (executable)
@@ -3,9 +3,10 @@
 
 from __future__ import print_function
 import collections
+import glob
 import sys
+from pathlib import Path
 import pprint
-from os.path import basename
 from xml_helper import xml_parse
 
 def man(page, number):
@@ -56,7 +57,8 @@ manpages = ['''
 
 MESON_FOOTER = '''\
 ]
-# Really, do not edit.'''
+# Really, do not edit.
+'''
 
 def make_mesonfile(rules, dist_files):
     # reformat rules as
@@ -76,13 +78,20 @@ def make_mesonfile(rules, dist_files):
     return '\n'.join((MESON_HEADER, pprint.pformat(lines)[1:-1], MESON_FOOTER))
 
 if __name__ == '__main__':
-    pages = sys.argv[1:]
+    source_glob = sys.argv[1]
+    target = Path(sys.argv[2])
+
+    pages = glob.glob(source_glob)
     pages = (p for p in pages
-             if basename(p) not in {
+             if Path(p).name not in {
                      'systemd.directives.xml',
                      'systemd.index.xml',
                      'directives-template.xml'})
 
     rules = create_rules(pages)
-    dist_files = (basename(p) for p in pages)
-    print(make_mesonfile(rules, dist_files))
+    dist_files = (Path(p).name for p in pages)
+    text = make_mesonfile(rules, dist_files)
+
+    tmp = target.with_suffix('.tmp')
+    tmp.write_text(text)
+    tmp.rename(target)
index 2bb0a8e845de8310418f30495f0bfbd63a40129e..8a3bd0da51281f758893589e23a3be572b0e89c0 100644 (file)
@@ -140,6 +140,8 @@ units = [
         ['systemd-reboot.service',              ''],
         ['systemd-rfkill.socket',               'ENABLE_RFKILL'],
         ['systemd-sysext.service',              'ENABLE_SYSEXT'],
+        ['systemd-sysupdate.timer',             'ENABLE_SYSUPDATE'],
+        ['systemd-sysupdate-reboot.timer',      'ENABLE_SYSUPDATE'],
         ['systemd-sysusers.service',            'ENABLE_SYSUSERS',
          'sysinit.target.wants/'],
         ['systemd-tmpfiles-clean.service',      'ENABLE_TMPFILES'],
@@ -236,6 +238,8 @@ in_units = [
         ['systemd-suspend.service',              ''],
         ['systemd-sysctl.service',               '',
          'sysinit.target.wants/'],
+        ['systemd-sysupdate.service',            'ENABLE_SYSUPDATE'],
+        ['systemd-sysupdate-reboot.service',     'ENABLE_SYSUPDATE'],
         ['systemd-timedated.service',            'ENABLE_TIMEDATED',
          'dbus-org.freedesktop.timedate1.service'],
         ['systemd-timesyncd.service',            'ENABLE_TIMESYNCD'],
diff --git a/units/systemd-sysupdate-reboot.service.in b/units/systemd-sysupdate-reboot.service.in
new file mode 100644 (file)
index 0000000..9d7b7d1
--- /dev/null
@@ -0,0 +1,20 @@
+#  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=Reboot Automatically After System Update
+Documentation=man:systemd-sysupdate-reboot.service(8)
+ConditionVirtualization=!container
+
+[Service]
+Type=oneshot
+ExecStart={{ROOTLIBEXECDIR}}/systemd-sysupdate reboot
+
+[Install]
+Also=systemd-sysupdate-reboot.timer
diff --git a/units/systemd-sysupdate-reboot.timer b/units/systemd-sysupdate-reboot.timer
new file mode 100644 (file)
index 0000000..95a44bf
--- /dev/null
@@ -0,0 +1,20 @@
+#  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=Reboot Automatically After System Update
+Documentation=man:systemd-sysupdate-reboot.service(8)
+ConditionVirtualization=!container
+
+[Timer]
+OnCalendar=4:10
+RandomizedDelaySec=30min
+
+[Install]
+WantedBy=timers.target
diff --git a/units/systemd-sysupdate.service.in b/units/systemd-sysupdate.service.in
new file mode 100644 (file)
index 0000000..085a9c4
--- /dev/null
@@ -0,0 +1,34 @@
+#  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=Automatic System Update
+Documentation=man:systemd-sysupdate.service(8)
+Wants=network-online.target
+After=network-online.target
+ConditionVirtualization=!container
+
+[Service]
+Type=simple
+NotifyAccess=main
+ExecStart={{ROOTLIBEXECDIR}}/systemd-sysupdate update
+CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE CAP_LINUX_IMMUTABLE
+NoNewPrivileges=yes
+MemoryDenyWriteExecute=yes
+ProtectHostname=yes
+RestrictRealtime=yes
+RestrictNamespaces=net
+RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
+SystemCallFilter=@system-service @mount
+SystemCallErrorNumber=EPERM
+SystemCallArchitectures=native
+LockPersonality=yes
+
+[Install]
+Also=systemd-sysupdate.timer
diff --git a/units/systemd-sysupdate.timer b/units/systemd-sysupdate.timer
new file mode 100644 (file)
index 0000000..6ecd98d
--- /dev/null
@@ -0,0 +1,30 @@
+#  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=Automatic System Update
+Documentation=man:systemd-sysupdate.service(8)
+
+# For containers we assume that the manager will handle updates. And we likely
+# can't even access our backing block device anyway.
+ConditionVirtualization=!container
+
+[Timer]
+# Trigger the update 15min after boot, and then â€“ on average â€“ every 6h, but
+# randomly distributed in a 2h…6h interval. In addition trigger things
+# persistently once on each Saturday, to ensure that even on systems that are
+# never booted up for long we have a chance to to do the update.
+OnBootSec=15min
+OnUnitActiveSec=2h
+OnCalendar=Sat
+RandomizedDelaySec=4h
+Persistent=yes
+
+[Install]
+WantedBy=timers.target
index 3d0f7b4f203fcb1d4df6474d78f643df1c8344cb..5e919950600bba882673d8fac19b9efda1fa3f3b 100644 (file)
@@ -19,5 +19,4 @@ ConditionPathIsReadWrite=/sys
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=-udevadm trigger --type=subsystems --action=add
-ExecStart=-udevadm trigger --type=devices --action=add
+ExecStart=-udevadm trigger --type=all --action=add --prioritized-subsystem=block,tpmrm