]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #25168 from valentindavid/valentindavid/umount-move-recursive...
authorLennart Poettering <lennart@poettering.net>
Mon, 23 Jan 2023 14:24:13 +0000 (15:24 +0100)
committerGitHub <noreply@github.com>
Mon, 23 Jan 2023 14:24:13 +0000 (15:24 +0100)
shutdown: move busy mounts to not block parent mounts

343 files changed:
.github/workflows/build_test.sh
.github/workflows/labeler.yml
.github/workflows/mkosi.yml
NEWS
TODO
catalog/systemd.catalog.in
docs/CODING_STYLE.md
docs/CREDENTIALS.md
docs/ENVIRONMENT.md
hwdb.d/60-keyboard.hwdb
hwdb.d/60-sensor.hwdb
man/bootctl.xml
man/cgroup-sandboxing.xml [new file with mode: 0644]
man/crypttab.xml
man/journalctl.xml
man/journald.conf.xml
man/kernel-install.xml
man/libudev.xml
man/loader.conf.xml
man/nss-myhostname.xml
man/org.freedesktop.hostname1.xml
man/org.freedesktop.systemd1.xml
man/os-release.xml
man/pam_systemd_home.xml
man/portablectl.xml
man/repart.d.xml
man/rules/meson.build
man/sd_bus_wait.xml
man/sd_notify.xml
man/sd_path_lookup.xml
man/sd_pid_get_owner_uid.xml
man/systemctl.xml
man/systemd-analyze.xml
man/systemd-boot-random-seed.service.xml
man/systemd-boot.xml
man/systemd-creds.xml
man/systemd-cryptenroll.xml
man/systemd-cryptsetup-generator.xml
man/systemd-gpt-auto-generator.xml
man/systemd-integritysetup-generator.xml
man/systemd-journald.service.xml
man/systemd-measure.xml
man/systemd-nspawn.xml
man/systemd-oomd.service.xml
man/systemd-pcrphase.service.xml
man/systemd-portabled.service.xml
man/systemd-repart.xml
man/systemd-stub.xml
man/systemd-sysext.xml
man/systemd-veritysetup-generator.xml
man/systemd-volatile-root.service.xml
man/systemd.exec.xml
man/systemd.link.xml
man/systemd.mount.xml
man/systemd.network.xml
man/systemd.path.xml
man/systemd.resource-control.xml
man/systemd.service.xml
man/systemd.system-credentials.xml
man/systemd.timer.xml
man/systemd.unit.xml
man/sysupdate.d.xml
man/tmpfiles.d.xml
man/udev_enumerate_new.xml
man/udevadm.xml
meson.build
mkosi.build
mkosi.conf.d/arch/10-arch.conf [moved from mkosi.conf.d/arch/10-mkosi.arch with 100% similarity]
mkosi.conf.d/centos/10-centos.conf [moved from mkosi.conf.d/centos_epel/10-mkosi.centos_epel with 97% similarity]
mkosi.conf.d/debian/10-debian.conf [moved from mkosi.conf.d/debian/10-mkosi.debian with 100% similarity]
mkosi.conf.d/fedora/10-fedora.conf [moved from mkosi.conf.d/fedora/10-mkosi.fedora with 100% similarity]
mkosi.conf.d/opensuse/10-opensuse.conf [moved from mkosi.conf.d/opensuse/10-mkosi.opensuse with 100% similarity]
mkosi.conf.d/ubuntu/10-ubuntu.conf [moved from mkosi.conf.d/ubuntu/10-mkosi.ubuntu with 100% similarity]
presets/90-systemd.preset
shell-completion/bash/bootctl
shell-completion/bash/systemctl.in
shell-completion/bash/systemd-analyze
shell-completion/zsh/_bootctl
shell-completion/zsh/_systemctl.in
shell-completion/zsh/_systemd-analyze
src/analyze/analyze-plot.c
src/analyze/analyze.c
src/analyze/analyze.h
src/basic/chase-symlinks.c
src/basic/chase-symlinks.h
src/basic/fileio.c
src/basic/hexdecoct.c
src/basic/hostname-util.c
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/basic/list.h
src/basic/log.c
src/basic/log.h
src/basic/macro.h
src/basic/parse-util.c
src/basic/parse-util.h
src/basic/process-util.c
src/basic/process-util.h
src/basic/special.h
src/basic/stdio-util.h
src/basic/string-util.c
src/basic/string-util.h
src/basic/strv.c
src/basic/strv.h
src/basic/time-util.c
src/basic/time-util.h
src/binfmt/binfmt.c
src/boot/bootctl-status.c
src/boot/bootctl-status.h
src/boot/bootctl-uki.c [new file with mode: 0644]
src/boot/bootctl-uki.h [new file with mode: 0644]
src/boot/bootctl.c
src/boot/bootctl.h
src/boot/efi/assert.c [deleted file]
src/boot/efi/boot.c
src/boot/efi/console.c
src/boot/efi/cpio.c
src/boot/efi/devicetree.c
src/boot/efi/drivers.c
src/boot/efi/efi-string.c
src/boot/efi/efi-string.h
src/boot/efi/fuzz-efi-printf.c [new file with mode: 0644]
src/boot/efi/graphics.c
src/boot/efi/linux.c
src/boot/efi/linux_x86.c
src/boot/efi/log.c [new file with mode: 0644]
src/boot/efi/log.h [new file with mode: 0644]
src/boot/efi/measure.c
src/boot/efi/meson.build
src/boot/efi/missing_efi.h
src/boot/efi/pe.c
src/boot/efi/random-seed.c
src/boot/efi/secure-boot.c
src/boot/efi/stub.c
src/boot/efi/test-efi-string.c
src/boot/efi/ticks.c
src/boot/efi/util.c
src/boot/efi/util.h
src/boot/efi/vmm.c
src/boot/efi/vmm.h
src/boot/pcrphase.c
src/busctl/busctl.c
src/core/bpf/meson.build
src/core/cgroup.c
src/core/dbus-manager.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment.c
src/core/mount.c
src/core/namespace.c
src/core/org.freedesktop.systemd1.conf
src/core/service.c
src/core/transaction.c
src/core/unit.c
src/coredump/coredump.c
src/coredump/coredump.conf
src/creds/creds.c
src/cryptenroll/cryptenroll-fido2.c
src/cryptenroll/cryptenroll-pkcs11.c
src/cryptenroll/cryptenroll-tpm2.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-util.c
src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
src/cryptsetup/cryptsetup-tpm2.c
src/cryptsetup/cryptsetup-tpm2.h
src/cryptsetup/cryptsetup.c
src/dissect/dissect.c
src/fstab-generator/fstab-generator.c
src/fundamental/macro-fundamental.h
src/fundamental/memory-util-fundamental.h
src/fundamental/tpm-pcr.h
src/gpt-auto-generator/gpt-auto-generator.c
src/home/homectl-fido2.c
src/home/homectl-pkcs11.c
src/home/homectl.c
src/home/homework-fido2.c
src/home/homework-fscrypt.c
src/home/homework-luks.c
src/hostname/hostnamectl.c
src/hostname/hostnamed.c
src/journal/journalctl.c
src/journal/journald-server.c
src/journal/test-journal-interleaving.c
src/kernel-install/90-loaderentry.install.in
src/kernel-install/test-kernel-install.sh
src/libsystemd-network/dhcp-internal.h
src/libsystemd-network/dhcp-network.c
src/libsystemd-network/dhcp-option.c
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/dhcp6-protocol.c
src/libsystemd-network/dhcp6-protocol.h
src/libsystemd-network/fuzz-dhcp-client.c
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/test-dhcp-client.c
src/libsystemd-network/test-dhcp6-client.c
src/libsystemd-network/test-ndisc-ra.c
src/libsystemd-network/test-ndisc-rs.c
src/libsystemd/libsystemd.sym
src/libsystemd/meson.build
src/libsystemd/sd-bus/bus-message.c
src/libsystemd/sd-bus/bus-message.h
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-device/device-monitor.c
src/libsystemd/sd-device/device-private.h
src/libsystemd/sd-device/device-util.c
src/libsystemd/sd-device/device-util.h
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-id128/id128-util.c
src/libsystemd/sd-id128/id128-util.h
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/journal-file.h
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-journal/test-journal-file.c [new file with mode: 0644]
src/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-login/test-login.c
src/libsystemd/sd-network/sd-network.c
src/libsystemd/sd-path/sd-path.c
src/locale/localed-util.c
src/network/netdev/l2tp-tunnel.c
src/network/networkctl.c
src/network/networkd-address.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp4.h
src/network/networkd-dhcp6.c
src/network/networkd-json.c
src/network/networkd-link.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-route.c
src/network/networkd-state-file.c
src/network/networkd-wifi.c
src/network/test-networkd-conf.c
src/nspawn/nspawn.c
src/partition/makefs.c
src/partition/repart.c
src/path/path.c
src/portable/portable.c
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-search-domain.c
src/resolve/resolved-dns-server.c
src/shared/ask-password-api.c
src/shared/blockdev-util.c
src/shared/blockdev-util.h
src/shared/bootspec.c
src/shared/btrfs-util.c
src/shared/btrfs-util.h
src/shared/creds-util.c
src/shared/discover-image.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/efi-loader.c
src/shared/efi-loader.h
src/shared/ethtool-util.c
src/shared/fdisk-util.c
src/shared/fdisk-util.h
src/shared/format-table.c
src/shared/format-table.h
src/shared/generator.c
src/shared/generator.h
src/shared/install.c
src/shared/logs-show.c
src/shared/logs-show.h
src/shared/loop-util.c
src/shared/loop-util.h
src/shared/mkfs-util.c
src/shared/mkfs-util.h
src/shared/open-file.c
src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/sysext/sysext.c
src/systemctl/systemctl-list-dependencies.c
src/systemctl/systemctl-show.c
src/systemctl/systemctl-util.c
src/systemctl/systemctl-util.h
src/systemctl/systemctl.c
src/systemd/sd-dhcp-client.h
src/systemd/sd-dhcp6-client.h
src/systemd/sd-login.h
src/systemd/sd-messages.h
src/systemd/sd-network.h
src/systemd/sd-path.h
src/test/meson.build
src/test/test-fs-util.c
src/test/test-hexdecoct.c
src/test/test-in-addr-util.c
src/test/test-list.c
src/test/test-load-fragment.c
src/test/test-log.c
src/test/test-loop-block.c
src/test/test-string-util.c
src/test/test-strv.c
src/test/test-time-util.c
src/test/test-tpm2.c
src/test/test-unit-name.c
src/timesync/timesyncd-server.c
src/timesync/timesyncd.c
src/udev/net/link-config.c
src/udev/net/link-config.h
src/udev/udev-builtin-net_setup_link.c
src/udev/udev-node.c
src/udev/udev-rules.c
src/udev/udevadm-trigger.c
src/ukify/ukify.py
test/README.testsuite
test/TEST-70-TPM2/test.sh
test/networkd-test.py
test/test-functions
test/test-network/conf/25-dhcp-client-ipv4-only.network
test/test-network/conf/25-dhcp-client-with-static-address.network [deleted file]
test/test-network/systemd-networkd-tests.py
test/udev-test.pl
test/units/testsuite-17.02.sh
test/units/testsuite-26.sh
test/units/testsuite-65.sh
test/units/testsuite-70.sh
test/units/testsuite-71.sh
test/units/testsuite-73.sh
test/units/testsuite-77-client.sh [moved from test/units/testsuite-77-socket.sh with 100% similarity]
test/units/testsuite-77-netcat.service [deleted file]
test/units/testsuite-77-netcat.sh [deleted file]
test/units/testsuite-77-server.socket [new file with mode: 0644]
test/units/testsuite-77-server@.service [new file with mode: 0644]
test/units/testsuite-77-socket.service [deleted file]
test/units/testsuite-77.sh
units/meson.build
units/systemd-growfs-root.service.in [new file with mode: 0644]
units/systemd-growfs@.service.in [new file with mode: 0644]
units/systemd-journald-audit.socket
units/systemd-journald.service.in
units/systemd-pcrfs-root.service.in [new file with mode: 0644]
units/systemd-pcrfs@.service.in [new file with mode: 0644]
units/systemd-pcrmachine.service.in [new file with mode: 0644]

index 0d18c6c18be3710f660020941d43e67140de65a7..c90044c9a85fc92b74b75c8e8acc42e5d616b9c5 100755 (executable)
@@ -143,7 +143,8 @@ for args in "${ARGS[@]}"; do
     if ! AR="$AR" \
          CC="$CC" CC_LD="$LD" CFLAGS="-Werror" \
          CXX="$CXX" CXX_LD="$LD" CXXFLAGS="-Werror" \
-         meson -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true --werror \
+         meson setup \
+               -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true --werror \
                -Dnobody-group=nogroup -Dcryptolib="${CRYPTOLIB:?}" \
                $args build; then
 
@@ -152,7 +153,7 @@ for args in "${ARGS[@]}"; do
     fi
 
     if ! meson compile -C build -v; then
-        fatal "'meson compile' failed with $args"
+        fatal "'meson compile' failed with '$args'"
     fi
 
     for loader in build/src/boot/efi/*.efi; do
@@ -163,5 +164,5 @@ for args in "${ARGS[@]}"; do
 
     git clean -dxf
 
-    success "Build with $args passed in $SECONDS seconds"
+    success "Build with '$args' passed in $SECONDS seconds"
 done
index 400e8c6fb62b905cfdb811eaba2d8143f6634726..e7e61bcacd0f7a0486fa2c8f8aa1e456d52e8d21 100644 (file)
@@ -21,14 +21,14 @@ jobs:
       pull-requests: write
     steps:
     - uses: actions/labeler@e54e5b338fbd6e6cdb5d60f51c22335fc57c401e
-      if: github.event_name == 'pull_request_target'
+      if: github.event_name == 'pull_request_target' && github.event.action != 'closed'
       with:
         repo-token: "${{ secrets.GITHUB_TOKEN }}"
         configuration-path: .github/labeler.yml
         sync-labels: "" # This is a workaround for issue 18671
 
     - uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
-      if: github.event_name == 'pull_request_target' && !github.event.pull_request.draft
+      if: github.event_name == 'pull_request_target' && github.event.action != 'closed' && !github.event.pull_request.draft
       with:
         script: |
           response = await github.rest.issues.listLabelsOnIssue({
@@ -83,7 +83,7 @@ jobs:
           })
 
     - uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
-      if: github.event_name == 'pull_request_target' && github.event.issue.pull_request && github.event.pull_request.merged == true
+      if: github.event_name == 'pull_request_target' && github.event.action == 'closed' && github.event.issue.pull_request
       with:
         script: |
           for (const label of ["please-review",
index 0e3cfae7b8f7c23a0f2f774ba12baee40c882e60..357b4f420594736abe97bd009f440583e072433f 100644 (file)
@@ -72,24 +72,14 @@ jobs:
             release: rawhide
           - distro: opensuse
             release: tumbleweed
-          - distro: centos_epel
+          - distro: centos
             release: 9-stream
-          - distro: centos_epel
+          - distro: centos
             release: 8-stream
 
     steps:
     - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b
-    - uses: systemd/mkosi@c9772ec920f1cd03181ba14e6fe2c3d35ccb4f92
-
-    # FIXME: temporary workaround for a file conflict between systemd (C9S) and
-    # systemd-boot (EPEL9). Drop this once systemd in C9S is updated to v252
-    # (should be done by the end of 2022).
-    - name: Fix C9S/EPEL9
-      if: ${{ matrix.release == '9-stream' }}
-      run: sudo sed -i '/add_packages/s/systemd-boot/systemd/g' /usr/local/lib/python3.10/dist-packages/mkosi/__init__.py
-
-    - name: Install
-      run: sudo apt-get update && sudo apt-get install --no-install-recommends python3-pexpect python3-jinja2
+    - uses: systemd/mkosi@ab2aff830096e681da1950a7d29f277caa89516c
 
     - name: Configure
       run: |
@@ -106,7 +96,7 @@ jobs:
         EOF
 
     - name: Build ${{ matrix.distro }}
-      run: sudo mkosi build
+      run: sudo mkosi --idmap no
 
     - name: Show ${{ matrix.distro }} image summary
       run: sudo mkosi summary
diff --git a/NEWS b/NEWS
index 7e1e53606050dcc76d816f5ca643f760b81ee943..4b70cce9cda878a7720c025ba0e1028a36e92596 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -113,12 +113,33 @@ CHANGES WITH 253 in spe:
 
         Changes in systemd-networkd and related tools:
 
+        * The [DHCPv4] section in .network file gained new SocketPriority=
+          setting that assigns the Linux socket priority used by the DHCPv4
+          raw socket. Can be used in conjunction with the EgressQOSMaps=setting
+          in [VLAN] section of .netdev file to send the desired ethernet 802.1Q
+          frame priority for DHCPv4 initial packets. This cannot be achieved
+          with netfilter mangle tables because of the raw socket bypass.
+
+        * The [DHCPv4] and [IPv6AcceptRA] sections in .network file gained new
+          QuickAck= boolean setting that enables the TCP quick ACK mode for the
+          routes configured by the acquired DHCPv4 lease or received router
+          advertisements (RAs).
+
         * The RouteMetric= option (for DHCPv4, DHCPv6, and IPv6 advertised
           routes) now accepts three values, for high, medium, and low preference
           of the router (which can be set with the RouterPreference=) setting.
 
         * systemd-networkd-wait-online now supports alternative interface names.
 
+        * The [DHCPv6] section in .network file gained new SendRelease=
+          setting which enables the DHCPv6 client to send release when
+          it stops. This is the analog of the [DHCPv4] SendRelease= setting.
+          It is enabled by default.
+
+        * If the Address= setting in [Network] or [Address] sections in .network
+          specified without its prefix length, then now systemd-networkd assumes
+          /32 for IPv4 or /128 for IPv6 addresses.
+
         Changes in systemd-dissect:
 
         * systemd-dissect gained a new option --list, to print the paths fo the
diff --git a/TODO b/TODO
index 899b6a1de1f068c037250d740b75923033a6a227..8de57904221efcb997bb7d2f8a4f54027496e066 100644 (file)
--- a/TODO
+++ b/TODO
@@ -126,6 +126,17 @@ Deprecations and removals:
 
 Features:
 
+* split out execute.c into new "systemd-executor" binary. Then make PID 1 fork
+  that off via vfork(), and then let that executor do the hard work. Ultimately
+  the executor then gets replaced by the real binary sooner or later. Reason:
+  currently the intermediary "stub" process is a CoW trap that doubles memory
+  usage of PID 1 on each service start. Also, strictly speaking we are not
+  allowed to do NSS from the stub process yet we do anyway. Next steps would
+  then be maybe use CLONE_INTO_CGROUP for the executor, given that we don't
+  need glibc anymore in the stub process then. Then, switch nspawn to just be a
+  frontend for this too, so that we have to ways into the executor: via unit
+  files/dbus/varlin through PID1 and via cmdline/OCI through nspawn.
+
 * sd-stub: detect if we are running with uefi console output on serial, and if so
   automatically add console= to kernel cmdline matching the same port.
 
@@ -440,9 +451,8 @@ Features:
   and via the time window TPM logic invalidated if node doesn't keep itself
   updated, or becomes corrupted in some way.
 
-* Always measure the LUKS rootfs volume key into PCR 15, and derive the machine
-  ID from it securely. This would then allow us to bind secrets a specific
-  system securely.
+* in the initrd, once the rootfs encryption key has been measured to PCR 15,
+  derive default machine ID to use from it, and pass it to host PID 1.
 
 * tree-wide: convert as much as possible over to use sd_event_set_signal_exit(), instead
   of manually hooking into SIGINT/SIGTERM
index 4c29128f71485254a281f8ca38b34d1f53826822..975e77fcec1112db63af7946c48a97d0b655d19f 100644 (file)
@@ -528,14 +528,24 @@ 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.
 
+-- 7db73c8af0d94eeb822ae04323fe6ab6
+Subject: Initial clock bump
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+The system clock has been advanced based on a timestamp file on disk, in order
+to ensure it remains roughly monotonic – even across reboots – if an RTC is not
+available or is unreliable.
+
 -- 3f7d5ef3e54f4302b4f0b143bb270cab
 Subject: TPM PCR Extended
 Defined-By: systemd
 Support: %SUPPORT_URL%
 
-The string '@MEASURING@' has been extended into Trusted Platform Module's (TPM)
-Platform Configuration Register (PCR) @PCR@, on banks @BANKS@.
+The Trusted Platform Module's (TPM) Platform Configuration Register (PCR)
+@PCR@, on banks @BANKS@, has been extended with the string '@MEASURING@'.
 
-Whenever the system transitions to a new runtime phase, a different string is
-extended into the specified PCR, to ensure that security policies for TPM-bound
-secrets and other resources are limited to specific phases of the runtime.
+Whenever the system transitions to a new runtime phase, the specified PCR is
+extended with a different string, to ensure that security policies for
+TPM-bound secrets and other resources are limited to specific phases of the
+runtime.
index c96325b3fe560fa5b689a9391a672a3f53579362..e86b41eb4aa7ffffba9f35e0b5b98bd1a2bbd520 100644 (file)
@@ -120,6 +120,19 @@ SPDX-License-Identifier: LGPL-2.1-or-later
   `_SD_ENUM_FORCE_S64()` macro in the enum definition, which forces the size of
   the enum to be signed 64bit wide.
 
+- Empty lines to separate code blocks are a good thing, please add them
+  abundantly. However, please stick to one at a time, i.e. multiple empty lines
+  immediately following each other are not OK. Also, we try to keep function calls and their immediate error handling together. Hence:
+
+  ```c
+  /* → empty line here is good */
+  r = some_function(…);
+  /* → empty line here would be bad */
+  if (r < 0)
+          return log_error_errno(r, "Some function failed: %m");
+  /* → empty line here is good */
+  ```
+
 ## Code Organization and Semantics
 
 - For our codebase we intend to use ISO C11 *with* GNU extensions (aka
@@ -210,8 +223,9 @@ SPDX-License-Identifier: LGPL-2.1-or-later
   the point where they can be initialized. Avoid huge variable declaration
   lists at the top of the function.
 
-  As an exception, `r` is typically used for a local state variable, but should
-  almost always be declared as the last variable at the top of the function.
+  As an exception, `int r` is typically used for a local state variable, but
+  should almost always be declared as the last variable at the top of the
+  function.
 
   ```c
   {
index debe0a714f4c1d71df207a276602c19ba7810a0b..04309a15fef167c990a9a23a3eb0f6679954c787 100644 (file)
@@ -337,10 +337,8 @@ Various services shipped with `systemd` consume credentials for tweaking behavio
   to receive a notification via VSOCK when a virtual machine has finished booting.
   Note that in case the hypervisor does not support `SOCK_DGRAM` over `AF_VSOCK`,
   `SOCK_SEQPACKET` will be tried instead. The credential payload should be in the
-  form: `vsock:<CID>:<PORT>`, where `<CID>` is optional and if omitted will
-  default to talking to the hypervisor (`0`). Also note that this requires
-  support for VHOST to be built-in both the guest and the host kernels, and the
-  kernel modules to be loaded.
+  form: `vsock:<CID>:<PORT>`. Also note that this requires support for VHOST to be
+  built-in both the guest and the host kernels, and the kernel modules to be loaded.
 
 * [`systemd-sysusers(8)`](https://www.freedesktop.org/software/systemd/man/systemd-sysusers.html)
   will look for the credentials `passwd.hashed-password.<username>`,
index 2b751d9d533d0cdbc44db60704e45ecfd18ec266..30ef1e7e42ad96d3f4d7944eb6835b5babaffc59 100644 (file)
@@ -114,6 +114,9 @@ All tools:
   for example in `systemd-nspawn`, will be logged to the audit log, if the
   kernel supports this.
 
+* `$SYSTEMD_ENABLE_LOG_CONTEXT` — if set, extra fields will always be logged to
+the journal instead of only when logging in debug mode.
+
 `systemctl`:
 
 * `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID 1's private D-Bus
@@ -506,7 +509,10 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
   journal. Note that journal files in compact mode are limited to 4G to allow use of
   32-bit offsets. Enabled by default.
 
-`systemd-pcrphase`:
+`systemd-pcrphase`, `systemd-cryptsetup`:
 
-* `$SYSTEMD_PCRPHASE_STUB_VERIFY` – Takes a boolean. If false the requested
-  measurement is done even if no EFI stub usage was reported via EFI variables.
+* `$SYSTEMD_FORCE_MEASURE=1` — If set, force measuring of resources (which are
+  marked for measurement) even if not booted on a kernel equipped with
+  systemd-stub. Normally, requested measurement of resources is conditionalized
+  on kernels that have booted with `systemd-stub`. With this environment
+  variable the test for that my be bypassed, for testing purposes.
index 169e3e6f66db767a3f2e51b4a65d1b88c8cc54dc..47d4583ef299ba91b5e9a8b17fce45ae9f9e2967 100644 (file)
@@ -416,6 +416,8 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnXPS12-9Q33*:*
  KEYBOARD_KEY_88=wlan
  KEYBOARD_KEY_65=direction                              # Screen Rotate
 
+# Dell G16 microphone mute
+evdev:name:Dell WMI hotkeys:dmi:bvn*:bvr*:bd*:svnDell*:pnDellG16*:*
 # Dell Latitude microphone mute
 evdev:name:Dell WMI hotkeys:dmi:bvn*:bvr*:bd*:svnDell*:pnLatitude*:*
 # Dell Precision microphone mute
@@ -607,6 +609,8 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPENVYx360Convertible13*:*
 
 # Spectre x360 16 2022
 evdev:name:Intel HID events:dmi:bvn*:bvr*:bd*:svnHP*:pn*HP[sS][pP][eE][cC][tT][rR][eE]*x3602-in-1*:*
+# ENVY x360
+evdev:name:Intel HID events:dmi:bvn*:bvr*:bd*:svnHP*:pnHPENVYx360Convertible*:*
  KEYBOARD_KEY_08=unknown                                #  Prevents random airplane mode activation
 
 # HP Elite x2 1013 G3
@@ -1829,6 +1833,10 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnSystem76*:pn*:*
  KEYBOARD_KEY_f7=f21                                    # Touchpad toggle
  KEYBOARD_KEY_f8=f21                                    # Touchpad toggle
 
+# Pangolin 12
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnSystem76*:pnPangolin*:pvrpang12*
+ KEYBOARD_KEY_76=f21                                    # Touchpad toggle
+
 ###########################################################
 # T-bao
 ###########################################################
@@ -2026,7 +2034,7 @@ evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:bvr*:bd*:svnPositivoBahia-VAIO:
 ###########################################################
 # Positivo
 ###########################################################
-# Positivo MASTER-N1110 
+# Positivo MASTER-N1110
 evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:svnPositivoTecnologiaSA:pn*:pvr*:rvnPositivoTecnologiaSA:rnNP11G-E*
 # Positivo DUO (k116)
 evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:svnPositivoTecnologiaSA:pn*:pvr*:rvnPositivoTecnologiaSA:rnK116*
index df81942fc8facbcd33b1f42da4c1cff4eedcdbbd..31f7bf031d7094ff190f1bd06fe85f07ee8fff5b 100644 (file)
@@ -233,6 +233,10 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svn*CHUWIINNOVATIONANDTECHNOLOGY*:pnHi10pro
 sensor:modalias:acpi:MXC6655*:dmi:*:svnCHUWIInnovationAndTechnology*:pnHi10X:*
  ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
 
+# Chuwi Hi10 X (N4120 processor version)
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnCHUWIInnovationAndTechnology*:pnHi10X:*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+
 # Chuwi Hi10 Go
 sensor:modalias:acpi:MXC6655*:dmi:*:svnCHUWIINNOVATIONLIMITED:pnHi10Go:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0,-1, 0; 0, 0, 1
@@ -652,6 +656,10 @@ sensor:modalias:i2c:bmc150_accel:dmi:*:svnLENOVO:*:pvrLenovoYoga300-11IBR:*
 sensor:modalias:acpi:BOSC0200*:dmi:*:svnLINX*:pnLINX1010B:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, -1
 
+# Linx 1020
+sensor:modalias:acpi:MIRAACC*:dmi:*:svnLINX*:pnLINX1020:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, -1
+
 # Linx 12X64, 12V64 and Vision 8
 sensor:modalias:acpi:KIOX000A*:dmi:*:svnLINX*:pnLINX12*64:*
 sensor:modalias:acpi:KIOX000A*:dmi:*:svnLINX:pnVISION004:*
index f03f836746f8e49dfb14d0e9daecb4824b62b55b..52c9179cd0f2520277e2e06f5128eb92091f627b 100644 (file)
   <refsect1>
     <title>Boot Loader Specification Commands</title>
 
-    <para>These commands are available for all boot loaders that implement the <ulink
-    url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink> and/or the <ulink
-    url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>, such as
+    <para>These commands are available for all boot loaders that
+    implement the <ulink
+    url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot
+    Loader Specification</ulink>, such as
     <command>systemd-boot</command>.</para>
 
     <variablelist>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>unlink</option> <replaceable>ID</replaceable></term>
+
+        <listitem><para>Removes a boot loader entry including the files it refers to. Takes a single boot
+        loader entry ID string or a glob pattern as argument. Referenced files such as kernel or initrd are
+        only removed if no other entry refers to them.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>cleanup</option></term>
+
+        <listitem><para>Removes files from the ESP and XBOOTLDR partitions that belong to the entry token but
+        are not referenced in any boot loader entries.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Boot Loader Interface Commands</title>
+
+    <para>These commands are available for all boot loaders that implement the <ulink
+    url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink> and the <ulink
+    url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>, such as
+    <command>systemd-boot</command>.</para>
+
+    <variablelist>
       <varlistentry>
         <term><option>set-default</option> <replaceable>ID</replaceable></term>
         <term><option>set-oneshot</option> <replaceable>ID</replaceable></term>
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>Kernel Image Commands</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>kernel-identify</option> <replaceable>kernel</replaceable></term>
+
+        <listitem><para>Takes a kernel image as argument. Checks what kind of kernel the image is.  Returns
+        one of uki, pe or unknown.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>kernel-inspect</option> <replaceable>kernel</replaceable></term>
+
+        <listitem><para>Takes a kernel image as argument. Prints details about the kernel.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Options</title>
     <para>The following options are understood:</para>
         the firmware's boot option menu.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--dry-run</option></term>
+        <listitem><para>Dry run for <option>--unlink</option> and <option>--cleanup</option>.</para>
+
+        <para>In dry run mode, the unlink and cleanup operations only print the files that would get deleted
+        without actually deleting them.</para></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="no-pager"/>
       <xi:include href="standard-options.xml" xpointer="json" />
       <xi:include href="standard-options.xml" xpointer="help"/>
       <programlisting>$ <command>bootctl status</command>
 System:
      Firmware: UEFI 2.40 (<replaceable>firmware-version</replaceable>)  ← firmware vendor and version
-  Secure Boot: disabled (setup)              ← secure boot status
+  Secure Boot: disabled (setup)              ← Secure Boot status
  TPM2 Support: yes
  Boot into FW: supported                     ← does the firmware support booting into itself
 
diff --git a/man/cgroup-sandboxing.xml b/man/cgroup-sandboxing.xml
new file mode 100644 (file)
index 0000000..56f7c40
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+
+<!--
+  SPDX-License-Identifier: LGPL-2.1-or-later
+-->
+
+<refsect1>
+
+<para id="singular">This option cannot be bypassed by prefixing <literal>+</literal> to the executable path
+in the service unit, as it applies to the whole control group.</para>
+
+<para id="plural">These options cannot be bypassed by prefixing <literal>+</literal> to the executable path
+in the service unit, as it applies to the whole control group.</para>
+
+</refsect1>
index 896a62358d0b23502f9f9d4c1f7380cf223cbaa7..d587f85289fdd58d020e31372a2ec9c90c82145f 100644 (file)
         order).</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>tpm2-measure-pcr=</option></term>
+
+        <listitem><para>Controls whether to measure the volume key of the encrypted volume to a TPM2 PCR. If
+        set to "no" (which is the default) no PCR extension is done. If set to "yes" the volume key is
+        measured into PCR 15. If set to a decimal integer in the range 0…23 the volume key is measured into
+        the specified PCR. The volume key is measured along with the activated volume name and its UUID. This
+        functionality is particularly useful for the encrypted volume backing the root file system, as it
+        then allows later TPM objects to be securely bound to the root file system and hence the specific
+        installation.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>tpm2-measure-bank=</option></term>
+
+        <listitem><para>Selects one or more TPM2 PCR banks to measure the volume key into, as configured with
+        <option>tpm2-measure-pcr=</option> above. Multiple banks may be specified, separated by a colon
+        character. If not specified automatically determines available and used banks. Expects a message
+        digest name (e.g. <literal>sha1</literal>, <literal>sha256</literal>, …) as argument, to identify the
+        bank.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>token-timeout=</option></term>
 
index d9ee51b302aca90db900fe330945a7155dd5aa48..109797776e6927b5126f1981f55b679ddc59a27c 100644 (file)
@@ -18,7 +18,7 @@
 
   <refnamediv>
     <refname>journalctl</refname>
-    <refpurpose>Print log entries from the the systemd journal</refpurpose>
+    <refpurpose>Print log entries from the systemd journal</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
   <refsect1>
     <title>Forward Secure Sealing (FSS) Options</title>
 
-    <para>The following options make be used together with the <option>--setup-keys</option> command, see below.</para>
+    <para>The following options may be used together with the <option>--setup-keys</option> command described
+    below:</para>
 
     <variablelist>
       <varlistentry>
index 24cee4c8b2f6d15d729be4df55d2c6c85a79ae66..50c33e479294b1357acae730554c13aa67feef40 100644 (file)
       <varlistentry>
         <term><varname>Audit=</varname></term>
 
-        <listitem><para>Takes a boolean value. If enabled <command>systemd-journal</command> will turn on
+        <listitem><para>Takes a boolean value. If enabled <command>systemd-journald</command> will turn on
         kernel auditing on start-up. If disabled it will turn it off. If unset it will neither enable nor
-        disable it, leaving the previous state unchanged. Note that this option does not control whether
-        <command>systemd-journald</command> collects generated audit records, it just controls whether it
-        tells the kernel to generate them. This means if another tool turns on auditing even if
-        <command>systemd-journald</command> left it off, it will still collect the generated
-        messages. Defaults to on.</para></listitem>
+        disable it, leaving the previous state unchanged.  This means if another tool turns on auditing even
+        if <command>systemd-journald</command> left it off, it will still collect the generated
+        messages. Defaults to on.</para>
+
+        <para>Note that this option does not control whether <command>systemd-journald</command> collects
+        generated audit records, it just controls whether it tells the kernel to generate them. If you need
+        to prevent <command>systemd-journald</command> from collecting the generated messages, the socket
+        unit <literal>systemd-journald-audit.socket</literal> can be disabled and in this case this setting
+        is without effect.</para>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
index f3fdc961f4def15e89d581c92349e23712944d19..e50aeee9499d7736d2f415a0078adcf05886734a 100644 (file)
 
       <para><varname>$KERNEL_INSTALL_MACHINE_ID</varname> is set for the plugins to the desired machine-id to
       use. It's always a 128-bit ID. Normally it's read from <filename>/etc/machine-id</filename>, but it can
-      also be overridden via <varname>$MACHINE_ID</varname> (see below).  If not specified via these methods a
-      fallback value will generated by <command>kernel-install</command>, and used only for a single
+      also be overridden via <varname>$MACHINE_ID</varname> (see below). If not specified via these methods,
+      a fallback value will generated by <command>kernel-install</command> and used only for a single
       invocation.</para>
 
       <para><varname>$KERNEL_INSTALL_ENTRY_TOKEN</varname> is set for the plugins to the desired entry
index 8632ea94204266926733f2ff325bc6f6804bb073..c2a42cbfd46e08974b5f3f59f45fadad0a101162 100644 (file)
@@ -43,7 +43,7 @@
     equivalent replacement with a more modern API.</para>
 
     <para>All functions require a libudev context to operate. This
-    context can be create via
+    context can be created via
     <citerefentry><refentrytitle>udev_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     It is used to track library state and link objects together. No
     global state is used by libudev, everything is always linked to
index 245f4c4536600384feaf2811810dde36bb0d64b5..80122177e50b4d83e3fba5673deede2bc817a372 100644 (file)
         <listitem><para>Danger: this feature might soft-brick your device if used improperly.</para>
 
         <para>Takes one of <literal>off</literal>, <literal>manual</literal> or <literal>force</literal>.
-        Controls the enrollment of secure boot keys. If set to <literal>off</literal>, no action whatsoever
+        Controls the enrollment of Secure Boot keys. If set to <literal>off</literal>, no action whatsoever
         is taken. If set to <literal>manual</literal> (the default) and the UEFI firmware is in setup-mode
         then entries to manually enroll Secure Boot variables are created in the boot menu. If set to
         <literal>force</literal>, in addition, if a directory named <filename>/loader/keys/auto/</filename>
         exists on the ESP then the keys in that directory are enrolled automatically.</para>
 
-        <para>The different sets of variables can be set up under <filename>/loader/keys/<replaceable>NAME</replaceable></filename>
-        where <replaceable>NAME</replaceable> is the name that is going to be used as the name of the entry.
-        This allows one to ship multiple sets of Secure Boot variables and choose which one to enroll at runtime.</para>
+        <para>The different sets of variables can be set up under
+        <filename>/loader/keys/<replaceable>NAME</replaceable></filename> where
+        <replaceable>NAME</replaceable> is the name that is going to be used as the name of the entry. This
+        allows one to ship multiple sets of Secure Boot variables and choose which one to enroll at runtime.
+        </para>
 
-        <para>Supported secure boot variables are one database for authorized images, one key exchange key (KEK)
-        and one platform key (PK). For more information, refer to the <ulink url="https://uefi.org/specifications">UEFI specification</ulink>,
-        under Secure Boot and Driver Signing. Another resource that describe the interplay of the different variables is the
+        <para>Supported Secure Boot variables are one database for authorized images, one key exchange key
+        (KEK) and one platform key (PK). For more information, refer to the <ulink
+        url="https://uefi.org/specifications">UEFI specification</ulink>, under Secure Boot and Driver
+        Signing. Another resource that describe the interplay of the different variables is the
         <ulink url="https://edk2-docs.gitbook.io/understanding-the-uefi-secure-boot-chain/secure_boot_chain_in_uefi/uefi_secure_boot">
         EDK2 documentation</ulink>.</para>
 
@@ -294,17 +297,17 @@ sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth
         <para>Work around BitLocker requiring a recovery key when the boot loader was
         updated (disabled by default).</para>
 
-        <para>Try to detect BitLocker encrypted drives along with an active TPM. If both are found
-        and Windows Boot Manager is selected in the boot menu, set the <literal>BootNext</literal>
-        EFI variable and restart the system. The firmware will then start Windows Boot Manager
-        directly, leaving the TPM PCRs in expected states so that Windows can unseal the encryption
-        key. This allows systemd-boot to be updated without having to provide the recovery key for
-        BitLocker drive unlocking.</para>
+        <para>Try to detect BitLocker encrypted drives along with an active TPM. If both are found and
+        Windows Boot Manager is selected in the boot menu, set the <literal>BootNext</literal> EFI variable
+        and restart the system. The firmware will then start Windows Boot Manager directly, leaving the TPM
+        PCRs in expected states so that Windows can unseal the encryption key. This allows
+        <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> to
+        be updated without having to provide the recovery key for BitLocker drive unlocking.</para>
 
         <para>Note that the PCRs that Windows uses can be configured with the
         <literal>Configure TPM platform validation profile for native UEFI firmware configurations</literal>
         group policy under <literal>Computer Configuration\Administrative Templates\Windows Components\BitLocker Drive Encryption</literal>.
-        When secure boot is enabled, changing this to PCRs <literal>0,2,7,11</literal> should be safe.
+        When Secure Boot is enabled, changing this to PCRs <literal>0,2,7,11</literal> should be safe.
         The TPM key protector needs to be removed and then added back for the PCRs on an already
         encrypted drive to change. If PCR 4 is not measured, this setting can be disabled to speed
         up booting into Windows.</para></listitem>
index b6544cf65d5cbd82377163779d0d61b9bc8fb15d..19e7aa237ac01de1b15227cf5e3349395402c494 100644 (file)
@@ -109,7 +109,9 @@ rpc:            db files
 
 netgroup:       nis</programlisting>
 
-    <para>To test, use <command>glibc</command>'s <command>getent</command> tool:</para>
+    <para>To test, use <command>glibc</command>'s
+    <citerefentry project='man-pages'><refentrytitle>getent</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    tool:</para>
 
     <programlisting>$ getent ahosts `hostname`
 ::1       STREAM omega
index 02d7063e582e0d3f3bd680fdfd9fef5c5d7aa1e4..be36d8fc7e3772b154eab47243300e095bebab03 100644 (file)
@@ -92,7 +92,7 @@ node /org/freedesktop/hostname1 {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s FirmwareVendor = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
-      readonly s FirmwareDate = '...';
+      readonly t FirmwareDate = ...;
   };
   interface org.freedesktop.DBus.Peer { ... };
   interface org.freedesktop.DBus.Introspectable { ... };
index 8eb30afd8599bca753b2c827e1efda2b35530fe4..bbb6b1433938c034fb834b4594a41ce2f086a5f7 100644 (file)
@@ -65,6 +65,10 @@ node /org/freedesktop/systemd1 {
                             out o unit);
       GetUnitByControlGroup(in  s cgroup,
                             out o unit);
+      GetUnitByPIDFD(in  h pidfd,
+                     out o unit,
+                     out s unit_id,
+                     out ay invocation_id);
       LoadUnit(in  s name,
                out o unit);
       StartUnit(in  s name,
@@ -796,6 +800,8 @@ node /org/freedesktop/systemd1 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="GetUnitByControlGroup()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUnitByPIDFD()"/>
+
     <variablelist class="dbus-method" generated="True" extra-ref="LoadUnit()"/>
 
     <variablelist class="dbus-method" generated="True" extra-ref="StartUnit()"/>
@@ -1219,7 +1225,11 @@ node /org/freedesktop/systemd1 {
       will fail.</para>
 
       <para><function>GetUnitByPID()</function> may be used to get the unit object path of the unit a process
-      ID belongs to. It takes a UNIX PID and returns the object path. The PID must refer to an existing system process.</para>
+      ID belongs to. It takes a UNIX PID and returns the object path. The PID must refer to an existing system process.
+      <function>GetUnitByPIDFD()</function> may be used to query with a Linux PIDFD (see:
+      <citerefentry><refentrytitle>pidfd_open</refentrytitle><manvolnum>2</manvolnum></citerefentry>) instead
+      of a PID, which is safer as UNIX PIDs can be recycled. The latter method returns the unit id and the
+      invocation id together with the unit object path.</para>
 
       <para><function>LoadUnit()</function> is similar to <function>GetUnit()</function> but will load the
       unit from disk if possible.</para>
index 7325f840b9c4815d622db44c3e8b2266ec04495b..113ef9fc18a8f0fc038862faa17882de9994f9aa 100644 (file)
         <varlistentry>
           <term><varname>PORTABLE_PREFIXES=</varname></term>
           <listitem><para>Takes a space-separated list of one or more valid prefix match strings for the
-          <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> logic. This field
-          serves two purposes: it is informational, identifying portable service images as such (and thus
-          allowing them to be distinguished from other OS images, such as bootable system images). It is also
-          used when a portable service image is attached: the specified or implied portable service prefix is
-          checked against the list specified here, to enforce restrictions how images may be attached to a
-          system.</para></listitem>
+          <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services Documentation</ulink> logic.
+          This field serves two purposes: it is informational, identifying portable service images as such
+          (and thus allowing them to be distinguished from other OS images, such as bootable system images).
+          It is also used when a portable service image is attached: the specified or implied portable
+          service prefix is checked against the list specified here, to enforce restrictions how images may
+          be attached to a system.</para></listitem>
         </varlistentry>
       </variablelist>
     </refsect2>
index 9fa0e0a7e7c21ec1776599c552bf3e3a63090239..9d07aa96c76c239c5d9f3caa44c531969ef1ff7b 100644 (file)
   <refsect1>
     <title>Module Types Provided</title>
 
-    <para>The module implements all four PAM operations: <option>auth</option> (reason: to allow
-    authentication using the encrypted data), <option>account</option> (reason: users with
+    <para>The module implements all four PAM operations: <option>auth</option> (to allow authentication using
+    the encrypted data), <option>account</option> (because users with
     <filename>systemd-homed.service</filename> user accounts are described in a <ulink
     url="https://systemd.io/USER_RECORD/">JSON user record</ulink> and may be configured in more detail than
-    in the traditional Linux user database), <option>session</option> (user sessions must be tracked in order
-    to implement automatic release when the last session of the user is gone), <option>password</option> (to
-    change the encryption password — also used for user authentication — through PAM).</para>
+    in the traditional Linux user database), <option>session</option> (because user sessions must be tracked
+    in order to implement automatic release when the last session of the user is gone),
+    <option>password</option> (to change the encryption password — also used for user authentication —
+    through PAM).</para>
   </refsect1>
 
   <refsect1>
index 963361e28cbedd73aea1c4262608ff44adc48118..162db7658a6871d2b1a9bf8c21d5559fca715bec 100644 (file)
@@ -48,7 +48,7 @@
     and transfer them as a whole between systems. When these images are attached the local system the contained units
     may run in most ways like regular system-provided units, either with full privileges or inside strict sandboxing,
     depending on the selected configuration. For more details, see
-    <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>.</para>
+    <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services Documentation</ulink>.</para>
 
     <para>Specifically portable service images may be of the following kind:</para>
 
         <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
         Images can be block images, btrfs subvolumes or directories. For more information on portable
         services with extensions, see the <literal>Extension Images</literal> paragraph on
-        <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>.
+        <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services Documentation</ulink>.
         </para>
 
         <para>Note that the same extensions have to be specified, in the same order, when attaching
index 1da38a4e91b7d52648462037894ac6e1f749b9ac..846e1984e59125c1674a28795ef7071ff5bbb11d 100644 (file)
 
         <para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para>
 
-        <para>When <command>systemd-repart</command> is invoked with the <option>--image=</option> or
-        <option>--root=</option> command line switches the source paths specified are taken relative to the
-        specified root directory or disk image root.</para></listitem>
+        <para>When
+        <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        is invoked with the <option>--image=</option> or <option>--root=</option> command line switches the
+        source paths specified are taken relative to the specified root directory or disk image root.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
         to <literal>off</literal> or <literal>data</literal>, the partition is populated with content as
         specified by <varname>CopyBlocks=</varname> or <varname>CopyFiles=</varname>. If set to
         <literal>hash</literal>, the partition will be populated with verity hashes from the matching verity
-        data partition. If set to <literal>signature</literal>, The partition will be populated with a JSON
+        data partition. If set to <literal>signature</literal>, the partition will be populated with a JSON
         object containing a signature of the verity root hash of the matching verity hash partition.</para>
 
         <para>A matching verity partition is a partition with the same verity match key (as configured with
index 5be6c9ba46ac799929d3a55752b9ffaac4b1c48f..194cc0b904c58c245a5635055ba5de83ea3226da 100644 (file)
@@ -815,7 +815,15 @@ manpages = [
    'sd_pid_get_slice',
    'sd_pid_get_unit',
    'sd_pid_get_user_slice',
-   'sd_pid_get_user_unit'],
+   'sd_pid_get_user_unit',
+   'sd_pidfd_get_cgroup',
+   'sd_pidfd_get_machine_name',
+   'sd_pidfd_get_owner_uid',
+   'sd_pidfd_get_session',
+   'sd_pidfd_get_slice',
+   'sd_pidfd_get_unit',
+   'sd_pidfd_get_user_slice',
+   'sd_pidfd_get_user_unit'],
   'HAVE_PAM'],
  ['sd_seat_get_active',
   '3',
@@ -966,7 +974,10 @@ manpages = [
  ['systemd-path', '1', [], ''],
  ['systemd-pcrphase.service',
   '8',
-  ['systemd-pcrphase',
+  ['systemd-pcrfs-root.service',
+   'systemd-pcrfs@.service',
+   'systemd-pcrmachine.service',
+   'systemd-pcrphase',
    'systemd-pcrphase-initrd.service',
    'systemd-pcrphase-sysinit.service'],
   'HAVE_GNU_EFI'],
index 5858be106273d4f940bd691c5ba1efb14494cd61..eaad91f52e8d30953ef8cf6654a00e23bd5ecd40 100644 (file)
@@ -46,7 +46,7 @@
     <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry> returns zero,
     indicating that no work is pending on the connection. Internally, this call invokes <citerefentry
     project='man-pages'><refentrytitle>ppoll</refentrytitle><manvolnum>2</manvolnum></citerefentry>, to wait for I/O on
-    the bus connection. If the <parameter>timeout_sec</parameter> parameter is specified, the call will block at most
+    the bus connection. If the <parameter>timeout_usec</parameter> parameter is specified, the call will block at most
     for the specified amount of time in µs. Pass <constant>UINT64_MAX</constant> to permit it to sleep
     indefinitely.</para>
 
index 0ba191c0cee8fc2c7d09f75c25bf3af8825a84ef..8ef1c507caceab86951007e08eef8f5f725ce117 100644 (file)
       <para>A service could send the following after completing
       initialization:</para>
 
-      <programlisting>sd_notifyf(0, "READY=1\n"
-        "STATUS=Processing requests…\n"
-        "MAINPID=%lu",
-        (unsigned long) getpid());</programlisting>
+      <programlisting>
+sd_notifyf(0, "READY=1\n"
+           "STATUS=Processing requests…\n"
+           "MAINPID=%lu",
+           (unsigned long) getpid());</programlisting>
     </example>
 
     <example>
 
       <para>A service could send the following shortly before exiting, on failure:</para>
 
-      <programlisting>sd_notifyf(0, "STATUS=Failed to start up: %s\n"
-        "ERRNO=%i",
-        strerror_r(errnum, (char[1024]){}, 1024),
-        errnum);</programlisting>
+      <programlisting>
+sd_notifyf(0, "STATUS=Failed to start up: %s\n"
+           "ERRNO=%i",
+           strerror_r(errnum, (char[1024]){}, 1024),
+           errnum);</programlisting>
     </example>
 
     <example>
       to synchronize against reception of all notifications sent before
       this call is made.</para>
 
-      <programlisting>sd_notify(0, "READY=1");
-      /* set timeout to 5 seconds */
-      sd_notify_barrier(0, 5 * 1000000);
+      <programlisting>
+sd_notify(0, "READY=1");
+/* set timeout to 5 seconds */
+sd_notify_barrier(0, 5 * 1000000);
       </programlisting>
     </example>
   </refsect1>
index 01fb1ed8f158352fb6b66b624c8cad18f85451a1..eda43960e799f857fd2aa47613e6e0a96df693ae 100644 (file)
         <constant>SD_PATH_CATALOG</constant>,
 
         <constant>SD_PATH_SYSTEMD_SEARCH_NETWORK</constant>,
+
+        <constant>SD_PATH_SYSTEMD_SYSTEM_ENVIRONMENT_GENERATOR</constant>,
+        <constant>SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR</constant>,
+        <constant>SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR</constant>,
+        <constant>SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR</constant>,
 };</funcsynopsisinfo>
 
       <funcprototype>
index c516083a5b9303447714f1a6146695e9b67de69c..5ce5a19144a3fcda6b8c66c9523a4e7e7d800c5f 100644 (file)
     <refname>sd_pid_get_slice</refname>
     <refname>sd_pid_get_user_slice</refname>
     <refname>sd_pid_get_cgroup</refname>
+    <refname>sd_pidfd_get_owner_uid</refname>
+    <refname>sd_pidfd_get_session</refname>
+    <refname>sd_pidfd_get_user_unit</refname>
+    <refname>sd_pidfd_get_unit</refname>
+    <refname>sd_pidfd_get_machine_name</refname>
+    <refname>sd_pidfd_get_slice</refname>
+    <refname>sd_pidfd_get_user_slice</refname>
+    <refname>sd_pidfd_get_cgroup</refname>
     <refname>sd_peer_get_owner_uid</refname>
     <refname>sd_peer_get_session</refname>
     <refname>sd_peer_get_user_unit</refname>
         <paramdef>char **<parameter>cgroup</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_pidfd_get_owner_uid</function></funcdef>
+        <paramdef>int <parameter>pidfd</parameter></paramdef>
+        <paramdef>uid_t *<parameter>uid</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_pidfd_get_session</function></funcdef>
+        <paramdef>int <parameter>pidfd</parameter></paramdef>
+        <paramdef>char **<parameter>session</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_pidfd_get_user_unit</function></funcdef>
+        <paramdef>int <parameter>pidfd</parameter></paramdef>
+        <paramdef>char **<parameter>unit</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_pidfd_get_unit</function></funcdef>
+        <paramdef>int <parameter>pidfd</parameter></paramdef>
+        <paramdef>char **<parameter>unit</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_pidfd_get_machine_name</function></funcdef>
+        <paramdef>int <parameter>pidfd</parameter></paramdef>
+        <paramdef>char **<parameter>name</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_pidfd_get_slice</function></funcdef>
+        <paramdef>int <parameter>pidfd</parameter></paramdef>
+        <paramdef>char **<parameter>slice</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_pidfd_get_user_slice</function></funcdef>
+        <paramdef>int <parameter>pidfd</parameter></paramdef>
+        <paramdef>char **<parameter>slice</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_pidfd_get_cgroup</function></funcdef>
+        <paramdef>int <parameter>pidfd</parameter></paramdef>
+        <paramdef>char **<parameter>cgroup</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_peer_get_owner_uid</function></funcdef>
         <paramdef>int <parameter>fd</parameter></paramdef>
     functions is passed as 0, the operation is executed for the
     calling process.</para>
 
+    <para>The <function>sd_pidfd_get_owner_uid()</function>,
+    <function>sd_pidfd_get_session()</function>,
+    <function>sd_pidfd_get_user_unit()</function>,
+    <function>sd_pidfd_get_unit()</function>,
+    <function>sd_pidfd_get_machine_name()</function>,
+    <function>sd_pidfd_get_slice()</function>,
+    <function>sd_pidfd_get_user_slice()</function> and
+    <function>sd_pidfd_get_cgroup()</function> calls operate similarly to their PID counterparts, but accept a
+    <constant>PIDFD</constant> instead of a <constant>PID</constant>, which means they are not subject to recycle
+    race conditions as the process is pinned by the file descriptor during the whole duration of the invocation.
+    Note that these require a kernel that supports <constant>PIDFD</constant>. A suitable file descriptor may be
+    acquired via
+    <citerefentry project='man-pages'><refentrytitle>pidfd_open</refentrytitle><manvolnum>2</manvolnum></citerefentry>.</para>
+
     <para>The <function>sd_peer_get_owner_uid()</function>,
     <function>sd_peer_get_session()</function>,
     <function>sd_peer_get_user_unit()</function>,
index a4023349d5f5e6dd88cc06b5d90b3272a5afcc8d..04b2e9813d90c2e4ba3788efd1a6790ffb53ef9c 100644 (file)
@@ -354,6 +354,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             dependencies. If no units are specified,
             <filename>default.target</filename> is implied.</para>
 
+            <para>The units that are shown are additionally filtered by <option>--type=</option> and
+            <option>--state=</option> if those options are specified. Note that we won't be able to
+            use a tree structure in this case, so <option>--plain</option> is implied.</para>
+
             <para>By default, only target units are recursively
             expanded. When <option>--all</option> is passed, all other
             units are recursively expanded as well.</para>
@@ -1657,8 +1661,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <listitem>
           <para>The argument is a comma-separated list of unit types such as <option>service</option> and
           <option>socket</option>. When units are listed with <command>list-units</command>,
-          <command>show</command>, or <command>status</command>, only units of the specified types will be
-          shown. By default, units of all types are shown.</para>
+          <command>list-dependencies</command>, <command>show</command>, or <command>status</command>,
+          only units of the specified types will be shown. By default, units of all types are shown.</para>
 
           <para>As a special case, if one of the arguments is <option>help</option>, a list of allowed values
           will be printed and the program will exit.</para>
@@ -1670,9 +1674,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
 
         <listitem>
           <para>The argument is a comma-separated list of unit LOAD, SUB, or ACTIVE states. When listing
-          units with <command>list-units</command>, <command>show</command>, or <command>status</command>,
-          show only those in the specified states. Use <option>--state=failed</option> or
-          <option>--failed</option> to show only failed units.</para>
+          units with <command>list-units</command>, <command>list-dependencies</command>, <command>show</command>
+          or <command>status</command>, show only those in the specified states. Use <option>--state=failed</option>
+          or <option>--failed</option> to show only failed units.</para>
 
           <para>As a special case, if one of the arguments is <option>help</option>, a list of allowed values
           will be printed and the program will exit.</para>
@@ -2012,10 +2016,18 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <term><option>--no-warn</option></term>
 
         <listitem>
-          <para>Don't generate the warning shown by default when using
-          <command>enable</command> or <command>disable</command> on units
-          without install information (i.e. don't have or have an empty
-          [Install] section).</para>
+          <para>Don't generate the warnings shown by default in the following cases:
+          <itemizedlist>
+            <listitem>
+              <para>when <command>systemctl</command> is invoked without procfs mounted on
+              <filename>/proc/</filename>,</para>
+            </listitem>
+            <listitem>
+              <para>when using <command>enable</command> or <command>disable</command> on units without
+              install information (i.e. don't have or have an empty [Install] section).</para>
+            </listitem>
+          </itemizedlist>
+          </para>
         </listitem>
       </varlistentry>
 
index 0d91cdd7d06d4b73e220647fde07bddfab0b718c..f81dcc324ae4b4f91a5674a5703f326536d80d62 100644 (file)
@@ -274,8 +274,8 @@ Timestamp units-load-finish: Thu 2019-03-14 23:28:07 CET
     <refsect2>
       <title><command>systemd-analyze plot</command></title>
 
-      <para>This command prints an SVG graphic detailing which system services have been started at what
-      time, highlighting the time they spent on initialization.</para>
+      <para>This command prints either an SVG graphic, detailing which system services have been started at what
+      time, highlighting the time they spent on initialization, or the raw time data in JSON or table format.</para>
 
       <example>
         <title><command>Plot a bootchart</command></title>
@@ -284,6 +284,13 @@ Timestamp units-load-finish: Thu 2019-03-14 23:28:07 CET
 $ eog bootup.svg&amp;
 </programlisting>
       </example>
+
+      <para>Note that this plot is based on the most recent per-unit timing data of loaded units. This means
+      that if a unit gets started, then stopped and then started again the information shown will cover the
+      most recent start cycle, not the first one. Thus it's recommended to consult this information only
+      shortly after boot, so that this distinction doesn't matter. Moreover, units that are not referenced by
+      any other unit through a dependency might be unloaded by the service manager once they terminate (and
+      did not fail). Such units will not show up in the plot.</para>
     </refsect2>
 
     <refsect2>
@@ -1204,7 +1211,17 @@ $ systemd-analyze verify /tmp/source:alias.service
         corresponds to a higher security threat. The JSON version of the table is printed to standard
         output. The <replaceable>MODE</replaceable> passed to the option can be one of three:
         <option>off</option> which is the default, <option>pretty</option> and <option>short</option>
-        which respectively output a prettified or shorted JSON version of the security table.</para></listitem>
+        which respectively output a prettified or shorted JSON version of the security table.
+
+        With the <command>plot</command> command, generate a JSON formatted output of the raw time data.
+        The format is a JSON array with objects containing the following fields: <varname>name</varname>
+        which is the unit name, <varname>activated</varname> which is the time after startup the
+        service was activated, <varname>activating</varname> which is how long after startup the service
+        was initially started, <varname>time</varname> which is how long the service took to activate
+        from when it was initially started, <varname>deactivated</varname> which is the time after startup
+        that the service was deactivated, <varname>deactivating</varname> which is the time after startup
+        that the service was initially told to deactivate.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -1235,6 +1252,21 @@ $ systemd-analyze verify /tmp/source:alias.service
         other paths.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--table</option></term>
+
+        <listitem><para>When used with the <command>plot</command> command, the raw time data is output in a table.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--no-legend</option></term>
+
+        <listitem><para>When used with the <command>plot</command> command in combination with either
+        <option>--table</option> or <option>--json=</option>, no legends or hints are included in the output.
+        </para></listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="host" />
       <xi:include href="user-system-options.xml" xpointer="machine" />
 
index 86ce639828a65e4a9ca0967ea484d1b65f0d80c9..8685bae1f9ce133faf86ec1c0003be03c8e38699 100644 (file)
@@ -32,7 +32,7 @@
     refreshes the boot loader random seed stored in the EFI System Partition (ESP), from the Linux kernel
     entropy pool. The boot loader random seed is primarily consumed and updated by
     <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> from the
-    UEFI environemnt (or
+    UEFI environment (or
     <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> if the
     former is not used, but the latter is), and passed as initial RNG seed to the OS. It is an effective way
     to ensure the OS comes up with a random pool that is fully initialized.</para>
index bfc93b3eeb8e9ec37bc71a20e93cb777d304a216..64ded052e16934f77fb3308f6906225d274a16a5 100644 (file)
@@ -56,7 +56,7 @@
 
       <listitem><para>A reboot into the UEFI firmware setup option, if supported by the firmware.</para></listitem>
 
-      <listitem><para>Secure boot variables enrollement if the UEFI firmware is in setup-mode and files are provided
+      <listitem><para>Secure Boot variables enrollment if the UEFI firmware is in setup-mode and files are provided
       on the ESP.</para></listitem>
     </itemizedlist>
 
@@ -95,7 +95,7 @@
       with a 'system token' stored in a persistent EFI variable and derives a random seed to use by the OS as
       entropy pool initialization, providing a full entropy pool during early boot.</para></listitem>
 
-      <listitem><para>The boot manager allows for secure boot variables to be enrolled if the UEFI firmware is
+      <listitem><para>The boot manager allows for Secure Boot variables to be enrolled if the UEFI firmware is
       in setup-mode. Additionally, variables can be automatically enrolled if configured.</para></listitem>
     </itemizedlist>
 
index 003fbcd463507698fa256bb058cf5094657d232d..49d78ee7fcce73391e477a8b6457efca6f7d894a 100644 (file)
         <listitem><para>Takes a path to a TPM2 PCR signature file as generated by the
         <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         tool and that may be used to allow the <command>decrypt</command> command to decrypt credentials that
-        are bound to specific signed PCR values. If this this is not specified explicitly, and a credential
+        are bound to specific signed PCR values. If this is not specified explicitly, and a credential
         with a signed PCR policy is attempted to be decrypted, a suitable signature file
         <filename>tpm2-pcr-signature.json</filename> is searched for in <filename>/etc/systemd/</filename>,
         <filename>/run/systemd/</filename>, <filename>/usr/lib/systemd/</filename> (in this order) and
index ad338cdcc5e0ba4b01ce03f7cdbf48d7ddfaf32b..a654d492a1167a9a4bbcc2daf1a0ceb3500fa44e 100644 (file)
 
               <row>
                 <entry>7</entry>
-                <entry>Secure boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes. The shim project will measure most of its (non-MOK) certificates and SBAT data into this PCR.</entry>
+                <entry>Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes. The shim project will measure most of its (non-MOK) certificates and SBAT data into this PCR.</entry>
               </row>
 
               <!-- Grub measures all its commands and the kernel command line into PCR 8… -->
 
               <row>
                 <entry>12</entry>
-                <entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any specified kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
+                <entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
               </row>
 
               <row>
                 <entry>14</entry>
                 <entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>
               </row>
+
+              <row>
+                <entry>15</entry>
+                <entry><citerefentry><refentrytitle>systemd-cryptsetup</refentrytitle><manvolnum>7</manvolnum></citerefentry> optionally measures the volume key of activated LUKS volumes into this PCR.</entry>
+              </row>
             </tbody>
           </tgroup>
         </table>
         <para>The <option>--tpm2-signature=</option> option takes a path to a TPM2 PCR signature file
         as generated by the
         <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-        tool. If this this is not specified explicitly a suitable signature file
+        tool. If this is not specified explicitly a suitable signature file
         <filename>tpm2-pcr-signature.json</filename> is searched for in <filename>/etc/systemd/</filename>,
         <filename>/run/systemd/</filename>, <filename>/usr/lib/systemd/</filename> (in this order) and
         used. If a signature file is specified or found it is used to verify if the volume can be unlocked
index 5ba024a866bb38e8851f45d9ba374e7e57fec5ee..feaf64bf75202c8d20df86e87f1bf9ef0b26f4ce 100644 (file)
@@ -63,7 +63,7 @@
         <literal>no</literal>, causes the generator to ignore any devices configured in
         <filename>/etc/crypttab</filename> (<varname>luks.uuid=</varname> will still work however).
         <varname>rd.luks.crypttab=</varname> is honored only in initrd while
-        <varname>luks.crypttab=</varname> is honored by both the main system and the initrd.
+        <varname>luks.crypttab=</varname> is honored by both the main system and in the initrd.
         </para></listitem>
       </varlistentry>
 
@@ -75,7 +75,7 @@
         part of the boot process as if it was listed in <filename>/etc/crypttab</filename>. This option may
         be specified more than once in order to set up multiple devices. <varname>rd.luks.uuid=</varname> is
         honored only in the initrd, while <varname>luks.uuid=</varname> is honored by both the main system
-        and the initrd.</para>
+        and in the initrd.</para>
 
         <para>If <filename>/etc/crypttab</filename> contains entries with the same UUID, then the name,
         keyfile and options specified there will be used. Otherwise, the device will have the name
         <manvolnum>5</manvolnum></citerefentry> field <replaceable>volume-name</replaceable>.</para>
 
         <para><varname>rd.luks.name=</varname> is honored only in the initrd, while
-        <varname>luks.name=</varname> is honored by both the main system and the initrd.</para>
+        <varname>luks.name=</varname> is honored by both the main system and in the initrd.</para>
         </listitem>
       </varlistentry>
 
 
         <para><varname>rd.luks.options=</varname> is honored only by initial
         RAM disk (initrd) while <varname>luks.options=</varname> is
-        honored by both the main system and the initrd.</para>
+        honored by both the main system and in the initrd.</para>
         </listitem>
       </varlistentry>
     </variablelist>
index 4ccc80994d0a0bbaaaf164f5e78c78580eab3c0f..bd542cb7f721a9c9a53836db418b12a8889f7900 100644 (file)
     systems, make sure to set the correct default subvolumes on them,
     using <command>btrfs subvolume set-default</command>.</para>
 
+    <para>If the system was booted via
+    <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> and the
+    stub reported to userspace that the kernel image was measured to a TPM2 PCR, then any discovered root and
+    <filename>/var/</filename> volume identifiers (and volume encryption key in case it is encrypted) will be
+    automatically measured into PCR 15 on activation, via
+    <citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
     <para><filename>systemd-gpt-auto-generator</filename> implements
     <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
   </refsect1>
       <citerefentry><refentrytitle>systemd.swap</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
index 44248b2e80159c0da557d5caba1ac452c1acc3e9..3e788f1c9868579bed83966cb0348dab67f34620 100644 (file)
@@ -27,8 +27,8 @@
   <refsect1>
     <title>Description</title>
 
-    <para><filename>systemd-integritysetup-generator</filename> is a generator that translates <filename>/etc/integritytab</filename> entries into
-    native systemd units early at boot. This will create
+    <para><command>systemd-integritysetup-generator</command> is a generator that translates
+    <filename>/etc/integritytab</filename> entries into native systemd units early at boot. This will create
     <citerefentry><refentrytitle>systemd-integritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     units as necessary.</para>
 
index 8fa864473d5476224899f6c3af75d03a848f4426..6b0fb3137c672e422a8e810174753fa3c72ed0a8 100644 (file)
@@ -332,7 +332,9 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
         <listitem><para>Sockets and other file node paths that <command>systemd-journald</command> will
         listen on and are visible in the file system. In addition to these,
         <command>systemd-journald</command> can listen for audit events using <citerefentry
-        project='man-pages'><refentrytitle>netlink</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para></listitem>
+        project='man-pages'><refentrytitle>netlink</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+        depending on whether <literal>systemd-journald-audit.socket</literal> is enabled or
+        not.</para></listitem>
       </varlistentry>
     </variablelist>
 
index 42871b0c77152b58cd1517e1c8330e103719f062..6c53d61d54576f7927947b5b332134e8785ae2f0 100644 (file)
         seen in TPM2 PCR register 11 after boot-up of a unified kernel image. Then, cryptographically sign
         the resulting values with the private/public key pair (RSA) configured via
         <option>--private-key=</option> and <option>--public-key=</option>. This will write a JSON object to
-        standard output that contains signatures for all specified PCR banks (see
-        <option>--pcr-bank=</option>) below, which may be used to unlock encrypted credentials (see
+        standard output that contains signatures for all specified PCR banks (see the
+        <option>--pcr-bank=</option> option below), which may be used to unlock encrypted credentials (see
         <citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>) or
         LUKS volumes (see
-        <citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>). This
-        allows binding secrets to a set of kernels for which such PCR 11 signatures can be provided.</para>
+        <citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
+        This allows binding secrets to a set of kernels for which such PCR 11 signatures can be
+        provided.</para>
 
         <para>Note that a TPM2 device must be available for this signing to take place, even though the
         result is not tied to any TPM2 device or its state.</para></listitem>
 
     <variablelist>
       <varlistentry>
-        <term><option>--linux=PATH</option></term>
-        <term><option>--osrel=PATH</option></term>
-        <term><option>--cmdline=PATH</option></term>
-        <term><option>--initrd=PATH</option></term>
-        <term><option>--splash=PATH</option></term>
-        <term><option>--dtb=PATH</option></term>
-        <term><option>--pcrpkey=PATH</option></term>
+        <term><option>--linux=<replaceable>PATH</replaceable></option></term>
+        <term><option>--osrel=<replaceable>PATH</replaceable></option></term>
+        <term><option>--cmdline=<replaceable>PATH</replaceable></option></term>
+        <term><option>--initrd=<replaceable>PATH</replaceable></option></term>
+        <term><option>--splash=<replaceable>PATH</replaceable></option></term>
+        <term><option>--dtb=<replaceable>PATH</replaceable></option></term>
+        <term><option>--pcrpkey=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>When used with the <command>calculate</command> or <command>sign</command> verb,
         configures the files to read the unified kernel image components from. Each option corresponds with
       </varlistentry>
 
       <varlistentry>
-        <term><option>--bank=DIGEST</option></term>
+        <term><option>--bank=<replaceable>DIGEST</replaceable></option></term>
 
         <listitem><para>Controls the PCR banks to pre-calculate the PCR values for – in case
         <command>calculate</command> or <command>sign</command> is invoked –, or the banks to show in the
       </varlistentry>
 
       <varlistentry>
-        <term><option>--private-key=PATH</option></term>
-        <term><option>--public-key=PATH</option></term>
+        <term><option>--private-key=<replaceable>PATH</replaceable></option></term>
+        <term><option>--public-key=<replaceable>PATH</replaceable></option></term>
 
         <listitem><para>These switches take paths to a pair of PEM encoded RSA key files, for use with
         the <command>sign</command> command.</para>
     <example>
       <title>Generate a unified kernel image, and calculate the expected TPM PCR 11 value</title>
 
-      <programlisting># objcopy \
-    --add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
-    --add-section .osrel=os-release.txt --change-section-vma .osrel=0x20000 \
-    --add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
-    --add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
-    --add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
-    --add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
-    /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
-    foo.efi
-# systemd-measure calculate \
-     --linux=vmlinux \
-     --osrel=os-release.txt \
-     --cmdline=cmdline.txt \
-     --initrd=initrd.cpio \
-     --splash=splash.bmp \
-     --dtb=devicetree.dtb
+      <programlisting># ukify --output foo.efi \
+     --os-release @os-release.txt \
+     --cmdline @cmdline.txt \
+     --splash splash.bmp \
+     --devicetree devicetree.dtb \
+     --measure \
+     vmlinux initrd.cpio
 11:sha1=d775a7b4482450ac77e03ee19bda90bd792d6ec7
 11:sha256=bc6170f9ce28eb051ab465cd62be8cf63985276766cf9faf527ffefb66f45651
 11:sha384=1cf67dff4757e61e5a73d2a21a6694d668629bbc3761747d493f7f49ad720be02fd07263e1f93061243aec599d1ee4b4
      --bank=sha256 \
      --private-key=tpm2-pcr-private.pem \
      --public-key=tpm2-pcr-public.pem > tpm2-pcr-signature.json
-# objcopy \
-    --add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
-    --add-section .osrel=os-release.txt --change-section-vma .osrel=0x20000 \
-    --add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
-    --add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
-    --add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
-    --add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
-    --add-section .pcrsig=tpm2-pcr-signature.json --change-section-vma .pcrsig=0x80000 \
-    --add-section .pcrpkey=tpm2-pcr-public.pem --change-section-vma .pcrpkey=0x90000 \
-    /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
-    foo.efi</programlisting>
+# ukify --output foo.efi \
+     --os-release @os-release.txt \
+     --cmdline @cmdline.txt \
+     --splash splash.bmp \
+     --devicetree devicetree.dtb \
+     --pcr-private-key tpm2-pcr-private.pem \
+     --pcr-public-key tpm2-pcr-public.pem \
+     --pcr-banks sha1,sha256 \
+     vmlinux initrd.cpio</programlisting>
 
      <para>Later on, enroll the signed PCR policy on a LUKS volume:</para>
 
      --public-key=tpm2-pcr-initrd-public.pem \
      --phase=enter-initrd \
      --append=tpm2-pcr-signature.json.tmp >tpm2-pcr-signature.json
-# objcopy \
-    --add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
-    --add-section .osrel=os-release.txt --change-section-vma .osrel=0x20000 \
-    --add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
-    --add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
-    --add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
-    --add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
-    --add-section .pcrsig=tpm2-pcr-signature.json --change-section-vma .pcrsig=0x80000 \
-    --add-section .pcrpkey=tpm2-pcr-public.pem --change-section-vma .pcrpkey=0x90000 \
-    /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
-    foo.efi</programlisting>
+# ukify --output foo.efi \
+     --os-release @os-release.txt \
+     --cmdline @cmdline.txt \
+     --splash splash.bmp \
+     --devicetree devicetree.dtb \
+     --pcr-private-key tpm2-pcr-initrd-private.pem \
+     --pcr-public-key tpm2-pcr-initrd-public.pem \
+     --section .pcrsig=@tpm2-pcr-signature.json \
+     --section .pcrpkey=@tpm2-pcr-public.pem \
+     vmlinux initrd.cpio</programlisting>
     </example>
 
     <para>Note that in this example the <literal>.pcrpkey</literal> PE section contains the key covering all
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
-      <citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>ukify</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>1</manvolnum></citerefentry>
index ec308927df6ab2407f775afd3dfc02ff770327c7..ca33d82a7468c90644f11eda5e62760093c00ec6 100644 (file)
@@ -1374,7 +1374,7 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
         <itemizedlist>
           <listitem><para>If <option>noidmap</option> is used, any user <option>z</option> in the range
           <option>0 … y</option> seen from inside of the container is mapped to <option>x + z</option> in the
-          <option>x … x + y</option> range on the host. All host users outside of that range are mapped to
+          <option>x … x + y</option> range on the host. Other host users are mapped to
           <option>nobody</option> inside the container.</para></listitem>
           <listitem><para>If <option>idmap</option> is used, any user <option>z</option> in the UID range
           <option>0 … y</option> as seen from inside the container is mapped to the same <option>z</option>
@@ -1386,7 +1386,7 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
         </itemizedlist></para>
 
         <para>Whichever ID mapping option is used, the same mapping will be used for users and groups IDs. If
-        <option>rootidmap</option> is used, the group owning the bind mounted directory will have no effect</para>
+        <option>rootidmap</option> is used, the group owning the bind mounted directory will have no effect.</para>
 
         <para>Note that when this option is used in combination with <option>--private-users</option>, the resulting
         mount points will be owned by the <constant>nobody</constant> user. That's because the mount and its files and
index dc02abd307e08e347de2e6055430e3d367c87460..45c791b831143a8443b1eed56548d23415be510d 100644 (file)
@@ -36,7 +36,8 @@
     <para>You can enable monitoring and actions on units by setting <varname>ManagedOOMSwap=</varname> and
     <varname>ManagedOOMMemoryPressure=</varname> in the unit configuration, see
     <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
-    <command>systemd-oomd</command> retrieves information about such units from <command>systemd</command>
+    <command>systemd-oomd</command> retrieves information about such units from
+    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     when it starts and watches for subsequent changes.</para>
 
     <para>Cgroups of units with <varname>ManagedOOMSwap=</varname> or
index 9b7cc80b3a73130914fa9f387945ff6257b2505c..dde13883f752592f4f7844e22aa4b7e26acac4d6 100644 (file)
     <refname>systemd-pcrphase.service</refname>
     <refname>systemd-pcrphase-sysinit.service</refname>
     <refname>systemd-pcrphase-initrd.service</refname>
+    <refname>systemd-pcrmachine.service</refname>
+    <refname>systemd-pcrfs-root.service</refname>
+    <refname>systemd-pcrfs@.service</refname>
     <refname>systemd-pcrphase</refname>
-    <refpurpose>Measure boot phase into TPM2 PCR 11</refpurpose>
+    <refpurpose>Measure boot phase into TPM2 PCR 11, machine ID and file system identity into PCR 15</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <para><filename>systemd-pcrphase.service</filename></para>
     <para><filename>systemd-pcrphase-sysinit.service</filename></para>
     <para><filename>systemd-pcrphase-initrd.service</filename></para>
-    <para><filename>/usr/lib/systemd/system-pcrphase</filename> <replaceable>STRING</replaceable></para>
+    <para><filename>systemd-pcrmachine.service</filename></para>
+    <para><filename>systemd-pcrfs-root.service</filename></para>
+    <para><filename>systemd-pcrfs@.service</filename></para>
+    <para><filename>/usr/lib/systemd/system-pcrphase</filename> <optional><replaceable>STRING</replaceable></optional></para>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
     <para><filename>systemd-pcrphase.service</filename>,
-    <filename>systemd-pcrphase-sysinit.service</filename> and
+    <filename>systemd-pcrphase-sysinit.service</filename>, and
     <filename>systemd-pcrphase-initrd.service</filename> are system services that measure specific strings
     into TPM2 PCR 11 during boot at various milestones of the boot process.</para>
 
+    <para><filename>systemd-pcrmachine.service</filename> is a system service that measures the machine ID
+    (see <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>) into
+    PCR 15.</para>
+
+    <para><filename>systemd-pcrfs-root.service</filename> and <filename>systemd-pcrfs@.service</filename> are
+    services that measure file system identity information (i.e. mount point, file system type, label and
+    UUID, partition label and UUID) into PCR 15. <filename>systemd-pcrfs-root.service</filename> does so for
+    the root file system, <filename>systemd-pcrfs@.service</filename> is a template unit that measures the
+    file system indicated by its instance identifier instead.</para>
+
     <para>These services require
     <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> to be
-    used in a unified kernel image (UKI) setup. They execute no operation when invoked when the stub has not
-    been used to invoke the kernel. The stub will measure the invoked kernel and associated vendor resources
-    into PCR 11 before handing control to it; once userspace is invoked these services then will extend
-    certain literal strings indicating various phases of the boot process into TPM2 PCR 11. During a regular
-    boot process the following strings are extended into PCR 11.</para>
+    used in a unified kernel image (UKI). They execute no operation when the stub has not been used to invoke
+    the kernel. The stub will measure the invoked kernel and associated vendor resources into PCR 11 before
+    handing control to it; once userspace is invoked these services then will extend TPM2 PCR 11 with certain
+    literal strings indicating phases of the boot process. During a regular boot process PCR 11 is extended
+    with the following strings:</para>
 
     <orderedlist>
-      <listitem><para><literal>enter-initrd</literal> is extended into PCR 11 early when the initrd
-      initializes, before activating system extension images for the initrd. It is supposed to act as barrier
-      between the time where the kernel initializes, and where the initrd starts operating and enables
-      system extension images, i.e. code shipped outside of the UKI. (This string is extended at start of
-      <filename>systemd-pcrphase-initrd.service</filename>.)</para></listitem>
-
-      <listitem><para><literal>leave-initrd</literal> is extended into PCR 11 when the initrd is about to
-      transition into the host file system, i.e. when it achieved its purpose. It is supposed to act as
-      barrier between kernel/initrd code and host OS code. (This string is extended at stop of
-      <filename>systemd-pcrphase-initrd.service</filename>.)</para></listitem>
-
-      <listitem><para><literal>sysinit</literal> is extended into PCR 11 when basic system initialization is
-      complete (which includes local file systems have been mounted), and the system begins starting regular
-      system services. (This string is extended at start of
-      <filename>systemd-pcrphase-sysinit.service</filename>.)</para></listitem>
-
-      <listitem><para><literal>ready</literal> is extended into PCR 11 during later boot-up, after remote
-      file systems have been activated (i.e. after <filename>remote-fs.target</filename>), but before users
-      are permitted to log in (i.e. before <filename>systemd-user-sessions.service</filename>). It is
-      supposed to act as barrier between the time where unprivileged regular users are still prohibited to
-      log in and where they are allowed to log in. (This string is extended at start of
-      <filename>systemd-pcrphase.service</filename>.)</para></listitem>
-
-      <listitem><para><literal>shutdown</literal> is extended into PCR 11 when system shutdown begins. It is
-      supposed to act as barrier between the time the system is fully up and running and where it is about to
-      shut down. (This string is extended at stop of
-      <filename>systemd-pcrphase.service</filename>.)</para></listitem>
-
-      <listitem><para><literal>final</literal> is extended into PCR 11 at the end of system shutdown. It is
-      supposed to act as barrier between the time the service manager still runs and when it transitions into
-      the final boot phase where service management is not available anymore. (This string is extended at
-      stop of <filename>systemd-pcrphase-sysinit.service</filename>.)</para></listitem>
-
+      <listitem><para><literal>enter-initrd</literal> — early when the initrd initializes, before activating
+      system extension images for the initrd. It acts as a barrier between the time where the kernel
+      initializes and where the initrd starts operating and enables system extension images, i.e. code
+      shipped outside of the UKI. (This extension happens when
+      <filename>systemd-pcrphase-initrd.service</filename> is started.)</para></listitem>
+
+      <listitem><para><literal>leave-initrd</literal> — when the initrd is about to transition into the host
+      file system. It acts as barrier between initrd code and host OS code. (This extension happens when
+      <filename>systemd-pcrphase-initrd.service</filename> is stopped.)</para></listitem>
+
+      <listitem><para><literal>sysinit</literal> — when basic system initialization is complete (which
+      includes local file systems having been mounted), and the system begins starting regular system
+      services. (This extension happens when <filename>systemd-pcrphase-sysinit.service</filename> is
+      started.)</para></listitem>
+
+      <listitem><para><literal>ready</literal> — during later boot-up, after remote file systems have been
+      activated (i.e. after <filename>remote-fs.target</filename>), but before users are permitted to log in
+      (i.e. before <filename>systemd-user-sessions.service</filename>). It acts as barrier between the time
+      where unprivileged regular users are still prohibited to log in and where they are allowed to log in.
+      (This extension happens when <filename>systemd-pcrphase.service</filename> is started.)
+      </para></listitem>
+
+      <listitem><para><literal>shutdown</literal> — when the system shutdown begins. It acts as barrier
+      between the time the system is fully up and running and where it is about to shut down. (This extension
+      happens when <filename>systemd-pcrphase.service</filename> is stopped.)</para></listitem>
+
+      <listitem><para><literal>final</literal> — at the end of system shutdown. It acts as barrier between
+      the time the service manager still runs and when it transitions into the final shutdown phase where
+      service management is not available anymore. (This extension happens when
+      <filename>systemd-pcrphase-sysinit.service</filename> is stopped.)</para></listitem>
     </orderedlist>
 
-    <para>During a regular system lifecycle, the strings <literal>enter-initrd</literal> →
-    <literal>leave-initrd</literal> → <literal>sysinit</literal> → <literal>ready</literal> →
-    <literal>shutdown</literal> → <literal>final</literal> are extended into PCR 11, one after the
-    other.</para>
+    <para>During a regular system lifecycle, PCR 11 is extended with the strings
+    <literal>enter-initrd</literal>, <literal>leave-initrd</literal>, <literal>sysinit</literal>,
+    <literal>ready</literal>, <literal>shutdown</literal>, and <literal>final</literal>.</para>
 
     <para>Specific phases of the boot process may be referenced via the series of strings measured, separated
-    by colons (the "boot path"). For example, the boot path for the regular system runtime is
+    by colons (the "phase path"). For example, the phase path for the regular system runtime is
     <literal>enter-initrd:leave-initrd:sysinit:ready</literal>, while the one for the initrd is just
-    <literal>enter-initrd</literal>. The boot path for the the boot phase before the initrd, is an empty
-    string; because that's hard to pass around a single colon (<literal>:</literal>) may be used
-    instead. Note that the aforementioned six strings are just the default strings and individual systems
-    might measure other strings at other times, and thus implement different and more fine-grained boot
-    phases to bind policy to.</para>
-
-    <para>By binding policy of TPM2 objects to a specific boot path it is possible to restrict access to them
-    to specific phases of the boot process, for example making it impossible to access the root file system's
-    encryption key after the system transitioned from the initrd into the host root file system.</para>
+    <literal>enter-initrd</literal>. The phase path for the boot phase before the initrd is an empty string;
+    because that's hard to pass around a single colon (<literal>:</literal>) may be used instead. Note that
+    the aforementioned six strings are just the default strings and individual systems might measure other
+    strings at other times, and thus implement different and more fine-grained boot phases to bind policy
+    to.</para>
+
+    <para>By binding policy of TPM2 objects to a specific phase path it is possible to restrict access to
+    them to specific phases of the boot process, for example making it impossible to access the root file
+    system's encryption key after the system transitioned from the initrd into the host root file system.
+    </para>
 
     <para>Use
     <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
-    pre-calculate expected PCR 11 values for specific boot phases (via the <option>--phase=</option> switch).</para>
+    pre-calculate expected PCR 11 values for specific boot phases (via the <option>--phase=</option> switch).
+    </para>
+
+    <para><filename>systemd-pcrfs-root.service</filename> and <filename>systemd-pcrfs@.service</filename> are
+    automatically pulled into the initial transaction by
+    <citerefentry><refentrytitle>systemd-gpt-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    for the root and <filename>/var/</filename> file
+    systems. <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    will do this for all mounts with the <option>x-systemd.pcrfs</option> mount option in
+    <filename>/etc/fstab</filename>.</para>
   </refsect1>
 
   <refsect1>
         TPM2 device will cause the invocation to fail.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--machine-id</option></term>
+
+        <listitem><para>Instead of measuring a word specified on the command line into PCR 11, measure the
+        host's machine ID into PCR 15.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--file-system=</option></term>
+
+        <listitem><para>Instead of measuring a word specified on the command line into PCR 11, measure
+        identity information of the specified file system into PCR 15. The parameter must be the path to the
+        established mount point of the file system to measure.</para></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
 
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-gpt-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 61f2c5ca9e59747a4946b48a3cfe75049a23a190..6dacea5e9b78bfc373ac93552e33fecff81c65ff 100644 (file)
@@ -35,8 +35,8 @@
     <para>Most of <command>systemd-portabled</command>'s functionality is accessible through the
     <citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> command.</para>
 
-    <para>See <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> for details about
-    the concepts this service implements.</para>
+    <para>See the <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services Documentation</ulink>
+    for details about the concepts this service implements.</para>
   </refsect1>
 
   <refsect1>
index be7abdbcd688dc286fdc37639c0503b6128b6d4b..9033ef76d6923a2533316b9914ff3d00766c1d93 100644 (file)
         <command>systemd-repart</command> was executed.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--sector-size=</option><arg>BYTES</arg></term>
+
+        <listitem><para>This option allows configuring the sector size of the image produced by
+        <command>systemd-repart</command>. It takes a value that is a power of <literal>2</literal> between
+        <literal>512</literal> and <literal>4096</literal>. This option is useful when building images for
+        disks that use a different sector size as the disk on which the image is produced.</para></listitem>.
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
       <xi:include href="standard-options.xml" xpointer="no-pager" />
index fcb0c24ce8074173180a9e0fd4df2493bcacd2ea..c2301687806ef4d2c190234fd30f095619dc1348 100644 (file)
   <refsect1>
     <title>Assembling Kernel Images</title>
 
-    <para>In order to assemble an UEFI PE kernel image from various components as described above, use an
-    <citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry> command line
-    like this:</para>
-
-    <programlisting>objcopy \
-    --add-section .osrel=os-release --change-section-vma .osrel=0x20000 \
-    --add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
-    --add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
-    --add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
-    --add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
-    --add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
-    /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
-    foo-unsigned.efi</programlisting>
-
-    <para>Note that these PE section offsets are example values and a properly assembled image must not
-    contain any overlapping sections (this includes already existing sections inside the stub before
-    assembly) or boot may fail.</para>
-
-    <para>This generates one PE executable file <filename>foo-unsigned.efi</filename> from the six individual
-    files for OS release information, kernel command line, boot splash image, kernel image, main initrd and
-    UEFI boot stub.</para>
-
-    <para>To then sign the resulting image for UEFI SecureBoot use an
-    <citerefentry project='archlinux'><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry> command like
-    the following:</para>
-
-    <programlisting>sbsign \
-    --key mykey.pem \
-    --cert mykey.crt \
-    --output foo.efi \
-    foo-unsigned.efi</programlisting>
-
-    <para>This expects a pair of X.509 private key and certificate as parameters and then signs the UEFI PE
-    executable we generated above for UEFI SecureBoot and generates a signed UEFI PE executable as
-    result.</para>
-
-    <para>See
-    <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
-    an example involving the <literal>.pcrsig</literal> and <literal>.pcrpkey</literal> sections.</para>
+    <para>In order to assemble a bootable Unified Kernel Image from various components as described above, use
+    <citerefentry><refentrytitle>ukify</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
   </refsect1>
 
   <refsect1>
       <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink>,
       <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>,
-      <citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-      <citerefentry project='archlinux'><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>ukify</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     </para>
   </refsect1>
index 99436ced59d067a3d3fb721a1b86bd2e5d7eed36..39a16d8e8fb8035728728ec2314d6d103ec70b6f 100644 (file)
     suitable for shipping resources that are processed by subsystems running in earliest boot. Specifically,
     OS extension images are not suitable for shipping system services or
     <citerefentry><refentrytitle>systemd-sysusers</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-    definitions. See <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> for a simple
-    mechanism for shipping system services in disk images, in a similar fashion to OS extensions. Note the
-    different isolation on these two mechanisms: while system extension directly extend the underlying OS
-    image with additional files that appear in a way very similar to as if they were shipped in the OS image
-    itself and thus imply no security isolation, portable services imply service level sandboxing in one way
-    or another. The <filename>systemd-sysext.service</filename> service is guaranteed to finish start-up
-    before <filename>basic.target</filename> is reached; i.e. at the time regular services initialize (those
-    which do not use <varname>DefaultDependencies=no</varname>), the files and directories system extensions
-    provide are available in <filename>/usr/</filename> and <filename>/opt/</filename> and may be
-    accessed.</para>
+    definitions. See the <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services Documentation</ulink>
+    for a simple mechanism for shipping system services in disk images, in a similar fashion to OS
+    extensions. Note the different isolation on these two mechanisms: while system extension directly extend
+    the underlying OS image with additional files that appear in a way very similar to as if they were
+    shipped in the OS image itself and thus imply no security isolation, portable services imply service
+    level sandboxing in one way or another. The <filename>systemd-sysext.service</filename> service is
+    guaranteed to finish start-up before <filename>basic.target</filename> is reached; i.e. at the time
+    regular services initialize (those which do not use <varname>DefaultDependencies=no</varname>), the files
+    and directories system extensions provide are available in <filename>/usr/</filename> and
+    <filename>/opt/</filename> and may be accessed.</para>
 
     <para>Note that there is no concept of enabling/disabling installed system extension images: all
     installed extension images are automatically activated at boot. However, you can place an empty directory
index 331b5c07bc5cb42cf7e9b67511cf15d27fdc9732..37ded91a936dd5f8898f09fb30aca778a5ef86c0 100644 (file)
@@ -27,8 +27,8 @@
   <refsect1>
     <title>Description</title>
 
-    <para><filename>systemd-veritysetup-generator</filename> is a generator that translates kernel command line options
-    configuring verity protected block devices into native systemd units early at boot and when
+    <para><command>systemd-veritysetup-generator</command> is a generator that translates kernel command line
+    options configuring verity protected block devices into native systemd units early at boot and when
     configuration of the system manager is reloaded. This will create
     <citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     units as necessary.</para>
     <para>Currently, only two verity devices may be set up with this generator, backing the root and <filename>/usr</filename> file systems of the
     OS.</para>
 
-    <para><filename>systemd-veritysetup-generator</filename> implements
+    <para><command>systemd-veritysetup-generator</command> implements
     <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
   </refsect1>
 
   <refsect1>
     <title>Kernel Command Line</title>
 
-    <para><filename>systemd-veritysetup-generator</filename>
+    <para><command>systemd-veritysetup-generator</command>
     understands the following kernel command line parameters:</para>
 
     <variablelist class='kernel-commandline-options'>
@@ -98,7 +98,8 @@
         <term><varname>systemd.verity_usr_hash=</varname></term>
         <term><varname>systemd.verity_usr_options=</varname></term>
 
-        <listitem><para>Equivalent to their counterparts for the root file system as described above, but apply to the <filename>/usr/</filename> file system instead.</para></listitem>
+        <listitem><para>Equivalent to their counterparts for the root file system as described above, but
+        apply to the <filename>/usr/</filename> file system instead.</para></listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
index e55526070cc296653761c1c4719152ca188240b7..0d3b40211aa030606bdf150d6039b83afc29eeb7 100644 (file)
     stateless systems.</para>
 
     <para>This service is only enabled if full volatile mode is selected, for example by specifying
-    <literal>systemd.volatile=yes</literal> on the kernel command line. This service runs only in the initrdyes,
+    <literal>systemd.volatile=yes</literal> on the kernel command line. This service runs only in the initrd,
     before the system transitions to the host's root directory. Note that this service is not used if
-    <literal>systemd.volatile=state</literal> is used, as in that mode the root directory is
-    non-volatile.</para>
+    <literal>systemd.volatile=state</literal> is used, as in that mode the root directory is non-volatile.
+    </para>
   </refsect1>
 
   <refsect1>
index d7480d266cdf127b6bedeae4e09f296e0c8f8e35..f69f8ae43710c998d98ebc7cf602ee8fc679f40d 100644 (file)
@@ -727,9 +727,9 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         <para>Note that this setting only has an effect on the unit's processes themselves (or any processes
         directly or indirectly forked off them). It has no effect on processes potentially invoked on request
         of them through tools such as <citerefentry
-        project='man-pages'><refentrytitle>at</refentrytitle><manvolnum>1p</manvolnum></citerefentry>,
+        project='man-pages'><refentrytitle>at</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
         <citerefentry
-        project='man-pages'><refentrytitle>crontab</refentrytitle><manvolnum>1p</manvolnum></citerefentry>,
+        project='man-pages'><refentrytitle>crontab</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
         <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>, or
         arbitrary IPC services.</para></listitem>
       </varlistentry>
@@ -934,7 +934,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
                 <entry>LimitNOFILE=</entry>
                 <entry>ulimit -n</entry>
                 <entry>Number of File Descriptors</entry>
-                <entry>Don't use. Be careful when raising the soft limit above 1024, since <function>select()</function> cannot function with file descriptors above 1023 on Linux. Nowadays, the hard limit defaults to 524288, a very high value compared to historical defaults. Typically applications should increase their soft limit to the hard limit on their own, if they are OK with working with file descriptors above 1023, i.e. do not use <function>select()</function>. Note that file descriptors are nowadays accounted like any other form of memory, thus there should not be any need to lower the hard limit. Use <varname>MemoryMax=</varname> to control overall service memory use, including file descriptor memory.</entry>
+                <entry>Don't use. Be careful when raising the soft limit above 1024, since <citerefentry project='man-pages'><refentrytitle>select</refentrytitle><manvolnum>2</manvolnum></citerefentry> cannot function with file descriptors above 1023 on Linux. Nowadays, the hard limit defaults to 524288, a very high value compared to historical defaults. Typically applications should increase their soft limit to the hard limit on their own, if they are OK with working with file descriptors above 1023, i.e. do not use <citerefentry project='man-pages'><refentrytitle>select</refentrytitle><manvolnum>2</manvolnum></citerefentry>. Note that file descriptors are nowadays accounted like any other form of memory, thus there should not be any need to lower the hard limit. Use <varname>MemoryMax=</varname> to control overall service memory use, including file descriptor memory.</entry>
               </row>
               <row>
                 <entry>LimitAS=</entry>
@@ -1090,9 +1090,15 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         <listitem><para>Controls which kernel architecture <citerefentry
         project='man-pages'><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry> shall report,
         when invoked by unit processes. Takes one of the architecture identifiers <constant>x86</constant>,
-        <constant>x86-64</constant>, <constant>ppc</constant>, <constant>ppc-le</constant>, <constant>ppc64</constant>,
-        <constant>ppc64-le</constant>, <constant>s390</constant> or <constant>s390x</constant>. Which personality
-        architectures are supported depends on the system architecture. Usually the 64bit versions of the various
+        <constant>x86-64</constant>, <constant>arm64</constant>, <constant>arm64-be</constant>, <constant>arm</constant>,
+        <constant>arm-be</constant>, <constant>alpha</constant>, <constant>arc</constant>, <constant>arc-be</constant>,
+        <constant>cris</constant>, <constant>ia64</constant>, <constant>loongarch64</constant>, <constant>m68k</constant>,
+        <constant>mips64-le</constant>, <constant>mips64</constant>, <constant>mips-le</constant>, <constant>mips</constant>,
+        <constant>nios2</constant>, <constant>parisc64</constant>, <constant>parisc</constant>, <constant>ppc64-le</constant>,
+        <constant>ppc64</constant>, <constant>ppc</constant>, <constant>ppc-le</constant>, <constant>riscv32</constant>,
+        <constant>riscv64</constant>, <constant>s390x</constant>, <constant>s390</constant>, <constant>sh64</constant>,
+        <constant>sh</constant>, <constant>sparc64</constant>, <constant>sparc</constant> or <constant>tilegx</constant>.
+        Which personality architectures are supported depends on the system architecture. Usually the 64bit versions of the various
         system architectures support their immediate 32bit personality architecture counterpart, but no others. For
         example, <constant>x86-64</constant> systems support the <constant>x86-64</constant> and
         <constant>x86</constant> personalities but no others. The personality feature is useful when running 32-bit
@@ -2007,7 +2013,9 @@ RestrictFileSystems=ext4</programlisting>
 
         <para>Note that this setting might not be supported on some systems (for example if the LSM eBPF hook is
         not enabled in the underlying kernel or if not using the unified control group hierarchy). In that case this setting
-        has no effect.</para></listitem>
+        has no effect.</para>
+
+        <xi:include href="cgroup-sandboxing.xml" xpointer="singular"/></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -3173,11 +3181,13 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
         11) with a prefix of <literal>io.systemd.credential:</literal> or
         <literal>io.systemd.credential.binary:</literal>. In both cases a key/value pair separated by
         <literal>=</literal> is expected, in the latter case the right-hand side is Base64 decoded when
-        parsed (thus permitting binary data to be passed in). Example qemu switch: <literal>-smbios
+        parsed (thus permitting binary data to be passed in). Example
+        <ulink url="https://www.qemu.org/docs/master/system/index.html">qemu</ulink>
+        switch: <literal>-smbios
         type=11,value=io.systemd.credential:xx=yy</literal>, or <literal>-smbios
         type=11,value=io.systemd.credential.binary:rick=TmV2ZXIgR29ubmEgR2l2ZSBZb3UgVXA=</literal>. Alternatively,
         use the <command>qemu</command> <literal>fw_cfg</literal> node
-        <literal>opt/io.systemd.credentials/</literal>. Example qemu switch: <literal>-fw_cfg
+        <literal>opt/io.systemd.credentials/</literal>. Example <command>qemu</command> switch: <literal>-fw_cfg
         name=opt/io.systemd.credentials/mycred,string=supersecret</literal>. They may also be specified on
         the kernel command line using the <literal>systemd.set_credential=</literal> switch (see
         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>) and from
index cc55b02b182f3e7801188ec9f9057de93beecc13..c0167b1c86a9d4a4ac912b52b18d463f73bf2a53 100644 (file)
       links.</para>
 
       <programlisting>[Link]
-NamePolicy=kernel database onboard slot path
+NamePolicy=kernel database on-board slot path
 MACAddressPolicy=persistent</programlisting>
     </example>
 
index 773ca04cd6db16a781d41973bacd82c7dee47211..550f006601e9af38ac197da86d85229d0fcf8606 100644 (file)
         <varname>Options=</varname> setting in a unit file.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>x-systemd.pcrfs</option></term>
+
+        <listitem><para>Measures file system identity information (mount point, type, label, UUID, partition
+        label, partition UUID) into PCR 15 after the file system has been mounted. This ensures the
+        <citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        or <filename>systemd-pcrfs-root.service</filename> services are pulled in by the mount unit.</para>
+
+        <para>Note that this option can only be used in <filename>/etc/fstab</filename>, and will be ignored
+        when part of the <varname>Options=</varname> setting in a unit file. It is also implied for the root
+        and <filename>/usr/</filename> partitions discovered by
+        <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>x-systemd.rw-only</option></term>
 
         <term><varname>Where=</varname></term>
         <listitem><para>Takes an absolute path of a file or directory for the mount point; in particular, the
         destination cannot be a symbolic link.  If the mount point does not exist at the time of mounting, it
-        is created as directory. This string must be reflected in the unit filename. (See above.) This option
+        is created as either a directory or a file. The former is the usual case; the latter is done only if this mount
+        is a bind mount and the source (<varname>What=</varname>) is not a directory.
+        This string must be reflected in the unit filename. (See above.) This option
         is mandatory.</para></listitem>
       </varlistentry>
 
index d83141de1103f8e16f4e98a7b0c2a206ae570f66..01d88976384731b79ebbee60b8ea382ca565c384 100644 (file)
@@ -737,7 +737,7 @@ Table=1234</programlisting></para>
           may trigger the start of the DHCPv6 client if the relevant flags are set in the RA data, or
           if no routers are found on the link. The default is to disable RA reception for bridge
           devices or when IP forwarding is enabled, and to enable it otherwise. Cannot be enabled on
-          bond devices and when link-local addressing is disabled.</para>
+          devices aggregated in a bond device or when link-local addressing is disabled.</para>
 
           <para>Further settings for the IPv6 RA support may be configured in the [IPv6AcceptRA]
           section, see below.</para>
@@ -1645,8 +1645,8 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
       <varlistentry>
         <term><varname>QuickAck=</varname></term>
         <listitem>
-          <para>Takes a boolean. When true enables TCP quick ack mode for the route. When unset, the
-          kernel's default will be used.</para>
+          <para>Takes a boolean. When true, the TCP quick ACK mode for the route is enabled. When unset,
+          the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
 
@@ -1890,6 +1890,20 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>SocketPriority=</varname></term>
+        <listitem>
+          <para>The Linux socket option <constant>SO_PRIORITY</constant> applied to the raw IP socket used for
+          initial DHCPv4 messages. Unset by default. Usual values range from 0 to 6.
+          More details about <constant>SO_PRIORITY</constant> socket option in
+          <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+          Can be used in conjunction with [VLAN] section <varname>EgressQOSMaps=</varname> setting of .netdev
+          file to set the 802.1Q VLAN ethernet tagged header priority, see
+          <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <!-- How to use the DHCP lease -->
 
       <varlistentry>
@@ -2032,6 +2046,14 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>QuickAck=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When true, the TCP quick ACK mode is enabled for the routes configured by
+          the acquired DHCPv4 lease. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>UseGateway=</varname></term>
         <listitem>
@@ -2270,6 +2292,7 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
         <term><varname>UseHostname=</varname></term>
         <term><varname>UseDomains=</varname></term>
         <term><varname>NetLabel=</varname></term>
+        <term><varname>SendRelease=</varname></term>
         <listitem>
           <para>As in the [DHCPv4] section.</para>
         </listitem>
@@ -2297,7 +2320,7 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
   <refsect1>
     <title>[DHCPPrefixDelegation] Section Options</title>
     <para>The [DHCPPrefixDelegation] section configures subnet prefixes of the delegated prefixes
-    acquired by a DHCPv6 client, or by a DHCPv4 client through the 6RD option on another interface.
+    acquired by a DHCPv6 client or by a DHCPv4 client through the 6RD option on another interface.
     The settings in this section are used only when the <varname>DHCPPrefixDelegation=</varname>
     setting in the [Network] section is enabled.</para>
 
@@ -2532,6 +2555,14 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>QuickAck=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When true, the TCP quick ACK mode is enabled for the routes configured by
+          the received RAs. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>UseMTU=</varname></term>
         <listitem>
index f8748bf700a3dd735caf2ecd1c602864a8a8fc5d..834f480b5ce10050a2b2677045ce87caf04efb8b 100644 (file)
       <title>See Also</title>
       <para>Environment variables with details on the trigger will be set for triggered units. See the
       <literal>Environment Variables Set on Triggered Units</literal> section in
-      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
       for more details.</para>
       <para>
         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
index 1142ad7758f0bb29a0e1cdc8ab9984db8b2d002b..4b19b18231a8ae1b766ea6dcfade166d05ec086c 100644 (file)
           support is not enabled in the underlying kernel or container manager). These settings will have no effect in
           that case. If compatibility with such systems is desired it is hence recommended to not exclusively rely on
           them for IP security.</para>
+
+          <xi:include href="cgroup-sandboxing.xml" xpointer="singular"/>
         </listitem>
       </varlistentry>
 
@@ -814,6 +816,8 @@ SocketBindDeny=any
 SocketBindAllow=ipv4:udp:10000-65535
 SocketBindDeny=any
 …</programlisting></para>
+
+          <xi:include href="cgroup-sandboxing.xml" xpointer="singular"/>
         </listitem>
       </varlistentry>
 
@@ -860,6 +864,8 @@ RestrictNetworkInterfaces=eth1 eth2
 RestrictNetworkInterfaces=~eth1</programlisting>
           Programs in the unit will be only able to use the eth2 network interface.
           </para>
+
+          <xi:include href="cgroup-sandboxing.xml" xpointer="singular"/>
         </listitem>
       </varlistentry>
 
@@ -912,6 +918,7 @@ DeviceAllow=block-loop
 DeviceAllow=/dev/loop-control
 …</programlisting></para>
 
+          <xi:include href="cgroup-sandboxing.xml" xpointer="singular"/>
         </listitem>
       </varlistentry>
 
@@ -956,6 +963,8 @@ DeviceAllow=/dev/loop-control
               </listitem>
             </varlistentry>
           </variablelist>
+
+          <xi:include href="cgroup-sandboxing.xml" xpointer="singular"/>
         </listitem>
       </varlistentry>
 
@@ -1139,8 +1148,9 @@ DeviceAllow=/dev/loop-control
         <varlistentry>
           <term>systemd 252</term>
           <listitem><para> Options for controlling the Legacy Control Group Hierarchy (<ulink
-          url="https://docs.kernel.org/admin-guide/cgroup-v1/index.html">Control Groups version 1</ulink> are
-          now fully deprecated: <varname>CPUShares=<replaceable>weight</replaceable></varname>,
+          url="https://docs.kernel.org/admin-guide/cgroup-v1/index.html">Control Groups version 1</ulink>)
+          are now fully deprecated:
+          <varname>CPUShares=<replaceable>weight</replaceable></varname>,
           <varname>StartupCPUShares=<replaceable>weight</replaceable></varname>,
           <varname>MemoryLimit=<replaceable>bytes</replaceable></varname>,
           <varname>BlockIOAccounting=</varname>,
@@ -1150,8 +1160,7 @@ DeviceAllow=/dev/loop-control
           <replaceable>weight</replaceable></varname>,
           <varname>BlockIOReadBandwidth=<replaceable>device</replaceable>
           <replaceable>bytes</replaceable></varname>,
-          <varname>BlockIOWriteBandwidth=<replaceable>device</replaceable>
-          <replaceable>bytes</replaceable></varname>.
+          <varname>BlockIOWriteBandwidth=<replaceable>device</replaceable> <replaceable>bytes</replaceable></varname>.
           Please switch to the unified cgroup hierarchy.</para></listitem>
         </varlistentry>
       </variablelist>
index f123fa2e4c8d3692bdf66f9aa245e73a7305a522..d44c48c7b1c79d9a5d32cd61ea5146bdfa2837ec 100644 (file)
 
               <row>
                 <entry><literal>+</literal></entry>
-                <entry>If the executable path is prefixed with <literal>+</literal> then the process is executed with full privileges. In this mode privilege restrictions configured with <varname>User=</varname>, <varname>Group=</varname>, <varname>CapabilityBoundingSet=</varname> or the various file system namespacing options (such as <varname>PrivateDevices=</varname>, <varname>PrivateTmp=</varname>) are not applied to the invoked command line (but still affect any other <varname>ExecStart=</varname>, <varname>ExecStop=</varname>, … lines).</entry>
+                <entry>If the executable path is prefixed with <literal>+</literal> then the process is executed with full privileges. In this mode privilege restrictions configured with <varname>User=</varname>, <varname>Group=</varname>, <varname>CapabilityBoundingSet=</varname> or the various file system namespacing options (such as <varname>PrivateDevices=</varname>, <varname>PrivateTmp=</varname>) are not applied to the invoked command line (but still affect any other <varname>ExecStart=</varname>, <varname>ExecStop=</varname>, … lines). However, note that this will not bypass options that apply to the whole control group, such as <varname>DevicePolicy=</varname>, see <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry> for the full list.</entry>
               </row>
 
               <row>
 
         <programlisting>ExecReload=kill -HUP $MAINPID</programlisting>
 
-        <para>Note however that reloading a daemon by enqueing a signal (as with the example line above) is
+        <para>Note however that reloading a daemon by enqueuing a signal (as with the example line above) is
         usually not a good choice, because this is an asynchronous operation and hence not suitable when
         ordering reloads of multiple services against each other. It is thus strongly recommended to either
         use <varname>Type=</varname><option>notify-reload</option> in place of
index 95a437adc4ddf211456cf85e4285f19f53abe0ca..a73aef9ed009a3fb52259fe62c40e687ee899ae6 100644 (file)
           Note that in case the hypervisor does not support <constant>SOCK_DGRAM</constant>
           over <constant>AF_VSOCK</constant>, <constant>SOCK_SEQPACKET</constant> will be
           tried instead. The credential payload for <constant>AF_VSOCK</constant> should be
-          in the form: <literal>vsock:CID:PORT</literal>, where <literal>CID</literal> is
-          optional and if omitted will default to talking to the hypervisor
-          (<constant>0</constant>).</para>
+          in the form: <literal>vsock:CID:PORT</literal>.</para>
         </listitem>
       </varlistentry>
 
index 953faa9b3342af42f1658dc1dcafcc3a1b97c1e5..a8c8241c94eeac885252a8e05152ca93d6a8fd77 100644 (file)
       <title>See Also</title>
       <para>Environment variables with details on the trigger will be set for triggered units. See the
       <literal>Environment Variables Set on Triggered Units</literal> section in
-      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
       for more details.</para>
       <para>
         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
index 0c17d918148d97dfcd8b73113c5b60c4b7878488..91015c8f47c334ac331a1fe28712d5ae79e6f5be 100644 (file)
             </para></listitem>
 
             <listitem><para><literal>device-tree-compatible(<replaceable>value</replaceable>)</literal>
-            matches systems with a device tree that is compatible with <literal>value</literal>.
+            matches systems with a device tree that are compatible with <literal>value</literal>.
             </para></listitem>
 
             <listitem><para><literal>smbios-field(<replaceable>field</replaceable>
index 3540b441760a22802b0cdabe9a95843d9f5f424b..bdf4bcbf7a517e2ec16d160acaafd7fe5ebcfe5c 100644 (file)
   <refsect1>
     <title>[Transfer] Section Options</title>
 
-    <para>This section defines general properties of this transfer:</para>
+    <para>This section defines general properties of this transfer.</para>
 
     <variablelist>
       <varlistentry>
   <refsect1>
     <title>[Source] Section Options</title>
 
-    <para>This section defines properties of the transfer source:</para>
+    <para>This section defines properties of the transfer source.</para>
 
     <variablelist>
       <varlistentry>
   <refsect1>
     <title>[Target] Section Options</title>
 
-    <para>This section defines properties of the transfer target:</para>
+    <para>This section defines properties of the transfer target.</para>
 
     <variablelist>
       <varlistentry>
         <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>
+        <para>Note that only certain combinations of source and target resource types are supported, see
+        above.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <ulink url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">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
+        <constant>regular-file</constant>, the writable bit is removed from the access mode. If 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
 
         <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
+        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>
index bd3bc33ab4d70eb2e6a3a66dd75b33aac24457c2..2b4b4f37bcb3ff75c0aa13d16798f9c80e71c6eb 100644 (file)
@@ -88,8 +88,9 @@ A+    /path-or-glob/to/append/acls/recursively -    -    -     -           POSIX
     <filename>/sys/</filename> or <filename>/proc/</filename>, as well as some other directories below
     <filename>/var/</filename>).</para>
 
-    <para><command>systemd-tmpfiles</command> uses this configuration to create volatile files and
-    directories during boot and to do periodic cleanup afterwards. See
+    <para><citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    uses this configuration to create volatile files and directories during boot and to do periodic cleanup
+    afterwards. See
     <citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
     the description of <filename>systemd-tmpfiles-setup.service</filename>,
     <filename>systemd-tmpfiles-clean.service</filename>, and associated units.</para>
index 3360cf04540993bcb18e26d9a2726f35a91093fc..4a961680275d61650c4c6f0112e94d4d60a2c36f 100644 (file)
@@ -59,7 +59,7 @@
     <title>Return Value</title>
 
     <para>On success, <function>udev_enumerate_new()</function> returns a
-    pointer to the allocated udev monitor. On failure,
+    pointer to the allocated enumeration object. On failure,
     <constant>NULL</constant> is returned.
     <function>udev_enumerate_ref()</function> returns the argument
     that it was passed, unmodified.
index ee0658dc14c43c66640d53a98d73e8d9eb18de83..0298123c65ffb88c0e8000b2117e56dbfd9da524 100644 (file)
           the "whole" block device in case a partition block device is specified. The devices will be sorted
           by their device node major number as primary ordering key and the minor number as secondary
           ordering key (i.e. they are shown in the order they'd be locked). Note that the number of lines
-          printed here can be less than the the number of <option>--device=</option> and
+          printed here can be less than the number of <option>--device=</option> and
           <option>--backing=</option> switches specified in case these resolve to the same "whole"
           devices.</para></listitem>
         </varlistentry>
index 64f2e00be5f710f4af46bfda2f033a178d0504d9..d37f47d48b7a1e4866d654d279b6cacd72f86dba 100644 (file)
@@ -716,24 +716,6 @@ endif
 
 ############################################################
 
-python = find_program('python3')
-if run_command(python, '-c', 'import jinja2', check : false).returncode() != 0
-        error('python3 jinja2 missing')
-endif
-
-python_310 = run_command(python, '-c',
-                         'import sys; sys.exit(0 if sys.version_info >= (3,10) else 1)',
-                         check : false).returncode() == 0
-if get_option('ukify') == 'auto'
-    want_ukify = python_310
-elif get_option('ukify') == 'true' and not python310
-    error('ukify requires Python >= 3.10')
-else
-    want_ukify = get_option('ukify') == 'true'
-endif
-
-############################################################
-
 gperf = find_program('gperf')
 
 gperf_test_format = '''
@@ -1989,6 +1971,24 @@ jinja2_cmdline = [meson_render_jinja2, config_h, version_h]
 
 ############################################################
 
+python = find_program('python3')
+if run_command(python, '-c', 'import jinja2', check : false).returncode() != 0
+        error('python3 jinja2 missing')
+endif
+
+python_39 = run_command(python, '-c',
+                        'import sys; sys.exit(0 if sys.version_info >= (3,9) else 1)',
+                        check : false).returncode() == 0
+if get_option('ukify') == 'auto'
+    want_ukify = python_39  and conf.get('HAVE_GNU_EFI') == 1
+elif get_option('ukify') == 'true' and (not python_39 or conf.get('HAVE_GNU_EFI') != 1)
+    error('ukify requires Python >= 3.9 and GNU EFI')
+else
+    want_ukify = get_option('ukify') == 'true'
+endif
+
+############################################################
+
 # binaries that have --help and are intended for use by humans,
 # usually, but not always, installed in /bin.
 public_programs = []
@@ -2622,6 +2622,8 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
                  'src/boot/bootctl-status.h',
                  'src/boot/bootctl-systemd-efi-options.c',
                  'src/boot/bootctl-systemd-efi-options.h',
+                 'src/boot/bootctl-uki.c',
+                 'src/boot/bootctl-uki.h',
                  'src/boot/bootctl-util.c',
                  'src/boot/bootctl-util.h'],
                 include_directories : includes,
@@ -2677,6 +2679,7 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
                         link_with : [libshared],
                         dependencies : [libopenssl,
                                         tpm2,
+                                        libblkid,
                                         versiondep],
                         install_rpath : rootpkglibdir,
                         install : true,
@@ -2920,7 +2923,8 @@ if conf.get('HAVE_LIBCRYPTSETUP') == 1
                 link_with : [libshared],
                 dependencies : [libcryptsetup,
                                 libp11kit,
-                                versiondep],
+                                versiondep,
+                                libopenssl],
                 install_rpath : rootpkglibdir,
                 install : true,
                 install_dir : rootlibexecdir)
@@ -4047,6 +4051,7 @@ public_programs += exe
 if want_tests != 'false' and want_kernel_install
         test('test-kernel-install',
              test_kernel_install_sh,
+             env : test_env,
              args : [exe.full_path(), loaderentry_install])
 endif
 
index c1435fd0fc06dd66992e0e69e04411343a6ccf3a..372711c6077f4ebac3f1783e236e9fbf6e7a556a 100755 (executable)
@@ -83,7 +83,7 @@ if [ ! -f "$BUILDDIR"/build.ninja ] ; then
                 rootprefix=/${rootprefix#/}
         fi
 
-        meson "$BUILDDIR" \
+        meson setup "$BUILDDIR" \
                 -D "sysvinit-path=$sysvinit_path" \
                 -D "rootprefix=$rootprefix" \
                 -D man=false \
similarity index 97%
rename from mkosi.conf.d/centos_epel/10-mkosi.centos_epel
rename to mkosi.conf.d/centos/10-centos.conf
index 768403f7cfb145d45a45a00a821245380141376a..a950cbd4d2aa4f486ad92456e4596e039c78f037 100644 (file)
@@ -4,11 +4,8 @@
 # Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
 
 [Distribution]
-Distribution=centos_epel
-
-[Output]
-Format=gpt_xfs
-HostonlyInitrd=no
+Distribution=centos
+Repositories=epel
 
 [Content]
 Packages=
index 25936d8f57ebff58cb4cfbe5daf4a8320969bc6a..2b8db9d476f1e77c5a4a480dc79e5d76b6ab451d 100644 (file)
@@ -24,6 +24,7 @@ enable systemd-homed.service
 enable systemd-userdbd.socket
 enable systemd-pstore.service
 enable systemd-boot-update.service
+enable systemd-journald-audit.socket
 
 disable console-getty.service
 disable debug-shell.service
index 0b7cef7871b02490a44a0f376406a5c8fc11c0ef..8d8b507ea95131b3a651537a9ea702457d3ba664 100644 (file)
@@ -31,7 +31,7 @@ _bootctl() {
     local i verb comps
     local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
     local -A OPTS=(
-        [STANDALONE]='-h --help -p --print-esp-path -x --print-boot-path --version --no-variables --no-pager --graceful'
+        [STANDALONE]='-h --help -p --print-esp-path -x --print-boot-path --version --no-variables --no-pager --graceful --dry-run'
         [ARG]='--esp-path --boot-path --make-machine-id-directory --root --image --install-source'
     )
 
@@ -67,8 +67,8 @@ _bootctl() {
 
     local -A VERBS=(
         # systemd-efi-options takes an argument, but it is free-form, so we cannot complete it
-        [STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list set-timeout set-timeout-oneshot'
-        [BOOTENTRY]='set-default set-oneshot'
+        [STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list set-timeout set-timeout-oneshot cleanup'
+        [BOOTENTRY]='set-default set-oneshot unlink'
         [BOOLEAN]='reboot-to-firmware'
     )
 
index f935960c77e0bcb52c063ca5afddeaeaebf75cfe..3c64467e2fc45f44b5dd630e7aa2d032273d6eec 100644 (file)
@@ -128,7 +128,7 @@ _systemctl () {
         [STANDALONE]='--all -a --reverse --after --before --defaults --force -f --full -l --global
                              --help -h --no-ask-password --no-block --legend=no --no-pager --no-reload --no-wall --now
                              --quiet -q --system --user --version --runtime --recursive -r --firmware-setup
-                             --show-types --plain --failed --value --fail --dry-run --wait'
+                             --show-types --plain --failed --value --fail --dry-run --wait --no-warn'
         [ARG]='--host -H --kill-whom --property -p --signal -s --type -t --state --job-mode --root
                              --preset-mode -n --lines -o --output -M --machine --message --timestamp --check-inhibitors'
     )
index b1baec997803c2a3bc5471cfbc879c0b2188c334..5edba7bf58c51983bfb362c3553cf8d2fa181443 100644 (file)
@@ -62,7 +62,7 @@ _systemd_analyze() {
     )
 
     local -A VERBS=(
-        [STANDALONE]='time blame plot unit-paths exit-status calendar timestamp timespan'
+        [STANDALONE]='time blame unit-paths exit-status calendar timestamp timespan'
         [CRITICAL_CHAIN]='critical-chain'
         [DOT]='dot'
         [DUMP]='dump'
@@ -72,6 +72,7 @@ _systemd_analyze() {
         [SECURITY]='security'
         [CONDITION]='condition'
         [INSPECT_ELF]='inspect-elf'
+        [PLOT]='plot'
     )
 
     local CONFIGS='systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
@@ -195,6 +196,11 @@ _systemd_analyze() {
             comps=$( compgen -A file -- "$cur" )
             compopt -o filenames
         fi
+
+    elif __contains_word "$verb" ${VERBS[PLOT]}; then
+        if [[ $cur = -* ]]; then
+            comps='--help --version --system --user --global --no-pager --json=off --json=pretty --json=short --table --no-legend'
+        fi
     fi
 
     COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
index 8634e8b9bc06496d1f5de2176b21b6e43ebb0faf..83d910cda15a6058d36a9bb25f12dfcb597522cb 100644 (file)
@@ -24,6 +24,10 @@ _bootctl_set-oneshot() {
     _bootctl_comp_ids
 }
 
+_bootctl_unlink() {
+    _bootctl_comp_ids
+}
+
 _bootctl_reboot-to-firmware() {
     local -a _completions
     _completions=( yes no )
@@ -48,6 +52,8 @@ _bootctl_reboot-to-firmware() {
         "set-oneshot:Set the default boot loader entry only for the next boot"
         "set-timeout:Set the menu timeout"
         "set-timeout-oneshot:Set the menu timeout for the next boot only"
+        "unlink:Remove boot loader entry"
+        "cleanup:Remove files in ESP not referenced in any boot entry"
     )
     if (( CURRENT == 1 )); then
         _describe -t commands 'bootctl command' _bootctl_cmds || compadd "$@"
@@ -73,6 +79,7 @@ _arguments \
     '--no-variables[Do not touch EFI variables]' \
     '--no-pager[Do not pipe output into a pager]' \
     '--graceful[Do not fail when locating ESP or writing fails]' \
+    '--dry-run[Dry run (unlink and cleanup)]' \
     '--root=[Operate under the specified directory]:PATH' \
     '--image=[Operate on the specified image]:PATH' \
     '--install-source[Where to pick files when using --root=/--image=]:options:(image host auto)' \
index d4717430305c2e7c81b8f36c77ed47c0cf8c7b32..2811582236dd69fb976c921eccf8d213369d66b4 100644 (file)
@@ -480,6 +480,7 @@ _arguments -s \
     '--show-types[When showing sockets, show socket type]' \
     '--check-inhibitors[Specify if inhibitors should be checked]:mode:_systemctl_check_inhibitors' \
     {-q,--quiet}'[Suppress output]' \
+    '--no-warn[Suppress several warnings shown by default]' \
     '--no-block[Do not wait until operation finished]' \
     '--legend=no[Do not print a legend, i.e. the column headers and the footer with hints]' \
     '--no-pager[Do not pipe output into a pager]' \
index e305995cef7314bca2f0381c2684821d5629411f..2e046ea1119d71df72bcbb3c86f027381302f4e1 100644 (file)
         _describe -t groups 'file system groups' _groups || compadd "$@"
     }
 
+(( $+functions[_systemd-analyze_plot] )) ||
+    _systemd-analyze_plot() {
+        local -a _options
+        _options=( '--json=off' '--json=pretty' '--json=short' '--table' '--no-legend' )
+        _describe 'plot options' _options
+    }
+
 (( $+functions[_systemd-analyze_commands] )) ||
     _systemd-analyze_commands(){
         local -a _systemd_analyze_cmds
@@ -48,7 +55,8 @@
             'time:Print time spent in the kernel before reaching userspace'
             'blame:Print list of running units ordered by time to init'
             'critical-chain:Print a tree of the time critical chain of units'
-            'plot:Output SVG graphic showing service initialization'
+            'plot:Output SVG graphic showing service initialization, or raw time data in
+JSON or table format'
             'dot:Dump dependency graph (in dot(1) format)'
             'dump:Dump server status'
             'cat-config:Cat systemd config files'
@@ -97,9 +105,11 @@ _arguments \
     '--offline=[Perform a security review of the specified unit files]:BOOL:(yes no)' \
     '--threshold=[Set a value to compare the overall security exposure level with]: NUMBER' \
     '--security-policy=[Use customized requirements to compare unit files against]: PATH' \
-    '--json=[Generate a JSON output of the security analysis table]:MODE:(pretty short off)' \
+    "--json=[Generate a JSON output of the security analysis table or plot's raw time data]:MODE:(pretty short off)" \
+    "--table=[Generate a table of plot's raw time data]" \
     '--profile=[Include the specified profile in the security review of units]: PATH' \
     '--no-pager[Do not pipe output into a pager]' \
+    "--no-legend[Do not show the headers and footers for plot's raw time data formats]" \
     '--man=[Do (not) check for existence of man pages]:BOOL:(yes no)' \
     '--generators=[Do (not) run unit generators]:BOOL:(yes no)' \
     '--order[When generating graph for dot, show only order]' \
index 100bdc3787c243ea079376ba8d8e3e5a5f3f4426..24f4add099f49e916d408fc35479a887dc11bb7b 100644 (file)
@@ -5,6 +5,7 @@
 #include "analyze-time-data.h"
 #include "bus-error.h"
 #include "bus-map-properties.h"
+#include "format-table.h"
 #include "sort-util.h"
 #include "version.h"
 
@@ -37,7 +38,7 @@ typedef struct HostInfo {
         char *architecture;
 } HostInfo;
 
-static HostInfofree_host_info(HostInfo *hi) {
+static HostInfo *free_host_info(HostInfo *hi) {
         if (!hi)
                 return NULL;
 
@@ -87,7 +88,7 @@ static int acquire_host_info(sd_bus *bus, HostInfo **hi) {
         }
 
         r = bus_map_all_properties(
-                        system_bus ?: bus,
+                        system_bus ? : bus,
                         "org.freedesktop.hostname1",
                         "/org/freedesktop/hostname1",
                         hostname_map,
@@ -156,15 +157,14 @@ static void svg_graph_box(double height, double begin, double end) {
                             SCALE_Y * height);
         }
 }
-
 static int plot_unit_times(UnitTimes *u, double width, int y) {
         bool b;
 
         if (!u->name)
                 return 0;
 
-        svg_bar("activating",   u->activating, u->activated, y);
-        svg_bar("active",       u->activated, u->deactivating, y);
+        svg_bar("activating", u->activating, u->activated, y);
+        svg_bar("active", u->activated, u->deactivating, y);
         svg_bar("deactivating", u->deactivating, u->deactivated, y);
 
         /* place the text on the left if we have passed the half of the svg width */
@@ -178,41 +178,27 @@ static int plot_unit_times(UnitTimes *u, double width, int y) {
         return 1;
 }
 
-int verb_plot(int argc, char *argv[], void *userdata) {
-        _cleanup_(free_host_infop) HostInfo *host = NULL;
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
-        _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL;
-        _cleanup_free_ char *pretty_times = NULL;
-        bool use_full_bus = arg_scope == LOOKUP_SCOPE_SYSTEM;
-        BootTimes *boot;
+static void limit_times_to_boot(const BootTimes *boot, UnitTimes *u) {
+        if (u->deactivated > u->activating && u->deactivated <= boot->finish_time && u->activated == 0
+            && u->deactivating == 0)
+                u->activated = u->deactivating = u->deactivated;
+        if (u->activated < u->activating || u->activated > boot->finish_time)
+                u->activated = boot->finish_time;
+        if (u->deactivating < u->activated || u->deactivating > boot->finish_time)
+                u->deactivating = boot->finish_time;
+        if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
+                u->deactivated = boot->finish_time;
+}
+
+static int produce_plot_as_svg(
+                UnitTimes *times,
+                const HostInfo *host,
+                const BootTimes *boot,
+                const char *pretty_times) {
+        int m = 1, y = 0;
         UnitTimes *u;
-        int n, m = 1, y = 0, r;
         double width;
 
-        r = acquire_bus(&bus, &use_full_bus);
-        if (r < 0)
-                return bus_log_connect_error(r, arg_transport);
-
-        n = acquire_boot_times(bus, &boot);
-        if (n < 0)
-                return n;
-
-        n = pretty_boot_time(bus, &pretty_times);
-        if (n < 0)
-                return n;
-
-        if (use_full_bus || arg_scope != LOOKUP_SCOPE_SYSTEM) {
-                n = acquire_host_info(bus, &host);
-                if (n < 0)
-                        return n;
-        }
-
-        n = acquire_time_data(bus, &times);
-        if (n <= 0)
-                return n;
-
-        typesafe_qsort(times, n, compare_unit_start);
-
         width = SCALE_X * (boot->firmware_time + boot->finish_time);
         if (width < 800.0)
                 width = 800.0;
@@ -245,16 +231,8 @@ int verb_plot(int argc, char *argv[], void *userdata) {
                 if (text_width > text_start && text_width + text_start > width)
                         width = text_width + text_start;
 
-                if (u->deactivated > u->activating &&
-                    u->deactivated <= boot->finish_time &&
-                    u->activated == 0 && u->deactivating == 0)
-                        u->activated = u->deactivating = u->deactivated;
-                if (u->activated < u->activating || u->activated > boot->finish_time)
-                        u->activated = boot->finish_time;
-                if (u->deactivating < u->activated || u->deactivating > boot->finish_time)
-                        u->deactivating = boot->finish_time;
-                if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
-                        u->deactivated = boot->finish_time;
+                limit_times_to_boot(boot, u);
+
                 m++;
         }
 
@@ -391,5 +369,101 @@ int verb_plot(int argc, char *argv[], void *userdata) {
 
         svg("</svg>\n");
 
+        return 0;
+}
+
+static int show_table(Table *table, const char *word) {
+        int r;
+
+        assert(table);
+        assert(word);
+
+        if (table_get_rows(table) > 1) {
+                table_set_header(table, arg_legend);
+
+                if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
+                        r = table_print_json(table, NULL, arg_json_format_flags | JSON_FORMAT_COLOR_AUTO);
+                else
+                        r = table_print(table, NULL);
+                if (r < 0)
+                        return table_log_print_error(r);
+        }
+
+        if (arg_legend) {
+                if (table_get_rows(table) > 1)
+                        printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
+                else
+                        printf("No %s.\n", word);
+        }
+
+        return 0;
+}
+
+static int produce_plot_as_text(UnitTimes *times, const BootTimes *boot) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        int r;
+
+        table = table_new("name", "activated", "activating", "time", "deactivated", "deactivating");
+        if (!table)
+                return log_oom();
+
+        for (; times->has_data; times++) {
+                limit_times_to_boot(boot, times);
+
+                r = table_add_many(
+                                table,
+                                TABLE_STRING, times->name,
+                                TABLE_TIMESPAN_MSEC, times->activated,
+                                TABLE_TIMESPAN_MSEC, times->activating,
+                                TABLE_TIMESPAN_MSEC, times->time,
+                                TABLE_TIMESPAN_MSEC, times->deactivated,
+                                TABLE_TIMESPAN_MSEC, times->deactivating);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
+        return show_table(table, "Units");
+}
+
+int verb_plot(int argc, char *argv[], void *userdata) {
+        _cleanup_(free_host_infop) HostInfo *host = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL;
+        _cleanup_free_ char *pretty_times = NULL;
+        bool use_full_bus = arg_scope == LOOKUP_SCOPE_SYSTEM;
+        BootTimes *boot;
+        int n, r;
+
+        r = acquire_bus(&bus, &use_full_bus);
+        if (r < 0)
+                return bus_log_connect_error(r, arg_transport);
+
+        n = acquire_boot_times(bus, &boot);
+        if (n < 0)
+                return n;
+
+        n = pretty_boot_time(bus, &pretty_times);
+        if (n < 0)
+                return n;
+
+        if (use_full_bus || arg_scope != LOOKUP_SCOPE_SYSTEM) {
+                n = acquire_host_info(bus, &host);
+                if (n < 0)
+                        return n;
+        }
+
+        n = acquire_time_data(bus, &times);
+        if (n <= 0)
+                return n;
+
+        typesafe_qsort(times, n, compare_unit_start);
+
+        if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) || arg_table)
+                r = produce_plot_as_text(times, boot);
+        else
+                r = produce_plot_as_svg(times, host, boot, pretty_times);
+        if (r < 0)
+                return r;
+
         return EXIT_SUCCESS;
 }
index cac2d3adba70847bae44e08f1d41a273862da17b..ef2e60962b3e346ad46a6014c6da832cededdfcf 100644 (file)
@@ -105,6 +105,8 @@ char *arg_unit = NULL;
 JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
 bool arg_quiet = false;
 char *arg_profile = NULL;
+bool arg_legend = true;
+bool arg_table = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
@@ -217,8 +219,10 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --security-policy=PATH  Use custom JSON security policy instead\n"
                "                             of built-in one\n"
                "     --json=pretty|short|off Generate JSON output of the security\n"
-               "                             analysis table\n"
+               "                             analysis table, or plot's raw time data\n"
                "     --no-pager              Do not pipe output into a pager\n"
+               "     --no-legend             Disable column headers and hints in plot\n"
+               "                             with either --table or --json=\n"
                "     --system                Operate on system systemd instance\n"
                "     --user                  Operate on user systemd instance\n"
                "     --global                Operate on global user configuration\n"
@@ -238,6 +242,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "                             specified time\n"
                "     --profile=name|PATH     Include the specified profile in the\n"
                "                             security review of the unit(s)\n"
+               "     --table                 Output plot's raw time data as a table\n"
                "  -h --help                  Show this help\n"
                "     --version               Show package version\n"
                "  -q --quiet                 Do not emit hints\n"
@@ -280,6 +285,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SECURITY_POLICY,
                 ARG_JSON,
                 ARG_PROFILE,
+                ARG_TABLE,
+                ARG_NO_LEGEND,
         };
 
         static const struct option options[] = {
@@ -310,6 +317,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "unit",             required_argument, NULL, 'U'                  },
                 { "json",             required_argument, NULL, ARG_JSON             },
                 { "profile",          required_argument, NULL, ARG_PROFILE          },
+                { "table",            optional_argument, NULL, ARG_TABLE            },
+                { "no-legend",        optional_argument, NULL, ARG_NO_LEGEND        },
                 {}
         };
 
@@ -448,14 +457,12 @@ static int parse_argv(int argc, char *argv[]) {
                         r = safe_atou(optarg, &arg_iterations);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to parse iterations: %s", optarg);
-
                         break;
 
                 case ARG_BASE_TIME:
                         r = parse_timestamp(optarg, &arg_base_time);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to parse --base-time= parameter: %s", optarg);
-
                         break;
 
                 case ARG_PROFILE:
@@ -486,6 +493,15 @@ static int parse_argv(int argc, char *argv[]) {
                         free_and_replace(arg_unit, mangled);
                         break;
                 }
+
+                case ARG_TABLE:
+                        arg_table = true;
+                        break;
+
+                case ARG_NO_LEGEND:
+                        arg_legend = false;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -497,9 +513,9 @@ static int parse_argv(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Option --offline= is only supported for security right now.");
 
-        if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf"))
+        if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Option --json= is only supported for security and inspect-elf right now.");
+                                       "Option --json= is only supported for security, inspect-elf, and plot right now.");
 
         if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -536,6 +552,16 @@ static int parse_argv(int argc, char *argv[]) {
         if (streq_ptr(argv[optind], "condition") && arg_unit && optind < argc - 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used.");
 
+        if ((!arg_legend && !streq_ptr(argv[optind], "plot")) ||
+           (streq_ptr(argv[optind], "plot") && !arg_legend && !arg_table && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --no-legend is only supported for plot with either --table or --json=.");
+
+        if (arg_table && !streq_ptr(argv[optind], "plot"))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --table is only supported for plot right now.");
+
+        if (arg_table && !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--table and --json= are mutually exclusive.");
+
         return 1; /* work to do */
 }
 
index da12058c43fc3c44e40a2b03450225cb69ed9dd6..e4af7b47e00c965c0625d0abe53cdadb95cfffa3 100644 (file)
@@ -36,6 +36,8 @@ extern char *arg_unit;
 extern JsonFormatFlags arg_json_format_flags;
 extern bool arg_quiet;
 extern char *arg_profile;
+extern bool arg_legend;
+extern bool arg_table;
 
 int acquire_bus(sd_bus **bus, bool *use_full_bus);
 
index a0134fd330bb3365006874b0b6b845c8394010cd..4cc0a01df8ef0a289b8ef034661ed5574fe353f6 100644 (file)
@@ -689,3 +689,42 @@ int chase_symlinks_and_fopen_unlocked(
 
         return 0;
 }
+
+int chase_symlinks_and_unlink(
+                const char *path,
+                const char *root,
+                ChaseSymlinksFlags chase_flags,
+                int unlink_flags,
+                char **ret_path) {
+
+        _cleanup_free_ char *p = NULL, *rp = NULL, *dir = NULL, *fname = NULL;
+        _cleanup_close_ int fd = -1;
+        int r;
+
+        assert(path);
+
+        r = path_extract_directory(path, &dir);
+        if (r < 0)
+                return r;
+        r = path_extract_filename(path, &fname);
+        if (r < 0)
+                return r;
+
+        fd = chase_symlinks_and_open(dir, root, chase_flags, O_PATH|O_DIRECTORY|O_CLOEXEC, ret_path ? &p : NULL);
+        if (fd < 0)
+                return fd;
+
+        if (p) {
+                rp = path_join(p, fname);
+                if (!rp)
+                        return -ENOMEM;
+        }
+
+        if (unlinkat(fd, fname, unlink_flags) < 0)
+                return -errno;
+
+        if (ret_path)
+                *ret_path = TAKE_PTR(rp);
+
+        return 0;
+}
index af0fcf155a26973c3adedc59ce4faee975cb1f83..be5e2bb69650135c9ed55178bd40ff3fa171d40b 100644 (file)
@@ -34,5 +34,6 @@ int chase_symlinks_and_opendir(const char *path, const char *root, ChaseSymlinks
 int chase_symlinks_and_stat(const char *path, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
 int chase_symlinks_and_access(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int access_mode, char **ret_path, int *ret_fd);
 int chase_symlinks_and_fopen_unlocked(const char *path, const char *root, ChaseSymlinksFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
+int chase_symlinks_and_unlink(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int unlink_flags, char **ret_path);
 
 int chase_symlinks_at(int dir_fd, const char *path, ChaseSymlinksFlags flags, char **ret_path, int *ret_fd);
index 3b920418b09ab78801930e7749279142d9fb8800..51b11ab9c7d12e85dc76500c3216e82deb9cc43b 100644 (file)
@@ -862,7 +862,6 @@ int executable_is_script(const char *path, char **interpreter) {
 int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
         _cleanup_free_ char *status = NULL;
         char *t, *f;
-        size_t len;
         int r;
 
         assert(terminator);
@@ -914,9 +913,7 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin
                         t--;
         }
 
-        len = strcspn(t, terminator);
-
-        f = strndup(t, len);
+        f = strdupcspn(t, terminator);
         if (!f)
                 return -ENOMEM;
 
index 0ff8eb3256eefac039c09fd82d8a4f5d7e17787b..898ed83f862ac6dd759febcd4cefb446bbf39918 100644 (file)
@@ -110,12 +110,17 @@ static int unhex_next(const char **p, size_t *l) {
         return r;
 }
 
-int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_len) {
+int unhexmem_full(
+                const char *p,
+                size_t l,
+                bool secure,
+                void **ret,
+                size_t *ret_len) {
+
         _cleanup_free_ uint8_t *buf = NULL;
         size_t buf_size;
         const char *x;
         uint8_t *z;
-        int r;
 
         assert(p || l == 0);
 
@@ -128,22 +133,20 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_
         if (!buf)
                 return -ENOMEM;
 
+        CLEANUP_ERASE_PTR(secure ? &buf : NULL, buf_size);
+
         for (x = p, z = buf;;) {
                 int a, b;
 
                 a = unhex_next(&x, &l);
                 if (a == -EPIPE) /* End of string */
                         break;
-                if (a < 0) {
-                        r = a;
-                        goto on_failure;
-                }
+                if (a < 0)
+                        return a;
 
                 b = unhex_next(&x, &l);
-                if (b < 0) {
-                        r = b;
-                        goto on_failure;
-                }
+                if (b < 0)
+                        return b;
 
                 *(z++) = (uint8_t) a << 4 | (uint8_t) b;
         }
@@ -156,12 +159,6 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_
                 *ret = TAKE_PTR(buf);
 
         return 0;
-
-on_failure:
-        if (secure)
-                explicit_bzero_safe(buf, buf_size);
-
-        return r;
 }
 
 /* https://tools.ietf.org/html/rfc4648#section-6
@@ -588,67 +585,66 @@ ssize_t base64mem_full(
                 const void *p,
                 size_t l,
                 size_t line_break,
-                char **out) {
+                char **ret) {
 
         const uint8_t *x;
-        char *r, *z;
+        char *b, *z;
         size_t m;
 
         assert(p || l == 0);
-        assert(out);
         assert(line_break > 0);
+        assert(ret);
 
         /* three input bytes makes four output bytes, padding is added so we must round up */
         m = 4 * (l + 2) / 3 + 1;
-
         if (line_break != SIZE_MAX)
                 m += m / line_break;
 
-        z = r = malloc(m);
-        if (!r)
+        z = b = malloc(m);
+        if (!b)
                 return -ENOMEM;
 
         for (x = p; x && x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
                 /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4);  /* 00XXYYYY */
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = base64char(x[2] & 63);                    /* 00ZZZZZZ */
         }
 
         switch (l % 3) {
         case 2:
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = base64char(x[0] >> 2);                   /* 00XXXXXX */
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = base64char((x[1] & 15) << 2);            /* 00YYYY00 */
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = '=';
-
                 break;
+
         case 1:
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = base64char(x[0] >> 2);        /* 00XXXXXX */
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = base64char((x[0] & 3) << 4);  /* 00XX0000 */
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = '=';
-                maybe_line_break(&z, r, line_break);
+                maybe_line_break(&z, b, line_break);
                 *(z++) = '=';
-
                 break;
         }
 
         *z = 0;
-        *out = r;
-        assert(z >= r); /* Let static analyzers know that the answer is non-negative. */
-        return z - r;
+        *ret = b;
+
+        assert(z >= b); /* Let static analyzers know that the answer is non-negative. */
+        return z - b;
 }
 
 static ssize_t base64_append_width(
@@ -766,12 +762,17 @@ static int unbase64_next(const char **p, size_t *l) {
         return ret;
 }
 
-int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) {
+int unbase64mem_full(
+                const char *p,
+                size_t l,
+                bool secure,
+                void **ret,
+                size_t *ret_size) {
+
         _cleanup_free_ uint8_t *buf = NULL;
         const char *x;
         uint8_t *z;
         size_t len;
-        int r;
 
         assert(p || l == 0);
 
@@ -786,60 +787,44 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r
         if (!buf)
                 return -ENOMEM;
 
+        CLEANUP_ERASE_PTR(secure ? &buf : NULL, len);
+
         for (x = p, z = buf;;) {
                 int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
 
                 a = unbase64_next(&x, &l);
                 if (a == -EPIPE) /* End of string */
                         break;
-                if (a < 0) {
-                        r = a;
-                        goto on_failure;
-                }
-                if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */
-                        r = -EINVAL;
-                        goto on_failure;
-                }
+                if (a < 0)
+                        return a;
+                if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
+                        return -EINVAL;
 
                 b = unbase64_next(&x, &l);
-                if (b < 0) {
-                        r = b;
-                        goto on_failure;
-                }
-                if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */
-                        r = -EINVAL;
-                        goto on_failure;
-                }
+                if (b < 0)
+                        return b;
+                if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
+                        return -EINVAL;
 
                 c = unbase64_next(&x, &l);
-                if (c < 0) {
-                        r = c;
-                        goto on_failure;
-                }
+                if (c < 0)
+                        return c;
 
                 d = unbase64_next(&x, &l);
-                if (d < 0) {
-                        r = d;
-                        goto on_failure;
-                }
+                if (d < 0)
+                        return d;
 
                 if (c == INT_MAX) { /* Padding at the third character */
 
-                        if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */
-                                r = -EINVAL;
-                                goto on_failure;
-                        }
+                        if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
+                                return -EINVAL;
 
                         /* b == 00YY0000 */
-                        if (b & 15) {
-                                r = -EINVAL;
-                                goto on_failure;
-                        }
+                        if (b & 15)
+                                return -EINVAL;
 
-                        if (l > 0) { /* Trailing rubbish? */
-                                r = -ENAMETOOLONG;
-                                goto on_failure;
-                        }
+                        if (l > 0) /* Trailing rubbish? */
+                                return -ENAMETOOLONG;
 
                         *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
                         break;
@@ -847,15 +832,11 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r
 
                 if (d == INT_MAX) {
                         /* c == 00ZZZZ00 */
-                        if (c & 3) {
-                                r = -EINVAL;
-                                goto on_failure;
-                        }
+                        if (c & 3)
+                                return -EINVAL;
 
-                        if (l > 0) { /* Trailing rubbish? */
-                                r = -ENAMETOOLONG;
-                                goto on_failure;
-                        }
+                        if (l > 0) /* Trailing rubbish? */
+                                return -ENAMETOOLONG;
 
                         *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
                         *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
@@ -869,18 +850,14 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r
 
         *z = 0;
 
+        assert((size_t) (z - buf) <= len);
+
         if (ret_size)
                 *ret_size = (size_t) (z - buf);
         if (ret)
                 *ret = TAKE_PTR(buf);
 
         return 0;
-
-on_failure:
-        if (secure)
-                explicit_bzero_safe(buf, len);
-
-        return r;
 }
 
 void hexdump(FILE *f, const void *p, size_t s) {
index b710f07929e19cf64be5a78af2eabfd1789cb65b..e743033b1ea1a6aae39c35cb7572acc3373ff4e9 100644 (file)
@@ -62,7 +62,7 @@ int gethostname_full(GetHostnameFlags flags, char **ret) {
         }
 
         if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
-                buf = strndup(s, strcspn(s, "."));
+                buf = strdupcspn(s, ".");
         else
                 buf = strdup(s);
         if (!buf)
index 07ac87c6c4020d18043ca2ff0c163d5a1405e693..30d90cce0d797ae305c6b402d5005c781500e668 100644 (file)
@@ -898,14 +898,6 @@ int in_addr_prefix_from_string_auto_internal(
                         break;
                 case PREFIXLEN_REFUSE:
                         return -ENOANO; /* To distinguish this error from others. */
-                case PREFIXLEN_LEGACY:
-                        if (family == AF_INET) {
-                                r = in4_addr_default_prefixlen(&buffer.in, &k);
-                                if (r < 0)
-                                        return r;
-                        } else
-                                k = 0;
-                        break;
                 default:
                         assert_not_reached();
                 }
index 19354ad404f560966ff1310f8cfeaf7e8a9a67af..200b9eb69df8484f88d99576f2a80f4bf1fa4cc3 100644 (file)
@@ -153,7 +153,6 @@ int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *r
 typedef enum InAddrPrefixLenMode {
         PREFIXLEN_FULL,   /* Default to prefixlen of address size, 32 for IPv4 or 128 for IPv6, if not specified. */
         PREFIXLEN_REFUSE, /* Fail with -ENOANO if prefixlen is not specified. */
-        PREFIXLEN_LEGACY, /* Default to legacy default prefixlen calculation from address if not specified. */
 } InAddrPrefixLenMode;
 
 int in_addr_prefix_from_string_auto_internal(const char *p, InAddrPrefixLenMode mode, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
index ca300396908c8d9dba5f9133e7f55e68bb5bcbe0..ffc8bd830453ecd3c451966c88749f45717a4502 100644 (file)
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include "macro.h"
-
 /* The head of the linked list. Use this in the structure that shall
  * contain the head of the linked list */
 #define LIST_HEAD(t,name)                                               \
 
 /* Prepend an item to the list */
 #define LIST_PREPEND(name,head,item)                                    \
-        do {                                                            \
+        ({                                                              \
                 typeof(*(head)) **_head = &(head), *_item = (item);     \
                 assert(_item);                                          \
                 if ((_item->name##_next = *_head))                      \
                         _item->name##_next->name##_prev = _item;        \
                 _item->name##_prev = NULL;                              \
                 *_head = _item;                                         \
-        } while (false)
+                _item;                                                  \
+        })
 
 /* Append an item to the list */
 #define LIST_APPEND(name,head,item)                                     \
-        do {                                                            \
+        ({                                                              \
                 typeof(*(head)) **_hhead = &(head), *_tail;             \
-                LIST_FIND_TAIL(name, *_hhead, _tail);                   \
+                _tail = LIST_FIND_TAIL(name, *_hhead);                  \
                 LIST_INSERT_AFTER(name, *_hhead, _tail, item);          \
-        } while (false)
+        })
 
 /* Remove an item from the list */
 #define LIST_REMOVE(name,head,item)                                     \
-        do {                                                            \
+        ({                                                            \
                 typeof(*(head)) **_head = &(head), *_item = (item);     \
                 assert(_item);                                          \
                 if (_item->name##_next)                                 \
                         *_head = _item->name##_next;                    \
                 }                                                       \
                 _item->name##_next = _item->name##_prev = NULL;         \
-        } while (false)
+                _item;                                                  \
+        })
 
 /* Find the head of the list */
-#define LIST_FIND_HEAD(name,item,head)                                  \
-        do {                                                            \
+#define LIST_FIND_HEAD(name,item)                                       \
+        ({                                                              \
                 typeof(*(item)) *_item = (item);                        \
-                if (!_item)                                             \
-                        (head) = NULL;                                  \
-                else {                                                  \
-                        while (_item->name##_prev)                      \
-                                _item = _item->name##_prev;             \
-                        (head) = _item;                                 \
-                }                                                       \
-        } while (false)
+                while (_item && _item->name##_prev)                     \
+                        _item = _item->name##_prev;                     \
+                _item;                                                  \
+        })
 
 /* Find the tail of the list */
-#define LIST_FIND_TAIL(name,item,tail)                                  \
-        do {                                                            \
+#define LIST_FIND_TAIL(name,item)                                       \
+        ({                                                              \
                 typeof(*(item)) *_item = (item);                        \
-                if (!_item)                                             \
-                        (tail) = NULL;                                  \
-                else {                                                  \
-                        while (_item->name##_next)                      \
-                                _item = _item->name##_next;             \
-                        (tail) = _item;                                 \
-                }                                                       \
-        } while (false)
+                while (_item && _item->name##_next)                     \
+                        _item = _item->name##_next;                     \
+                _item;                                                  \
+        })
 
 /* Insert an item after another one (a = where, b = what) */
 #define LIST_INSERT_AFTER(name,head,a,b)                                \
-        do {                                                            \
+        ({                                                              \
                 typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
                 assert(_b);                                             \
                 if (!_a) {                                              \
                         _b->name##_prev = _a;                           \
                         _a->name##_next = _b;                           \
                 }                                                       \
-        } while (false)
+                _b;                                                     \
+        })
 
 /* Insert an item before another one (a = where, b = what) */
 #define LIST_INSERT_BEFORE(name,head,a,b)                               \
-        do {                                                            \
+        ({                                                              \
                 typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
                 assert(_b);                                             \
                 if (!_a) {                                              \
                         _b->name##_next = _a;                           \
                         _a->name##_prev = _b;                           \
                 }                                                       \
-        } while (false)
+                _b;                                                     \
+        })
 
 #define LIST_JUST_US(name,item)                                         \
         (!(item)->name##_prev && !(item)->name##_next)
 
 /* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */
 #define LIST_JOIN(name,a,b)                                             \
-        do {                                                            \
+        ({                                                              \
                 assert(b);                                              \
                 if (!(a))                                               \
                         (a) = (b);                                      \
                 else {                                                  \
                         typeof(*(a)) *_head = (b), *_tail;              \
-                        LIST_FIND_TAIL(name, (a), _tail);               \
+                        _tail = LIST_FIND_TAIL(name, (a));              \
                         _tail->name##_next = _head;                     \
                         _head->name##_prev = _tail;                     \
                 }                                                       \
                 (b) = NULL;                                             \
-        } while (false)
+                a;                                                      \
+        })
 
 #define LIST_POP(name, a)                                               \
         ({                                                              \
                         LIST_REMOVE(name, *_a, _p);                     \
                 _p;                                                     \
         })
+
+/* Now include "macro.h", because we want our definition of assert() which the macros above use. We include
+ * it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */
+#include "macro.h"
index 282130345f3b458858864202794b18fb1a329c09..fc5793139ed709c656248ae0b6eb6018aa96ba83 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "alloc-util.h"
 #include "argv-util.h"
+#include "env-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "stdio-util.h"
 #include "string-table.h"
 #include "string-util.h"
+#include "strv.h"
 #include "syslog-util.h"
 #include "terminal-util.h"
 #include "time-util.h"
 #include "utf8.h"
 
 #define SNDBUF_SIZE (8*1024*1024)
+#define IOVEC_MAX 128U
 
 static log_syntax_callback_t log_syntax_callback = NULL;
 static void *log_syntax_callback_userdata = NULL;
@@ -68,6 +71,17 @@ static bool prohibit_ipc = false;
  * use here. */
 static char *log_abort_msg = NULL;
 
+typedef struct LogContext {
+        /* Depending on which destructor is used (log_context_free() or log_context_detach()) the memory
+         * referenced by this is freed or not */
+        char **fields;
+        bool owned;
+        LIST_FIELDS(struct LogContext, ll);
+} LogContext;
+
+static thread_local LIST_HEAD(LogContext, _log_context) = NULL;
+static thread_local size_t _log_context_num_fields = 0;
+
 #if LOG_MESSAGE_VERIFICATION || defined(__COVERITY__)
 bool _log_message_dummy = false; /* Always false */
 #endif
@@ -589,6 +603,20 @@ static int log_do_header(
         return 0;
 }
 
+static void log_do_context(struct iovec *iovec, size_t iovec_len, size_t *n) {
+        assert(iovec);
+        assert(n);
+
+        LIST_FOREACH(ll, c, _log_context)
+                STRV_FOREACH(s, c->fields) {
+                        if (*n + 2 >= iovec_len)
+                                return;
+
+                        iovec[(*n)++] = IOVEC_MAKE_STRING(*s);
+                        iovec[(*n)++] = IOVEC_MAKE_STRING("\n");
+                }
+}
+
 static int write_to_journal(
                 int level,
                 int error,
@@ -602,21 +630,27 @@ static int write_to_journal(
                 const char *buffer) {
 
         char header[LINE_MAX];
+        size_t n = 0, iovec_len;
+        struct iovec *iovec;
 
         if (journal_fd < 0)
                 return 0;
 
+        iovec_len = MIN(4 + _log_context_num_fields * 2, IOVEC_MAX);
+        iovec = newa(struct iovec, iovec_len);
+
         log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra);
 
-        struct iovec iovec[4] = {
-                IOVEC_MAKE_STRING(header),
-                IOVEC_MAKE_STRING("MESSAGE="),
-                IOVEC_MAKE_STRING(buffer),
-                IOVEC_MAKE_STRING("\n"),
-        };
+        iovec[n++] = IOVEC_MAKE_STRING(header);
+        iovec[n++] = IOVEC_MAKE_STRING("MESSAGE=");
+        iovec[n++] = IOVEC_MAKE_STRING(buffer);
+        iovec[n++] = IOVEC_MAKE_STRING("\n");
+
+        log_do_context(iovec, iovec_len, &n);
+
         const struct msghdr msghdr = {
                 .msg_iov = iovec,
-                .msg_iovlen = ELEMENTSOF(iovec),
+                .msg_iovlen = n,
         };
 
         if (sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL) < 0)
@@ -948,21 +982,27 @@ int log_struct_internal(
 
                 if (journal_fd >= 0) {
                         char header[LINE_MAX];
-                        struct iovec iovec[17];
-                        size_t n = 0;
+                        struct iovec *iovec;
+                        size_t n = 0, m, iovec_len;
                         int r;
                         bool fallback = false;
 
+                        iovec_len = MIN(17 + _log_context_num_fields * 2, IOVEC_MAX);
+                        iovec = newa(struct iovec, iovec_len);
+
                         /* If the journal is available do structured logging.
                          * Do not report the errno if it is synthetic. */
                         log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
                         iovec[n++] = IOVEC_MAKE_STRING(header);
 
                         va_start(ap, format);
-                        r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, true, error, format, ap);
+                        r = log_format_iovec(iovec, iovec_len, &n, true, error, format, ap);
+                        m = n;
                         if (r < 0)
                                 fallback = true;
                         else {
+                                log_do_context(iovec, iovec_len, &n);
+
                                 const struct msghdr msghdr = {
                                         .msg_iov = iovec,
                                         .msg_iovlen = n,
@@ -972,7 +1012,7 @@ int log_struct_internal(
                         }
 
                         va_end(ap);
-                        for (size_t i = 1; i < n; i += 2)
+                        for (size_t i = 1; i < m; i += 2)
                                 free(iovec[i].iov_base);
 
                         if (!fallback) {
@@ -1041,18 +1081,25 @@ int log_struct_iovec_internal(
             journal_fd >= 0) {
 
                 char header[LINE_MAX];
+                struct iovec *iovec;
+                size_t n = 0, iovec_len;
+
+                iovec_len = MIN(1 + n_input_iovec * 2 + _log_context_num_fields * 2, IOVEC_MAX);
+                iovec = newa(struct iovec, iovec_len);
+
                 log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
 
-                struct iovec iovec[1 + n_input_iovec*2];
-                iovec[0] = IOVEC_MAKE_STRING(header);
+                iovec[n++] = IOVEC_MAKE_STRING(header);
                 for (size_t i = 0; i < n_input_iovec; i++) {
-                        iovec[1+i*2] = input_iovec[i];
-                        iovec[1+i*2+1] = IOVEC_MAKE_STRING("\n");
+                        iovec[n++] = input_iovec[i];
+                        iovec[n++] = IOVEC_MAKE_STRING("\n");
                 }
 
+                log_do_context(iovec, iovec_len, &n);
+
                 const struct msghdr msghdr = {
                         .msg_iov = iovec,
-                        .msg_iovlen = 1 + n_input_iovec*2,
+                        .msg_iovlen = n,
                 };
 
                 if (sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL) >= 0)
@@ -1477,3 +1524,88 @@ void log_setup(void) {
         if (log_on_console() && show_color < 0)
                 log_show_color(true);
 }
+
+static int saved_log_context_enabled = -1;
+
+bool log_context_enabled(void) {
+        int r;
+
+        if (log_get_max_level() == LOG_DEBUG)
+                return true;
+
+        if (saved_log_context_enabled >= 0)
+                return saved_log_context_enabled;
+
+        r = getenv_bool_secure("SYSTEMD_ENABLE_LOG_CONTEXT");
+        if (r < 0 && r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $SYSTEMD_ENABLE_LOG_CONTEXT, ignoring: %m");
+
+        saved_log_context_enabled = r > 0;
+
+        return saved_log_context_enabled;
+}
+
+LogContext* log_context_attach(LogContext *c) {
+        assert(c);
+
+        _log_context_num_fields += strv_length(c->fields);
+
+        return LIST_PREPEND(ll, _log_context, c);
+}
+
+LogContext* log_context_detach(LogContext *c) {
+        if (!c)
+                return NULL;
+
+        assert(_log_context_num_fields >= strv_length(c->fields));
+        _log_context_num_fields -= strv_length(c->fields);
+
+        LIST_REMOVE(ll, _log_context, c);
+        return NULL;
+}
+
+LogContext* log_context_new(char **fields, bool owned) {
+        LogContext *c = new(LogContext, 1);
+        if (!c)
+                return NULL;
+
+        *c = (LogContext) {
+                .fields = fields,
+                .owned = owned,
+        };
+
+        return log_context_attach(c);
+}
+
+LogContext* log_context_free(LogContext *c) {
+        if (!c)
+                return NULL;
+
+        log_context_detach(c);
+
+        if (c->owned)
+                strv_free(c->fields);
+
+        return mfree(c);
+}
+
+LogContext* log_context_new_consume(char **fields) {
+        LogContext *c = log_context_new(fields, /*owned=*/ true);
+        if (!c)
+                strv_free(fields);
+
+        return c;
+}
+
+size_t log_context_num_contexts(void) {
+        size_t n = 0;
+
+        LIST_FOREACH(ll, c, _log_context)
+                n++;
+
+        return n;
+}
+
+size_t log_context_num_fields(void) {
+        return _log_context_num_fields;
+}
index 2b1ac5f8c640a4881db4e806c69e35b0ec9ddf59..fcb8fcfb69aeac17d1ea209f4d123d44319ed76a 100644 (file)
@@ -7,8 +7,10 @@
 #include <string.h>
 #include <syslog.h>
 
+#include "list.h"
 #include "macro.h"
 #include "ratelimit.h"
+#include "stdio-util.h"
 
 /* Some structures we reference but don't want to pull in headers for */
 struct iovec;
@@ -420,3 +422,83 @@ typedef struct LogRateLimit {
 #define log_ratelimit_warning_errno(error, ...)   log_ratelimit_full_errno(LOG_WARNING, error, __VA_ARGS__)
 #define log_ratelimit_error_errno(error, ...)     log_ratelimit_full_errno(LOG_ERR,     error, __VA_ARGS__)
 #define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__)
+
+/*
+ * The log context allows attaching extra metadata to log messages written to the journal via log.h. We keep
+ * track of a thread local log context onto which we can push extra metadata fields that should be logged.
+ *
+ * LOG_CONTEXT_PUSH() will add the provided field to the log context and will remove it again when the
+ * current block ends. LOG_CONTEXT_PUSH_STRV() will do the same but for all fields in the given strv.
+ * LOG_CONTEXT_PUSHF() is like LOG_CONTEXT_PUSH() but takes a format string and arguments.
+ *
+ * Using the macros is as simple as putting them anywhere inside a block to add a field to all following log
+ * messages logged from inside that block.
+ *
+ * void myfunction(...) {
+ *         ...
+ *
+ *         LOG_CONTEXT_PUSHF("MYMETADATA=%s", "abc");
+ *
+ *         // Every journal message logged will now have the MYMETADATA=abc
+ *         // field included.
+ * }
+ *
+ * One special case to note is async code, where we use callbacks that are invoked to continue processing
+ * when some event occurs. For async code, there's usually an associated "userdata" struct containing all the
+ * information associated with the async operation. In this "userdata" struct, we can store a log context
+ * allocated with log_context_new() and freed with log_context_free(). We can then add and remove fields to
+ * the `fields` member of the log context object and all those fields will be logged along with each log
+ * message.
+ */
+
+typedef struct LogContext LogContext;
+
+bool log_context_enabled(void);
+
+LogContext* log_context_attach(LogContext *c);
+LogContext* log_context_detach(LogContext *c);
+
+LogContext* log_context_new(char **fields, bool owned);
+LogContext* log_context_free(LogContext *c);
+
+/* Same as log_context_new(), but frees the given fields strv on failure. */
+LogContext* log_context_new_consume(char **fields);
+
+/* Returns the number of attached log context objects. */
+size_t log_context_num_contexts(void);
+/* Returns the number of fields in all attached log contexts. */
+size_t log_context_num_fields(void);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_detach);
+DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_free);
+
+#define LOG_CONTEXT_PUSH(...) \
+        LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__))
+
+#define LOG_CONTEXT_PUSHF(...) \
+        LOG_CONTEXT_PUSH(snprintf_ok((char[LINE_MAX]) {}, LINE_MAX, __VA_ARGS__))
+
+#define _LOG_CONTEXT_PUSH_STRV(strv, c) \
+        _unused_ _cleanup_(log_context_freep) LogContext *c = log_context_new(strv, /*owned=*/ false);
+
+#define LOG_CONTEXT_PUSH_STRV(strv) \
+        _LOG_CONTEXT_PUSH_STRV(strv, UNIQ_T(c, UNIQ))
+
+/* LOG_CONTEXT_CONSUME_STR()/LOG_CONTEXT_CONSUME_STRV() are identical to
+ * LOG_CONTEXT_PUSH_STR()/LOG_CONTEXT_PUSH_STRV() except they take ownership of the given str/strv argument.
+ */
+
+#define _LOG_CONTEXT_CONSUME_STR(s, c, strv) \
+        _unused_ _cleanup_strv_free_ strv = strv_new(s);                                                \
+        if (!strv)                                                                                      \
+                free(s);                                                                                \
+        _unused_ _cleanup_(log_context_freep) LogContext *c = log_context_new_consume(TAKE_PTR(strv))
+
+#define LOG_CONTEXT_CONSUME_STR(s) \
+        _LOG_CONTEXT_CONSUME_STR(s, UNIQ_T(c, UNIQ), UNIQ_T(sv, UNIQ))
+
+#define _LOG_CONTEXT_CONSUME_STRV(strv, c) \
+        _unused_ _cleanup_(log_context_freep) LogContext *c = log_context_new_consume(strv);
+
+#define LOG_CONTEXT_CONSUME_STRV(strv) \
+        _LOG_CONTEXT_CONSUME_STRV(strv, UNIQ_T(c, UNIQ))
index a00d60824dd1249bdea44f410c928407f2ffb3c2..0d032866cfea0c0dba110e521bbfef5ae1773dd8 100644 (file)
@@ -175,12 +175,12 @@ static inline int __coverity_check_and_return__(int condition) {
 #define assert_message_se(expr, message)                                \
         do {                                                            \
                 if (_unlikely_(!(expr)))                                \
-                        log_assert_failed(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
+                        log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \
         } while (false)
 
 #define assert_log(expr, message) ((_likely_(expr))                     \
         ? (true)                                                        \
-        : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__), false))
+        : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false))
 
 #endif  /* __COVERITY__ */
 
@@ -195,7 +195,7 @@ static inline int __coverity_check_and_return__(int condition) {
 #endif
 
 #define assert_not_reached()                                            \
-        log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
+        log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
 
 #define assert_return(expr, r)                                          \
         do {                                                            \
index 3b3efb0ab8c561fe0772711f4af540878b0f4bd3..3445d3130761c708c595cc8ec242b8af7e62080d 100644 (file)
@@ -256,6 +256,26 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) {
         return 0;
 }
 
+int parse_sector_size(const char *t, uint64_t *ret) {
+        int r;
+
+        assert(t);
+        assert(ret);
+
+        uint64_t ss;
+
+        r = safe_atou64(t, &ss);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse sector size parameter %s", t);
+        if (ss < 512 || ss > 4096) /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */
+                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Sector size not between 512 and 4096: %s", t);
+        if (!ISPOWEROF2(ss))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size not power of 2: %s", t);
+
+        *ret = ss;
+        return 0;
+}
+
 int parse_range(const char *t, unsigned *lower, unsigned *upper) {
         _cleanup_free_ char *word = NULL;
         unsigned l, u;
index 8d8d52327b751bf3fc5fc5f91c63d5fe8a506e60..877199529ddc607adc0843b0a734bbfeba5130d6 100644 (file)
@@ -18,6 +18,7 @@ int parse_ifindex(const char *s);
 int parse_mtu(int family, const char *s, uint32_t *ret);
 
 int parse_size(const char *t, uint64_t base, uint64_t *size);
+int parse_sector_size(const char *t, uint64_t *ret);
 int parse_range(const char *t, unsigned *lower, unsigned *upper);
 int parse_errno(const char *t);
 
index fa00c962ab55c7434199ff7cf358288528c5b9a3..70aa15f0600695d9878732cfd87086a733da320e 100644 (file)
@@ -1456,6 +1456,20 @@ int pidfd_get_pid(int fd, pid_t *ret) {
         return parse_pid(p, ret);
 }
 
+int pidfd_verify_pid(int pidfd, pid_t pid) {
+        pid_t current_pid;
+        int r;
+
+        assert(pidfd >= 0);
+        assert(pid > 0);
+
+        r = pidfd_get_pid(pidfd, &current_pid);
+        if (r < 0)
+                return r;
+
+        return current_pid != pid ? -ESRCH : 0;
+}
+
 static int rlimit_to_nice(rlim_t limit) {
         if (limit <= 1)
                 return PRIO_MAX-1; /* i.e. 19 */
index 8a981cd7301cbd6c5e4244070ba7efb5d90cebc4..6ceb8ebf7d70f4331a72c0f3608fb442c7ec6b11 100644 (file)
@@ -190,6 +190,7 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
         })
 
 int pidfd_get_pid(int fd, pid_t *ret);
+int pidfd_verify_pid(int pidfd, pid_t pid);
 
 int setpriority_closest(int priority);
 
index 5d1111fd712be76261241da9358c02cd0695a474..0e4342eb40daa55074041b921cff564379a24e52 100644 (file)
 #define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service"
 #define SPECIAL_VOLATILE_ROOT_SERVICE "systemd-volatile-root.service"
 #define SPECIAL_UDEVD_SERVICE "systemd-udevd.service"
+#define SPECIAL_GROWFS_SERVICE "systemd-growfs@.service"
+#define SPECIAL_GROWFS_ROOT_SERVICE "systemd-growfs-root.service"
+#define SPECIAL_PCRFS_SERVICE "systemd-pcrfs@.service"
+#define SPECIAL_PCRFS_ROOT_SERVICE "systemd-pcrfs-root.service"
 
 /* Services systemd relies on */
 #define SPECIAL_DBUS_SERVICE "dbus.service"
index f647f125ac8e7d25a2a89dbc20a36a61be3b9c0b..4e93ac90c9ab0d258740c1c0e9db8b379b4f8399 100644 (file)
@@ -7,15 +7,20 @@
 #include <sys/types.h>
 
 #include "macro.h"
-#include "memory-util.h"
 
-#define snprintf_ok(buf, len, fmt, ...)                                 \
-        ({                                                              \
-                char *_buf = (buf);                                     \
-                size_t _len = (len);                                    \
-                int _snpf = snprintf(_buf, _len, (fmt), ##__VA_ARGS__); \
-                _snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL;      \
-        })
+_printf_(3, 4)
+static inline char *snprintf_ok(char *buf, size_t len, const char *format, ...) {
+        va_list ap;
+        int r;
+
+        va_start(ap, format);
+        DISABLE_WARNING_FORMAT_NONLITERAL;
+        r = vsnprintf(buf, len, format, ap);
+        REENABLE_WARNING;
+        va_end(ap);
+
+        return r >= 0 && (size_t) r < len ? buf : NULL;
+}
 
 #define xsprintf(buf, fmt, ...) \
         assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, ##__VA_ARGS__), "xsprintf: " #buf "[] must be big enough")
@@ -26,7 +31,7 @@ do {                                                                    \
         size_t _i, _k;                                                  \
         /* See https://github.com/google/sanitizers/issues/992 */       \
         if (HAS_FEATURE_MEMORY_SANITIZER)                               \
-                zero(_argtypes);                                        \
+                memset(_argtypes, 0, sizeof(_argtypes));                \
         _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
         assert(_k < ELEMENTSOF(_argtypes));                             \
         for (_i = 0; _i < _k; _i++) {                                   \
index 5e3a92c7c0c9d6c2cd568816c9daf47f6aa24162..ad8c9863bdda531ac78c93d7f354e612588fdd04 100644 (file)
@@ -1187,6 +1187,49 @@ char *string_replace_char(char *str, char old_char, char new_char) {
         return str;
 }
 
+int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
+        char *b;
+
+        assert(s || n == 0);
+        assert(mode >= 0);
+        assert(mode < _MAKE_CSTRING_MODE_MAX);
+
+        /* Converts a sized character buffer into a NUL-terminated NUL string, refusing if there are embedded
+         * NUL bytes. Whether to expect a trailing NUL byte can be specified via 'mode' */
+
+        if (n == 0) {
+                if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
+                        return -EINVAL;
+
+                if (!ret)
+                        return 0;
+
+                b = new0(char, 1);
+        } else {
+                const char *nul;
+
+                nul = memchr(s, 0, n);
+                if (nul) {
+                        if (nul < s + n - 1 || /* embedded NUL? */
+                            mode == MAKE_CSTRING_REFUSE_TRAILING_NUL)
+                                return -EINVAL;
+
+                        n--;
+                } else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
+                        return -EINVAL;
+
+                if (!ret)
+                        return 0;
+
+                b = memdup_suffix0(s, n);
+        }
+        if (!b)
+                return -ENOMEM;
+
+        *ret = b;
+        return 0;
+}
+
 size_t strspn_from_end(const char *str, const char *accept) {
         size_t n = 0;
 
@@ -1201,3 +1244,19 @@ size_t strspn_from_end(const char *str, const char *accept) {
 
         return n;
 }
+
+char *strdupspn(const char *a, const char *accept) {
+        if (isempty(a) || isempty(accept))
+                return strdup("");
+
+        return strndup(a, strspn(a, accept));
+}
+
+char *strdupcspn(const char *a, const char *reject) {
+        if (isempty(a))
+                return strdup("");
+        if (isempty(reject))
+                return strdup(a);
+
+        return strndup(a, strcspn(a, reject));
+}
index a78b7960e3606986274293a6a4da726a3ad3168e..e0a47a21a97dc1617fccc23225531d86572fcd37 100644 (file)
@@ -239,4 +239,17 @@ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);
 
 char *string_replace_char(char *str, char old_char, char new_char);
 
+typedef enum MakeCStringMode {
+        MAKE_CSTRING_REFUSE_TRAILING_NUL,
+        MAKE_CSTRING_ALLOW_TRAILING_NUL,
+        MAKE_CSTRING_REQUIRE_TRAILING_NUL,
+        _MAKE_CSTRING_MODE_MAX,
+        _MAKE_CSTRING_MODE_INVALID = -1,
+} MakeCStringMode;
+
+int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret);
+
 size_t strspn_from_end(const char *str, const char *accept);
+
+char *strdupspn(const char *a, const char *accept);
+char *strdupcspn(const char *a, const char *reject);
index 74e87046cca9dac00e30a8361a6021acb33f184d..2b7a61d442b7a8e8e23768b6a3580b5329a0650a 100644 (file)
@@ -789,6 +789,22 @@ rollback:
         return -ENOMEM;
 }
 
+int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
+        char *j;
+
+        assert(l);
+        assert(lhs);
+
+        if (!rhs) /* value is optional, in which case we suppress the field */
+                return 0;
+
+        j = strjoin(lhs, "=", rhs);
+        if (!j)
+                return -ENOMEM;
+
+        return strv_consume(l, j);
+}
+
 int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
         bool b = false;
         int r;
index f82c76589d06a5b99988074d0a85caf874f48435..1f8da85fccd94dcc81fa86ba45164848658c9e1e 100644 (file)
@@ -241,6 +241,8 @@ char** strv_skip(char **l, size_t n);
 
 int strv_extend_n(char ***l, const char *value, size_t n);
 
+int strv_extend_assignment(char ***l, const char *lhs, const char *rhs);
+
 int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
 
 #define strv_free_and_replace(a, b)             \
index d5c10571c9b169be9a667e2d69911572edde0359..b700f364eff9be9aac033008209d88bb18b7e399 100644 (file)
@@ -308,54 +308,48 @@ char *format_timestamp_style(
         };
 
         struct tm tm;
+        bool utc, us;
         time_t sec;
         size_t n;
-        bool utc = false, us = false;
-        int r;
 
         assert(buf);
+        assert(style >= 0);
+        assert(style < _TIMESTAMP_STYLE_MAX);
 
-        switch (style) {
-                case TIMESTAMP_PRETTY:
-                case TIMESTAMP_UNIX:
-                        break;
-                case TIMESTAMP_US:
-                        us = true;
-                        break;
-                case TIMESTAMP_UTC:
-                        utc = true;
-                        break;
-                case TIMESTAMP_US_UTC:
-                        us = true;
-                        utc = true;
-                        break;
-                default:
-                        return NULL;
-        }
-
-        if (l < (size_t) (3 +                  /* week day */
-                          1 + 10 +             /* space and date */
-                          1 + 8 +              /* space and time */
-                          (us ? 1 + 6 : 0) +   /* "." and microsecond part */
-                          1 + 1 +              /* space and shortest possible zone */
-                          1))
-                return NULL; /* Not enough space even for the shortest form. */
         if (!timestamp_is_set(t))
                 return NULL; /* Timestamp is unset */
 
         if (style == TIMESTAMP_UNIX) {
-                r = snprintf(buf, l, "@" USEC_FMT, t / USEC_PER_SEC);  /* round down µs → s */
-                if (r < 0 || (size_t) r >= l)
-                        return NULL; /* Doesn't fit */
+                if (l < (size_t) (1 + 1 + 1))
+                        return NULL; /* not enough space for even the shortest of forms */
 
-                return buf;
+                return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC);  /* round down µs → s */
         }
 
+        utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
+        us = IN_SET(style, TIMESTAMP_US, TIMESTAMP_US_UTC);
+
+        if (l < (size_t) (3 +                   /* week day */
+                          1 + 10 +              /* space and date */
+                          style == TIMESTAMP_DATE ? 0 :
+                          (1 + 8 +              /* space and time */
+                           (us ? 1 + 6 : 0) +   /* "." and microsecond part */
+                           1 + (utc ? 3 : 1)) + /* space and shortest possible zone */
+                          1))
+                return NULL; /* Not enough space even for the shortest form. */
+
         /* Let's not format times with years > 9999 */
         if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) {
-                assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1);
-                strcpy(buf, "--- XXXX-XX-XX XX:XX:XX");
-                return buf;
+                static const char* const xxx[_TIMESTAMP_STYLE_MAX] = {
+                        [TIMESTAMP_PRETTY] = "--- XXXX-XX-XX XX:XX:XX",
+                        [TIMESTAMP_US]     = "--- XXXX-XX-XX XX:XX:XX.XXXXXX",
+                        [TIMESTAMP_UTC]    = "--- XXXX-XX-XX XX:XX:XX UTC",
+                        [TIMESTAMP_US_UTC] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC",
+                        [TIMESTAMP_DATE]   = "--- XXXX-XX-XX",
+                };
+
+                assert(l >= strlen(xxx[style]) + 1);
+                return strcpy(buf, xxx[style]);
         }
 
         sec = (time_t) (t / USEC_PER_SEC); /* Round down */
@@ -367,6 +361,14 @@ char *format_timestamp_style(
         assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
         memcpy(buf, weekdays[tm.tm_wday], 4);
 
+        if (style == TIMESTAMP_DATE) {
+                /* Special format string if only date should be shown. */
+                if (strftime(buf + 3, l - 3, " %Y-%m-%d", &tm) <= 0)
+                        return NULL; /* Doesn't fit */
+
+                return buf;
+        }
+
         /* Add the main components */
         if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
                 return NULL; /* Doesn't fit */
index c98f95a5301d85ccc6f29ce0c5b479b530c3de58..c5ae0c98d493ece73adc5689765c418b7a23e184 100644 (file)
@@ -35,6 +35,7 @@ typedef enum TimestampStyle {
         TIMESTAMP_UTC,
         TIMESTAMP_US_UTC,
         TIMESTAMP_UNIX,
+        TIMESTAMP_DATE,
         _TIMESTAMP_STYLE_MAX,
         _TIMESTAMP_STYLE_INVALID = -EINVAL,
 } TimestampStyle;
index 624c63ec98b33f8db92b64138b0b6e1428f13de7..e1ddf97914c9d8baf47f111de2847091fd08e102 100644 (file)
@@ -40,11 +40,9 @@ static int apply_rule(const char *filename, unsigned line, const char *rule) {
         assert(rule[0]);
 
         _cleanup_free_ char *rulename = NULL;
-        const char *e;
         int r;
 
-        e = strchrnul(rule + 1, rule[0]);
-        rulename = strndup(rule + 1, e - rule - 1);
+        rulename = strdupcspn(rule + 1, CHAR_TO_STR(rule[0]));
         if (!rulename)
                 return log_oom();
 
index 0adb354bdb830909ea581e97aa54df2a7044e6b2..ffdf7e7d700279399da3247f7987bdcd586648db 100644 (file)
@@ -18,6 +18,7 @@
 #include "find-esp.h"
 #include "path-util.h"
 #include "pretty-print.h"
+#include "recurse-dir.h"
 #include "terminal-util.h"
 #include "tpm2-util.h"
 
@@ -503,6 +504,250 @@ int verb_status(int argc, char *argv[], void *userdata) {
         return r;
 }
 
+static int ref_file(Hashmap *known_files, const char *fn, int increment) {
+        char *k = NULL;
+        int n, r;
+
+        assert(known_files);
+
+        /* just gracefully ignore this. This way the caller doesn't
+           have to verify whether the bootloader entry is relevant */
+        if (!fn)
+                return 0;
+
+        n = PTR_TO_INT(hashmap_get2(known_files, fn, (void**)&k));
+        n += increment;
+
+        assert(n >= 0);
+
+        if (n == 0) {
+                (void) hashmap_remove(known_files, fn);
+                free(k);
+        } else if (!k) {
+                _cleanup_free_ char *t = NULL;
+
+                t = strdup(fn);
+                if (!t)
+                        return -ENOMEM;
+                r = hashmap_put(known_files, t, INT_TO_PTR(n));
+                if (r < 0)
+                        return r;
+                TAKE_PTR(t);
+        } else {
+                r = hashmap_update(known_files, fn, INT_TO_PTR(n));
+                if (r < 0)
+                        return r;
+        }
+
+        return n;
+}
+
+static void deref_unlink_file(Hashmap *known_files, const char *fn, const char *root) {
+        _cleanup_free_ char *path = NULL;
+        int r;
+
+        /* just gracefully ignore this. This way the caller doesn't
+           have to verify whether the bootloader entry is relevant */
+        if (!fn || !root)
+                return;
+
+        r = ref_file(known_files, fn, -1);
+        if (r < 0)
+                return (void) log_warning_errno(r, "Failed to deref \"%s\", ignoring: %m", fn);
+        if (r > 0)
+                return;
+
+        if (arg_dry_run) {
+                r = chase_symlinks_and_access(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, &path, NULL);
+                if (r < 0)
+                        log_info("Unable to determine whether \"%s\" exists, ignoring: %m", fn);
+                else
+                        log_info("Would remove %s", path);
+                return;
+        }
+
+        r = chase_symlinks_and_unlink(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, 0, &path);
+        if (r >= 0)
+                log_info("Removed %s", path);
+        else if (r != -ENOENT)
+                return (void) log_warning_errno(r, "Failed to remove \"%s\", ignoring: %m", path ?: fn);
+
+        _cleanup_free_ char *d = NULL;
+        if (path_extract_directory(fn, &d) >= 0 && !path_equal(d, "/")) {
+                r = chase_symlinks_and_unlink(d, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, AT_REMOVEDIR, NULL);
+                if (r < 0 && !IN_SET(r, -ENOTEMPTY, -ENOENT))
+                        log_warning_errno(r, "Failed to remove directory \"%s\", ignoring: %m", d);
+        }
+}
+
+static int count_known_files(const BootConfig *config, const char* root, Hashmap **ret_known_files) {
+        _cleanup_(hashmap_free_free_keyp) Hashmap *known_files = NULL;
+        int r = 0;
+
+        assert(config);
+        assert(ret_known_files);
+
+        known_files = hashmap_new(&path_hash_ops);
+        if (!known_files)
+                return -ENOMEM;
+
+        for (size_t i = 0; i < config->n_entries; i++) {
+                const BootEntry *e = config->entries + i;
+
+                if (!path_equal(e->root, root))
+                        continue;
+
+                r = ref_file(known_files, e->kernel, +1);
+                if (r < 0)
+                        return r;
+                r = ref_file(known_files, e->efi, +1);
+                if (r < 0)
+                        return r;
+                STRV_FOREACH(s, e->initrd) {
+                        r = ref_file(known_files, *s, +1);
+                        if (r < 0)
+                                return r;
+                }
+                r = ref_file(known_files, e->device_tree, +1);
+                if (r < 0)
+                        return r;
+                STRV_FOREACH(s, e->device_tree_overlay) {
+                        r = ref_file(known_files, *s, +1);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        *ret_known_files = TAKE_PTR(known_files);
+
+        return 0;
+}
+
+static int boot_config_find_in(const BootConfig *config, const char *root, const char *id) {
+        assert(config);
+
+        if (!root || !id)
+                return -1;
+
+        for (size_t i = 0; i < config->n_entries; i++)
+                if (path_equal(config->entries[i].root, root)
+                        && fnmatch(id, config->entries[i].id, FNM_CASEFOLD) == 0)
+                        return i;
+
+        return -1;
+}
+
+static int unlink_entry(const BootConfig *config, const char *root, const char *id) {
+        _cleanup_(hashmap_free_free_keyp) Hashmap *known_files = NULL;
+        const BootEntry *e = NULL;
+        int r;
+
+        assert(config);
+
+        r = count_known_files(config, root, &known_files);
+        if (r < 0)
+                return log_error_errno(r, "Failed to count files in %s: %m", root);
+
+        r = boot_config_find_in(config, root, id);
+        if (r < 0)
+                return -ENOENT;
+
+        if (r == config->default_entry)
+                log_warning("%s is the default boot entry", id);
+        if (r == config->selected_entry)
+                log_warning("%s is the selected boot entry", id);
+
+        e = &config->entries[r];
+
+        deref_unlink_file(known_files, e->kernel, e->root);
+        deref_unlink_file(known_files, e->efi, e->root);
+        STRV_FOREACH(s, e->initrd)
+                deref_unlink_file(known_files, *s, e->root);
+        deref_unlink_file(known_files, e->device_tree, e->root);
+        STRV_FOREACH(s, e->device_tree_overlay)
+                deref_unlink_file(known_files, *s, e->root);
+
+        if (arg_dry_run)
+                log_info("Would remove %s", e->path);
+        else {
+                r = chase_symlinks_and_unlink(e->path, root, CHASE_PROHIBIT_SYMLINKS, 0, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to remove \"%s\": %m", e->path);
+
+                log_info("Removed %s", e->path);
+        }
+
+        return 0;
+}
+
+static int list_remove_orphaned_file(
+                RecurseDirEvent event,
+                const char *path,
+                int dir_fd,
+                int inode_fd,
+                const struct dirent *de,
+                const struct statx *sx,
+                void *userdata) {
+        Hashmap *known_files = userdata;
+
+        assert(path);
+        assert(known_files);
+
+        if (event != RECURSE_DIR_ENTRY)
+                return RECURSE_DIR_CONTINUE;
+
+        if (hashmap_get(known_files, path))
+                return RECURSE_DIR_CONTINUE; /* keep! */
+
+        if (arg_dry_run)
+                log_info("Would remove %s", path);
+        else if (unlinkat(dir_fd, de->d_name, 0) < 0)
+                log_warning_errno(errno, "Failed to remove \"%s\", ignoring: %m", path);
+        else
+                log_info("Removed %s", path);
+
+        return RECURSE_DIR_CONTINUE;
+}
+
+static int cleanup_orphaned_files(
+                const BootConfig *config,
+                const char *root) {
+        _cleanup_(hashmap_free_free_keyp) Hashmap *known_files = NULL;
+        _cleanup_free_ char *full = NULL, *p = NULL;
+        _cleanup_close_ int dir_fd = -1;
+        int r = -1;
+
+        assert(config);
+        assert(root);
+
+        log_info("Cleaning %s", root);
+
+        r = settle_entry_token();
+        if (r < 0)
+                return r;
+
+        r = count_known_files(config, root, &known_files);
+        if (r < 0)
+                return log_error_errno(r, "Failed to count files in %s: %m", root);
+
+        dir_fd = chase_symlinks_and_open(arg_entry_token, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS,
+                        O_DIRECTORY|O_CLOEXEC, &full);
+        if (dir_fd == -ENOENT)
+                return 0;
+        if (dir_fd < 0)
+                return log_error_errno(dir_fd, "Failed to open '%s/%s': %m", root, arg_entry_token);
+
+        p = path_join("/", arg_entry_token);
+        if (!p)
+                return log_oom();
+
+        r = recurse_dir(dir_fd, p, 0, UINT_MAX, RECURSE_DIR_SORT, list_remove_orphaned_file, known_files);
+        if (r < 0)
+                return log_error_errno(r, "Failed to cleanup %s: %m", full);
+
+        return r;
+}
+
 int verb_list(int argc, char *argv[], void *userdata) {
         _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
         dev_t esp_devid = 0, xbootldr_devid = 0;
@@ -534,6 +779,24 @@ int verb_list(int argc, char *argv[], void *userdata) {
                 return 0;
         }
 
-        pager_open(arg_pager_flags);
-        return show_boot_entries(&config, arg_json_format_flags);
+        if (streq(argv[0], "list")) {
+                pager_open(arg_pager_flags);
+                return show_boot_entries(&config, arg_json_format_flags);
+        } else if (streq(argv[0], "cleanup")) {
+                if (arg_xbootldr_path && xbootldr_devid != esp_devid)
+                        cleanup_orphaned_files(&config, arg_xbootldr_path);
+                return cleanup_orphaned_files(&config, arg_esp_path);
+        } else {
+                assert(streq(argv[0], "unlink"));
+                if (arg_xbootldr_path && xbootldr_devid != esp_devid) {
+                        r = unlink_entry(&config, arg_xbootldr_path, argv[1]);
+                        if (r == 0 || r != -ENOENT)
+                                return r;
+                }
+                return unlink_entry(&config, arg_esp_path, argv[1]);
+        }
+}
+
+int verb_unlink(int argc, char *argv[], void *userdata) {
+        return verb_list(argc, argv, userdata);
 }
index 0b57c86f91716eda3079ee6da95efae120a4b31e..f7998a3303eb5cec2980664764d0606e5256f58f 100644 (file)
@@ -2,3 +2,4 @@
 
 int verb_status(int argc, char *argv[], void *userdata);
 int verb_list(int argc, char *argv[], void *userdata);
+int verb_unlink(int argc, char *argv[], void *userdata);
diff --git a/src/boot/bootctl-uki.c b/src/boot/bootctl-uki.c
new file mode 100644 (file)
index 0000000..aa2868f
--- /dev/null
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "bootctl.h"
+#include "bootctl-uki.h"
+#include "fd-util.h"
+#include "parse-util.h"
+#include "pe-header.h"
+
+#define MAX_SECTIONS 96
+
+static const uint8_t dos_file_magic[2] = "MZ";
+static const uint8_t pe_file_magic[4] = "PE\0\0";
+
+static const uint8_t name_osrel[8] = ".osrel";
+static const uint8_t name_linux[8] = ".linux";
+static const uint8_t name_initrd[8] = ".initrd";
+static const uint8_t name_cmdline[8] = ".cmdline";
+static const uint8_t name_uname[8] = ".uname";
+
+static int pe_sections(FILE *uki, struct PeSectionHeader **ret, size_t *ret_n) {
+        _cleanup_free_ struct PeSectionHeader *sections = NULL;
+        struct DosFileHeader dos;
+        struct PeHeader pe;
+        size_t scount;
+        uint64_t soff, items;
+        int rc;
+
+        items = fread(&dos, 1, sizeof(dos), uki);
+        if (items != sizeof(dos))
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "DOS header read error");
+        if (memcmp(dos.Magic, dos_file_magic, sizeof(dos_file_magic)) != 0)
+                goto no_sections;
+
+        rc = fseek(uki, le32toh(dos.ExeHeader), SEEK_SET);
+        if (rc < 0)
+                return log_error_errno(errno, "seek to PE header");
+        items = fread(&pe, 1, sizeof(pe), uki);
+        if (items != sizeof(pe))
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE header read error");
+        if (memcmp(pe.Magic, pe_file_magic, sizeof(pe_file_magic)) != 0)
+                goto no_sections;
+
+        soff = le32toh(dos.ExeHeader) + sizeof(pe) + le16toh(pe.FileHeader.SizeOfOptionalHeader);
+        rc = fseek(uki, soff, SEEK_SET);
+        if (rc < 0)
+                return log_error_errno(errno, "seek to PE section headers");
+
+        scount = le16toh(pe.FileHeader.NumberOfSections);
+        if (scount > MAX_SECTIONS)
+                goto no_sections;
+        sections = new(struct PeSectionHeader, scount);
+        if (!sections)
+                return log_oom();
+        items = fread(sections, sizeof(*sections), scount, uki);
+        if (items != scount)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section header read error");
+
+        *ret = TAKE_PTR(sections);
+        *ret_n = scount;
+        return 0;
+
+no_sections:
+        *ret = NULL;
+        *ret_n = 0;
+        return 0;
+}
+
+static int find_pe_section(struct PeSectionHeader *sections, size_t scount,
+                           const uint8_t *name, size_t namelen, size_t *ret) {
+        for (size_t s = 0; s < scount; s++) {
+                if (memcmp_nn(sections[s].Name, sizeof(sections[s].Name),
+                              name, namelen) == 0) {
+                        if (ret)
+                                *ret = s;
+                        return 1;
+                }
+        }
+        return 0;
+}
+
+static bool is_uki(struct PeSectionHeader *sections, size_t scount) {
+        return (find_pe_section(sections, scount, name_osrel, sizeof(name_osrel), NULL) &&
+                find_pe_section(sections, scount, name_linux, sizeof(name_linux), NULL) &&
+                find_pe_section(sections, scount, name_initrd, sizeof(name_initrd), NULL));
+}
+
+int verb_kernel_identify(int argc, char *argv[], void *userdata) {
+        _cleanup_fclose_ FILE *uki = NULL;
+        _cleanup_free_ struct PeSectionHeader *sections = NULL;
+        size_t scount;
+        int rc;
+
+        uki = fopen(argv[1], "re");
+        if (!uki)
+                return log_error_errno(errno, "Failed to open UKI file '%s': %m", argv[1]);
+
+        rc = pe_sections(uki, &sections, &scount);
+        if (rc < 0)
+                return EXIT_FAILURE;
+
+        if (sections) {
+                if (is_uki(sections, scount)) {
+                        puts("uki");
+                        return EXIT_SUCCESS;
+                }
+                puts("pe");
+                return EXIT_SUCCESS;
+        }
+
+        puts("unknown");
+        return EXIT_SUCCESS;
+}
+
+static int read_pe_section(FILE *uki, const struct PeSectionHeader *section,
+                           void **ret, size_t *ret_n) {
+        _cleanup_free_ void *data = NULL;
+        uint32_t size, bytes;
+        uint64_t soff;
+        int rc;
+
+        soff = le32toh(section->PointerToRawData);
+        size = le32toh(section->VirtualSize);
+
+        if (size > 16 * 1024)
+                return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "PE section too big");
+
+        rc = fseek(uki, soff, SEEK_SET);
+        if (rc < 0)
+                return log_error_errno(errno, "seek to PE section");
+
+        data = malloc(size+1);
+        if (!data)
+                return log_oom();
+        ((uint8_t*) data)[size] = 0; /* safety NUL byte */
+
+        bytes = fread(data, 1, size, uki);
+        if (bytes != size)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section read error");
+
+        *ret = TAKE_PTR(data);
+        if (ret_n)
+                *ret_n = size;
+        return 0;
+}
+
+static void inspect_uki(FILE *uki, struct PeSectionHeader *sections, size_t scount) {
+        _cleanup_free_ char *cmdline = NULL;
+        _cleanup_free_ char *uname = NULL;
+        size_t idx;
+
+        if (find_pe_section(sections, scount, name_cmdline, sizeof(name_cmdline), &idx))
+                read_pe_section(uki, sections + idx, (void**)&cmdline, NULL);
+
+        if (find_pe_section(sections, scount, name_uname, sizeof(name_uname), &idx))
+                read_pe_section(uki, sections + idx, (void**)&uname, NULL);
+
+        if (cmdline)
+                printf("    Cmdline: %s\n", cmdline);
+        if (uname)
+                printf("    Version: %s\n", uname);
+}
+
+int verb_kernel_inspect(int argc, char *argv[], void *userdata) {
+        _cleanup_fclose_ FILE *uki = NULL;
+        _cleanup_free_ struct PeSectionHeader *sections = NULL;
+        size_t scount;
+        int rc;
+
+        uki = fopen(argv[1], "re");
+        if (!uki)
+                return log_error_errno(errno, "Failed to open UKI file '%s': %m", argv[1]);
+
+        rc = pe_sections(uki, &sections, &scount);
+        if (rc < 0)
+                return EXIT_FAILURE;
+
+        if (sections) {
+                if (is_uki(sections, scount)) {
+                        puts("Kernel Type: uki");
+                        inspect_uki(uki, sections, scount);
+                        return EXIT_SUCCESS;
+                }
+                puts("Kernel Type: pe");
+                return EXIT_SUCCESS;
+        }
+
+        puts("Kernel Type: unknown");
+        return EXIT_SUCCESS;
+}
diff --git a/src/boot/bootctl-uki.h b/src/boot/bootctl-uki.h
new file mode 100644 (file)
index 0000000..effb984
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+int verb_kernel_identify(int argc, char *argv[], void *userdata);
+int verb_kernel_inspect(int argc, char *argv[], void *userdata);
index b443f9970b25b73e29c87c04cfcb05c07ff6fa73..914825564ebed106531d100be0cd573c29300054 100644 (file)
@@ -9,6 +9,7 @@
 #include "bootctl-set-efivar.h"
 #include "bootctl-status.h"
 #include "bootctl-systemd-efi-options.h"
+#include "bootctl-uki.h"
 #include "build.h"
 #include "dissect-image.h"
 #include "escape.h"
@@ -47,6 +48,7 @@ char *arg_root = NULL;
 char *arg_image = NULL;
 InstallSource arg_install_source = ARG_INSTALL_SOURCE_AUTO;
 char *arg_efi_boot_option_description = NULL;
+bool arg_dry_run = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
@@ -129,27 +131,33 @@ static int help(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return log_oom();
 
-        printf("%1$s  [OPTIONS...] COMMAND ...\n"
+        printf("%1$s [OPTIONS...] COMMAND ...\n"
                "\n%5$sControl EFI firmware boot settings and manage boot loader.%6$s\n"
                "\n%3$sGeneric EFI Firmware/Boot Loader Commands:%4$s\n"
-               "  status              Show status of installed boot loader and EFI variables\n"
+               "  status               Show status of installed boot loader and EFI variables\n"
                "  reboot-to-firmware [BOOL]\n"
-               "                      Query or set reboot-to-firmware EFI flag\n"
+               "                       Query or set reboot-to-firmware EFI flag\n"
                "  systemd-efi-options [STRING]\n"
-               "                      Query or set system options string in EFI variable\n"
+               "                       Query or set system options string in EFI variable\n"
                "\n%3$sBoot Loader Specification Commands:%4$s\n"
-               "  list                List boot loader entries\n"
-               "  set-default ID      Set default boot loader entry\n"
-               "  set-oneshot ID      Set default boot loader entry, for next boot only\n"
-               "  set-timeout SECONDS Set the menu timeout\n"
+               "  list                 List boot loader entries\n"
+               "  unlink ID            Remove boot loader entry\n"
+               "  cleanup              Remove files in ESP not referenced in any boot entry\n"
+               "\n%3$sBoot Loader Interface Commands:%4$s\n"
+               "  set-default ID       Set default boot loader entry\n"
+               "  set-oneshot ID       Set default boot loader entry, for next boot only\n"
+               "  set-timeout SECONDS  Set the menu timeout\n"
                "  set-timeout-oneshot SECONDS\n"
-               "                      Set the menu timeout for the next boot only\n"
+               "                       Set the menu timeout for the next boot only\n"
                "\n%3$ssystemd-boot Commands:%4$s\n"
-               "  install             Install systemd-boot to the ESP and EFI variables\n"
-               "  update              Update systemd-boot in the ESP and EFI variables\n"
-               "  remove              Remove systemd-boot from the ESP and EFI variables\n"
-               "  is-installed        Test whether systemd-boot is installed in the ESP\n"
-               "  random-seed         Initialize random seed in ESP and EFI variables\n"
+               "  install              Install systemd-boot to the ESP and EFI variables\n"
+               "  update               Update systemd-boot in the ESP and EFI variables\n"
+               "  remove               Remove systemd-boot from the ESP and EFI variables\n"
+               "  is-installed         Test whether systemd-boot is installed in the ESP\n"
+               "  random-seed          Initialize random seed in ESP and EFI variables\n"
+               "\n%3$sKernel Image Commands:%4$s\n"
+               "  kernel-identify      Identify kernel image type\n"
+               "  kernel-inspect       Prints details about the kernel image\n"
                "\n%3$sOptions:%4$s\n"
                "  -h --help            Show this help\n"
                "     --version         Print version\n"
@@ -176,6 +184,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "                       Install all supported EFI architectures\n"
                "     --efi-boot-option-description=DESCRIPTION\n"
                "                       Description of the entry in the boot option list\n"
+               "     --dry-run         Dry run (unlink and cleanup)\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
@@ -203,6 +212,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_JSON,
                 ARG_ARCH_ALL,
                 ARG_EFI_BOOT_OPTION_DESCRIPTION,
+                ARG_DRY_RUN,
         };
 
         static const struct option options[] = {
@@ -227,6 +237,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "json",                        required_argument, NULL, ARG_JSON                        },
                 { "all-architectures",           no_argument,       NULL, ARG_ARCH_ALL                    },
                 { "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION },
+                { "dry-run",                     no_argument,       NULL, ARG_DRY_RUN                     },
                 {}
         };
 
@@ -376,6 +387,10 @@ static int parse_argv(int argc, char *argv[]) {
                                 return r;
                         break;
 
+                case ARG_DRY_RUN:
+                        arg_dry_run = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -384,7 +399,7 @@ static int parse_argv(int argc, char *argv[]) {
                 }
 
         if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
-                        "install", "update", "remove", "is-installed", "random-seed"))
+                        "install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Options --root= and --image= are not supported with verb %s.",
                                        argv[optind]);
@@ -395,6 +410,9 @@ static int parse_argv(int argc, char *argv[]) {
         if (arg_install_source != ARG_INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
 
+        if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup"))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry is only supported with --unlink or --cleanup");
+
         return 1;
 }
 
@@ -406,7 +424,11 @@ static int bootctl_main(int argc, char *argv[]) {
                 { "update",              VERB_ANY, 1,        0,            verb_install             },
                 { "remove",              VERB_ANY, 1,        0,            verb_remove              },
                 { "is-installed",        VERB_ANY, 1,        0,            verb_is_installed        },
+                { "kernel-identify",     2,        2,        0,            verb_kernel_identify     },
+                { "kernel-inspect",      2,        2,        0,            verb_kernel_inspect      },
                 { "list",                VERB_ANY, 1,        0,            verb_list                },
+                { "unlink",              2,        2,        0,            verb_unlink              },
+                { "cleanup",             VERB_ANY, 1,        0,            verb_list                },
                 { "set-default",         2,        2,        0,            verb_set_efivar          },
                 { "set-oneshot",         2,        2,        0,            verb_set_efivar          },
                 { "set-timeout",         2,        2,        0,            verb_set_efivar          },
index 5a14faf1a47d3c2ba0ad72af74db624fd8b856a9..311b954c2c8b3fdec90e1589c2e1bf591b03eb16 100644 (file)
@@ -39,6 +39,7 @@ extern char *arg_root;
 extern char *arg_image;
 extern InstallSource arg_install_source;
 extern char *arg_efi_boot_option_description;
+extern bool arg_dry_run;
 
 static inline const char *arg_dollar_boot_path(void) {
         /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
diff --git a/src/boot/efi/assert.c b/src/boot/efi/assert.c
deleted file mode 100644 (file)
index bb16d2b..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <efi.h>
-#include <efilib.h>
-
-#include "util.h"
-
-void efi_assert(const char *expr, const char *file, unsigned line, const char *function) {
-        log_error_stall(L"systemd-boot assertion '%a' failed at %a:%u, function %a(). Halting.", expr, file, line, function);
-        for (;;)
-                BS->Stall(60 * 1000 * 1000);
-}
index 29996b882666ed6e85cde0aee1c8f4ab57a65dc4..8d6e689b37be478747862c3048870ca1463047b0 100644 (file)
@@ -3,6 +3,7 @@
 #include <efi.h>
 #include <efigpt.h>
 #include <efilib.h>
+#include <inttypes.h>
 
 #include "bcd.h"
 #include "bootspec-fundamental.h"
@@ -27,7 +28,7 @@
 #ifndef GNU_EFI_USE_MS_ABI
         /* We do not use uefi_call_wrapper() in systemd-boot. As such, we rely on the
          * compiler to do the calling convention conversion for us. This is check is
-         * to make sure the -DGNU_EFI_USE_MS_ABI was passed to the comiler. */
+         * to make sure the -DGNU_EFI_USE_MS_ABI was passed to the compiler. */
         #error systemd-boot requires compilation with GNU_EFI_USE_MS_ABI defined.
 #endif
 
@@ -427,7 +428,7 @@ static char16_t *update_timeout_efivar(uint32_t *t, bool inc) {
         case TIMEOUT_MENU_HIDDEN:
                 return xstrdup16(u"Menu disabled. Hold down key at bootup to show menu.");
         default:
-                return xpool_print(L"Menu timeout set to %u s.", *t);
+                return xasprintf("Menu timeout set to %u s.", *t);
         }
 }
 
@@ -442,22 +443,9 @@ static bool unicode_supported(void) {
         return cache;
 }
 
-static void ps_string(const char16_t *fmt, const void *value) {
-        assert(fmt);
-        if (value)
-                Print(fmt, value);
-}
-
-static void ps_bool(const char16_t *fmt, bool value) {
-        assert(fmt);
-        Print(fmt, yes_no(value));
-}
-
 static bool ps_continue(void) {
-        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");
+        const char16_t *sep = unicode_supported() ? u"───" : u"---";
+        printf("\n%ls Press any key to continue, ESC or q to quit. %ls\n\n", sep, sep);
 
         uint64_t key;
         return console_key_read(&key, UINT64_MAX) == EFI_SUCCESS &&
@@ -479,112 +467,143 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
         secure = secure_boot_mode();
         (void) efivar_get(LOADER_GUID, L"LoaderDevicePartUUID", &device_part_uuid);
 
-        /* We employ some unusual indentation here for readability. */
-
-        ps_string(L"  systemd-boot version: %a\n",      GIT_VERSION);
-        ps_string(L"          loaded image: %s\n",      loaded_image_path);
-        ps_string(L" loader partition UUID: %s\n",      device_part_uuid);
-        ps_string(L"          architecture: %a\n",      EFI_MACHINE_TYPE_NAME);
-            Print(L"    UEFI specification: %u.%02u\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
-        ps_string(L"       firmware vendor: %s\n",      ST->FirmwareVendor);
-            Print(L"      firmware version: %u.%02u\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
-            Print(L"        OS indications: %lu\n",     get_os_indications_supported());
-            Print(L"           secure boot: %s (%s)\n", yes_no(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)), secure_boot_mode_to_string(secure));
-          ps_bool(L"                  shim: %s\n",      shim_loaded());
-          ps_bool(L"                   TPM: %s\n",      tpm_present());
-            Print(L"          console mode: %d/%ld (%" PRIuN L"x%" PRIuN L" @%ux%u)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - INT64_C(1), x_max, y_max, screen_width, screen_height);
+        printf("  systemd-boot version: " GIT_VERSION "\n");
+        if (loaded_image_path)
+                printf("          loaded image: %ls\n", loaded_image_path);
+        if (device_part_uuid)
+                printf(" loader partition UUID: %ls\n", device_part_uuid);
+        printf("          architecture: " EFI_MACHINE_TYPE_NAME "\n");
+        printf("    UEFI specification: %u.%02u\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+        printf("       firmware vendor: %ls\n", ST->FirmwareVendor);
+        printf("      firmware version: %u.%02u\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+        printf("        OS indications: %#" PRIx64 "\n", get_os_indications_supported());
+        printf("           secure boot: %ls (%ls)\n",
+                        yes_no(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)),
+                        secure_boot_mode_to_string(secure));
+        printf("                  shim: %ls\n", yes_no(shim_loaded()));
+        printf("                   TPM: %ls\n", yes_no(tpm_present()));
+        printf("          console mode: %i/%" PRIi64 " (%zux%zu @%ux%u)\n",
+                        ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - INT64_C(1),
+                        x_max, y_max, screen_width, screen_height);
 
         if (!ps_continue())
                 return;
 
         switch (config->timeout_sec_config) {
         case TIMEOUT_UNSET:
-            break;
+                break;
         case TIMEOUT_MENU_FORCE:
-            Print(L"      timeout (config): menu-force\n"); break;
+                printf("      timeout (config): menu-force\n");
+                break;
         case TIMEOUT_MENU_HIDDEN:
-            Print(L"      timeout (config): menu-hidden\n"); break;
+                printf("      timeout (config): menu-hidden\n");
+                break;
         default:
-            Print(L"      timeout (config): %u s\n", config->timeout_sec_config);
+                printf("      timeout (config): %u s\n", config->timeout_sec_config);
         }
 
         switch (config->timeout_sec_efivar) {
         case TIMEOUT_UNSET:
-            break;
+                break;
         case TIMEOUT_MENU_FORCE:
-            Print(L"     timeout (EFI var): menu-force\n"); break;
+                printf("     timeout (EFI var): menu-force\n");
+                break;
         case TIMEOUT_MENU_HIDDEN:
-            Print(L"     timeout (EFI var): menu-hidden\n"); break;
+                printf("     timeout (EFI var): menu-hidden\n");
+                break;
         default:
-            Print(L"     timeout (EFI var): %u s\n", config->timeout_sec_efivar);
+                printf("     timeout (EFI var): %u s\n", config->timeout_sec_efivar);
         }
 
-        ps_string(L"      default (config): %s\n", config->entry_default_config);
-        ps_string(L"     default (EFI var): %s\n", config->entry_default_efivar);
-        ps_string(L"    default (one-shot): %s\n", config->entry_oneshot);
-        ps_string(L"           saved entry: %s\n", config->entry_saved);
-          ps_bool(L"                editor: %s\n", config->editor);
-          ps_bool(L"          auto-entries: %s\n", config->auto_entries);
-          ps_bool(L"         auto-firmware: %s\n", config->auto_firmware);
-          ps_bool(L"                  beep: %s\n", config->beep);
-          ps_bool(L"  reboot-for-bitlocker: %s\n", config->reboot_for_bitlocker);
+        if (config->entry_default_config)
+                printf("      default (config): %ls\n", config->entry_default_config);
+        if (config->entry_default_efivar)
+                printf("     default (EFI var): %ls\n", config->entry_default_efivar);
+        if (config->entry_oneshot)
+                printf("    default (one-shot): %ls\n", config->entry_oneshot);
+        if (config->entry_saved)
+                printf("           saved entry: %ls\n", config->entry_saved);
+        printf("                editor: %ls\n", yes_no(config->editor));
+        printf("          auto-entries: %ls\n", yes_no(config->auto_entries));
+        printf("         auto-firmware: %ls\n", yes_no(config->auto_firmware));
+        printf("                  beep: %ls\n", yes_no(config->beep));
+        printf("  reboot-for-bitlocker: %ls\n", yes_no(config->reboot_for_bitlocker));
 
         switch (config->secure_boot_enroll) {
         case ENROLL_OFF:
-                Print(L"    secure-boot-enroll: off\n"); break;
+                printf("    secure-boot-enroll: off\n");
+                break;
         case ENROLL_MANUAL:
-                Print(L"    secure-boot-enroll: manual\n"); break;
+                printf("    secure-boot-enroll: manual\n");
+                break;
         case ENROLL_FORCE:
-                Print(L"    secure-boot-enroll: force\n"); break;
+                printf("    secure-boot-enroll: force\n");
+                break;
         default:
                 assert_not_reached();
         }
 
         switch (config->console_mode) {
         case CONSOLE_MODE_AUTO:
-            Print(L" console-mode (config): %s\n", L"auto"); break;
+                printf(" console-mode (config): auto\n");
+                break;
         case CONSOLE_MODE_KEEP:
-            Print(L" console-mode (config): %s\n", L"keep"); break;
+                printf(" console-mode (config): keep\n");
+                break;
         case CONSOLE_MODE_FIRMWARE_MAX:
-            Print(L" console-mode (config): %s\n", L"max"); break;
+                printf(" console-mode (config): max\n");
+                break;
         default:
-            Print(L" console-mode (config): %ld\n", config->console_mode); break;
+                printf(" console-mode (config): %" PRIi64 "\n", config->console_mode);
+                break;
         }
 
         /* EFI var console mode is always a concrete value or unset. */
         if (config->console_mode_efivar != CONSOLE_MODE_KEEP)
-            Print(L"console-mode (EFI var): %ld\n", config->console_mode_efivar);
+                printf("console-mode (EFI var): %" PRIi64 "\n", config->console_mode_efivar);
 
         if (!ps_continue())
                 return;
 
         for (UINTN i = 0; i < config->entry_count; i++) {
                 ConfigEntry *entry = config->entries[i];
-
-                _cleanup_free_ char16_t *dp = NULL;
-                if (entry->device)
-                        (void) device_path_to_str(DevicePathFromHandle(entry->device), &dp);
-
-                    Print(L"  config entry: %" PRIuN L"/%" PRIuN L"\n", i + 1, config->entry_count);
-                ps_string(L"            id: %s\n", entry->id);
-                ps_string(L"         title: %s\n", entry->title);
-                ps_string(L"    title show: %s\n", streq16(entry->title, entry->title_show) ? NULL : entry->title_show);
-                ps_string(L"      sort key: %s\n", entry->sort_key);
-                ps_string(L"       version: %s\n", entry->version);
-                ps_string(L"    machine-id: %s\n", entry->machine_id);
-                ps_string(L"        device: %s\n", dp);
-                ps_string(L"        loader: %s\n", entry->loader);
+                EFI_DEVICE_PATH *dp = NULL;
+                _cleanup_free_ char16_t *dp_str = NULL;
+
+                if (entry->device &&
+                    BS->HandleProtocol(entry->device, &(EFI_GUID) EFI_DEVICE_PATH_PROTOCOL_GUID, (void **) &dp) ==
+                                    EFI_SUCCESS)
+                        (void) device_path_to_str(dp, &dp_str);
+
+                printf("  config entry: %zu/%zu\n", i + 1, config->entry_count);
+                printf("            id: %ls\n", entry->id);
+                if (entry->title)
+                        printf("         title: %ls\n", entry->title);
+                if (entry->title_show && !streq16(entry->title, entry->title_show))
+                        printf("    title show: %ls\n", entry->title_show);
+                if (entry->sort_key)
+                        printf("      sort key: %ls\n", entry->sort_key);
+                if (entry->version)
+                        printf("       version: %ls\n", entry->version);
+                if (entry->machine_id)
+                        printf("    machine-id: %ls\n", entry->machine_id);
+                if (dp_str)
+                        printf("        device: %ls\n", dp_str);
+                if (entry->loader)
+                        printf("        loader: %ls\n", entry->loader);
                 STRV_FOREACH(initrd, entry->initrd)
-                    Print(L"        initrd: %s\n", *initrd);
-                ps_string(L"    devicetree: %s\n", entry->devicetree);
-                ps_string(L"       options: %s\n", entry->options);
-                  ps_bool(L" internal call: %s\n", !!entry->call);
-
-                  ps_bool(L"counting boots: %s\n", entry->tries_left >= 0);
+                        printf("        initrd: %ls\n", *initrd);
+                if (entry->devicetree)
+                        printf("    devicetree: %ls\n", entry->devicetree);
+                if (entry->options)
+                        printf("       options: %ls\n", entry->options);
+                printf(" internal call: %ls\n", yes_no(!!entry->call));
+
+                printf("counting boots: %ls\n", yes_no(entry->tries_left >= 0));
                 if (entry->tries_left >= 0) {
-                    Print(L"         tries: %u left, %u done\n", entry->tries_left, entry->tries_done);
-                    Print(L"  current path: %s\\%s\n",  entry->path, entry->current_name);
-                    Print(L"     next path: %s\\%s\n",  entry->path, entry->next_name);
+                        printf("         tries: %i left, %i done\n", entry->tries_left, entry->tries_done);
+                        printf("  current path: %ls\\%ls\n", entry->path, entry->current_name);
+                        printf("     next path: %ls\\%ls\n", entry->path, entry->next_name);
                 }
 
                 if (!ps_continue())
@@ -597,14 +616,14 @@ static EFI_STATUS reboot_into_firmware(void) {
         EFI_STATUS err;
 
         if (!FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
-                return log_error_status_stall(EFI_UNSUPPORTED, L"Reboot to firmware interface not supported.");
+                return log_error_status(EFI_UNSUPPORTED, "Reboot to firmware interface not supported.");
 
         (void) efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", &osind);
         osind |= EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
 
         err = efivar_set_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", osind, EFI_VARIABLE_NON_VOLATILE);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error setting OsIndications: %r", err);
+                return log_error_status(err, "Error setting OsIndications: %m");
 
         RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
         assert_not_reached();
@@ -640,13 +659,13 @@ static bool menu_run(
         ST->ConOut->EnableCursor(ST->ConOut, false);
 
         /* draw a single character to make ClearScreen work on some firmware */
-        Print(L" ");
+        ST->ConOut->OutputString(ST->ConOut, (char16_t *) u" ");
 
         err = console_set_mode(config->console_mode_efivar != CONSOLE_MODE_KEEP ?
                                config->console_mode_efivar : config->console_mode);
         if (err != EFI_SUCCESS) {
                 clear_screen(COLOR_NORMAL);
-                log_error_stall(L"Error switching console mode: %r", err);
+                log_error_status(err, "Error switching console mode: %m");
         }
 
         UINTN line_width = 0, entry_padding = 3;
@@ -761,7 +780,7 @@ static bool menu_run(
 
                 if (timeout_remain > 0) {
                         free(status);
-                        status = xpool_print(L"Boot in %u s.", timeout_remain);
+                        status = xasprintf("Boot in %u s.", timeout_remain);
                 }
 
                 if (status) {
@@ -940,9 +959,9 @@ static bool menu_run(
                         break;
 
                 case KEYPRESS(0, 0, 'v'):
-                        status = xpool_print(
-                                        L"systemd-boot " GIT_VERSION L" (" EFI_MACHINE_TYPE_NAME L"), "
-                                        L"UEFI Specification %u.%02u, Vendor %s %u.%02u",
+                        status = xasprintf(
+                                        "systemd-boot " GIT_VERSION " (" EFI_MACHINE_TYPE_NAME "), "
+                                        "UEFI Specification %u.%02u, Vendor %ls %u.%02u",
                                         ST->Hdr.Revision >> 16,
                                         ST->Hdr.Revision & 0xffff,
                                         ST->FirmwareVendor,
@@ -964,10 +983,12 @@ static bool menu_run(
                 case KEYPRESS(0, 0, 'r'):
                         err = console_set_mode(CONSOLE_MODE_NEXT);
                         if (err != EFI_SUCCESS)
-                                status = xpool_print(L"Error changing console mode: %r", err);
+                                status = xasprintf_status(err, "Error changing console mode: %m");
                         else {
                                 config->console_mode_efivar = ST->ConOut->Mode->Mode;
-                                status = xpool_print(L"Console mode changed to %ld.", config->console_mode_efivar);
+                                status = xasprintf(
+                                                "Console mode changed to %" PRIi64 ".",
+                                                config->console_mode_efivar);
                         }
                         new_mode = true;
                         break;
@@ -977,10 +998,13 @@ static bool menu_run(
                         err = console_set_mode(config->console_mode == CONSOLE_MODE_KEEP ?
                                                console_mode_initial : config->console_mode);
                         if (err != EFI_SUCCESS)
-                                status = xpool_print(L"Error resetting console mode: %r", err);
+                                status = xasprintf_status(err, "Error resetting console mode: %m");
                         else
-                                status = xpool_print(L"Console mode reset to %s default.",
-                                                     config->console_mode == CONSOLE_MODE_KEEP ? L"firmware" : L"configuration file");
+                                status = xasprintf(
+                                                "Console mode reset to %s default.",
+                                                config->console_mode == CONSOLE_MODE_KEEP ?
+                                                                "firmware" :
+                                                                "configuration file");
                         new_mode = true;
                         break;
 
@@ -993,9 +1017,9 @@ static bool menu_run(
                         if (FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) {
                                 firmware_setup = true;
                                 /* Let's make sure the user really wants to do this. */
-                                status = xpool_print(L"Press Enter to reboot into firmware interface.");
+                                status = xstrdup16(u"Press Enter to reboot into firmware interface.");
                         } else
-                                status = xpool_print(L"Reboot into firmware interface not supported.");
+                                status = xstrdup16(u"Reboot into firmware interface not supported.");
                         break;
 
                 default:
@@ -1189,7 +1213,7 @@ static void config_defaults_load_from_file(Config *config, char *content) {
                         else {
                                 uint64_t u;
                                 if (!parse_number8(value, &u, NULL) || u > TIMEOUT_TYPE_MAX) {
-                                        log_error_stall(L"Error parsing 'timeout' config option: %a", value);
+                                        log_error("Error parsing 'timeout' config option: %s", value);
                                         continue;
                                 }
                                 config->timeout_sec_config = u;
@@ -1200,7 +1224,7 @@ static void config_defaults_load_from_file(Config *config, char *content) {
 
                 if (streq8(key, "default")) {
                         if (value[0] == '@' && !strcaseeq8(value, "@saved")) {
-                                log_error_stall(L"Unsupported special entry identifier: %a", value);
+                                log_error("Unsupported special entry identifier: %s", value);
                                 continue;
                         }
                         free(config->entry_default_config);
@@ -1211,35 +1235,35 @@ static void config_defaults_load_from_file(Config *config, char *content) {
                 if (streq8(key, "editor")) {
                         err = parse_boolean(value, &config->editor);
                         if (err != EFI_SUCCESS)
-                                log_error_stall(L"Error parsing 'editor' config option: %a", value);
+                                log_error("Error parsing 'editor' config option: %s", value);
                         continue;
                 }
 
                 if (streq8(key, "auto-entries")) {
                         err = parse_boolean(value, &config->auto_entries);
                         if (err != EFI_SUCCESS)
-                                log_error_stall(L"Error parsing 'auto-entries' config option: %a", value);
+                                log_error("Error parsing 'auto-entries' config option: %s", value);
                         continue;
                 }
 
                 if (streq8(key, "auto-firmware")) {
                         err = parse_boolean(value, &config->auto_firmware);
                         if (err != EFI_SUCCESS)
-                                log_error_stall(L"Error parsing 'auto-firmware' config option: %a", value);
+                                log_error("Error parsing 'auto-firmware' config option: %s", value);
                         continue;
                 }
 
                 if (streq8(key, "beep")) {
                         err = parse_boolean(value, &config->beep);
                         if (err != EFI_SUCCESS)
-                                log_error_stall(L"Error parsing 'beep' config option: %a", value);
+                                log_error("Error parsing 'beep' config option: %s", value);
                         continue;
                 }
 
                 if (streq8(key, "reboot-for-bitlocker")) {
                         err = parse_boolean(value, &config->reboot_for_bitlocker);
                         if (err != EFI_SUCCESS)
-                                log_error_stall(L"Error parsing 'reboot-for-bitlocker' config option: %a", value);
+                                log_error("Error parsing 'reboot-for-bitlocker' config option: %s", value);
                 }
 
                 if (streq8(key, "secure-boot-enroll")) {
@@ -1250,7 +1274,7 @@ static void config_defaults_load_from_file(Config *config, char *content) {
                         else if (streq8(value, "off"))
                                 config->secure_boot_enroll = ENROLL_OFF;
                         else
-                                log_error_stall(L"Error parsing 'secure-boot-enroll' config option: %a", value);
+                                log_error("Error parsing 'secure-boot-enroll' config option: %s", value);
                         continue;
                 }
 
@@ -1264,7 +1288,7 @@ static void config_defaults_load_from_file(Config *config, char *content) {
                         else {
                                 uint64_t u;
                                 if (!parse_number8(value, &u, NULL) || u > CONSOLE_MODE_RANGE_MAX) {
-                                        log_error_stall(L"Error parsing 'console-mode' config option: %a", value);
+                                        log_error("Error parsing 'console-mode' config option: %s", value);
                                         continue;
                                 }
                                 config->console_mode = u;
@@ -1329,9 +1353,9 @@ static void config_entry_parse_tries(
         entry->tries_done = tries_done;
         entry->path = xstrdup16(path);
         entry->current_name = xstrdup16(file);
-        entry->next_name = xpool_print(
-                        L"%.*s%u-%u%s",
-                        prefix_len,
+        entry->next_name = xasprintf(
+                        "%.*ls%" PRIu64 "-%" PRIu64 "%ls",
+                        (int) prefix_len,
                         file,
                         LESS_BY(tries_left, 1u),
                         MIN(tries_done + 1, (uint64_t) INT_MAX),
@@ -1354,7 +1378,7 @@ static void config_entry_bump_counters(ConfigEntry *entry, EFI_FILE *root_dir) {
         if (!entry->path || !entry->current_name || !entry->next_name)
                 return;
 
-        old_path = xpool_print(L"%s\\%s", entry->path, entry->current_name);
+        old_path = xasprintf("%ls\\%ls", entry->path, entry->current_name);
 
         err = root_dir->Open(root_dir, &handle, old_path, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
         if (err != EFI_SUCCESS)
@@ -1368,7 +1392,7 @@ static void config_entry_bump_counters(ConfigEntry *entry, EFI_FILE *root_dir) {
         strcpy16(file_info->FileName, entry->next_name);
         err = handle->SetInfo(handle, &GenericFileInfo, file_info_size, file_info);
         if (err != EFI_SUCCESS) {
-                log_error_stall(L"Failed to rename '%s' to '%s', ignoring: %r", old_path, entry->next_name, err);
+                log_error_status(err, "Failed to rename '%ls' to '%ls', ignoring: %m", old_path, entry->next_name);
                 return;
         }
 
@@ -1377,7 +1401,7 @@ static void config_entry_bump_counters(ConfigEntry *entry, EFI_FILE *root_dir) {
 
         /* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
          * success */
-        new_path = xpool_print(L"%s\\%s", entry->path, entry->next_name);
+        new_path = xasprintf("%ls\\%ls", entry->path, entry->next_name);
         efivar_set(LOADER_GUID, L"LoaderBootCountPath", new_path, 0);
 
         /* If the file we just renamed is the loader path, then let's update that. */
@@ -1491,7 +1515,7 @@ static void config_entry_add_type1(
 
                         new = xstr8_to_16(value);
                         if (entry->options) {
-                                char16_t *s = xpool_print(L"%s %s", entry->options, new);
+                                char16_t *s = xasprintf("%ls %ls", entry->options, new);
                                 free(entry->options);
                                 entry->options = s;
                         } else
@@ -1576,7 +1600,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
         if (err == EFI_SUCCESS)
                 config->timeout_sec = config->timeout_sec_efivar;
         else if (err != EFI_NOT_FOUND)
-                log_error_stall(u"Error reading LoaderConfigTimeout EFI variable: %r", err);
+                log_error_status(err, "Error reading LoaderConfigTimeout EFI variable: %m");
 
         err = efivar_get_timeout(u"LoaderConfigTimeoutOneShot", &config->timeout_sec);
         if (err == EFI_SUCCESS) {
@@ -1585,7 +1609,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
 
                 config->force_menu = true; /* force the menu when this is set */
         } else if (err != EFI_NOT_FOUND)
-                log_error_stall(u"Error reading LoaderConfigTimeoutOneShot EFI variable: %r", err);
+                log_error_status(err, "Error reading LoaderConfigTimeoutOneShot EFI variable: %m");
 
         err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigConsoleMode", &value);
         if (err == EFI_SUCCESS)
@@ -1808,7 +1832,7 @@ static void config_title_generate(Config *config) {
                         continue;
 
                 _cleanup_free_ char16_t *t = config->entries[i]->title_show;
-                config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->version);
+                config->entries[i]->title_show = xasprintf("%ls (%ls)", t, config->entries[i]->version);
         }
 
         if (entries_unique(config->entries, unique, config->entry_count))
@@ -1825,11 +1849,7 @@ static void config_title_generate(Config *config) {
                         continue;
 
                 _cleanup_free_ char16_t *t = config->entries[i]->title_show;
-                config->entries[i]->title_show = xpool_print(
-                        L"%s (%.*s)",
-                        t,
-                        strnlen16(config->entries[i]->machine_id, 8),
-                        config->entries[i]->machine_id);
+                config->entries[i]->title_show = xasprintf("%ls (%.8ls)", t, config->entries[i]->machine_id);
         }
 
         if (entries_unique(config->entries, unique, config->entry_count))
@@ -1841,7 +1861,7 @@ static void config_title_generate(Config *config) {
                         continue;
 
                 _cleanup_free_ char16_t *t = config->entries[i]->title_show;
-                config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->id);
+                config->entries[i]->title_show = xasprintf("%ls (%ls)", t, config->entries[i]->id);
         }
 }
 
@@ -2006,10 +2026,9 @@ static EFI_STATUS boot_windows_bitlocker(void) {
 
         for (UINTN i = 0; i < boot_order_size / sizeof(uint16_t); i++) {
                 _cleanup_free_ char *buf = NULL;
-                char16_t name[sizeof(L"Boot0000")];
                 UINTN buf_size;
 
-                SPrint(name, sizeof(name), L"Boot%04x", (uint32_t) boot_order[i]);
+                _cleanup_free_ char16_t *name = xasprintf("Boot%04x", boot_order[i]);
                 err = efivar_get_raw(EFI_GLOBAL_GUID, name, &buf, &buf_size);
                 if (err != EFI_SUCCESS)
                         continue;
@@ -2202,7 +2221,7 @@ static void config_entry_add_unified(
                         .title = xstrdup16(good_name),
                         .version = xstrdup16(good_version),
                         .device = device,
-                        .loader = xpool_print(L"\\EFI\\Linux\\%s", f->FileName),
+                        .loader = xasprintf("\\EFI\\Linux\\%ls", f->FileName),
                         .sort_key = xstrdup16(good_sort_key),
                         .key = 'l',
                         .tries_done = -1,
@@ -2281,9 +2300,9 @@ static EFI_STATUS initrd_prepare(
         STRV_FOREACH(i, entry->initrd) {
                 _cleanup_free_ char16_t *o = options;
                 if (o)
-                        options = xpool_print(L"%s initrd=%s", o, *i);
+                        options = xasprintf("%ls initrd=%ls", o, *i);
                 else
-                        options = xpool_print(L"initrd=%s", *i);
+                        options = xasprintf("initrd=%ls", *i);
 
                 _cleanup_(file_closep) EFI_FILE *handle = NULL;
                 err = root->Open(root, &handle, *i, EFI_FILE_MODE_READ, 0);
@@ -2314,7 +2333,7 @@ static EFI_STATUS initrd_prepare(
 
         if (entry->options) {
                 _cleanup_free_ char16_t *o = options;
-                options = xpool_print(L"%s %s", o, entry->options);
+                options = xasprintf("%ls %ls", o, entry->options);
         }
 
         *ret_options = TAKE_PTR(options);
@@ -2341,38 +2360,38 @@ static EFI_STATUS image_start(
         _cleanup_(file_closep) EFI_FILE *image_root = NULL;
         err = open_volume(entry->device, &image_root);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error opening root path: %r", err);
+                return log_error_status(err, "Error opening root path: %m");
 
         err = make_file_device_path(entry->device, entry->loader, &path);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error making file device path: %r", err);
+                return log_error_status(err, "Error making file device path: %m");
 
         UINTN initrd_size = 0;
         _cleanup_free_ void *initrd = NULL;
         _cleanup_free_ char16_t *options_initrd = NULL;
         err = initrd_prepare(image_root, entry, &options_initrd, &initrd, &initrd_size);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error preparing initrd: %r", err);
+                return log_error_status(err, "Error preparing initrd: %m");
 
         err = shim_load_image(parent_image, path, &image);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
+                return log_error_status(err, "Error loading %ls: %m", entry->loader);
 
         if (entry->devicetree) {
                 err = devicetree_install(&dtstate, image_root, entry->devicetree);
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Error loading %s: %r", entry->devicetree, err);
+                        return log_error_status(err, "Error loading %ls: %m", entry->devicetree);
         }
 
         _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
         err = initrd_register(initrd, initrd_size, &initrd_handle);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error registering initrd: %r", err);
+                return log_error_status(err, "Error registering initrd: %m");
 
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
         err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **) &loaded_image);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error getting LoadedImageProtocol handle: %r", err);
+                return log_error_status(err, "Error getting LoadedImageProtocol handle: %m");
 
         char16_t *options = options_initrd ?: entry->options;
         if (options) {
@@ -2396,7 +2415,7 @@ static EFI_STATUS image_start(
                 err = pe_kernel_info(loaded_image->ImageBase, &compat_address);
                 if (err != EFI_SUCCESS) {
                         if (err != EFI_UNSUPPORTED)
-                                return log_error_status_stall(err, L"Error finding kernel compat entry address: %r", err);
+                                return log_error_status(err, "Error finding kernel compat entry address: %m");
                 } else if (compat_address > 0) {
                         EFI_IMAGE_ENTRY_POINT kernel_entry =
                                 (EFI_IMAGE_ENTRY_POINT) ((uint8_t *) loaded_image->ImageBase + compat_address);
@@ -2409,7 +2428,7 @@ static EFI_STATUS image_start(
                         err = EFI_UNSUPPORTED;
         }
 
-        return log_error_status_stall(err, L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err);
+        return log_error_status(err, "Failed to execute %ls (%ls): %m", entry->title_show, entry->loader);
 }
 
 static void config_free(Config *config) {
@@ -2496,9 +2515,9 @@ static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir)
 
                 entry = xnew(ConfigEntry, 1);
                 *entry = (ConfigEntry) {
-                        .id = xpool_print(L"secure-boot-keys-%s", dirent->FileName),
-                        .title = xpool_print(L"Enroll Secure Boot keys: %s", dirent->FileName),
-                        .path = xpool_print(L"\\loader\\keys\\%s", dirent->FileName),
+                        .id = xasprintf("secure-boot-keys-%ls", dirent->FileName),
+                        .title = xasprintf("Enroll Secure Boot keys: %ls", dirent->FileName),
+                        .path = xasprintf("\\loader\\keys\\%ls", dirent->FileName),
                         .type = LOADER_SECURE_BOOT_KEYS,
                         .tries_done = -1,
                         .tries_left = -1,
@@ -2541,10 +2560,10 @@ static void export_variables(
         efivar_set_time_usec(LOADER_GUID, L"LoaderTimeInitUSec", init_usec);
         efivar_set(LOADER_GUID, L"LoaderInfo", L"systemd-boot " GIT_VERSION, 0);
 
-        infostr = xpool_print(L"%s %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+        infostr = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
         efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", infostr, 0);
 
-        typestr = xpool_print(L"UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+        typestr = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
         efivar_set(LOADER_GUID, L"LoaderFirmwareType", typestr, 0);
 
         (void) efivar_set_uint64_le(LOADER_GUID, L"LoaderFeatures", loader_features, 0);
@@ -2626,7 +2645,7 @@ static EFI_STATUS discover_root_dir(EFI_LOADED_IMAGE_PROTOCOL *loaded_image, EFI
                 return open_volume(loaded_image->DeviceHandle, ret_dir);
 }
 
-EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+static EFI_STATUS real_main(EFI_HANDLE image) {
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
         _cleanup_(file_closep) EFI_FILE *root_dir = NULL;
         _cleanup_(config_free) Config config = {};
@@ -2635,11 +2654,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         uint64_t init_usec;
         bool menu = false;
 
-        InitializeLib(image, sys_table);
         init_usec = time_usec();
-        debug_hook(L"systemd-boot");
-        /* Uncomment the next line if you need to wait for debugger. */
-        // debug_break();
 
         err = BS->OpenProtocol(image,
                         &LoadedImageProtocol,
@@ -2648,7 +2663,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                         NULL,
                         EFI_OPEN_PROTOCOL_GET_PROTOCOL);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
+                return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m");
 
         (void) device_path_to_str(loaded_image->FilePath, &loaded_image_path);
 
@@ -2656,14 +2671,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
 
         err = discover_root_dir(loaded_image, &root_dir);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Unable to open root directory: %r", err);
+                return log_error_status(err, "Unable to open root directory: %m");
 
         (void) load_drivers(image, loaded_image, root_dir);
 
         config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);
 
         if (config.entry_count == 0) {
-                log_error_stall(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
+                log_error("No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
                 goto out;
         }
 
@@ -2728,3 +2743,15 @@ out:
         BS->CloseProtocol(image, &LoadedImageProtocol, image, NULL);
         return err;
 }
+
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+        InitializeLib(image, sys_table);
+
+        debug_hook("systemd-boot");
+        /* Uncomment the next line if you need to wait for debugger. */
+        // debug_break();
+
+        EFI_STATUS err = real_main(image);
+        log_wait();
+        return err;
+}
index 14c0008afb47bf751a5d8644c196160a67cf5dd8..2e5c857b04d4e3a2859730d0d44ac76e9d2412e4 100644 (file)
@@ -67,7 +67,7 @@ EFI_STATUS console_key_read(uint64_t *key, uint64_t timeout_usec) {
 
         err = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &timer);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error creating timer event: %r", err);
+                return log_error_status(err, "Error creating timer event: %m");
 
         EFI_EVENT events[] = {
                 timer,
@@ -88,14 +88,14 @@ EFI_STATUS console_key_read(uint64_t *key, uint64_t timeout_usec) {
                                 TimerRelative,
                                 MIN(timeout_usec, watchdog_ping_usec) * 10);
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Error arming timer event: %r", err);
+                        return log_error_status(err, "Error arming timer event: %m");
 
                 (void) BS->SetWatchdogTimer(watchdog_timeout_sec, 0x10000, 0, NULL);
                 err = BS->WaitForEvent(n_events, events, &index);
                 (void) BS->SetWatchdogTimer(watchdog_timeout_sec, 0x10000, 0, NULL);
 
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Error waiting for events: %r", err);
+                        return log_error_status(err, "Error waiting for events: %m");
 
                 /* We have keyboard input, process it after this loop. */
                 if (timer != events[index])
@@ -172,6 +172,7 @@ static EFI_STATUS change_mode(int64_t mode) {
         mode = CLAMP(mode, CONSOLE_MODE_RANGE_MIN, CONSOLE_MODE_RANGE_MAX);
         old_mode = MAX(CONSOLE_MODE_RANGE_MIN, ST->ConOut->Mode->Mode);
 
+        log_wait();
         err = ST->ConOut->SetMode(ST->ConOut, mode);
         if (err == EFI_SUCCESS)
                 return EFI_SUCCESS;
index 7f123fa37643562fb53dff26d704da3d0023502d..9c33e33e9cff8d8448ae55e5d65d6989002de253 100644 (file)
@@ -318,7 +318,7 @@ static char16_t *get_dropin_dir(const EFI_DEVICE_PATH *file_path) {
                 return NULL;
 
         convert_efi_path(file_path_str);
-        return xpool_print(u"%s.extra.d", file_path_str);
+        return xasprintf("%ls.extra.d", file_path_str);
 }
 
 EFI_STATUS pack_cpio(
@@ -357,8 +357,7 @@ EFI_STATUS pack_cpio(
                  * its file handles. */
                 goto nothing;
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(
-                                err, L"Unable to open root directory: %r", err);
+                return log_error_status(err, "Unable to open root directory: %m");
 
         if (!dropin_dir)
                 dropin_dir = rel_dropin_dir = get_dropin_dir(loaded_image->FilePath);
@@ -368,14 +367,14 @@ EFI_STATUS pack_cpio(
                 /* No extra subdir, that's totally OK */
                 goto nothing;
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to open extra directory of loaded image: %r", err);
+                return log_error_status(err, "Failed to open extra directory of loaded image: %m");
 
         for (;;) {
                 _cleanup_free_ char16_t *d = NULL;
 
                 err = readdir_harder(extra_dir, &dirent, &dirent_size);
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
+                        return log_error_status(err, "Failed to read extra directory of loaded image: %m");
                 if (!dirent) /* End of directory */
                         break;
 
@@ -420,7 +419,7 @@ EFI_STATUS pack_cpio(
          * archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
         err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err);
+                return log_error_status(err, "Failed to pack cpio prefix: %m");
 
         for (UINTN i = 0; i < n_items; i++) {
                 _cleanup_free_ char *content = NULL;
@@ -428,7 +427,7 @@ EFI_STATUS pack_cpio(
 
                 err = file_read(extra_dir, items[i], 0, 0, &content, &contentsize);
                 if (err != EFI_SUCCESS) {
-                        log_error_status_stall(err, L"Failed to read %s, ignoring: %r", items[i], err);
+                        log_error_status(err, "Failed to read %ls, ignoring: %m", items[i]);
                         continue;
                 }
 
@@ -440,22 +439,21 @@ EFI_STATUS pack_cpio(
                                 &inode,
                                 &buffer, &buffer_size);
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", dirent->FileName, err);
+                        return log_error_status(err, "Failed to pack cpio file %ls: %m", dirent->FileName);
         }
 
         err = pack_cpio_trailer(&buffer, &buffer_size);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
+                return log_error_status(err, "Failed to pack cpio trailer: %m");
 
         err = tpm_log_event(
                         tpm_pcr, POINTER_TO_PHYSICAL_ADDRESS(buffer), buffer_size, tpm_description, ret_measured);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(
+                return log_error_status(
                                 err,
-                                L"Unable to add cpio TPM measurement for PCR %u (%s), ignoring: %r",
+                                "Unable to add cpio TPM measurement for PCR %u (%ls), ignoring: %m",
                                 tpm_pcr,
-                                tpm_description,
-                                err);
+                                tpm_description);
 
         *ret_buffer = TAKE_PTR(buffer);
         *ret_buffer_size = buffer_size;
@@ -501,7 +499,7 @@ EFI_STATUS pack_cpio_literal(
 
         err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err);
+                return log_error_status(err, "Failed to pack cpio prefix: %m");
 
         err = pack_cpio_one(
                         target_filename,
@@ -511,21 +509,20 @@ EFI_STATUS pack_cpio_literal(
                         &inode,
                         &buffer, &buffer_size);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", target_filename, err);
+                return log_error_status(err, "Failed to pack cpio file %ls: %m", target_filename);
 
         err = pack_cpio_trailer(&buffer, &buffer_size);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
+                return log_error_status(err, "Failed to pack cpio trailer: %m");
 
         err = tpm_log_event(
                         tpm_pcr, POINTER_TO_PHYSICAL_ADDRESS(buffer), buffer_size, tpm_description, ret_measured);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(
+                return log_error_status(
                                 err,
-                                L"Unable to add cpio TPM measurement for PCR %u (%s), ignoring: %r",
+                                "Unable to add cpio TPM measurement for PCR %u (%ls), ignoring: %m",
                                 tpm_pcr,
-                                tpm_description,
-                                err);
+                                tpm_description);
 
         *ret_buffer = TAKE_PTR(buffer);
         *ret_buffer_size = buffer_size;
index 12015fce6b8533bb06d3509cb795c4730a3a46ac..0007bb8fe04a1537f7040dc6a34f617ff7f3775b 100644 (file)
@@ -36,8 +36,7 @@ static EFI_STATUS devicetree_fixup(struct devicetree_state *state, UINTN len) {
 
         err = BS->LocateProtocol(&EfiDtFixupProtocol, NULL, (void **) &fixup);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(EFI_SUCCESS,
-                                              L"Could not locate device tree fixup protocol, skipping.");
+                return log_error_status(EFI_SUCCESS, "Could not locate device tree fixup protocol, skipping.");
 
         size = devicetree_allocated(state);
         err = fixup->Fixup(fixup, PHYSICAL_ADDRESS_TO_POINTER(state->addr), &size,
index 7f2057f5a15e89a3bacd78a855d12e21228bb8d7..5962b48366a39f1f0909fd765ca8d627bf4cc13c 100644 (file)
@@ -20,29 +20,29 @@ static EFI_STATUS load_one_driver(
         assert(loaded_image);
         assert(fname);
 
-        spath = xpool_print(L"\\EFI\\systemd\\drivers\\%s", fname);
+        spath = xasprintf("\\EFI\\systemd\\drivers\\%ls", fname);
         err = make_file_device_path(loaded_image->DeviceHandle, spath, &path);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error making file device path: %r", err);
+                return log_error_status(err, "Error making file device path: %m");
 
         err = BS->LoadImage(false, parent_image, path, NULL, 0, &image);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to load image %s: %r", fname, err);
+                return log_error_status(err, "Failed to load image %ls: %m", fname);
 
         err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **)&loaded_image);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to find protocol in driver image %s: %r", fname, err);
+                return log_error_status(err, "Failed to find protocol in driver image %ls: %m", fname);
 
         if (loaded_image->ImageCodeType != EfiBootServicesCode &&
             loaded_image->ImageCodeType != EfiRuntimeServicesCode)
-                return log_error_status_stall(EFI_INVALID_PARAMETER, L"Image %s is not a driver, refusing.", fname);
+                return log_error("Image %ls is not a driver, refusing.", fname);
 
         err = BS->StartImage(image, NULL, NULL);
         if (err != EFI_SUCCESS) {
                 /* EFI_ABORTED signals an initializing driver. It uses this error code on success
                  * so that it is unloaded after. */
                 if (err != EFI_ABORTED)
-                        log_error_stall(L"Failed to start image %s: %r", fname, err);
+                        log_error_status(err, "Failed to start image %ls: %m", fname);
                 return err;
         }
 
@@ -59,7 +59,7 @@ EFI_STATUS reconnect_all_drivers(void) {
 
         err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
+                return log_error_status(err, "Failed to get list of handles: %m");
 
         for (size_t i = 0; i < n_handles; i++)
                 /* Some firmware gives us some bogus handles (or they might become bad due to
@@ -87,12 +87,12 @@ EFI_STATUS load_drivers(
         if (err == EFI_NOT_FOUND)
                 return EFI_SUCCESS;
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to open \\EFI\\systemd\\drivers: %r", err);
+                return log_error_status(err, "Failed to open \\EFI\\systemd\\drivers: %m");
 
         for (;;) {
                 err = readdir_harder(drivers_dir, &dirent, &dirent_size);
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
+                        return log_error_status(err, "Failed to read extra directory of loaded image: %m");
                 if (!dirent) /* End of directory */
                         break;
 
index 79c296eda375f301f0778308c2adc73aa662a00d..860dfc00b2c0bce3ad31316da370ebb9440a366c 100644 (file)
@@ -2,10 +2,12 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include <wchar.h>
 
 #include "efi-string.h"
 
 #if SD_BOOT
+#  include "missing_efi.h"
 #  include "util.h"
 #else
 #  include <stdlib.h>
@@ -378,6 +380,495 @@ bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack) {
 DEFINE_PARSE_NUMBER(char, parse_number8);
 DEFINE_PARSE_NUMBER(char16_t, parse_number16);
 
+static const char * const warn_table[] = {
+        [EFI_SUCCESS]               = "Success",
+#if SD_BOOT
+        [EFI_WARN_UNKNOWN_GLYPH]    = "Unknown glyph",
+        [EFI_WARN_DELETE_FAILURE]   = "Delete failure",
+        [EFI_WARN_WRITE_FAILURE]    = "Write failure",
+        [EFI_WARN_BUFFER_TOO_SMALL] = "Buffer too small",
+        [EFI_WARN_STALE_DATA]       = "Stale data",
+        [EFI_WARN_FILE_SYSTEM]      = "File system",
+        [EFI_WARN_RESET_REQUIRED]   = "Reset required",
+#endif
+};
+
+/* Errors have MSB set, remove it to keep the table compact. */
+#define NOERR(err) ((err) & ~EFI_ERROR_MASK)
+
+static const char * const err_table[] = {
+        [NOERR(EFI_ERROR_MASK)]           = "Error",
+        [NOERR(EFI_LOAD_ERROR)]           = "Load error",
+#if SD_BOOT
+        [NOERR(EFI_INVALID_PARAMETER)]    = "Invalid parameter",
+        [NOERR(EFI_UNSUPPORTED)]          = "Unsupported",
+        [NOERR(EFI_BAD_BUFFER_SIZE)]      = "Bad buffer size",
+        [NOERR(EFI_BUFFER_TOO_SMALL)]     = "Buffer too small",
+        [NOERR(EFI_NOT_READY)]            = "Not ready",
+        [NOERR(EFI_DEVICE_ERROR)]         = "Device error",
+        [NOERR(EFI_WRITE_PROTECTED)]      = "Write protected",
+        [NOERR(EFI_OUT_OF_RESOURCES)]     = "Out of resources",
+        [NOERR(EFI_VOLUME_CORRUPTED)]     = "Volume corrupt",
+        [NOERR(EFI_VOLUME_FULL)]          = "Volume full",
+        [NOERR(EFI_NO_MEDIA)]             = "No media",
+        [NOERR(EFI_MEDIA_CHANGED)]        = "Media changed",
+        [NOERR(EFI_NOT_FOUND)]            = "Not found",
+        [NOERR(EFI_ACCESS_DENIED)]        = "Access denied",
+        [NOERR(EFI_NO_RESPONSE)]          = "No response",
+        [NOERR(EFI_NO_MAPPING)]           = "No mapping",
+        [NOERR(EFI_TIMEOUT)]              = "Time out",
+        [NOERR(EFI_NOT_STARTED)]          = "Not started",
+        [NOERR(EFI_ALREADY_STARTED)]      = "Already started",
+        [NOERR(EFI_ABORTED)]              = "Aborted",
+        [NOERR(EFI_ICMP_ERROR)]           = "ICMP error",
+        [NOERR(EFI_TFTP_ERROR)]           = "TFTP error",
+        [NOERR(EFI_PROTOCOL_ERROR)]       = "Protocol error",
+        [NOERR(EFI_INCOMPATIBLE_VERSION)] = "Incompatible version",
+        [NOERR(EFI_SECURITY_VIOLATION)]   = "Security violation",
+        [NOERR(EFI_CRC_ERROR)]            = "CRC error",
+        [NOERR(EFI_END_OF_MEDIA)]         = "End of media",
+        [29]                              = "Reserved (29)",
+        [30]                              = "Reserved (30)",
+        [NOERR(EFI_END_OF_FILE)]          = "End of file",
+        [NOERR(EFI_INVALID_LANGUAGE)]     = "Invalid language",
+        [NOERR(EFI_COMPROMISED_DATA)]     = "Compromised data",
+        [NOERR(EFI_IP_ADDRESS_CONFLICT)]  = "IP address conflict",
+        [NOERR(EFI_HTTP_ERROR)]           = "HTTP error",
+#endif
+};
+
+static const char *status_to_string(EFI_STATUS status) {
+        if (status <= ELEMENTSOF(warn_table) - 1)
+                return warn_table[status];
+        if (status >= EFI_ERROR_MASK && status <= ((ELEMENTSOF(err_table) - 1) | EFI_ERROR_MASK))
+                return err_table[NOERR(status)];
+        return NULL;
+}
+
+typedef struct {
+        size_t padded_len; /* Field width in printf. */
+        size_t len;        /* Precision in printf. */
+        bool pad_zero;
+        bool align_left;
+        bool alternative_form;
+        bool long_arg;
+        bool longlong_arg;
+        bool have_field_width;
+
+        const char *str;
+        const wchar_t *wstr;
+
+        /* For numbers. */
+        bool is_signed;
+        bool lowercase;
+        int8_t base;
+        char sign_pad; /* For + and (space) flags. */
+} SpecifierContext;
+
+typedef struct {
+        char16_t stack_buf[128]; /* We use stack_buf first to avoid allocations in most cases. */
+        char16_t *dyn_buf;       /* Allocated buf or NULL if stack_buf is used. */
+        char16_t *buf;           /* Points to the current active buf. */
+        size_t n_buf;            /* Len of buf (in char16_t's, not bytes!). */
+        size_t n;                /* Used len of buf (in char16_t's). This is always <n_buf. */
+
+        EFI_STATUS status;
+        const char *format;
+        va_list ap;
+} FormatContext;
+
+static void grow_buf(FormatContext *ctx, size_t need) {
+        assert(ctx);
+
+        assert_se(!__builtin_add_overflow(ctx->n, need, &need));
+
+        if (need < ctx->n_buf)
+                return;
+
+        /* Greedily allocate if we can. */
+        if (__builtin_mul_overflow(need, 2, &ctx->n_buf))
+                ctx->n_buf = need;
+
+        /* We cannot use realloc here as ctx->buf may be ctx->stack_buf, which we cannot free. */
+        char16_t *new_buf = xnew(char16_t, ctx->n_buf);
+        memcpy(new_buf, ctx->buf, ctx->n * sizeof(*ctx->buf));
+
+        free(ctx->dyn_buf);
+        ctx->buf = ctx->dyn_buf = new_buf;
+}
+
+static void push_padding(FormatContext *ctx, char pad, size_t len) {
+        assert(ctx);
+        while (len > 0) {
+                len--;
+                ctx->buf[ctx->n++] = pad;
+        }
+}
+
+static bool push_str(FormatContext *ctx, SpecifierContext *sp) {
+        assert(ctx);
+        assert(sp);
+
+        sp->padded_len = LESS_BY(sp->padded_len, sp->len);
+
+        grow_buf(ctx, sp->padded_len + sp->len);
+
+        if (!sp->align_left)
+                push_padding(ctx, ' ', sp->padded_len);
+
+        /* In userspace unit tests we cannot just memcpy() the wide string. */
+        if (sp->wstr && sizeof(wchar_t) == sizeof(char16_t)) {
+                memcpy(ctx->buf + ctx->n, sp->wstr, sp->len * sizeof(*sp->wstr));
+                ctx->n += sp->len;
+        } else
+                for (size_t i = 0; i < sp->len; i++)
+                        ctx->buf[ctx->n++] = sp->str ? sp->str[i] : sp->wstr[i];
+
+        if (sp->align_left)
+                push_padding(ctx, ' ', sp->padded_len);
+
+        assert(ctx->n < ctx->n_buf);
+        return true;
+}
+
+static bool push_num(FormatContext *ctx, SpecifierContext *sp, uint64_t u) {
+        const char *digits = sp->lowercase ? "0123456789abcdef" : "0123456789ABCDEF";
+        char16_t tmp[32];
+        size_t n = 0;
+
+        assert(ctx);
+        assert(sp);
+        assert(IN_SET(sp->base, 10, 16));
+
+        /* "%.0u" prints nothing if value is 0. */
+        if (u == 0 && sp->len == 0)
+                return true;
+
+        if (sp->is_signed && (int64_t) u < 0) {
+                /* We cannot just do "u = -(int64_t)u" here because -INT64_MIN overflows. */
+
+                uint64_t rem = -((int64_t) u % sp->base);
+                u = (int64_t) u / -sp->base;
+                tmp[n++] = digits[rem];
+                sp->sign_pad = '-';
+        }
+
+        while (u > 0 || n == 0) {
+                uint64_t rem = u % sp->base;
+                u /= sp->base;
+                tmp[n++] = digits[rem];
+        }
+
+        /* Note that numbers never get truncated! */
+        size_t prefix = (sp->sign_pad != 0 ? 1 : 0) + (sp->alternative_form ? 2 : 0);
+        size_t number_len = prefix + MAX(n, sp->len);
+        grow_buf(ctx, MAX(sp->padded_len, number_len));
+
+        size_t padding = 0;
+        if (sp->pad_zero)
+                /* Leading zeroes go after the sign or 0x prefix. */
+                number_len = MAX(number_len, sp->padded_len);
+        else
+                padding = LESS_BY(sp->padded_len, number_len);
+
+        if (!sp->align_left)
+                push_padding(ctx, ' ', padding);
+
+        if (sp->sign_pad != 0)
+                ctx->buf[ctx->n++] = sp->sign_pad;
+        if (sp->alternative_form) {
+                ctx->buf[ctx->n++] = '0';
+                ctx->buf[ctx->n++] = sp->lowercase ? 'x' : 'X';
+        }
+
+        push_padding(ctx, '0', LESS_BY(number_len, n + prefix));
+
+        while (n > 0)
+                ctx->buf[ctx->n++] = tmp[--n];
+
+        if (sp->align_left)
+                push_padding(ctx, ' ', padding);
+
+        assert(ctx->n < ctx->n_buf);
+        return true;
+}
+
+/* This helps unit testing. */
+#if SD_BOOT
+#  define NULLSTR "(null)"
+#  define wcsnlen strnlen16
+#else
+#  define NULLSTR "(nil)"
+#endif
+
+static bool handle_format_specifier(FormatContext *ctx, SpecifierContext *sp) {
+        /* Parses one item from the format specifier in ctx and put the info into sp. If we are done with
+         * this specifier returns true, otherwise this function should be called again. */
+
+        /* This implementation assumes 32bit ints. Also note that all types smaller than int are promoted to
+         * int in vararg functions, which is why we fetch only ints for any such types. The compiler would
+         * otherwise warn about fetching smaller types. */
+        assert_cc(sizeof(int) == 4);
+        assert_cc(sizeof(wchar_t) <= sizeof(int));
+        assert_cc(sizeof(intmax_t) <= sizeof(long long));
+
+        assert(ctx);
+        assert(sp);
+
+        switch (*ctx->format) {
+        case '#':
+                sp->alternative_form = true;
+                return false;
+        case '.':
+                sp->have_field_width = true;
+                return false;
+        case '-':
+                sp->align_left = true;
+                return false;
+        case '+':
+        case ' ':
+                sp->sign_pad = *ctx->format;
+                return false;
+
+        case '0':
+                if (!sp->have_field_width) {
+                        sp->pad_zero = true;
+                        return false;
+                }
+
+                /* If field width has already been provided then 0 is part of precision (%.0s). */
+                _fallthrough_;
+
+        case '*':
+        case '1' ... '9': {
+                int64_t i;
+
+                if (*ctx->format == '*')
+                        i = va_arg(ctx->ap, int);
+                else {
+                        uint64_t u;
+                        if (!parse_number8(ctx->format, &u, &ctx->format) || u > INT_MAX)
+                                assert_not_reached();
+                        ctx->format--; /* Point it back to the last digit. */
+                        i = u;
+                }
+
+                if (sp->have_field_width) {
+                        /* Negative precision is ignored. */
+                        if (i >= 0)
+                                sp->len = (size_t) i;
+                } else {
+                        /* Negative field width is treated as positive field width with '-' flag. */
+                        if (i < 0) {
+                                i *= -1;
+                                sp->align_left = true;
+                        }
+                        sp->padded_len = i;
+                }
+
+                return false;
+        }
+
+        case 'h':
+                if (*(ctx->format + 1) == 'h')
+                        ctx->format++;
+                /* char/short gets promoted to int, nothing to do here. */
+                return false;
+
+        case 'l':
+                if (*(ctx->format + 1) == 'l') {
+                        ctx->format++;
+                        sp->longlong_arg = true;
+                } else
+                        sp->long_arg = true;
+                return false;
+
+        case 'z':
+                sp->long_arg = sizeof(size_t) == sizeof(long);
+                sp->longlong_arg = !sp->long_arg && sizeof(size_t) == sizeof(long long);
+                return false;
+
+        case 'j':
+                sp->long_arg = sizeof(intmax_t) == sizeof(long);
+                sp->longlong_arg = !sp->long_arg && sizeof(intmax_t) == sizeof(long long);
+                return false;
+
+        case 't':
+                sp->long_arg = sizeof(ptrdiff_t) == sizeof(long);
+                sp->longlong_arg = !sp->long_arg && sizeof(ptrdiff_t) == sizeof(long long);
+                return false;
+
+        case '%':
+                sp->str = "%";
+                sp->len = 1;
+                return push_str(ctx, sp);
+
+        case 'c':
+                sp->wstr = &(wchar_t){ va_arg(ctx->ap, int) };
+                sp->len = 1;
+                return push_str(ctx, sp);
+
+        case 's':
+                if (sp->long_arg) {
+                        sp->wstr = va_arg(ctx->ap, const wchar_t *) ?: L"(null)";
+                        sp->len = wcsnlen(sp->wstr, sp->len);
+                } else {
+                        sp->str = va_arg(ctx->ap, const char *) ?: "(null)";
+                        sp->len = strnlen8(sp->str, sp->len);
+                }
+                return push_str(ctx, sp);
+
+        case 'd':
+        case 'i':
+        case 'u':
+        case 'x':
+        case 'X':
+                sp->lowercase = *ctx->format == 'x';
+                sp->is_signed = IN_SET(*ctx->format, 'd', 'i');
+                sp->base = IN_SET(*ctx->format, 'x', 'X') ? 16 : 10;
+                if (sp->len == SIZE_MAX)
+                        sp->len = 1;
+
+                uint64_t v;
+                if (sp->longlong_arg)
+                        v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long long) :
+                                            va_arg(ctx->ap, unsigned long long);
+                else if (sp->long_arg)
+                        v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long) : va_arg(ctx->ap, unsigned long);
+                else
+                        v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, int) : va_arg(ctx->ap, unsigned);
+
+                return push_num(ctx, sp, v);
+
+        case 'p': {
+                const void *ptr = va_arg(ctx->ap, const void *);
+                if (!ptr) {
+                        sp->str = NULLSTR;
+                        sp->len = STRLEN(NULLSTR);
+                        return push_str(ctx, sp);
+                }
+
+                sp->base = 16;
+                sp->lowercase = true;
+                sp->alternative_form = true;
+                sp->len = 0; /* Precision is ignored for %p. */
+                return push_num(ctx, sp, (uintptr_t) ptr);
+        }
+
+        case 'm': {
+                sp->str = status_to_string(ctx->status);
+                if (sp->str) {
+                        sp->len = strlen8(sp->str);
+                        return push_str(ctx, sp);
+                }
+
+                sp->base = 16;
+                sp->lowercase = true;
+                sp->alternative_form = true;
+                sp->len = 0;
+                return push_num(ctx, sp, ctx->status);
+        }
+
+        default:
+                assert_not_reached();
+        }
+}
+
+/* printf_internal is largely compatible to userspace vasprintf. Any features omitted should trigger asserts.
+ *
+ * Supported:
+ *  - Flags: #, 0, +, -, space
+ *  - Lengths: h, hh, l, ll, z, j, t
+ *  - Specifiers: %, c, s, u, i, d, x, X, p, m
+ *  - Precision and width (inline or as int arg using *)
+ *
+ * Notable differences:
+ *  - Passing NULL to %s is permitted and will print "(null)"
+ *  - %p will also use "(null)"
+ *  - The provided EFI_STATUS is used for %m instead of errno
+ *  - "\n" is translated to "\r\n" */
+_printf_(2, 0) static char16_t *printf_internal(EFI_STATUS status, const char *format, va_list ap, bool ret) {
+        assert(format);
+
+        FormatContext ctx = {
+                .buf = ctx.stack_buf,
+                .n_buf = ELEMENTSOF(ctx.stack_buf),
+                .format = format,
+                .status = status,
+        };
+
+        /* We cannot put this into the struct without making a copy. */
+        va_copy(ctx.ap, ap);
+
+        while (*ctx.format != '\0') {
+                SpecifierContext sp = { .len = SIZE_MAX };
+
+                switch (*ctx.format) {
+                case '%':
+                        ctx.format++;
+                        while (!handle_format_specifier(&ctx, &sp))
+                                ctx.format++;
+                        ctx.format++;
+                        break;
+                case '\n':
+                        ctx.format++;
+                        sp.str = "\r\n";
+                        sp.len = 2;
+                        push_str(&ctx, &sp);
+                        break;
+                default:
+                        sp.str = ctx.format++;
+                        while (!IN_SET(*ctx.format, '%', '\n', '\0'))
+                                ctx.format++;
+                        sp.len = ctx.format - sp.str;
+                        push_str(&ctx, &sp);
+                }
+        }
+
+        va_end(ctx.ap);
+
+        assert(ctx.n < ctx.n_buf);
+        ctx.buf[ctx.n++] = '\0';
+
+        if (ret) {
+                if (ctx.dyn_buf)
+                        return TAKE_PTR(ctx.dyn_buf);
+
+                char16_t *ret_buf = xnew(char16_t, ctx.n);
+                memcpy(ret_buf, ctx.buf, ctx.n * sizeof(*ctx.buf));
+                return ret_buf;
+        }
+
+#if SD_BOOT
+        ST->ConOut->OutputString(ST->ConOut, ctx.buf);
+#endif
+
+        return mfree(ctx.dyn_buf);
+}
+
+void printf_status(EFI_STATUS status, const char *format, ...) {
+        va_list ap;
+        va_start(ap, format);
+        printf_internal(status, format, ap, false);
+        va_end(ap);
+}
+
+void vprintf_status(EFI_STATUS status, const char *format, va_list ap) {
+        printf_internal(status, format, ap, false);
+}
+
+char16_t *xasprintf_status(EFI_STATUS status, const char *format, ...) {
+        va_list ap;
+        va_start(ap, format);
+        char16_t *ret = printf_internal(status, format, ap, true);
+        va_end(ap);
+        return ret;
+}
+
+char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) {
+        return printf_internal(status, format, ap, true);
+}
+
 #if SD_BOOT
 /* To provide the actual implementation for these we need to remove the redirection to the builtins. */
 #  undef memcmp
index aaa9b399c887d04d16d8cec0a723aece3883e16d..2a28db3593c8ae11af3c05f498eb03158fdc35ac 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <stdarg.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <uchar.h>
@@ -109,7 +110,32 @@ bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack);
 bool parse_number8(const char *s, uint64_t *ret_u, const char **ret_tail);
 bool parse_number16(const char16_t *s, uint64_t *ret_u, const char16_t **ret_tail);
 
+typedef size_t EFI_STATUS;
+
+#if !SD_BOOT
+/* Provide these for unit testing. */
+enum {
+        EFI_ERROR_MASK = ((EFI_STATUS) 1 << (sizeof(EFI_STATUS) * CHAR_BIT - 1)),
+        EFI_SUCCESS = 0,
+        EFI_LOAD_ERROR = 1 | EFI_ERROR_MASK,
+};
+#endif
+
+#ifdef __clang__
+#  define _gnu_printf_(a, b) _printf_(a, b)
+#else
+#  define _gnu_printf_(a, b) __attribute__((format(gnu_printf, a, b)))
+#endif
+
+_gnu_printf_(2, 3) void printf_status(EFI_STATUS status, const char *format, ...);
+_gnu_printf_(2, 0) void vprintf_status(EFI_STATUS status, const char *format, va_list ap);
+_gnu_printf_(2, 3) _warn_unused_result_ char16_t *xasprintf_status(EFI_STATUS status, const char *format, ...);
+_gnu_printf_(2, 0) _warn_unused_result_ char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap);
+
 #if SD_BOOT
+#  define printf(...) printf_status(EFI_SUCCESS, __VA_ARGS__)
+#  define xasprintf(...) xasprintf_status(EFI_SUCCESS, __VA_ARGS__)
+
 /* The compiler normally has knowledge about standard functions such as memcmp, but this is not the case when
  * compiling with -ffreestanding. By referring to builtins, the compiler can check arguments and do
  * optimizations again. Note that we still need to provide implementations as the compiler is free to not
diff --git a/src/boot/efi/fuzz-efi-printf.c b/src/boot/efi/fuzz-efi-printf.c
new file mode 100644 (file)
index 0000000..218a427
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "efi-string.h"
+#include "fuzz.h"
+#include "utf8.h"
+
+typedef struct {
+        EFI_STATUS status;
+        int16_t field_width;
+        int16_t precision;
+        const void *ptr;
+        char c;
+        unsigned char uchar;
+        signed char schar;
+        unsigned short ushort;
+        signed short sshort;
+        unsigned int uint;
+        signed int sint;
+        unsigned long ulong;
+        signed long slong;
+        unsigned long long ulonglong;
+        signed long long slonglong;
+        size_t size;
+        ssize_t ssize;
+        intmax_t intmax;
+        uintmax_t uintmax;
+        ptrdiff_t ptrdiff;
+        char str[];
+} Input;
+
+#define PRINTF_ONE(...)                                                        \
+        ({                                                                     \
+                _cleanup_free_ char16_t *_ret = xasprintf_status(__VA_ARGS__); \
+                DO_NOT_OPTIMIZE(_ret);                                         \
+        })
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+        if (outside_size_range(size, sizeof(Input), 1024 * 1024))
+                return 0;
+
+        const Input *i = (const Input *) data;
+        size_t len = size - offsetof(Input, str);
+
+        PRINTF_ONE(i->status, "%*.*s", i->field_width, (int) len, i->str);
+        PRINTF_ONE(i->status, "%*.*ls", i->field_width, (int) (len / sizeof(wchar_t)), (const wchar_t *) i->str);
+
+        PRINTF_ONE(i->status, "%% %*.*m", i->field_width, i->precision);
+        PRINTF_ONE(i->status, "%*p", i->field_width, i->ptr);
+        PRINTF_ONE(i->status, "%*c %12340c %56789c", i->field_width, i->c, i->c, i->c);
+
+        PRINTF_ONE(i->status, "%*.*hhu", i->field_width, i->precision, i->uchar);
+        PRINTF_ONE(i->status, "%*.*hhi", i->field_width, i->precision, i->schar);
+        PRINTF_ONE(i->status, "%*.*hu", i->field_width, i->precision, i->ushort);
+        PRINTF_ONE(i->status, "%*.*hi", i->field_width, i->precision, i->sshort);
+        PRINTF_ONE(i->status, "%*.*u", i->field_width, i->precision, i->uint);
+        PRINTF_ONE(i->status, "%*.*i", i->field_width, i->precision, i->sint);
+        PRINTF_ONE(i->status, "%*.*lu", i->field_width, i->precision, i->ulong);
+        PRINTF_ONE(i->status, "%*.*li", i->field_width, i->precision, i->slong);
+        PRINTF_ONE(i->status, "%*.*llu", i->field_width, i->precision, i->ulonglong);
+        PRINTF_ONE(i->status, "%*.*lli", i->field_width, i->precision, i->slonglong);
+
+        PRINTF_ONE(i->status, "%+*.*hhi", i->field_width, i->precision, i->schar);
+        PRINTF_ONE(i->status, "%-*.*hi", i->field_width, i->precision, i->sshort);
+        PRINTF_ONE(i->status, "% *.*i", i->field_width, i->precision, i->sint);
+        PRINTF_ONE(i->status, "%0*li", i->field_width, i->slong);
+        PRINTF_ONE(i->status, "%#*.*llx", i->field_width, i->precision, i->ulonglong);
+
+        PRINTF_ONE(i->status, "%-*.*zx", i->field_width, i->precision, i->size);
+        PRINTF_ONE(i->status, "% *.*zi", i->field_width, i->precision, i->ssize);
+        PRINTF_ONE(i->status, "%0*ji", i->field_width, i->intmax);
+        PRINTF_ONE(i->status, "%#0*jX", i->field_width, i->uintmax);
+        PRINTF_ONE(i->status, "%*.*ti", i->field_width, i->precision, i->ptrdiff);
+
+        return 0;
+}
index dc646bce1f2293da6e75b62222434b741e6dac9f..350d1bc4347ed21c023a9930037650b11e8b0f4f 100644 (file)
@@ -25,16 +25,17 @@ EFI_STATUS graphics_mode(bool on) {
                 return err == EFI_NOT_FOUND ? EFI_SUCCESS : err;
 
         /* check current mode */
-        err =ConsoleControl->GetMode(ConsoleControl, &current, &uga_exists, &stdin_locked);
+        err = ConsoleControl->GetMode(ConsoleControl, &current, &uga_exists, &stdin_locked);
         if (err != EFI_SUCCESS)
                 return err;
 
         /* do not touch the mode */
-        new  = on ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText;
+        new = on ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText;
         if (new == current)
                 return EFI_SUCCESS;
 
-        err =ConsoleControl->SetMode(ConsoleControl, new);
+        log_wait();
+        err = ConsoleControl->SetMode(ConsoleControl, new);
 
         /* some firmware enables the cursor when switching modes */
         ST->ConOut->EnableCursor(ST->ConOut, false);
index 48801f9dd8695632a6ef93a25df4f3162c0b57ac..727e5071015f1f78e8c35491898eff48deb4524d 100644 (file)
@@ -120,17 +120,17 @@ EFI_STATUS linux_exec(
                                 initrd_length);
 #endif
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, u"Bad kernel image: %r", err);
+                return log_error_status(err, "Bad kernel image: %m");
 
         _cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL;
         err = load_image(parent, linux_buffer, linux_length, &kernel_image);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, u"Error loading kernel image: %r", err);
+                return log_error_status(err, "Error loading kernel image: %m");
 
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
         err = BS->HandleProtocol(kernel_image, &LoadedImageProtocol, (void **) &loaded_image);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, u"Error getting kernel loaded image protocol: %r", err);
+                return log_error_status(err, "Error getting kernel loaded image protocol: %m");
 
         if (cmdline) {
                 loaded_image->LoadOptions = (void *) cmdline;
@@ -140,8 +140,9 @@ EFI_STATUS linux_exec(
         _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
         err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, u"Error registering initrd: %r", err);
+                return log_error_status(err, "Error registering initrd: %m");
 
+        log_wait();
         err = BS->StartImage(kernel_image, NULL, NULL);
 
         /* Try calling the kernel compat entry point if one exists. */
@@ -151,5 +152,5 @@ EFI_STATUS linux_exec(
                 err = compat_entry(kernel_image, ST);
         }
 
-        return log_error_status_stall(err, u"Error starting kernel image: %r", err);
+        return log_error_status(err, "Error starting kernel image: %m");
 }
index 6a5e4311079d116e2960e06824ce7adf2064dcd6..eaae988d975579fad441843abf376aad264a9425 100644 (file)
@@ -141,28 +141,27 @@ EFI_STATUS linux_exec_efi_handover(
 
         const BootParams *image_params = (const BootParams *) linux_buffer;
         if (image_params->hdr.header != SETUP_MAGIC || image_params->hdr.boot_flag != BOOT_FLAG_MAGIC)
-                return log_error_status_stall(EFI_UNSUPPORTED, u"Unsupported kernel image.");
+                return log_error_status(EFI_UNSUPPORTED, "Unsupported kernel image.");
         if (image_params->hdr.version < SETUP_VERSION_2_11)
-                return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel too old.");
+                return log_error_status(EFI_UNSUPPORTED, "Kernel too old.");
         if (!image_params->hdr.relocatable_kernel)
-                return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel is not relocatable.");
+                return log_error_status(EFI_UNSUPPORTED, "Kernel is not relocatable.");
 
         /* The xloadflags were added in version 2.12+ of the boot protocol but the handover support predates
          * that, so we cannot safety-check this for 2.11. */
         if (image_params->hdr.version >= SETUP_VERSION_2_12 &&
             !FLAGS_SET(image_params->hdr.xloadflags, XLF_EFI_HANDOVER))
-                return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel does not support EFI handover protocol.");
+                return log_error_status(EFI_UNSUPPORTED, "Kernel does not support EFI handover protocol.");
 
         bool can_4g = image_params->hdr.version >= SETUP_VERSION_2_12 &&
                         FLAGS_SET(image_params->hdr.xloadflags, XLF_CAN_BE_LOADED_ABOVE_4G);
 
         if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX)
-                return log_error_status_stall(
+                return log_error_status(
                                 EFI_UNSUPPORTED,
-                                u"Unified kernel image was loaded above 4G, but kernel lacks support.");
+                                "Unified kernel image was loaded above 4G, but kernel lacks support.");
         if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) + initrd_length > UINT32_MAX)
-                return log_error_status_stall(
-                                EFI_UNSUPPORTED, u"Initrd is above 4G, but kernel lacks support.");
+                return log_error_status(EFI_UNSUPPORTED, "Initrd is above 4G, but kernel lacks support.");
 
         _cleanup_pages_ Pages boot_params_page = xmalloc_pages(
                         can_4g ? AllocateAnyPages : AllocateMaxAddress,
@@ -210,6 +209,7 @@ EFI_STATUS linux_exec_efi_handover(
         boot_params->hdr.ramdisk_size = initrd_length;
         boot_params->ext_ramdisk_size = ((uint64_t) initrd_length) >> 32;
 
+        log_wait();
         linux_efi_handover(parent, (uintptr_t) linux_buffer, boot_params);
         return EFI_LOAD_ERROR;
 }
diff --git a/src/boot/efi/log.c b/src/boot/efi/log.c
new file mode 100644 (file)
index 0000000..b1a613e
--- /dev/null
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "log.h"
+
+static unsigned log_count = 0;
+
+void efi_assert(const char *expr, const char *file, unsigned line, const char *function) {
+        log_error("systemd-boot assertion '%s' failed at %s:%u@%s. Halting.", expr, file, line, function);
+        for (;;)
+                BS->Stall(60 * 1000 * 1000);
+}
+
+EFI_STATUS log_internal(EFI_STATUS status, const char *format, ...) {
+        assert(format);
+
+        int32_t attr = ST->ConOut->Mode->Attribute;
+
+        if (ST->ConOut->Mode->CursorColumn > 0)
+                ST->ConOut->OutputString(ST->ConOut, (char16_t *) u"\r\n");
+        ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
+
+        va_list ap;
+        va_start(ap, format);
+        vprintf_status(status, format, ap);
+        va_end(ap);
+
+        ST->ConOut->OutputString(ST->ConOut, (char16_t *) u"\r\n");
+        ST->ConOut->SetAttribute(ST->ConOut, attr);
+
+        log_count++;
+        return status;
+}
+
+void log_wait(void) {
+        if (log_count == 0)
+                return;
+
+        BS->Stall(MIN(4u, log_count) * 2500 * 1000);
+        log_count = 0;
+}
diff --git a/src/boot/efi/log.h b/src/boot/efi/log.h
new file mode 100644 (file)
index 0000000..9bdcfad
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "efi-string.h"
+
+void log_wait(void);
+_gnu_printf_(2, 3) EFI_STATUS log_internal(EFI_STATUS status, const char *format, ...);
+#define log_error_status(status, ...) log_internal(status, __VA_ARGS__)
+#define log_error(...) log_internal(EFI_INVALID_PARAMETER, __VA_ARGS__)
+#define log_oom() log_internal(EFI_OUT_OF_RESOURCES, "Out of memory.")
+#define log_trace() log_internal(EFI_SUCCESS, "%s:%i@%s", __FILE__, __LINE__, __func__)
index dc3bd064db407a3ad68474b8e2f42cd0227a28bf..141e8c6042ac632695fd870ccff166839eec3595 100644 (file)
@@ -205,11 +205,10 @@ EFI_STATUS tpm_log_load_options(const char16_t *load_options, bool *ret_measured
                         load_options,
                         &measured);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(
+                return log_error_status(
                                 err,
-                                L"Unable to add load options (i.e. kernel command) line measurement to PCR %u: %r",
-                                TPM_PCR_INDEX_KERNEL_PARAMETERS,
-                                err);
+                                "Unable to add load options (i.e. kernel command) line measurement to PCR %u: %m",
+                                TPM_PCR_INDEX_KERNEL_PARAMETERS);
 
         if (ret_measured)
                 *ret_measured = measured;
index fb8ec26564fafcf9fa4a9a1ba133ca75d3077deb..eaa50e0e69798c4f01dc0097d22b15a6e1b371f3 100644 (file)
@@ -198,15 +198,6 @@ efi_cflags = [
         ]
 )
 
-# Our code size has increased enough to possibly create overlapping PE sections
-# at sd-stub runtime, which will often enough prevent the image from booting.
-# This only happens because the usual instructions for assembling a unified
-# kernel image contain hardcoded addresses for section VMAs added in. Until a
-# proper solution is in place, we can at least compile with as least -O1 to
-# reduce the likelihood of this happening.
-# https://github.com/systemd/systemd/issues/24202
-efi_cflags += '-O1'
-
 efi_cflags += cc.get_supported_arguments({
         'ia32':   ['-mno-sse', '-mno-mmx'],
         'x86_64': ['-mno-red-zone', '-mno-sse', '-mno-mmx'],
@@ -357,6 +348,7 @@ efi_headers = files(
         'graphics.h',
         'initrd.h',
         'linux.h',
+        'log.h',
         'measure.h',
         'missing_efi.h',
         'part-discovery.h',
@@ -370,13 +362,14 @@ efi_headers = files(
 )
 
 common_sources = files(
-        'assert.c',
         'console.c',
         'devicetree.c',
+        'drivers.c',
         'disk.c',
         'efi-string.c',
         'graphics.c',
         'initrd.c',
+        'log.c',
         'measure.c',
         'part-discovery.c',
         'pe.c',
@@ -384,13 +377,12 @@ common_sources = files(
         'secure-boot.c',
         'ticks.c',
         'util.c',
+        'vmm.c',
 )
 
 systemd_boot_sources = files(
         'boot.c',
-        'drivers.c',
         'shim.c',
-        'vmm.c',
 )
 
 stub_sources = files(
@@ -421,6 +413,7 @@ if efi_arch[1] in ['ia32', 'x86_64', 'arm', 'aarch64']
         fuzzers += [
                 [files('fuzz-bcd.c', 'bcd.c', 'efi-string.c')],
                 [files('fuzz-efi-string.c', 'efi-string.c')],
+                [files('fuzz-efi-printf.c', 'efi-string.c')],
         ]
 endif
 
@@ -474,6 +467,7 @@ foreach tuple : [['systemd-boot@0@.@1@', systemd_boot_objects, false, 'systemd-b
                            '-j', '.sdata',
                            '-j', '.sdmagic',
                            '-j', '.text',
+                           '--strip-all',
                            '--section-alignment=512',
                            efi_format,
                            '@INPUT@', '@OUTPUT@'],
index 250c84c2486551b5115ca111ad66be0bb31cf77e..b3310ea67e83229a4ce2576ba10c79073c8e3e21 100644 (file)
@@ -398,3 +398,15 @@ typedef struct {
         void *StdErr;
 } EFI_SHELL_PARAMETERS_PROTOCOL;
 #endif
+
+#ifndef EFI_WARN_UNKNOWN_GLYPH
+#  define EFI_WARN_UNKNOWN_GLYPH 1
+#endif
+
+#ifndef EFI_WARN_RESET_REQUIRED
+#  define EFI_WARN_STALE_DATA 5
+#  define EFI_WARN_FILE_SYSTEM 6
+#  define EFI_WARN_RESET_REQUIRED 7
+#  define EFI_IP_ADDRESS_CONFLICT EFIERR(34)
+#  define EFI_HTTP_ERROR EFIERR(35)
+#endif
index 3d5da14d1070e296dedb1054d73c3541f5984d18..7ba63b1b5a3116ae7a3b4b3038c634d1b3d612e6 100644 (file)
@@ -158,7 +158,7 @@ static void locate_sections(
 
                 if (in_memory) {
                         if (prev_section_addr > sect->VirtualAddress)
-                                log_error_stall(u"Overlapping PE sections detected. Boot may fail due to image memory corruption!");
+                                log_error("Overlapping PE sections detected. Boot may fail due to image memory corruption!");
                         prev_section_addr = sect->VirtualAddress + sect->VirtualSize;
                 }
 
index 332f537d91c74a44be168fc2e7fb30a107a7c058..f445ac006757a617c6a1132da6ac17fc32f0ecdb 100644 (file)
@@ -48,7 +48,7 @@ static EFI_STATUS acquire_rng(void *ret, UINTN size) {
 
         err = rng->GetRNG(rng, NULL, size, ret);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to acquire RNG data: %r", err);
+                return log_error_status(err, "Failed to acquire RNG data: %m");
         return EFI_SUCCESS;
 }
 
@@ -63,12 +63,12 @@ static EFI_STATUS acquire_system_token(void **ret, UINTN *ret_size) {
         err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size);
         if (err != EFI_SUCCESS) {
                 if (err != EFI_NOT_FOUND)
-                        log_error_stall(L"Failed to read LoaderSystemToken EFI variable: %r", err);
+                        log_error_status(err, "Failed to read LoaderSystemToken EFI variable: %m");
                 return err;
         }
 
         if (size <= 0)
-                return log_error_status_stall(EFI_NOT_FOUND, L"System token too short, ignoring.");
+                return log_error_status(EFI_NOT_FOUND, "System token too short, ignoring.");
 
         *ret = TAKE_PTR(data);
         *ret_size = size;
@@ -201,29 +201,29 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
                         0);
         if (err != EFI_SUCCESS) {
                 if (err != EFI_NOT_FOUND && err != EFI_WRITE_PROTECTED)
-                        log_error_stall(L"Failed to open random seed file: %r", err);
+                        log_error_status(err, "Failed to open random seed file: %m");
                 return err;
         }
 
         err = get_file_info_harder(handle, &info, NULL);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to get file info for random seed: %r", err);
+                return log_error_status(err, "Failed to get file info for random seed: %m");
 
         size = info->FileSize;
         if (size < RANDOM_MAX_SIZE_MIN)
-                return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too short.");
+                return log_error("Random seed file is too short.");
 
         if (size > RANDOM_MAX_SIZE_MAX)
-                return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
+                return log_error("Random seed file is too large.");
 
         seed = xmalloc(size);
         rsize = size;
         err = handle->Read(handle, &rsize, seed);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to read random seed file: %r", err);
+                return log_error_status(err, "Failed to read random seed file: %m");
         if (rsize != size) {
                 explicit_bzero_safe(seed, rsize);
-                return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short read on random seed file.");
+                return log_error_status(EFI_PROTOCOL_ERROR, "Short read on random seed file.");
         }
 
         sha256_process_bytes(&size, sizeof(size), &hash);
@@ -232,14 +232,14 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
 
         err = handle->SetPosition(handle, 0);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
+                return log_error_status(err, "Failed to seek to beginning of random seed file: %m");
 
         /* Let's also include the UEFI monotonic counter (which is supposedly increasing on every single
          * boot) in the hash, so that even if the changes to the ESP for some reason should not be
          * persistent, the random seed we generate will still be different on every single boot. */
         err = BS->GetNextMonotonicCount(&uefi_monotonic_counter);
         if (err != EFI_SUCCESS && !seeded_by_efi)
-                return log_error_status_stall(err, L"Failed to acquire UEFI monotonic counter: %r", err);
+                return log_error_status(err, "Failed to acquire UEFI monotonic counter: %m");
         size = sizeof(uefi_monotonic_counter);
         sha256_process_bytes(&size, sizeof(size), &hash);
         sha256_process_bytes(&uefi_monotonic_counter, size, &hash);
@@ -264,26 +264,26 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
         if (size < info->FileSize) {
                 err = handle->SetPosition(handle, size);
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Failed to seek to offset of random seed file: %r", err);
+                        return log_error_status(err, "Failed to seek to offset of random seed file: %m");
                 wsize = info->FileSize - size;
                 err = handle->Write(handle, &wsize, seed /* All zeros now */);
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
+                        return log_error_status(err, "Failed to write random seed file: %m");
                 if (wsize != info->FileSize - size)
-                        return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
+                        return log_error_status(EFI_PROTOCOL_ERROR, "Short write on random seed file.");
                 err = handle->Flush(handle);
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
+                        return log_error_status(err, "Failed to flush random seed file: %m");
                 err = handle->SetPosition(handle, 0);
                 if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
+                        return log_error_status(err, "Failed to seek to beginning of random seed file: %m");
 
                 /* We could truncate the file here with something like:
                  *
                  *     info->FileSize = size;
                  *     err = handle->SetInfo(handle, &GenericFileInfo, info->Size, info);
                  *     if (err != EFI_SUCCESS)
-                 *             return log_error_status_stall(err, L"Failed to truncate random seed file: %r", err);
+                 *             return log_error_status(err, "Failed to truncate random seed file: %u");
                  *
                  * But this is considered slightly risky, because EFI filesystem drivers are a little bit
                  * flimsy. So instead we rely on userspace eventually truncating this when it writes a new
@@ -293,18 +293,18 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
         wsize = size;
         err = handle->Write(handle, &wsize, random_bytes);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
+                return log_error_status(err, "Failed to write random seed file: %m");
         if (wsize != size)
-                return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
+                return log_error_status(EFI_PROTOCOL_ERROR, "Short write on random seed file.");
         err = handle->Flush(handle);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
+                return log_error_status(err, "Failed to flush random seed file: %m");
 
         err = BS->AllocatePool(EfiACPIReclaimMemory,
                                offsetof(struct linux_efi_random_seed, seed) + DESIRED_SEED_SIZE,
                                (void **) &new_seed_table);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to allocate EFI table for random seed: %r", err);
+                return log_error_status(err, "Failed to allocate EFI table for random seed: %m");
         new_seed_table->size = DESIRED_SEED_SIZE;
 
         /* hash = hash_key || 1 */
@@ -316,7 +316,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
 
         err = BS->InstallConfigurationTable(&(EFI_GUID)LINUX_EFI_RANDOM_SEED_TABLE_GUID, new_seed_table);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed to install EFI table for random seed: %r", err);
+                return log_error_status(err, "Failed to install EFI table for random seed: %m");
         TAKE_PTR(new_seed_table);
 
         if (previous_seed_table) {
index 62128681341a1cec8714a236017e1f2f2c57b73b..2594c8798f4d3aeb17458e89850d169fc5b008bd 100644 (file)
@@ -1,9 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "console.h"
 #include "sbat.h"
 #include "secure-boot.h"
-#include "console.h"
 #include "util.h"
+#include "vmm.h"
 
 bool secure_boot_enabled(void) {
         bool secure = false;  /* avoid false maybe-uninitialized warning */
@@ -43,41 +44,42 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
 
         clear_screen(COLOR_NORMAL);
 
-        Print(L"Enrolling secure boot keys from directory: %s\n"
-              L"Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n",
-              path);
-
-        unsigned timeout_sec = 15;
-        for(;;) {
-                /* Enrolling secure boot keys is safe to do in virtualized environments as there is nothing
-                 * we can brick there. */
-                if (in_hypervisor())
-                        break;
-
-                PrintAt(0, ST->ConOut->Mode->CursorRow, L"Enrolling in %2u s, press any key to abort.", timeout_sec);
-
-                uint64_t key;
-                err = console_key_read(&key, 1000 * 1000);
-                if (err == EFI_NOT_READY)
-                        continue;
-                if (err == EFI_TIMEOUT) {
-                        if (timeout_sec == 0) /* continue enrolling keys */
-                                break;
-                        timeout_sec--;
-                        continue;
+        printf("Enrolling secure boot keys from directory: %ls\n", path);
+
+        /* Enrolling secure boot keys is safe to do in virtualized environments as there is nothing
+         * we can brick there. */
+        if (!in_hypervisor()) {
+                printf("Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n");
+
+                unsigned timeout_sec = 15;
+                for (;;) {
+                        printf("\rEnrolling in %2u s, press any key to abort.", timeout_sec);
+
+                        uint64_t key;
+                        err = console_key_read(&key, 1000 * 1000);
+                        if (err == EFI_NOT_READY)
+                                continue;
+                        if (err == EFI_TIMEOUT) {
+                                if (timeout_sec == 0) /* continue enrolling keys */
+                                        break;
+                                timeout_sec--;
+                                continue;
+                        }
+                        if (err != EFI_SUCCESS)
+                                return log_error_status(
+                                                err,
+                                                "Error waiting for user input to enroll Secure Boot keys: %m");
+
+                        /* user aborted, returning EFI_SUCCESS here allows the user to go back to the menu */
+                        return EFI_SUCCESS;
                 }
-                if (err != EFI_SUCCESS)
-                        return log_error_status_stall(err, L"Error waiting for user input to enroll Secure Boot keys: %r", err);
-
-                /* user aborted, returning EFI_SUCCESS here allows the user to go back to the menu */
-                return EFI_SUCCESS;
         }
 
         _cleanup_(file_closep) EFI_FILE *dir = NULL;
 
         err = open_directory(root_dir, path, &dir);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Failed opening keys directory %s: %r", path, err);
+                return log_error_status(err, "Failed opening keys directory %ls: %m", path);
 
         struct {
                 const char16_t *name;
@@ -95,7 +97,7 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
         for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++) {
                 err = file_read(dir, sb_vars[i].filename, 0, 0, &sb_vars[i].buffer, &sb_vars[i].size);
                 if (err != EFI_SUCCESS) {
-                        log_error_stall(L"Failed reading file %s\\%s: %r", path, sb_vars[i].filename, err);
+                        log_error_status(err, "Failed reading file %ls\\%ls: %m", path, sb_vars[i].filename);
                         goto out_deallocate;
                 }
         }
@@ -109,7 +111,7 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
 
                 err = efivar_set_raw(&sb_vars[i].vendor, sb_vars[i].name, sb_vars[i].buffer, sb_vars[i].size, sb_vars_opts);
                 if (err != EFI_SUCCESS) {
-                        log_error_stall(L"Failed to write %s secure boot variable: %r", sb_vars[i].name, err);
+                        log_error_status(err, "Failed to write %ls secure boot variable: %m", sb_vars[i].name);
                         goto out_deallocate;
                 }
         }
index aa83b341666828b670d732219d95f1672697fc05..be186635ba0efa52f94cfb254eb59c1110b43dea 100644 (file)
@@ -114,14 +114,14 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
         /* if LoaderFirmwareInfo is not set, let's set it */
         if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
                 _cleanup_free_ char16_t *s = NULL;
-                s = xpool_print(L"%s %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+                s = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
                 efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", s, 0);
         }
 
         /* ditto for LoaderFirmwareType */
         if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
                 _cleanup_free_ char16_t *s = NULL;
-                s = xpool_print(L"UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+                s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
                 efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
         }
 
@@ -173,14 +173,14 @@ static bool use_load_options(
         *ret = xstrdup16(shell->Argv[1]);
         for (size_t i = 2; i < shell->Argc; i++) {
                 _cleanup_free_ char16_t *old = *ret;
-                *ret = xpool_print(u"%s %s", old, shell->Argv[i]);
+                *ret = xasprintf("%ls %ls", old, shell->Argv[i]);
         }
 
         mangle_stub_cmdline(*ret);
         return true;
 }
 
-EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+static EFI_STATUS real_main(EFI_HANDLE image) {
         _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
         size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
         size_t linux_size, initrd_size, dt_size;
@@ -194,11 +194,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         uint64_t loader_features = 0;
         EFI_STATUS err;
 
-        InitializeLib(image, sys_table);
-        debug_hook(L"systemd-stub");
-        /* Uncomment the next line if you need to wait for debugger. */
-        // debug_break();
-
         err = BS->OpenProtocol(
                         image,
                         &LoadedImageProtocol,
@@ -207,7 +202,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                         NULL,
                         EFI_OPEN_PROTOCOL_GET_PROTOCOL);
         if (err != EFI_SUCCESS)
-                return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
+                return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m");
 
         if (efivar_get_uint64_le(LOADER_GUID, L"LoaderFeatures", &loader_features) != EFI_SUCCESS ||
             !FLAGS_SET(loader_features, EFI_LOADER_FEATURE_RANDOM_SEED)) {
@@ -222,7 +217,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) {
                 if (err == EFI_SUCCESS)
                         err = EFI_NOT_FOUND;
-                return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
+                return log_error_status(err, "Unable to locate embedded .linux section: %m");
         }
 
         /* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written
@@ -412,7 +407,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 err = devicetree_install_from_memory(
                                 &dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size);
                 if (err != EFI_SUCCESS)
-                        log_error_stall(L"Error loading embedded devicetree: %r", err);
+                        log_error_status(err, "Error loading embedded devicetree: %m");
         }
 
         err = linux_exec(image, cmdline,
@@ -421,3 +416,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         graphics_mode(false);
         return err;
 }
+
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+        InitializeLib(image, sys_table);
+
+        debug_hook("systemd-stub");
+        /* Uncomment the next line if you need to wait for debugger. */
+        // debug_break();
+
+        EFI_STATUS err = real_main(image);
+        log_wait();
+        return err;
+}
index 7b43e1d629ea89c175691d93eb6149ffc851445d..c26973d8bd16e1a996e5f139534a1469b87c591c 100644 (file)
@@ -468,6 +468,148 @@ TEST(parse_number16) {
         assert_se(streq16(tail, u"rest"));
 }
 
+_printf_(1, 2) static void test_printf_one(const char *format, ...) {
+        va_list ap, ap_efi;
+        va_start(ap, format);
+        va_copy(ap_efi, ap);
+
+        _cleanup_free_ char *buf = NULL;
+        int r = vasprintf(&buf, format, ap);
+        assert_se(r >= 0);
+        log_info("/* %s(%s) -> \"%.100s\" */", __func__, format, buf);
+
+        _cleanup_free_ char16_t *buf_efi = xvasprintf_status(0, format, ap_efi);
+
+        bool eq = true;
+        for (size_t i = 0; i <= (size_t) r; i++) {
+                if (buf[i] != buf_efi[i])
+                        eq = false;
+                buf[i] = buf_efi[i];
+        }
+
+        log_info("%.100s", buf);
+        assert_se(eq);
+
+        va_end(ap);
+        va_end(ap_efi);
+}
+
+TEST(xvasprintf_status) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-zero-length"
+        test_printf_one("");
+#pragma GCC diagnostic pop
+        test_printf_one("string");
+        test_printf_one("%%-%%%%");
+
+        test_printf_one("%p %p %32p %*p %*p", NULL, (void *) 0xF, &errno, 0, &saved_argc, 20, &saved_argv);
+        test_printf_one("%-10p %-32p %-*p %-*p", NULL, &errno, 0, &saved_argc, 20, &saved_argv);
+
+        test_printf_one("%c %3c %*c %*c %-8c", '1', '!', 0, 'a', 9, '_', '>');
+
+        test_printf_one("%s %s %s", "012345", "6789", "ab");
+        test_printf_one("%.4s %.4s %.4s %.0s", "cdefgh", "ijkl", "mn", "@");
+        test_printf_one("%8s %8s %8s", "opqrst", "uvwx", "yz");
+        test_printf_one("%8.4s %8.4s %8.4s %8.0s", "ABCDEF", "GHIJ", "KL", "$");
+        test_printf_one("%4.8s %4.8s %4.8s", "ABCDEFGHIJ", "ABCDEFGH", "ABCD");
+
+        test_printf_one("%.*s %.*s %.*s %.*s", 4, "012345", 4, "6789", 4, "ab", 0, "&");
+        test_printf_one("%*s %*s %*s", 8, "cdefgh", 8, "ijkl", 8, "mn");
+        test_printf_one("%*.*s %*.*s %*.*s %*.*s", 8, 4, "opqrst", 8, 4, "uvwx", 8, 4, "yz", 8, 0, "#");
+        test_printf_one("%*.*s %*.*s %*.*s", 3, 8, "OPQRST", 3, 8, "UVWX", 3, 8, "YZ");
+
+        test_printf_one("%ls %ls %ls", L"012345", L"6789", L"ab");
+        test_printf_one("%.4ls %.4ls %.4ls %.0ls", L"cdefgh", L"ijkl", L"mn", L"@");
+        test_printf_one("%8ls %8ls %8ls", L"opqrst", L"uvwx", L"yz");
+        test_printf_one("%8.4ls %8.4ls %8.4ls %8.0ls", L"ABCDEF", L"GHIJ", L"KL", L"$");
+        test_printf_one("%4.8ls %4.8ls %4.8ls", L"ABCDEFGHIJ", L"ABCDEFGH", L"ABCD");
+
+        test_printf_one("%.*ls %.*ls %.*ls %.*ls", 4, L"012345", 4, L"6789", 4, L"ab", 0, L"&");
+        test_printf_one("%*ls %*ls %*ls", 8, L"cdefgh", 8, L"ijkl", 8, L"mn");
+        test_printf_one("%*.*ls %*.*ls %*.*ls %*.*ls", 8, 4, L"opqrst", 8, 4, L"uvwx", 8, 4, L"yz", 8, 0, L"#");
+        test_printf_one("%*.*ls %*.*ls %*.*ls", 3, 8, L"OPQRST", 3, 8, L"UVWX", 3, 8, L"YZ");
+
+        test_printf_one("%-14s %-10.0s %-10.3s", "left", "", "chopped");
+        test_printf_one("%-14ls %-10.0ls %-10.3ls", L"left", L"", L"chopped");
+
+        test_printf_one("%.6s", (char[]){ 'n', 'o', ' ', 'n', 'u', 'l' });
+        test_printf_one("%.6ls", (wchar_t[]){ 'n', 'o', ' ', 'n', 'u', 'l' });
+
+        test_printf_one("%u %u %u", 0U, 42U, 1234567890U);
+        test_printf_one("%i %i %i", 0, -42, -1234567890);
+        test_printf_one("%x %x %x", 0x0U, 0x42U, 0x123ABCU);
+        test_printf_one("%X %X %X", 0x1U, 0x43U, 0x234BCDU);
+        test_printf_one("%#x %#x %#x", 0x2U, 0x44U, 0x345CDEU);
+        test_printf_one("%#X %#X %#X", 0x3U, 0x45U, 0x456FEDU);
+
+        test_printf_one("%u %lu %llu %zu", UINT_MAX, ULONG_MAX, ULLONG_MAX, SIZE_MAX);
+        test_printf_one("%i %i %zi", INT_MIN, INT_MAX, SSIZE_MAX);
+        test_printf_one("%li %li %lli %lli", LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX);
+        test_printf_one("%x %#lx %llx %#zx", UINT_MAX, ULONG_MAX, ULLONG_MAX, SIZE_MAX);
+        test_printf_one("%X %#lX %llX %#zX", UINT_MAX, ULONG_MAX, ULLONG_MAX, SIZE_MAX);
+        test_printf_one("%ju %ji %ji", UINTMAX_MAX, INTMAX_MIN, INTMAX_MAX);
+        test_printf_one("%ti %ti", PTRDIFF_MIN, PTRDIFF_MAX);
+
+        test_printf_one("%" PRIu32 " %" PRIi32 " %" PRIi32, UINT32_MAX, INT32_MIN, INT32_MAX);
+        test_printf_one("%" PRIx32 " %" PRIX32, UINT32_MAX, UINT32_MAX);
+        test_printf_one("%#" PRIx32 " %#" PRIX32, UINT32_MAX, UINT32_MAX);
+
+        test_printf_one("%" PRIu64 " %" PRIi64 " %" PRIi64, UINT64_MAX, INT64_MIN, INT64_MAX);
+        test_printf_one("%" PRIx64 " %" PRIX64, UINT64_MAX, UINT64_MAX);
+        test_printf_one("%#" PRIx64 " %#" PRIX64, UINT64_MAX, UINT64_MAX);
+
+        test_printf_one("%.11u %.11i %.11x %.11X %#.11x %#.11X", 1U, -2, 3U, 0xA1U, 0xB2U, 0xC3U);
+        test_printf_one("%13u %13i %13x %13X %#13x %#13X", 4U, -5, 6U, 0xD4U, 0xE5U, 0xF6U);
+        test_printf_one("%9.5u %9.5i %9.5x %9.5X %#9.5x %#9.5X", 7U, -8, 9U, 0xA9U, 0xB8U, 0xC7U);
+        test_printf_one("%09u %09i %09x %09X %#09x %#09X", 4U, -5, 6U, 0xD6U, 0xE5U, 0xF4U);
+
+        test_printf_one("%*u %.*u %*i %.*i", 15, 42U, 15, 43U, 15, -42, 15, -43);
+        test_printf_one("%*.*u %*.*i", 14, 10, 13U, 14, 10, -14);
+        test_printf_one("%*x %*X %.*x %.*X", 15, 0x1AU, 15, 0x2BU, 15, 0x3CU, 15, 0x4DU);
+        test_printf_one("%#*x %#*X %#.*x %#.*X", 15, 0xA1U, 15, 0xB2U, 15, 0xC3U, 15, 0xD4U);
+        test_printf_one("%*.*x %*.*X", 14, 10, 0x1AU, 14, 10, 0x2BU);
+        test_printf_one("%#*.*x %#*.*X", 14, 10, 0x3CU, 14, 10, 0x4DU);
+
+        test_printf_one("%+.5i %+.5i % .7i % .7i", -15, 51, -15, 51);
+        test_printf_one("%+5.i %+5.i % 7.i % 7.i", -15, 51, -15, 51);
+
+        test_printf_one("%-10u %-10i %-10x %#-10X %- 10i", 1u, -2, 0xA2D2u, 0XB3F4u, -512);
+        test_printf_one("%-10.6u %-10.6i %-10.6x %#-10.6X %- 10.6i", 1u, -2, 0xA2D2u, 0XB3F4u, -512);
+        test_printf_one("%-6.10u %-6.10i %-6.10x %#-6.10X %- 6.10i", 3u, -4, 0x2A2Du, 0X3B4Fu, -215);
+        test_printf_one("%*.u %.*i %.*i", -4, 9u, -4, 8, -4, -6);
+
+        test_printf_one("%.0u %.0i %.0x %.0X", 0u, 0, 0u, 0u);
+        test_printf_one("%.*u %.*i %.*x %.*X", 0, 0u, 0, 0, 0, 0u, 0, 0u);
+        test_printf_one("%*u %*i %*x %*X", -1, 0u, -1, 0, -1, 0u, -1, 0u);
+
+        test_printf_one("%*s%*s%*s", 256, "", 256, "", 4096, ""); /* Test buf growing. */
+        test_printf_one("%0*i%0*i%0*i", 256, 0, 256, 0, 4096, 0); /* Test buf growing. */
+        test_printf_one("%0*i", INT16_MAX, 0); /* Poor programmer's memzero. */
+
+        /* Non printf-compatible behavior tests below. */
+        char16_t *s;
+
+        assert_se(s = xasprintf_status(0, "\n \r \r\n"));
+        assert_se(streq16(s, u"\r\n \r \r\r\n"));
+        s = mfree(s);
+
+        assert_se(s = xasprintf_status(EFI_SUCCESS, "%m"));
+        assert_se(streq16(s, u"Success"));
+        s = mfree(s);
+
+        assert_se(s = xasprintf_status(EFI_SUCCESS, "%15m"));
+        assert_se(streq16(s, u"        Success"));
+        s = mfree(s);
+
+        assert_se(s = xasprintf_status(EFI_LOAD_ERROR, "%m"));
+        assert_se(streq16(s, u"Load error"));
+        s = mfree(s);
+
+        assert_se(s = xasprintf_status(0x42, "%m"));
+        assert_se(streq16(s, u"0x42"));
+        s = mfree(s);
+}
+
 TEST(efi_memcmp) {
         assert_se(efi_memcmp(NULL, NULL, 0) == 0);
         assert_se(efi_memcmp(NULL, NULL, 1) == 0);
index 1b74ba15d07915390ce38ee9ba7eb4c7b9fd4571..2f6ff878ca9a6e8d868aabed569b5dc22c785395 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "ticks.h"
 #include "util.h"
+#include "vmm.h"
 
 #ifdef __x86_64__
 static uint64_t ticks_read(void) {
index b1c19a5c9bba5f0c0e8d2f663a43c87fa26cd1fa..86bc9cb543b8941dac0d3e466e48f5bd473cc672 100644 (file)
@@ -2,9 +2,6 @@
 
 #include <efi.h>
 #include <efilib.h>
-#if defined(__i386__) || defined(__x86_64__)
-#  include <cpuid.h>
-#endif
 
 #include "ticks.h"
 #include "util.h"
@@ -342,34 +339,6 @@ EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, UINTN off, UINTN size,
         return err;
 }
 
-void log_error_stall(const char16_t *fmt, ...) {
-        va_list args;
-
-        assert(fmt);
-
-        int32_t attr = ST->ConOut->Mode->Attribute;
-        ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
-
-        if (ST->ConOut->Mode->CursorColumn > 0)
-                Print(L"\n");
-
-        va_start(args, fmt);
-        VPrint(fmt, args);
-        va_end(args);
-
-        Print(L"\n");
-
-        ST->ConOut->SetAttribute(ST->ConOut, attr);
-
-        /* Give the user a chance to see the message. */
-        BS->Stall(3 * 1000 * 1000);
-}
-
-EFI_STATUS log_oom(void) {
-        log_error_stall(L"Out of memory.");
-        return EFI_OUT_OF_RESOURCES;
-}
-
 void print_at(UINTN x, UINTN y, UINTN attr, const char16_t *str) {
         assert(str);
         ST->ConOut->SetCursorPosition(ST->ConOut, x, y);
@@ -378,6 +347,7 @@ void print_at(UINTN x, UINTN y, UINTN attr, const char16_t *str) {
 }
 
 void clear_screen(UINTN attr) {
+        log_wait();
         ST->ConOut->SetAttribute(ST->ConOut, attr);
         ST->ConOut->ClearScreen(ST->ConOut);
 }
@@ -589,7 +559,7 @@ void hexdump(const char16_t *prefix, const void *data, UINTN size) {
 
         buf[size*2] = 0;
 
-        log_error_stall(L"%s[%" PRIuN "]: %s", prefix, size, buf);
+        log_error("%ls[%zu]: %ls", prefix, size, buf);
 }
 #endif
 
@@ -748,20 +718,6 @@ EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret) {
         return EFI_SUCCESS;
 }
 
-#if defined(__i386__) || defined(__x86_64__)
-bool in_hypervisor(void) {
-        uint32_t eax, ebx, ecx, edx;
-
-        /* This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
-         * environment. */
-
-        if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
-                return false;
-
-        return !!(ecx & 0x80000000U);
-}
-#endif
-
 void *find_configuration_table(const EFI_GUID *guid) {
         for (UINTN i = 0; i < ST->NumberOfTableEntries; i++)
                 if (efi_guid_equal(&ST->ConfigurationTable[i].VendorGuid, guid))
index 836f223cc053a44179b9d9558c6375ef2d4d106a..bbc772d854561c89b330c5f1ca34a29b012a4a85 100644 (file)
@@ -5,25 +5,12 @@
 #include <efilib.h>
 #include <stddef.h>
 
+#include "log.h"
 #include "string-util-fundamental.h"
 
 #define UINTN_MAX (~(UINTN)0)
 #define INTN_MAX ((INTN)(UINTN_MAX>>1))
 
-/* gnu-efi format specifiers for integers are fixed to either 64bit with 'l' and 32bit without a size prefix.
- * We rely on %u/%d/%x to format regular ints, so ensure the size is what we expect. At the same time, we also
- * need specifiers for (U)INTN which are native (pointer) sized. */
-assert_cc(sizeof(int) == sizeof(uint32_t));
-#if __SIZEOF_POINTER__ == 4
-#  define PRIuN L"u"
-#  define PRIiN L"d"
-#elif __SIZEOF_POINTER__ == 8
-#  define PRIuN L"lu"
-#  define PRIiN L"ld"
-#else
-#  error "Unexpected pointer size"
-#endif
-
 static inline void free(void *p) {
         if (!p)
                 return;
@@ -67,7 +54,6 @@ static inline void *xrealloc(void *p, size_t old_size, size_t new_size) {
         return t;
 }
 
-#define xpool_print(fmt, ...) ((char16_t *) ASSERT_SE_PTR(PoolPrint((fmt), ##__VA_ARGS__)))
 #define xnew(type, n) ((type *) xmalloc_multiply(sizeof(type), (n)))
 
 typedef struct {
@@ -139,17 +125,6 @@ static inline void unload_imagep(EFI_HANDLE *image) {
         &(const EFI_GUID) { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } }
 #define EFI_GLOBAL_GUID &(const EFI_GUID) EFI_GLOBAL_VARIABLE
 
-void log_error_stall(const char16_t *fmt, ...);
-EFI_STATUS log_oom(void);
-
-/* This works just like log_error_errno() from userspace, but requires you
- * to provide err a second time if you want to use %r in the message! */
-#define log_error_status_stall(err, fmt, ...) \
-        ({ \
-                log_error_stall(fmt, ##__VA_ARGS__); \
-                err; \
-        })
-
 void print_at(UINTN x, UINTN y, UINTN attr, const char16_t *str);
 void clear_screen(UINTN attr);
 
@@ -191,7 +166,7 @@ void debug_break(void);
 extern uint8_t _text, _data;
 /* Report the relocated position of text and data sections so that a debugger
  * can attach to us. See debug-sd-boot.sh for how this can be done. */
-#  define debug_hook(identity) Print(identity L"@0x%lx,0x%lx\n", POINTER_TO_PHYSICAL_ADDRESS(&_text), POINTER_TO_PHYSICAL_ADDRESS(&_data))
+#  define debug_hook(identity) printf(identity "@%p,%p\n", &_text, &_data)
 #else
 #  define debug_hook(identity)
 #endif
@@ -210,14 +185,6 @@ EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file);
 EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp);
 EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret);
 
-#if defined(__i386__) || defined(__x86_64__)
-bool in_hypervisor(void);
-#else
-static inline bool in_hypervisor(void) {
-        return false;
-}
-#endif
-
 static inline bool efi_guid_equal(const EFI_GUID *a, const EFI_GUID *b) {
         return memcmp(a, b, sizeof(EFI_GUID)) == 0;
 }
index 10d4a75ab208667c663f75628d829506d0d43a5a..b24d556700ee55e402f894ab3fcb98716a67d24a 100644 (file)
@@ -3,6 +3,9 @@
 #include <efi.h>
 #include <efilib.h>
 #include <stdbool.h>
+#if defined(__i386__) || defined(__x86_64__)
+#  include <cpuid.h>
+#endif
 
 #include "drivers.h"
 #include "efi-string.h"
@@ -94,9 +97,8 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
 
         for (size_t order = 0;; order++) {
                 _cleanup_free_ EFI_DEVICE_PATH *dp = NULL;
-                char16_t order_str[STRLEN("VMMBootOrder") + 4 + 1];
 
-                SPrint(order_str, sizeof(order_str), u"VMMBootOrder%04x", order);
+                _cleanup_free_ char16_t *order_str = xasprintf("VMMBootOrder%04zx", order);
                 dp_err = efivar_get_raw(&(EFI_GUID)VMM_BOOT_ORDER_GUID, order_str, (char**)&dp, NULL);
 
                 for (size_t i = 0; i < n_handles; i++) {
@@ -132,3 +134,156 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
         }
         assert_not_reached();
 }
+
+static bool cpuid_in_hypervisor(void) {
+#if defined(__i386__) || defined(__x86_64__)
+        unsigned eax, ebx, ecx, edx;
+
+        /* This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
+         * environment. */
+
+        if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
+                return false;
+
+        if (FLAGS_SET(ecx, 0x80000000U))
+                return true;
+#endif
+
+        return false;
+}
+
+typedef struct {
+        uint8_t anchor_string[4];
+        uint8_t entry_point_structure_checksum;
+        uint8_t entry_point_length;
+        uint8_t major_version;
+        uint8_t minor_version;
+        uint16_t max_structure_size;
+        uint8_t entry_point_revision;
+        uint8_t formatted_area[5];
+        uint8_t intermediate_anchor_string[5];
+        uint8_t intermediate_checksum;
+        uint16_t table_length;
+        uint32_t table_address;
+        uint16_t number_of_smbios_structures;
+        uint8_t smbios_bcd_revision;
+} _packed_ SmbiosEntryPoint;
+
+typedef struct {
+        uint8_t anchor_string[5];
+        uint8_t entry_point_structure_checksum;
+        uint8_t entry_point_length;
+        uint8_t major_version;
+        uint8_t minor_version;
+        uint8_t docrev;
+        uint8_t entry_point_revision;
+        uint8_t reserved;
+        uint32_t table_maximum_size;
+        uint64_t table_address;
+} _packed_ Smbios3EntryPoint;
+
+typedef struct {
+        uint8_t type;
+        uint8_t length;
+        uint8_t handle[2];
+} _packed_ SmbiosHeader;
+
+typedef struct {
+        SmbiosHeader header;
+        uint8_t vendor;
+        uint8_t bios_version;
+        uint16_t bios_segment;
+        uint8_t bios_release_date;
+        uint8_t bios_size;
+        uint64_t bios_characteristics;
+        uint8_t bios_characteristics_ext[2];
+} _packed_ SmbiosTableType0;
+
+static void *find_smbios_configuration_table(uint64_t *ret_size) {
+        assert(ret_size);
+
+        Smbios3EntryPoint *entry3 = find_configuration_table(&(EFI_GUID) SMBIOS3_TABLE_GUID);
+        if (entry3 && memcmp(entry3->anchor_string, "_SM3_", 5) == 0 &&
+            entry3->entry_point_length <= sizeof(*entry3)) {
+                *ret_size = entry3->table_maximum_size;
+                return PHYSICAL_ADDRESS_TO_POINTER(entry3->table_address);
+        }
+
+        SmbiosEntryPoint *entry = find_configuration_table(&(EFI_GUID) SMBIOS_TABLE_GUID);
+        if (entry && memcmp(entry->anchor_string, "_SM_", 4) == 0 &&
+            entry->entry_point_length <= sizeof(*entry)) {
+                *ret_size = entry->table_length;
+                return PHYSICAL_ADDRESS_TO_POINTER(entry->table_address);
+        }
+
+        return NULL;
+}
+
+static SmbiosHeader *get_smbios_table(uint8_t type) {
+        uint64_t size = 0;
+        uint8_t *p = find_smbios_configuration_table(&size);
+        if (!p)
+                return false;
+
+        for (;;) {
+                if (size < sizeof(SmbiosHeader))
+                        return NULL;
+
+                SmbiosHeader *header = (SmbiosHeader *) p;
+
+                /* End of table. */
+                if (header->type == 127)
+                        return NULL;
+
+                if (size < header->length)
+                        return NULL;
+
+                if (header->type == type)
+                        return header; /* Yay! */
+
+                /* Skip over formatted area. */
+                size -= header->length;
+                p += header->length;
+
+                /* Skip over string table. */
+                for (;;) {
+                        while (size > 0 && *p != '\0') {
+                                p++;
+                                size--;
+                        }
+                        if (size == 0)
+                                return NULL;
+                        p++;
+                        size--;
+
+                        /* Double NUL terminates string table. */
+                        if (*p == '\0') {
+                                if (size == 0)
+                                        return NULL;
+                                p++;
+                                break;
+                        }
+                }
+        }
+
+        return NULL;
+}
+
+static bool smbios_in_hypervisor(void) {
+        /* Look up BIOS Information (Type 0). */
+        SmbiosTableType0 *type0 = (SmbiosTableType0 *) get_smbios_table(0);
+        if (!type0 || type0->header.length < sizeof(SmbiosTableType0))
+                return false;
+
+        /* Bit 4 of 2nd BIOS characteristics extension bytes indicates virtualization. */
+        return FLAGS_SET(type0->bios_characteristics_ext[1], 1 << 4);
+}
+
+bool in_hypervisor(void) {
+        static int cache = -1;
+        if (cache >= 0)
+                return cache;
+
+        cache = cpuid_in_hypervisor() || smbios_in_hypervisor();
+        return cache;
+}
index 7bac1a324a364c631819c8d76063a701d773e90f..e98ec74af1cc6e4ac169f26bd6f161db950162e2 100644 (file)
@@ -6,3 +6,5 @@
 
 bool is_direct_boot(EFI_HANDLE device);
 EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir);
+
+bool in_hypervisor(void);
index 14f79f23c0b612405968eccaacf7bd930808d602..003e0b8ad8d9dcffb9b6de66726ba421d32657bf 100644 (file)
@@ -2,14 +2,21 @@
 
 #include <getopt.h>
 
+#include <sd-device.h>
 #include <sd-messages.h>
 
+#include "blkid-util.h"
+#include "blockdev-util.h"
 #include "build.h"
+#include "chase-symlinks.h"
+#include "efi-loader.h"
 #include "efivars.h"
-#include "env-util.h"
+#include "escape.h"
+#include "fd-util.h"
 #include "main-func.h"
+#include "mountpoint-util.h"
 #include "openssl-util.h"
-#include "parse-util.h"
+#include "parse-argument.h"
 #include "pretty-print.h"
 #include "tpm-pcr.h"
 #include "tpm2-util.h"
 static bool arg_graceful = false;
 static char *arg_tpm2_device = NULL;
 static char **arg_banks = NULL;
+static char *arg_file_system = NULL;
+static bool arg_machine_id = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_file_system, freep);
 
 static int help(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *link = NULL;
@@ -29,7 +39,9 @@ static int help(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return log_oom();
 
-        printf("%1$s  [OPTIONS...] WORD ...\n"
+        printf("%1$s  [OPTIONS...] WORD\n"
+               "%1$s  [OPTIONS...] --file-system=PATH\n"
+               "%1$s  [OPTIONS...] --machine-id\n"
                "\n%5$sMeasure boot phase into TPM2 PCR 11.%6$s\n"
                "\n%3$sOptions:%4$s\n"
                "  -h --help              Show this help\n"
@@ -37,6 +49,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --bank=DIGEST       Select TPM bank (SHA1, SHA256)\n"
                "     --tpm2-device=PATH  Use specified TPM2 device\n"
                "     --graceful          Exit gracefully if no TPM2 device is found\n"
+               "     --file-system=PATH  Measure UUID/labels of file system into PCR 15\n"
+               "     --machine-id        Measure machine ID into PCR 15\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
@@ -54,6 +68,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_BANK,
                 ARG_TPM2_DEVICE,
                 ARG_GRACEFUL,
+                ARG_FILE_SYSTEM,
+                ARG_MACHINE_ID,
         };
 
         static const struct option options[] = {
@@ -62,10 +78,12 @@ static int parse_argv(int argc, char *argv[]) {
                 { "bank",        required_argument, NULL, ARG_BANK        },
                 { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
                 { "graceful",    no_argument,       NULL, ARG_GRACEFUL    },
+                { "file-system", required_argument, NULL, ARG_FILE_SYSTEM },
+                { "machine-id",  no_argument,       NULL, ARG_MACHINE_ID  },
                 {}
         };
 
-        int c;
+        int c, r;
 
         assert(argc >= 0);
         assert(argv);
@@ -113,6 +131,17 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_graceful = true;
                         break;
 
+                case ARG_FILE_SYSTEM:
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_file_system);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case ARG_MACHINE_ID:
+                        arg_machine_id = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -120,49 +149,101 @@ static int parse_argv(int argc, char *argv[]) {
                         assert_not_reached();
                 }
 
+        if (arg_file_system && arg_machine_id)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--file-system= and --machine-id may not be combined.");
+
         return 1;
 }
 
-static int determine_banks(struct tpm2_context *c) {
-        _cleanup_free_ TPMI_ALG_HASH *algs = NULL;
-        int n_algs, r;
+static int determine_banks(struct tpm2_context *c, unsigned target_pcr_nr) {
+        _cleanup_strv_free_ char **l = NULL;
+        int r;
 
         assert(c);
 
         if (!strv_isempty(arg_banks)) /* Explicitly configured? Then use that */
                 return 0;
 
-        n_algs = tpm2_get_good_pcr_banks(c->esys_context, UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE, &algs);
-        if (n_algs <= 0)
-                return n_algs;
+        r = tpm2_get_good_pcr_banks_strv(c->esys_context, UINT32_C(1) << target_pcr_nr, &l);
+        if (r < 0)
+                return r;
+
+        strv_free_and_replace(arg_banks, l);
+        return 0;
+}
+
+static int get_file_system_word(
+                sd_device *d,
+                const char *prefix,
+                char **ret) {
+
+        int r;
+
+        assert(d);
+        assert(prefix);
+        assert(ret);
+
+        _cleanup_close_ int block_fd = sd_device_open(d, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+        if (block_fd < 0)
+                return block_fd;
+
+        _cleanup_(blkid_free_probep) blkid_probe b = blkid_new_probe();
+        if (!b)
+                return -ENOMEM;
 
-        for (int i = 0; i < n_algs; i++) {
-                const EVP_MD *implementation;
-                const char *salg;
+        errno = 0;
+        r = blkid_probe_set_device(b, block_fd, 0, 0);
+        if (r != 0)
+                return errno_or_else(ENOMEM);
 
-                salg = tpm2_pcr_bank_to_string(algs[i]);
-                if (!salg)
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 operates with unknown PCR algorithm, can't measure.");
+        (void) blkid_probe_enable_superblocks(b, 1);
+        (void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_UUID|BLKID_SUBLKS_LABEL);
+        (void) blkid_probe_enable_partitions(b, 1);
+        (void) blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
 
-                implementation = EVP_get_digestbyname(salg);
-                if (!implementation)
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 operates with unsupported PCR algorithm, can't measure.");
+        errno = 0;
+        r = blkid_do_safeprobe(b);
+        if (r == _BLKID_SAFEPROBE_ERROR)
+                return errno_or_else(EIO);
+        if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
+                return -ENOPKG;
 
-                r = strv_extend(&arg_banks, EVP_MD_name(implementation));
+        assert(r == _BLKID_SAFEPROBE_FOUND);
+
+        _cleanup_strv_free_ char **l = strv_new(prefix);
+        if (!l)
+                return log_oom();
+
+        FOREACH_STRING(field, "TYPE", "UUID", "LABEL", "PART_ENTRY_UUID", "PART_ENTRY_TYPE", "PART_ENTRY_NAME") {
+                const char *v = NULL;
+
+                (void) blkid_probe_lookup_value(b, field, &v, NULL);
+
+                _cleanup_free_ char *escaped = xescape(strempty(v), ":"); /* Avoid ambiguity around ":" */
+                if (!escaped)
+                        return log_oom();
+
+                r = strv_consume(&l, TAKE_PTR(escaped));
                 if (r < 0)
                         return log_oom();
+
         }
 
+        assert(strv_length(l) == 7); /* We always want 7 components, to avoid ambiguous strings */
+
+        _cleanup_free_ char *word = strv_join(l, ":");
+        if (!word)
+                return log_oom();
+
+        *ret = TAKE_PTR(word);
         return 0;
 }
 
 static int run(int argc, char *argv[]) {
         _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
-        _cleanup_free_ char *joined = NULL, *pcr_string = NULL;
-        const char *word;
-        unsigned pcr_nr;
+        _cleanup_free_ char *joined = NULL, *word = NULL;
+        unsigned target_pcr_nr;
         size_t length;
-        TSS2_RC rc;
         int r;
 
         log_setup();
@@ -171,16 +252,79 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
-        if (optind+1 != argc)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected a single argument.");
+        if (arg_file_system) {
+                _cleanup_free_ char *normalized = NULL, *normalized_escaped = NULL;
+                _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+                _cleanup_close_ int dfd = -EBADF;
+
+                if (optind != argc)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected no argument.");
+
+                dfd = chase_symlinks_and_open(arg_file_system, NULL, 0, O_DIRECTORY|O_CLOEXEC, &normalized);
+                if (dfd < 0)
+                        return log_error_errno(dfd, "Failed to open path '%s': %m", arg_file_system);
 
-        word = argv[optind];
+                r = fd_is_mount_point(dfd, NULL, 0);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine if path '%s' is mount point: %m", normalized);
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "Specified path '%s' is not a mount point, refusing: %m", normalized);
+
+                normalized_escaped = xescape(normalized, ":"); /* Avoid ambiguity around ":" */
+                if (!normalized_escaped)
+                        return log_oom();
 
-        /* Refuse to measure an empty word. We want to be able to write the series of measured words
-         * separated by colons, where multiple separating colons are collapsed. Thus it makes sense to
-         * disallow an empty word to avoid ambiguities. */
-        if (isempty(word))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "String to measure cannot be empty, refusing.");
+                _cleanup_free_ char* prefix = strjoin("file-system:", normalized_escaped);
+                if (!prefix)
+                        return log_oom();
+
+                r = block_device_new_from_fd(dfd, BLOCK_DEVICE_LOOKUP_BACKING, &d);
+                if (r < 0) {
+                        log_notice_errno(r, "Unable to determine backing block device of '%s', measuring generic fallback file system identity string: %m", arg_file_system);
+
+                        word = strjoin(prefix, "::::::");
+                        if (!word)
+                                return log_oom();
+                } else {
+                        r = get_file_system_word(d, prefix, &word);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to get file system identifier string for '%s': %m", arg_file_system);
+                }
+
+                target_pcr_nr = TPM_PCR_INDEX_VOLUME_KEY; /* → PCR 15 */
+
+        } else if (arg_machine_id) {
+                sd_id128_t mid;
+
+                if (optind != argc)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected no argument.");
+
+                r = sd_id128_get_machine(&mid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire machine ID: %m");
+
+                word = strjoin("machine-id:", SD_ID128_TO_STRING(mid));
+                if (!word)
+                        return log_oom();
+
+                target_pcr_nr = TPM_PCR_INDEX_VOLUME_KEY; /* → PCR 15 */
+
+        } else {
+                if (optind+1 != argc)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected a single argument.");
+
+                word = strdup(argv[optind]);
+                if (!word)
+                        return log_oom();
+
+                /* Refuse to measure an empty word. We want to be able to write the series of measured words
+                 * separated by colons, where multiple separating colons are collapsed. Thus it makes sense to
+                 * disallow an empty word to avoid ambiguities. */
+                if (isempty(word))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "String to measure cannot be empty, refusing.");
+
+                target_pcr_nr = TPM_PCR_INDEX_KERNEL_IMAGE; /* → PCR 11 */
+        }
 
         if (arg_graceful && tpm2_support() != TPM2_SUPPORT_FULL) {
                 log_notice("No complete TPM2 support detected, exiting gracefully.");
@@ -189,32 +333,13 @@ static int run(int argc, char *argv[]) {
 
         length = strlen(word);
 
-        int b = getenv_bool("SYSTEMD_PCRPHASE_STUB_VERIFY");
-        if (b < 0 && b != -ENXIO)
-                log_warning_errno(b, "Unable to parse $SYSTEMD_PCRPHASE_STUB_VERIFY value, ignoring.");
-
         /* Skip logic if sd-stub is not used, after all PCR 11 might have a very different purpose then. */
-        r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
-        if (r == -ENOENT) {
-                if (b != 0) {
-                        log_info("Kernel stub did not measure kernel image into PCR %u, skipping measurement.", TPM_PCR_INDEX_KERNEL_IMAGE);
-                        return EXIT_SUCCESS;
-                } else
-                        log_notice("Kernel stub did not measure kernel image into PCR %u, but told to measure anyway, hence proceeding.", TPM_PCR_INDEX_KERNEL_IMAGE);
-        } else if (r < 0)
-                return log_error_errno(r, "Failed to read StubPcrKernelImage EFI variable: %m");
-        else {
-                /* Let's validate that the stub announced PCR 11 as we expected. */
-                r = safe_atou(pcr_string, &pcr_nr);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
-                if (pcr_nr != TPM_PCR_INDEX_KERNEL_IMAGE) {
-                        if (b != 0)
-                                return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
-                        else
-                                log_notice("Kernel stub measured kernel image into PCR %u, which is different than expected %u, but told to measure anyway, hence proceeding.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
-                } else
-                        log_debug("Kernel stub reported same PCR %u as we want to use, proceeding.", TPM_PCR_INDEX_KERNEL_IMAGE);
+        r = efi_stub_measured();
+        if (r < 0)
+                return log_error_errno(r, "Failed to detect if we are running on a kernel image with TPM measurement enabled: %m");
+        if (r == 0) {
+                log_info("Kernel stub did not measure kernel image into PCR %u, skipping userspace measurement, too.", TPM_PCR_INDEX_KERNEL_IMAGE);
+                return EXIT_SUCCESS;
         }
 
         r = dlopen_tpm2();
@@ -225,62 +350,27 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        r = determine_banks(&c);
+        r = determine_banks(&c, target_pcr_nr);
         if (r < 0)
                 return r;
         if (strv_isempty(arg_banks)) /* Still none? */
                 return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Found a TPM2 without enabled PCR banks. Can't operate.");
 
-        TPML_DIGEST_VALUES values = {};
-        STRV_FOREACH(bank, arg_banks) {
-                const EVP_MD *implementation;
-                int id;
-
-                assert_se(implementation = EVP_get_digestbyname(*bank));
-
-                if (values.count >= ELEMENTSOF(values.digests))
-                        return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Too many banks selected.");
-
-                if ((size_t) EVP_MD_size(implementation) > sizeof(values.digests[values.count].digest))
-                        return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Hash result too large for TPM2.");
-
-                id = tpm2_pcr_bank_from_string(EVP_MD_name(implementation));
-                if (id < 0)
-                        return log_error_errno(id, "Can't map hash name to TPM2.");
-
-                values.digests[values.count].hashAlg = id;
-
-                if (EVP_Digest(word, length, (unsigned char*) &values.digests[values.count].digest, NULL, implementation, NULL) != 1)
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to hash word.");
-
-                values.count++;
-        }
-
         joined = strv_join(arg_banks, ", ");
         if (!joined)
                 return log_oom();
 
-        log_debug("Measuring '%s' into PCR index %u, banks %s.", word, TPM_PCR_INDEX_KERNEL_IMAGE, joined);
-
-        rc = sym_Esys_PCR_Extend(
-                        c.esys_context,
-                        ESYS_TR_PCR0 + TPM_PCR_INDEX_KERNEL_IMAGE, /* → PCR 11 */
-                        ESYS_TR_PASSWORD,
-                        ESYS_TR_NONE,
-                        ESYS_TR_NONE,
-                        &values);
-        if (rc != TSS2_RC_SUCCESS)
-                return log_error_errno(
-                                SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                "Failed to measure '%s': %s",
-                                word,
-                                sym_Tss2_RC_Decode(rc));
+        log_debug("Measuring '%s' into PCR index %u, banks %s.", word, target_pcr_nr, joined);
+
+        r = tpm2_extend_bytes(c.esys_context, arg_banks, target_pcr_nr, word, length, NULL, 0);
+        if (r < 0)
+                return r;
 
         log_struct(LOG_INFO,
                    "MESSAGE_ID=" SD_MESSAGE_TPM_PCR_EXTEND_STR,
-                   LOG_MESSAGE("Successfully extended PCR index %u with '%s' (banks %s).", TPM_PCR_INDEX_KERNEL_IMAGE, word, joined),
+                   LOG_MESSAGE("Extended PCR index %u with '%s' (banks %s).", target_pcr_nr, word, joined),
                    "MEASURING=%s", word,
-                   "PCR=%u", TPM_PCR_INDEX_KERNEL_IMAGE,
+                   "PCR=%u", target_pcr_nr,
                    "BANKS=%s", joined);
 
         return EXIT_SUCCESS;
index 32c6ed7cacf813117aa77519297c70e823860745..87becdbc7ead17de323b8d9c04360c245d1ce08f 100644 (file)
@@ -1022,16 +1022,16 @@ static int introspect(int argc, char **argv, void *userdata) {
                         return bus_log_parse_error(r);
 
                 for (;;) {
-                        Member *z;
-                        _cleanup_free_ char *buf = NULL;
                         _cleanup_fclose_ FILE *mf = NULL;
+                        _cleanup_free_ char *buf = NULL;
+                        const char *name, *contents;
                         size_t sz = 0;
-                        const char *name;
+                        Member *z;
+                        char type;
 
                         r = sd_bus_message_enter_container(reply, 'e', "sv");
                         if (r < 0)
                                 return bus_log_parse_error(r);
-
                         if (r == 0)
                                 break;
 
@@ -1039,7 +1039,13 @@ static int introspect(int argc, char **argv, void *userdata) {
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
-                        r = sd_bus_message_enter_container(reply, 'v', NULL);
+                        r = sd_bus_message_peek_type(reply, &type, &contents);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+                        if (type != 'v')
+                                return bus_log_parse_error(EINVAL);
+
+                        r = sd_bus_message_enter_container(reply, 'v', contents);
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
@@ -1056,6 +1062,7 @@ static int introspect(int argc, char **argv, void *userdata) {
                         z = set_get(members, &((Member) {
                                                 .type = "property",
                                                 .interface = m->interface,
+                                                .signature = (char*) contents,
                                                 .name = (char*) name }));
                         if (z)
                                 free_and_replace(z->value, buf);
index f65401617a93e0f9895f5d67580fe1d6004b3a7a..28e703664f2d2d7c51c40017d49aab397c5cc5cd 100644 (file)
@@ -7,6 +7,7 @@ endif
 bpf_clang_flags = [
         '-std=gnu11',
         '-Wno-compare-distinct-pointer-types',
+        '-fno-stack-protector',
         '-O2',
         '-target',
         'bpf',
@@ -16,6 +17,7 @@ bpf_clang_flags = [
 
 bpf_gcc_flags = [
         '-std=gnu11',
+        '-fno-stack-protector',
         '-O2',
         '-mkernel=5.2',
         '-mcpu=v3',
index e04b903d5c85273b1e009b535425bc1cfe9d8e12..7f3782ae4139afe45d842ebac23d7043381e9825 100644 (file)
@@ -1651,7 +1651,8 @@ static void cgroup_context_apply(
 
                         if (unit_has_unified_memory_config(u)) {
                                 val = c->memory_max;
-                                log_cgroup_compat(u, "Applying MemoryMax=%" PRIu64 " as MemoryLimit=", val);
+                                if (val != CGROUP_LIMIT_MAX)
+                                        log_cgroup_compat(u, "Applying MemoryMax=%" PRIu64 " as MemoryLimit=", val);
                         } else
                                 val = c->memory_limit;
 
index 6109ab6ce09e4e0832aac597d737dd2016a7a720..53121fa1a6bd89fe92384dcc077205f122607bc3 100644 (file)
@@ -653,6 +653,62 @@ static int method_get_unit_by_control_group(sd_bus_message *message, void *userd
         return reply_unit_path(u, message, error);
 }
 
+static int method_get_unit_by_pidfd(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        Manager *m = ASSERT_PTR(userdata);
+        _cleanup_free_ char *path = NULL;
+        int r, pidfd;
+        pid_t pid;
+        Unit *u;
+
+        assert(message);
+
+        r = sd_bus_message_read(message, "h", &pidfd);
+        if (r < 0)
+                return r;
+
+        r = pidfd_get_pid(pidfd, &pid);
+        if (r < 0)
+                return sd_bus_error_set_errnof(error, r, "Failed to get PID from PIDFD: %m");
+
+        u = manager_get_unit_by_pid(m, pid);
+        if (!u)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_UNIT_FOR_PID, "PID "PID_FMT" does not belong to any loaded unit.", pid);
+
+        r = mac_selinux_unit_access_check(u, message, "status", error);
+        if (r < 0)
+                return r;
+
+        path = unit_dbus_path(u);
+        if (!path)
+                return log_oom();
+
+        r = sd_bus_message_new_method_return(message, &reply);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "os", path, u->id);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append_array(reply, 'y', u->invocation_id.bytes, sizeof(u->invocation_id.bytes));
+        if (r < 0)
+                return r;
+
+        /* Double-check that the process is still alive and that the PID did not change before returning the
+         * answer. */
+        r = pidfd_verify_pid(pidfd, pid);
+        if (r == -ESRCH)
+                return sd_bus_error_setf(error,
+                                         BUS_ERROR_NO_SUCH_PROCESS,
+                                         "The PIDFD's PID "PID_FMT" changed during the lookup operation.",
+                                         pid);
+        if (r < 0)
+                return sd_bus_error_set_errnof(error, r, "Failed to get PID from PIDFD: %m");
+
+        return sd_bus_send(NULL, reply, NULL);
+}
+
 static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = ASSERT_PTR(userdata);
         const char *name;
@@ -2911,6 +2967,11 @@ const sd_bus_vtable bus_manager_vtable[] = {
                                 SD_BUS_RESULT("o", unit),
                                 method_get_unit_by_control_group,
                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("GetUnitByPIDFD",
+                                SD_BUS_ARGS("h", pidfd),
+                                SD_BUS_RESULT("o", unit, "s", unit_id, "ay", invocation_id),
+                                method_get_unit_by_pidfd,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD_WITH_ARGS("LoadUnit",
                                 SD_BUS_ARGS("s", name),
                                 SD_BUS_RESULT("o", unit),
index 4c440895c19ecf961016a19bdc9f5255edfb6fc4..ac3f096b0da49c2296017e03364073916779ab51 100644 (file)
@@ -4908,7 +4908,7 @@ static int exec_child(
 
         /* If the user namespace was not set up above, try to do it now.
          * It's preferred to set up the user namespace later (after all other namespaces) so as not to be
-         * restricted by rules pertaining to combining user namspaces with other namespaces (e.g. in the
+         * restricted by rules pertaining to combining user namespaces with other namespaces (e.g. in the
          * case of mount namespaces being less privileged when the mount point list is copied from a
          * different user namespace). */
 
@@ -5626,6 +5626,23 @@ int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_p
         return 0;
 }
 
+int exec_context_destroy_mount_ns_dir(Unit *u) {
+        _cleanup_free_ char *p = NULL;
+
+        if (!u || !MANAGER_IS_SYSTEM(u->manager))
+                return 0;
+
+        p = path_join("/run/systemd/propagate/", u->id);
+        if (!p)
+                return -ENOMEM;
+
+        /* This is only filled transiently (see mount_in_namespace()), should be empty or even non-existent*/
+        if (rmdir(p) < 0 && errno != ENOENT)
+                log_unit_debug_errno(u, errno, "Unable to remove propagation dir '%s', ignoring: %m", p);
+
+        return 0;
+}
+
 static void exec_command_done(ExecCommand *c) {
         assert(c);
 
@@ -6633,7 +6650,7 @@ void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
 
         if (*l) {
                 /* It's kind of important, that we keep the order here */
-                LIST_FIND_TAIL(command, *l, end);
+                end = LIST_FIND_TAIL(command, *l);
                 LIST_INSERT_AFTER(command, *l, end, e);
         } else
               *l = e;
index 62ad6d2eb2aacf453420762843944b89ce860f7a..325f340862c59aa790952bb0676595eb4811fead 100644 (file)
@@ -459,6 +459,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix);
 
 int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_root);
 int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_root, const char *unit);
+int exec_context_destroy_mount_ns_dir(Unit *u);
 
 const char* exec_context_fdname(const ExecContext *c, int fd_index);
 
index 6c55bc5187c1527fb36850567ee29ff088cb4181..0ed2d1b31d47f043c8517b7d424df197c3719d0f 100644 (file)
@@ -680,7 +680,7 @@ int config_parse_socket_listen(
         p->n_auxiliary_fds = 0;
         p->socket = s;
 
-        LIST_FIND_TAIL(port, s->ports, tail);
+        tail = LIST_FIND_TAIL(port, s->ports);
         LIST_INSERT_AFTER(port, s->ports, tail, p);
 
         p = NULL;
index 4ea609a4fde9c80f40fe6f140a5022f39758d8a6..f47c511da6033de7463112cf93e81b3cf5335fcf 100644 (file)
@@ -13,6 +13,7 @@
 #include "device.h"
 #include "exit-status.h"
 #include "format-util.h"
+#include "fs-util.h"
 #include "fstab-util.h"
 #include "initrd-util.h"
 #include "libmount-util.h"
@@ -27,6 +28,7 @@
 #include "process-util.h"
 #include "serialize.h"
 #include "special.h"
+#include "stat-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
@@ -1074,6 +1076,7 @@ fail:
 static void mount_enter_mounting(Mount *m) {
         int r;
         MountParameters *p;
+        bool source_is_dir = true;
 
         assert(m);
 
@@ -1081,16 +1084,28 @@ static void mount_enter_mounting(Mount *m) {
         if (r < 0)
                 goto fail;
 
-        (void) mkdir_p_label(m->where, m->directory_mode);
+        p = get_mount_parameters_fragment(m);
+        if (p && mount_is_bind(p)) {
+                r = is_dir(p->what, /* follow = */ true);
+                if (r < 0 && r != -ENOENT)
+                        log_unit_info_errno(UNIT(m), r, "Failed to determine type of bind mount source '%s', ignoring: %m", p->what);
+                else if (r == 0)
+                        source_is_dir = false;
+        }
 
-        unit_warn_if_dir_nonempty(UNIT(m), m->where);
+        if (source_is_dir)
+                (void) mkdir_p_label(m->where, m->directory_mode);
+        else
+                (void) touch_file(m->where, /* parents = */ true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
+
+        if (source_is_dir)
+                unit_warn_if_dir_nonempty(UNIT(m), m->where);
         unit_warn_leftover_processes(UNIT(m), unit_log_leftover_process_start);
 
         m->control_command_id = MOUNT_EXEC_MOUNT;
         m->control_command = m->exec_command + MOUNT_EXEC_MOUNT;
 
         /* Create the source directory for bind-mounts if needed */
-        p = get_mount_parameters_fragment(m);
         if (p && mount_is_bind(p)) {
                 r = mkdir_p_label(p->what, m->directory_mode);
                 /* mkdir_p_label() can return -EEXIST if the target path exists and is not a directory - which is
index d46daa3c05c8c0a663745731c44f0e800901ba1b..feae4dcbbf084cc6ce1b13ec7e6e39e8fe6bda7b 100644 (file)
@@ -2093,6 +2093,7 @@ int setup_namespace(
                 r = loop_device_make_by_path(
                                 root_image,
                                 FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
+                                /* sector_size= */ UINT32_MAX,
                                 FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
                                 LOCK_SH,
                                 &loop_device);
index fe9de012b647c4ea8fb240f9c92236f6205c6738..1cef421db81037457bdf83d3546b6d41d803ce18 100644 (file)
                        send_interface="org.freedesktop.systemd1.Manager"
                        send_member="GetUnitByControlGroup"/>
 
+                <allow send_destination="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
+                       send_member="GetUnitByPIDFD"/>
+
                 <allow send_destination="org.freedesktop.systemd1"
                        send_interface="org.freedesktop.systemd1.Manager"
                        send_member="LoadUnit"/>
index a376b70f4271d19f00bb33e5dee2d5252fbcdcae..9c2fb2ee1c8d3a7da074164a9c53df7f686e0bc6 100644 (file)
@@ -3832,7 +3832,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
 
                                 s->reload_result = f;
 
-                                /* If the last notification we received from the service process indiciates
+                                /* If the last notification we received from the service process indicates
                                  * we are still reloading, then don't leave reloading state just yet, just
                                  * transition into SERVICE_RELOAD_NOTIFY, to wait for the READY=1 coming,
                                  * too. */
index bafbb80b47d1d4edb7989940eb925e0c2a9d8495..982b6cf6dc329fef4447e5589a59caad296a975c 100644 (file)
@@ -976,11 +976,11 @@ int transaction_add_job_and_dependencies(
         }
 
         if (is_new && !ignore_requirements && type != JOB_NOP) {
-                Set *following;
+                _cleanup_set_free_ Set *following = NULL;
 
                 /* If we are following some other unit, make sure we
                  * add all dependencies of everybody following. */
-                if (unit_following_set(ret->unit, &following) > 0) {
+                if (unit_following_set(ret->unit, &following) > 0)
                         SET_FOREACH(dep, following) {
                                 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e);
                                 if (r < 0) {
@@ -991,16 +991,13 @@ int transaction_add_job_and_dependencies(
                                 }
                         }
 
-                        set_free(following);
-                }
-
                 /* Finally, recursively add in all dependencies. */
                 if (IN_SET(type, JOB_START, JOB_RESTART)) {
                         UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START) {
                                 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
                                 if (r < 0) {
                                         if (r != -EBADR) /* job type not applicable */
-                                                goto fail;
+                                                return r;
 
                                         sd_bus_error_free(e);
                                 }
@@ -1022,7 +1019,7 @@ int transaction_add_job_and_dependencies(
                                 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e);
                                 if (r < 0) {
                                         if (r != -EBADR) /* job type not applicable */
-                                                goto fail;
+                                                return r;
 
                                         sd_bus_error_free(e);
                                 }
@@ -1032,7 +1029,7 @@ int transaction_add_job_and_dependencies(
                                 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e);
                                 if (r < 0) {
                                         if (r != -EBADR) /* job type not applicable */
-                                                goto fail;
+                                                return r;
 
                                         sd_bus_error_free(e);
                                 }
@@ -1050,30 +1047,42 @@ int transaction_add_job_and_dependencies(
                 }
 
                 if (IN_SET(type, JOB_STOP, JOB_RESTART)) {
-                        UnitDependencyAtom atom;
-                        JobType ptype;
-
+                        _cleanup_set_free_ Set *propagated_restart = NULL;
                         /* We propagate STOP as STOP, but RESTART only as TRY_RESTART, in order not to start
                          * dependencies that are not around. */
-                        if (type == JOB_RESTART) {
-                                atom = UNIT_ATOM_PROPAGATE_RESTART;
-                                ptype = JOB_TRY_RESTART;
-                        } else {
-                                ptype = JOB_STOP;
-                                atom = UNIT_ATOM_PROPAGATE_STOP;
-                        }
 
-                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, atom) {
-                                JobType nt;
+                        if (type == JOB_RESTART)
+                                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_RESTART) {
+                                        JobType nt;
+
+                                        r = set_ensure_put(&propagated_restart, NULL, dep);
+                                        if (r < 0)
+                                                return r;
+
+                                        nt = job_type_collapse(JOB_TRY_RESTART, dep);
+                                        if (nt == JOB_NOP)
+                                                continue;
 
-                                nt = job_type_collapse(ptype, dep);
-                                if (nt == JOB_NOP)
+                                        r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
+                                        if (r < 0) {
+                                                if (r != -EBADR) /* job type not applicable */
+                                                        return r;
+
+                                                sd_bus_error_free(e);
+                                        }
+                                }
+
+                        /* The 'stop' part of a restart job is also propagated to
+                         * units with UNIT_ATOM_PROPAGATE_STOP */
+                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_STOP) {
+                                /* Units experienced restart propagation are skipped */
+                                if (set_contains(propagated_restart, dep))
                                         continue;
 
-                                r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
+                                r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, false, false, ignore_order, e);
                                 if (r < 0) {
                                         if (r != -EBADR) /* job type not applicable */
-                                                goto fail;
+                                                return r;
 
                                         sd_bus_error_free(e);
                                 }
@@ -1087,9 +1096,6 @@ int transaction_add_job_and_dependencies(
         }
 
         return 0;
-
-fail:
-        return r;
 }
 
 int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
index 0e66cae4341ee5293f96f5ca909fbb5b5143fd21..166246901244365730298a0b858ecbf715c7aefc 100644 (file)
@@ -5765,6 +5765,7 @@ void unit_destroy_runtime_data(Unit *u, const ExecContext *context) {
                 exec_context_destroy_runtime_directory(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
 
         exec_context_destroy_credentials(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME], u->id);
+        exec_context_destroy_mount_ns_dir(u);
 }
 
 int unit_clean(Unit *u, ExecCleanMask mask) {
index 192dc4c654b044dbcba6cc2740c1a5e952b5629c..013ebb4c2871d4c50ace650a7f29ba4de35aabcc 100644 (file)
@@ -597,7 +597,7 @@ static int save_external_coredump(
                 /* tmpfs might get full quickly, so check the available space too.
                  * But don't worry about errors here, failing to access the storage
                  * location will be better logged when writing to it. */
-                if (statvfs("/var/lib/systemd/coredump/", &sv) >= 0)
+                if (fstatvfs(fd, &sv) >= 0)
                         max_size = MIN((uint64_t)sv.f_frsize * (uint64_t)sv.f_bfree, max_size);
 
                 log_debug("Limiting core file size to %" PRIu64 " bytes due to cgroup memory limits.", max_size);
index 1f75d48d336f9084e053ce95f1d3e6b28df1cf60..8d52d8ded7dbac1cd2d4f9e15cdd78d60752b1bf 100644 (file)
@@ -17,8 +17,9 @@
 [Coredump]
 #Storage=external
 #Compress=yes
-#ProcessSizeMax=2G
-#ExternalSizeMax=2G
+# On 32-bit, the default is 1G instead of 32G.
+#ProcessSizeMax=32G
+#ExternalSizeMax=32G
 #JournalSizeMax=767M
 #MaxUse=
 #KeepFree=
index 71bf355b383ede14d95e11e055c3903a3033b234..3eb94cd36a291bd6b22aa2a0268b5dbbe3ec061d 100644 (file)
@@ -326,16 +326,12 @@ static int write_blob(FILE *f, const void *data, size_t size) {
 
         if (arg_transcode == TRANSCODE_OFF &&
             arg_json_format_flags != JSON_FORMAT_OFF) {
-
                 _cleanup_(erase_and_freep) char *suffixed = NULL;
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
 
-                if (memchr(data, 0, size))
-                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Credential data contains embedded NUL, can't parse as JSON.");
-
-                suffixed = memdup_suffix0(data, size);
-                if (!suffixed)
-                        return log_oom();
+                r = make_cstring(data, size, MAKE_CSTRING_REFUSE_TRAILING_NUL, &suffixed);
+                if (r < 0)
+                        return log_error_errno(r, "Unable to convert binary string to C string: %m");
 
                 r = json_parse(suffixed, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
                 if (r < 0)
index e49b4a0cfe9b8a73791136ef950229818c7b7139..2baeb92e07f72ec7b99e3e5c398f0a096e742987 100644 (file)
@@ -19,6 +19,7 @@ int load_volume_key_fido2(
         _cleanup_(erase_and_freep) void *decrypted_key = NULL;
         _cleanup_(erase_and_freep) char *passphrase = NULL;
         size_t decrypted_key_size;
+        ssize_t passphrase_size;
         int r;
 
         assert_se(cd);
@@ -43,8 +44,8 @@ int load_volume_key_fido2(
 
         /* Because cryptenroll requires a LUKS header, we can assume that this device is not
          * a PLAIN device. In this case, we need to base64 encode the secret to use as the passphrase */
-        r = base64mem(decrypted_key, decrypted_key_size, &passphrase);
-        if (r < 0)
+        passphrase_size = base64mem(decrypted_key, decrypted_key_size, &passphrase);
+        if (passphrase_size < 0)
                 return log_oom();
 
         r = crypt_volume_key_get(
@@ -53,7 +54,7 @@ int load_volume_key_fido2(
                         ret_vk,
                         ret_vks,
                         passphrase,
-                        /* passphrase_size= */ r);
+                        passphrase_size);
         if (r < 0)
                 return log_error_errno(r, "Unlocking via FIDO2 device failed: %m");
 
@@ -74,6 +75,7 @@ int enroll_fido2(
         _cleanup_free_ char *keyslot_as_string = NULL;
         size_t cid_size, salt_size, secret_size;
         _cleanup_free_ void *cid = NULL;
+        ssize_t base64_encoded_size;
         const char *node, *un;
         int r, keyslot;
 
@@ -106,9 +108,9 @@ int enroll_fido2(
                 return r;
 
         /* Before we use the secret, we base64 encode it, for compat with homed, and to make it easier to type in manually */
-        r = base64mem(secret, secret_size, &base64_encoded);
-        if (r < 0)
-                return log_error_errno(r, "Failed to base64 encode secret key: %m");
+        base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+        if (base64_encoded_size < 0)
+                return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
 
         r = cryptsetup_set_minimal_pbkdf(cd);
         if (r < 0)
@@ -120,7 +122,7 @@ int enroll_fido2(
                         volume_key,
                         volume_key_size,
                         base64_encoded,
-                        strlen(base64_encoded));
+                        base64_encoded_size);
         if (keyslot < 0)
                 return log_error_errno(keyslot, "Failed to add new FIDO2 key to %s: %m", node);
 
index 9f07a2e01d0a55cf575f301d7b1742402bb5b5be..54b6b8624281db74177adfefde6f54d48d4a9f5b 100644 (file)
@@ -21,6 +21,7 @@ int enroll_pkcs11(
         size_t decrypted_key_size, encrypted_key_size;
         _cleanup_free_ void *encrypted_key = NULL;
         _cleanup_(X509_freep) X509 *cert = NULL;
+        ssize_t base64_encoded_size;
         const char *node;
         EVP_PKEY *pkey;
         int keyslot, r;
@@ -60,9 +61,9 @@ int enroll_pkcs11(
 
         /* Let's base64 encode the key to use, for compat with homed (and it's easier to type it in by
          * keyboard, if that might ever end up being necessary.) */
-        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-        if (r < 0)
-                return log_error_errno(r, "Failed to base64 encode secret key: %m");
+        base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        if (base64_encoded_size < 0)
+                return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
 
         r = cryptsetup_set_minimal_pbkdf(cd);
         if (r < 0)
@@ -74,7 +75,7 @@ int enroll_pkcs11(
                         volume_key,
                         volume_key_size,
                         base64_encoded,
-                        strlen(base64_encoded));
+                        base64_encoded_size);
         if (keyslot < 0)
                 return log_error_errno(keyslot, "Failed to add new PKCS#11 key to %s: %m", node);
 
index 5c902908c4ed97963fd3b4a94246f3b1c7b95df0..3098b2e7ac155353e57f22df6c1c3f0179c0d8f1 100644 (file)
@@ -8,6 +8,8 @@
 #include "hexdecoct.h"
 #include "json.h"
 #include "memory-util.h"
+#include "random-util.h"
+#include "sha256.h"
 #include "tpm2-util.h"
 
 static int search_policy_hash(
@@ -145,8 +147,17 @@ int enroll_tpm2(struct crypt_device *cd,
         uint16_t pcr_bank, primary_alg;
         const char *node;
         _cleanup_(erase_and_freep) char *pin_str = NULL;
+        ssize_t base64_encoded_size;
         int r, keyslot;
         TPM2Flags flags = 0;
+        uint8_t binary_salt[SHA256_DIGEST_SIZE] = {};
+        /*
+         * erase the salt, we'd rather attempt to not have this in a coredump
+         * as an attacker would have all the parameters but pin used to create
+         * the session key. This problem goes away when we move to a trusted
+         * primary key, aka the SRK.
+         */
+        CLEANUP_ERASE(binary_salt);
 
         assert(cd);
         assert(volume_key);
@@ -160,6 +171,22 @@ int enroll_tpm2(struct crypt_device *cd,
                 r = get_pin(&pin_str, &flags);
                 if (r < 0)
                         return r;
+
+                r = crypto_random_bytes(binary_salt, sizeof(binary_salt));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire random salt: %m");
+
+                uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
+                CLEANUP_ERASE(salted_pin);
+                r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), binary_salt, sizeof(binary_salt), salted_pin);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to perform PBKDF2: %m");
+
+                pin_str = erase_and_free(pin_str);
+                /* re-stringify pin_str */
+                base64_encoded_size = base64mem(salted_pin, sizeof(salted_pin), &pin_str);
+                if (base64_encoded_size < 0)
+                        return log_error_errno(base64_encoded_size, "Failed to base64 encode salted pin: %m");
         }
 
         r = tpm2_load_pcr_public_key(pubkey_path, &pubkey, &pubkey_size);
@@ -230,9 +257,9 @@ int enroll_tpm2(struct crypt_device *cd,
         }
 
         /* let's base64 encode the key to use, for compat with homed (and it's easier to every type it in by keyboard, if that might end up being necessary. */
-        r = base64mem(secret, secret_size, &base64_encoded);
-        if (r < 0)
-                return log_error_errno(r, "Failed to base64 encode secret key: %m");
+        base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+        if (base64_encoded_size < 0)
+                return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
 
         r = cryptsetup_set_minimal_pbkdf(cd);
         if (r < 0)
@@ -244,7 +271,7 @@ int enroll_tpm2(struct crypt_device *cd,
                         volume_key,
                         volume_key_size,
                         base64_encoded,
-                        strlen(base64_encoded));
+                        base64_encoded_size);
         if (keyslot < 0)
                 return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
 
@@ -257,6 +284,8 @@ int enroll_tpm2(struct crypt_device *cd,
                         primary_alg,
                         blob, blob_size,
                         hash, hash_size,
+                        use_pin ? binary_salt : NULL,
+                        use_pin ? sizeof(binary_salt) : 0,
                         flags,
                         &v);
         if (r < 0)
index abe80720afe4027568ee3da2c2671045c592fa85..dd379169ff845b68b6963f85cca4a77cde0762e4 100644 (file)
@@ -42,8 +42,8 @@ _public_ int cryptsetup_token_open_pin(
                 void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
 
         _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
-        _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL;
-        size_t blob_size, policy_hash_size, decrypted_key_size, pubkey_size;
+        _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL, *salt = NULL;
+        size_t blob_size, policy_hash_size, decrypted_key_size, pubkey_size, salt_size = 0;
         _cleanup_(erase_and_freep) void *decrypted_key = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         uint32_t hash_pcr_mask, pubkey_pcr_mask;
@@ -51,6 +51,7 @@ _public_ int cryptsetup_token_open_pin(
                 .search_pcr_mask = UINT32_MAX
         };
         uint16_t pcr_bank, primary_alg;
+        ssize_t base64_encoded_size;
         TPM2Flags flags = 0;
         const char *json;
         int r;
@@ -89,6 +90,8 @@ _public_ int cryptsetup_token_open_pin(
                         &blob_size,
                         &policy_hash,
                         &policy_hash_size,
+                        &salt,
+                        &salt_size,
                         &flags);
         if (r < 0)
                 return log_debug_open_error(cd, r);
@@ -109,6 +112,8 @@ _public_ int cryptsetup_token_open_pin(
                         blob_size,
                         policy_hash,
                         policy_hash_size,
+                        salt,
+                        salt_size,
                         flags,
                         &decrypted_key,
                         &decrypted_key_size);
@@ -116,13 +121,13 @@ _public_ int cryptsetup_token_open_pin(
                 return log_debug_open_error(cd, r);
 
         /* Before using this key as passphrase we base64 encode it, for compat with homed */
-        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-        if (r < 0)
-                return log_debug_open_error(cd, r);
+        base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        if (base64_encoded_size < 0)
+                return log_debug_open_error(cd, base64_encoded_size);
 
         /* free'd automatically by libcryptsetup */
-        *ret_password_len = strlen(base64_encoded);
         *ret_password = TAKE_PTR(base64_encoded);
+        *ret_password_len = base64_encoded_size;
 
         return 0;
 }
@@ -167,9 +172,9 @@ _public_ void cryptsetup_token_dump(
                 const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
 
         _cleanup_free_ char *hash_pcrs_str = NULL, *pubkey_pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL, *pubkey_str = NULL;
-        _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL;
+        _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL, *salt = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-        size_t blob_size, policy_hash_size, pubkey_size;
+        size_t blob_size, policy_hash_size, pubkey_size, salt_size = 0;
         uint32_t hash_pcr_mask, pubkey_pcr_mask;
         uint16_t pcr_bank, primary_alg;
         TPM2Flags flags = 0;
@@ -194,6 +199,8 @@ _public_ void cryptsetup_token_dump(
                         &blob_size,
                         &policy_hash,
                         &policy_hash_size,
+                        &salt,
+                        &salt_size,
                         &flags);
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m");
@@ -226,6 +233,7 @@ _public_ void cryptsetup_token_dump(
         crypt_log(cd, "\ttpm2-blob:        %s\n", blob_str);
         crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
         crypt_log(cd, "\ttpm2-pin:         %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
+        crypt_log(cd, "\ttpm2-salt:        %s\n", true_false(salt));
 }
 
 /*
index e305d8ba7958f8676adfafa3a085b8a426dca9b2..4e3090bc9297b7e5ef85c98d1998a836412ecac1 100644 (file)
@@ -58,27 +58,13 @@ int crypt_dump_hex_string(const char *hex_str, char **ret_dump_str) {
 }
 
 int crypt_normalize_pin(const void *pin, size_t pin_size, char **ret_pin_string) {
-
-        _cleanup_free_ char *pin_string = NULL;
-
-        assert(pin || !pin_size);
+        assert(pin || pin_size == 0);
         assert(ret_pin_string);
 
-        if (!pin) {
+        if (pin_size == 0) {
                 *ret_pin_string = NULL;
                 return 0;
         }
 
-        /* Refuse embedded NULL bytes, but allow trailing NULL */
-        if (memchr(pin, 0, pin_size - 1))
-                return -EINVAL;
-
-        /* Enforce trailing NULL byte if missing */
-        pin_string = memdup_suffix0(pin, pin_size);
-        if (!pin_string)
-                return -ENOMEM;
-
-        *ret_pin_string = TAKE_PTR(pin_string);
-
-        return 0;
+        return make_cstring(pin, pin_size, MAKE_CSTRING_ALLOW_TRAILING_NUL, ret_pin_string);
 }
index a0e1ccbeeb88fb8596546c4e8c6535bfc82dda18..a1c85e600c7e3d7211faef1caa8f9bb0ea28dc33 100644 (file)
@@ -25,6 +25,7 @@ int acquire_luks2_key(
         _cleanup_(erase_and_freep) void *decrypted_key = NULL;
         _cleanup_(erase_and_freep) char *base64_encoded = NULL;
         _cleanup_strv_free_erase_ char **pins = NULL;
+        ssize_t base64_encoded_size;
 
         assert(ret_keyslot_passphrase);
         assert(ret_keyslot_passphrase_size);
@@ -58,12 +59,12 @@ int acquire_luks2_key(
                 return r;
 
         /* Before using this key as passphrase we base64 encode it, for compat with homed */
-        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-        if (r < 0)
-                return crypt_log_error_errno(cd, r, "Failed to base64 encode key: %m");
+        base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        if (base64_encoded_size < 0)
+                return crypt_log_error_errno(cd, (int) base64_encoded_size, "Failed to base64 encode key: %m");
 
         *ret_keyslot_passphrase = TAKE_PTR(base64_encoded);
-        *ret_keyslot_passphrase_size = strlen(*ret_keyslot_passphrase);
+        *ret_keyslot_passphrase_size = base64_encoded_size;
 
         return 0;
 }
index 2e0450aa5bab0c2d9d1b17b3fb82fd2b70c72773..1ed9e2bb86bed6fb3b40fd19c5486c9c9d6fa9e7 100644 (file)
@@ -187,6 +187,7 @@ int acquire_luks2_key(
         _cleanup_free_ char *pkcs11_uri = NULL;
         _cleanup_free_ void *encrypted_key = NULL;
         systemd_pkcs11_plugin_params *pkcs11_params = userdata;
+        ssize_t base64_encoded_size;
 
         assert(json);
         assert(ret_password);
@@ -213,12 +214,12 @@ int acquire_luks2_key(
         if (r < 0)
                 return r;
 
-        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-        if (r < 0)
-                return crypt_log_error_errno(cd, r, "Can not base64 encode key: %m");
+        base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        if (base64_encoded_size < 0)
+                return crypt_log_error_errno(cd, (int) base64_encoded_size, "Can not base64 encode key: %m");
 
         *ret_password = TAKE_PTR(base64_encoded);
-        *ret_password_size = strlen(*ret_password);
+        *ret_password_size = base64_encoded_size;
 
         return 0;
 }
index be496d4949183b08472dc93b51014ee9b41b6162..80a2c0d31611114bc1c6c866d941ad2e55acef8b 100644 (file)
@@ -9,6 +9,7 @@
 #include "luks2-tpm2.h"
 #include "parse-util.h"
 #include "random-util.h"
+#include "sha256.h"
 #include "strv.h"
 #include "tpm2-util.h"
 
@@ -26,12 +27,15 @@ int acquire_luks2_key(
                 size_t key_data_size,
                 const void *policy_hash,
                 size_t policy_hash_size,
+                const void *salt,
+                size_t salt_size,
                 TPM2Flags flags,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size) {
 
         _cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL;
         _cleanup_free_ char *auto_device = NULL;
+        _cleanup_(erase_and_freep) char *b64_salted_pin = NULL;
         int r;
 
         assert(ret_decrypted_key);
@@ -50,6 +54,23 @@ int acquire_luks2_key(
         if ((flags & TPM2_FLAGS_USE_PIN) && !pin)
                 return -ENOANO;
 
+        /* If we're using a PIN, and the luks header has a salt, it better have a pin too */
+        if ((flags & TPM2_FLAGS_USE_PIN) && salt && !pin)
+                return -ENOANO;
+
+        if (pin) {
+                uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
+                CLEANUP_ERASE(salted_pin);
+                r = tpm2_util_pbkdf2_hmac_sha256(pin, strlen(pin), salt, salt_size, salted_pin);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to perform PBKDF2: %m");
+
+                r = base64mem(salted_pin, sizeof(salted_pin), &b64_salted_pin);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to base64 encode salted pin: %m");
+                pin = b64_salted_pin;
+        }
+
         if (pubkey_pcr_mask != 0) {
                 r = tpm2_load_pcr_signature(signature_path, &signature_json);
                 if (r < 0)
index f3625124e53f07064fdec4eb3f630a8aa80a81f0..36d514caa0d5d5ff6c9c49452bc14e4d0bcc39df 100644 (file)
@@ -20,6 +20,8 @@ int acquire_luks2_key(
                 size_t key_data_size,
                 const void *policy_hash,
                 size_t policy_hash_size,
+                const void *salt,
+                size_t salt_size,
                 TPM2Flags flags,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size);
index 838c02bfc96d62f3032fdd76226fd23c55c6b8b5..2a8a38c593892ac7921fab401db399a2adb0ee83 100644 (file)
@@ -9,6 +9,7 @@
 #include "json.h"
 #include "parse-util.h"
 #include "random-util.h"
+#include "sha256.h"
 #include "tpm2-util.h"
 
 static int get_pin(usec_t until, AskPasswordFlags ask_password_flags, bool headless, char **ret_pin_str) {
@@ -69,6 +70,8 @@ int acquire_tpm2_key(
                 size_t key_data_size,
                 const void *policy_hash,
                 size_t policy_hash_size,
+                const void *salt,
+                size_t salt_size,
                 TPM2Flags flags,
                 usec_t until,
                 bool headless,
@@ -140,7 +143,7 @@ int acquire_tpm2_key(
                                 ret_decrypted_key_size);
 
         for (int i = 5;; i--) {
-                _cleanup_(erase_and_freep) char *pin_str = NULL;
+                _cleanup_(erase_and_freep) char *pin_str = NULL, *b64_salted_pin = NULL;
 
                 if (i <= 0)
                         return -EACCES;
@@ -149,13 +152,28 @@ int acquire_tpm2_key(
                 if (r < 0)
                         return r;
 
+                if (salt) {
+                        uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
+                        CLEANUP_ERASE(salted_pin);
+
+                        r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), salt, salt_size, salted_pin);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to perform PBKDF2: %m");
+
+                        r = base64mem(salted_pin, sizeof(salted_pin), &b64_salted_pin);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to base64 encode salted pin: %m");
+                } else
+                        /* no salting needed, backwards compat with non-salted pins */
+                        b64_salted_pin = TAKE_PTR(pin_str);
+
                 r = tpm2_unseal(device,
                                 hash_pcr_mask,
                                 pcr_bank,
                                 pubkey, pubkey_size,
                                 pubkey_pcr_mask,
                                 signature_json,
-                                pin_str,
+                                b64_salted_pin,
                                 primary_alg,
                                 blob,
                                 blob_size,
@@ -188,6 +206,8 @@ int find_tpm2_auto_data(
                 size_t *ret_blob_size,
                 void **ret_policy_hash,
                 size_t *ret_policy_hash_size,
+                void **ret_salt,
+                size_t *ret_salt_size,
                 TPM2Flags *ret_flags,
                 int *ret_keyslot,
                 int *ret_token) {
@@ -197,9 +217,9 @@ int find_tpm2_auto_data(
         assert(cd);
 
         for (token = start_token; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
-                _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL;
+                _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL, *salt = NULL;
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-                size_t blob_size, policy_hash_size, pubkey_size;
+                size_t blob_size, policy_hash_size, pubkey_size, salt_size = 0;
                 uint32_t hash_pcr_mask, pubkey_pcr_mask;
                 uint16_t pcr_bank, primary_alg;
                 TPM2Flags flags;
@@ -221,6 +241,7 @@ int find_tpm2_auto_data(
                                 &primary_alg,
                                 &blob, &blob_size,
                                 &policy_hash, &policy_hash_size,
+                                &salt, &salt_size,
                                 &flags);
                 if (r == -EUCLEAN) /* Gracefully handle issues in JSON fields not owned by us */
                         continue;
@@ -243,6 +264,8 @@ int find_tpm2_auto_data(
                         *ret_blob_size = blob_size;
                         *ret_policy_hash = TAKE_PTR(policy_hash);
                         *ret_policy_hash_size = policy_hash_size;
+                        *ret_salt = TAKE_PTR(salt);
+                        *ret_salt_size = salt_size;
                         *ret_keyslot = keyslot;
                         *ret_token = token;
                         *ret_flags = flags;
index a34eb8443d4352842e8b8f42a6e50d761018e865..f6549b7d1d97aa87c2c14e4a7c459ab9ac260862 100644 (file)
@@ -28,6 +28,8 @@ int acquire_tpm2_key(
                 size_t key_data_size,
                 const void *policy_hash,
                 size_t policy_hash_size,
+                const void *salt,
+                size_t salt_size,
                 TPM2Flags flags,
                 usec_t until,
                 bool headless,
@@ -49,6 +51,8 @@ int find_tpm2_auto_data(
                 size_t *ret_blob_size,
                 void **ret_policy_hash,
                 size_t *ret_policy_hash_size,
+                void **ret_salt,
+                size_t *ret_salt_size,
                 TPM2Flags *ret_flags,
                 int *ret_keyslot,
                 int *ret_token);
@@ -72,6 +76,8 @@ static inline int acquire_tpm2_key(
                 size_t key_data_size,
                 const void *policy_hash,
                 size_t policy_hash_size,
+                const void *salt,
+                size_t salt_size,
                 TPM2Flags flags,
                 usec_t until,
                 bool headless,
@@ -97,6 +103,8 @@ static inline int find_tpm2_auto_data(
                 size_t *ret_blob_size,
                 void **ret_policy_hash,
                 size_t *ret_policy_hash_size,
+                void **ret_salt,
+                size_t *ret_salt_size,
                 TPM2Flags *ret_flags,
                 int *ret_keyslot,
                 int *ret_token) {
index a79a08e566bc58a34867de51b8996c6632d23145..38ee7f8935300294bbcd1a23454dc3c4deb5863f 100644 (file)
@@ -8,6 +8,7 @@
 #include <unistd.h>
 
 #include "sd-device.h"
+#include "sd-messages.h"
 
 #include "alloc-util.h"
 #include "ask-password-api.h"
@@ -18,6 +19,7 @@
 #include "cryptsetup-util.h"
 #include "device-util.h"
 #include "efi-api.h"
+#include "efi-loader.h"
 #include "env-util.h"
 #include "escape.h"
 #include "fileio.h"
@@ -38,6 +40,7 @@
 #include "random-util.h"
 #include "string-table.h"
 #include "strv.h"
+#include "tpm-pcr.h"
 #include "tpm2-util.h"
 
 /* internal helper */
@@ -89,13 +92,15 @@ static bool arg_fido2_device_auto = false;
 static void *arg_fido2_cid = NULL;
 static size_t arg_fido2_cid_size = 0;
 static char *arg_fido2_rp_id = NULL;
-static char *arg_tpm2_device = NULL;
+static char *arg_tpm2_device = NULL; /* These and the following fields are about locking an encrypted volume to the local TPM */
 static bool arg_tpm2_device_auto = false;
 static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
 static char *arg_tpm2_signature = NULL;
 static bool arg_tpm2_pin = false;
 static bool arg_headless = false;
 static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC;
+static unsigned arg_tpm2_measure_pcr = UINT_MAX; /* This and the following field is about measuring the unlocked volume key to the local TPM */
+static char **arg_tpm2_measure_banks = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
@@ -107,6 +112,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_fido2_cid, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_fido2_rp_id, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_measure_banks, strv_freep);
 
 static const char* const passphrase_type_table[_PASSPHRASE_TYPE_MAX] = {
         [PASSPHRASE_REGULAR] = "passphrase",
@@ -420,6 +426,48 @@ static int parse_one_option(const char *option) {
 
                 arg_tpm2_pin = r;
 
+        } else if ((val = startswith(option, "tpm2-measure-pcr="))) {
+                unsigned pcr;
+
+                r = safe_atou(val, &pcr);
+                if (r < 0) {
+                        r = parse_boolean(val);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+                                return 0;
+                        }
+
+                        pcr = r ? TPM_PCR_INDEX_VOLUME_KEY : UINT_MAX;
+                } else if (pcr >= TPM2_PCRS_MAX) {
+                        log_error("Selected TPM index for measurement %u outside of allowed range 0…%u, ignoring.", pcr, TPM2_PCRS_MAX-1);
+                        return 0;
+                }
+
+                arg_tpm2_measure_pcr = pcr;
+
+        } else if ((val = startswith(option, "tpm2-measure-bank="))) {
+
+#if HAVE_OPENSSL
+                _cleanup_strv_free_ char **l = NULL;
+
+                l = strv_split(optarg, ":");
+                if (!l)
+                        return log_oom();
+
+                STRV_FOREACH(i, l) {
+                        const EVP_MD *implementation;
+
+                        implementation = EVP_get_digestbyname(*i);
+                        if (!implementation)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown bank '%s', refusing.", val);
+
+                        if (strv_extend(&arg_tpm2_measure_banks, EVP_MD_name(implementation)) < 0)
+                                return log_oom();
+                }
+#else
+                log_error("Build lacks OpenSSL support, cannot measure to PCR banks, ignoring: %s", option);
+#endif
+
         } else if ((val = startswith(option, "try-empty-password="))) {
 
                 r = parse_boolean(val);
@@ -762,6 +810,157 @@ static int get_password(
         return 0;
 }
 
+static int measure_volume_key(
+                struct crypt_device *cd,
+                const char *name,
+                const void *volume_key,
+                size_t volume_key_size) {
+
+        int r;
+
+        assert(cd);
+        assert(name);
+        assert(volume_key);
+        assert(volume_key_size > 0);
+
+        if (arg_tpm2_measure_pcr == UINT_MAX) {
+                log_debug("Not measuring volume key, deactivated.");
+                return 0;
+        }
+
+        r = efi_stub_measured();
+        if (r < 0)
+                return log_warning_errno(r, "Failed to detect if we are running on a kernel image with TPM measurement enabled: %m");
+        if (r == 0) {
+                log_debug("Kernel stub did not measure kernel image into the expected PCR, skipping userspace measurement, too.");
+                return 0;
+        }
+
+#if HAVE_TPM2
+        r = dlopen_tpm2();
+        if (r < 0)
+                return log_error_errno(r, "Failed to load TPM2 libraries: %m");
+
+        _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
+        r = tpm2_context_init(arg_tpm2_device, &c);
+        if (r < 0)
+                return r;
+
+        _cleanup_strv_free_ char **l = NULL;
+        if (strv_isempty(arg_tpm2_measure_banks)) {
+                r = tpm2_get_good_pcr_banks_strv(c.esys_context, UINT32_C(1) << arg_tpm2_measure_pcr, &l);
+                if (r < 0)
+                        return r;
+        }
+
+        _cleanup_free_ char *joined = strv_join(l ?: arg_tpm2_measure_banks, ", ");
+        if (!joined)
+                return log_oom();
+
+        /* Note: we don't directly measure the volume key, it might be a security problem to send an
+         * unprotected direct hash of the secret volume key over the wire to the TPM. Hence let's instead
+         * send a HMAC signature instead. */
+
+        _cleanup_free_ char *escaped = NULL;
+        escaped = xescape(name, ":"); /* avoid ambiguity around ":" once we join things below */
+        if (!escaped)
+                return log_oom();
+
+        _cleanup_free_ char *s = NULL;
+        s = strjoin("cryptsetup:", escaped, ":", strempty(crypt_get_uuid(cd)));
+        if (!s)
+                return log_oom();
+
+        r = tpm2_extend_bytes(c.esys_context, l ?: arg_tpm2_measure_banks, arg_tpm2_measure_pcr, s, SIZE_MAX, volume_key, volume_key_size);
+        if (r < 0)
+                return r;
+
+        log_struct(LOG_INFO,
+                   "MESSAGE_ID=" SD_MESSAGE_TPM_PCR_EXTEND_STR,
+                   LOG_MESSAGE("Successfully extended PCR index %u with '%s' and volume key (banks %s).", arg_tpm2_measure_pcr, s, joined),
+                   "MEASURING=%s", s,
+                   "PCR=%u", arg_tpm2_measure_pcr,
+                   "BANKS=%s", joined);
+
+        return 0;
+#else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring.");
+#endif
+}
+
+static int measured_crypt_activate_by_volume_key(
+                struct crypt_device *cd,
+                const char *name,
+                const void *volume_key,
+                size_t volume_key_size,
+                uint32_t flags) {
+
+        int r;
+
+        assert(cd);
+        assert(name);
+
+        /* A wrapper around crypt_activate_by_volume_key() which also measures to a PCR if that's requested. */
+
+        r = crypt_activate_by_volume_key(cd, name, volume_key, volume_key_size, flags);
+        if (r < 0)
+                return r;
+
+        if (volume_key_size == 0) {
+                log_debug("Not measuring volume key, none specified.");
+                return r;
+        }
+
+        (void) measure_volume_key(cd, name, volume_key, volume_key_size); /* OK if fails */
+        return r;
+}
+
+static int measured_crypt_activate_by_passphrase(
+                struct crypt_device *cd,
+                const char *name,
+                int keyslot,
+                const char *passphrase,
+                size_t passphrase_size,
+                uint32_t flags) {
+
+        _cleanup_(erase_and_freep) void *vk = NULL;
+        size_t vks;
+        int r;
+
+        assert(cd);
+
+        /* A wrapper around crypt_activate_by_passphrase() which also measures to a PCR if that's
+         * requested. Note that we need the volume key for the measurement, and
+         * crypt_activate_by_passphrase() doesn't give us access to this. Hence, we operate indirectly, and
+         * retrieve the volume key first, and then activate through that. */
+
+        if (arg_tpm2_measure_pcr == UINT_MAX) {
+                log_debug("Not measuring volume key, deactivated.");
+                goto shortcut;
+        }
+
+        r = crypt_get_volume_key_size(cd);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                log_debug("Not measuring volume key, none defined.");
+                goto shortcut;
+        }
+
+        vk = malloc(vks = r);
+        if (!vk)
+                return -ENOMEM;
+
+        r = crypt_volume_key_get(cd, keyslot, vk, &vks, passphrase, passphrase_size);
+        if (r < 0)
+                return r;
+
+        return measured_crypt_activate_by_volume_key(cd, name, vk, vks, flags);
+
+shortcut:
+        return crypt_activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags);
+}
+
 static int attach_tcrypt(
                 struct crypt_device *cd,
                 const char *name,
@@ -830,7 +1029,7 @@ static int attach_tcrypt(
                 return log_error_errno(r, "Failed to load tcrypt superblock on device %s: %m", crypt_get_device_name(cd));
         }
 
-        r = crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
+        r = measured_crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
         if (r < 0)
                 return log_error_errno(r, "Failed to activate tcrypt device %s: %m", crypt_get_device_name(cd));
 
@@ -928,6 +1127,14 @@ static int run_security_device_monitor(
 }
 
 static bool libcryptsetup_plugins_support(void) {
+
+#if HAVE_TPM2
+        /* Currently, there's no way for us to query the volume key when plugins are used. Hence don't use
+         * plugins, if measurement has been requested. */
+        if (arg_tpm2_measure_pcr != UINT_MAX)
+                return false;
+#endif
+
 #if HAVE_LIBCRYPTSETUP_PLUGINS
         int r;
 
@@ -1157,17 +1364,18 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
         }
 
         if (pass_volume_key)
-                r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
+                r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
         else {
                 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+                ssize_t base64_encoded_size;
 
                 /* Before using this key as passphrase we base64 encode it, for compat with homed */
 
-                r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-                if (r < 0)
+                base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+                if (base64_encoded_size < 0)
                         return log_oom();
 
-                r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, strlen(base64_encoded), flags);
+                r = measured_crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, base64_encoded_size, flags);
         }
         if (r == -EPERM) {
                 log_error_errno(r, "Failed to activate with FIDO2 decrypted key. (Key incorrect?)");
@@ -1304,9 +1512,10 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
         assert(decrypted_key);
 
         if (pass_volume_key)
-                r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
+                r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
         else {
                 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+                ssize_t base64_encoded_size;
 
                 /* Before using this key as passphrase we base64 encode it. Why? For compatibility
                  * with homed's PKCS#11 hookup: there we want to use the key we acquired through
@@ -1316,11 +1525,11 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
                  * without embedded NUL here too, and that's easiest to generate from a binary blob
                  * via base64 encoding. */
 
-                r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-                if (r < 0)
+                base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+                if (base64_encoded_size < 0)
                         return log_oom();
 
-                r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, strlen(base64_encoded), flags);
+                r = measured_crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, base64_encoded_size, flags);
         }
         if (r == -EPERM) {
                 log_error_errno(r, "Failed to activate with PKCS#11 decrypted key. (Key incorrect?)");
@@ -1449,6 +1658,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                         key_file, arg_keyfile_size, arg_keyfile_offset,
                                         key_data, key_data_size,
                                         /* policy_hash= */ NULL, /* policy_hash_size= */ 0, /* we don't know the policy hash */
+                                        /* salt= */ NULL, /* salt_size= */ 0,
                                         arg_tpm2_pin ? TPM2_FLAGS_USE_PIN : 0,
                                         until,
                                         arg_headless,
@@ -1494,8 +1704,8 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                          * works. */
 
                         for (;;) {
-                                _cleanup_free_ void *pubkey = NULL;
-                                size_t pubkey_size = 0;
+                                _cleanup_free_ void *pubkey = NULL, *salt = NULL;
+                                size_t pubkey_size = 0, salt_size = 0;
                                 uint32_t hash_pcr_mask, pubkey_pcr_mask;
                                 uint16_t pcr_bank, primary_alg;
                                 TPM2Flags tpm2_flags;
@@ -1511,6 +1721,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                                 &primary_alg,
                                                 &blob, &blob_size,
                                                 &policy_hash, &policy_hash_size,
+                                                &salt, &salt_size,
                                                 &tpm2_flags,
                                                 &keyslot,
                                                 &token);
@@ -1540,6 +1751,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                                 /* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
                                                 blob, blob_size,
                                                 policy_hash, policy_hash_size,
+                                                salt, salt_size,
                                                 tpm2_flags,
                                                 until,
                                                 arg_headless,
@@ -1592,17 +1804,18 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
         assert(decrypted_key);
 
         if (pass_volume_key)
-                r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
+                r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
         else {
                 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+                ssize_t base64_encoded_size;
 
                 /* Before using this key as passphrase we base64 encode it, for compat with homed */
 
-                r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-                if (r < 0)
+                base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+                if (base64_encoded_size < 0)
                         return log_oom();
 
-                r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, strlen(base64_encoded), flags);
+                r = measured_crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, base64_encoded_size, flags);
         }
         if (r == -EPERM) {
                 log_error_errno(r, "Failed to activate with TPM2 decrypted key. (Key incorrect?)");
@@ -1629,9 +1842,9 @@ static int attach_luks_or_plain_or_bitlk_by_key_data(
         assert(key_data);
 
         if (pass_volume_key)
-                r = crypt_activate_by_volume_key(cd, name, key_data, key_data_size, flags);
+                r = measured_crypt_activate_by_volume_key(cd, name, key_data, key_data_size, flags);
         else
-                r = crypt_activate_by_passphrase(cd, name, arg_key_slot, key_data, key_data_size, flags);
+                r = measured_crypt_activate_by_passphrase(cd, name, arg_key_slot, key_data, key_data_size, flags);
         if (r == -EPERM) {
                 log_error_errno(r, "Failed to activate. (Key incorrect?)");
                 return -EAGAIN; /* Log actual error, but return EAGAIN */
@@ -1682,9 +1895,9 @@ static int attach_luks_or_plain_or_bitlk_by_key_file(
                 return log_error_errno(r, "Failed to read key file '%s': %m", key_file);
 
         if (pass_volume_key)
-                r = crypt_activate_by_volume_key(cd, name, kfdata, kfsize, flags);
+                r = measured_crypt_activate_by_volume_key(cd, name, kfdata, kfsize, flags);
         else
-                r = crypt_activate_by_passphrase(cd, name, arg_key_slot, kfdata, kfsize, flags);
+                r = measured_crypt_activate_by_passphrase(cd, name, arg_key_slot, kfdata, kfsize, flags);
         if (r == -EPERM) {
                 log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file);
                 return -EAGAIN; /* Log actual error, but return EAGAIN */
@@ -1710,9 +1923,9 @@ static int attach_luks_or_plain_or_bitlk_by_passphrase(
         r = -EINVAL;
         STRV_FOREACH(p, passwords) {
                 if (pass_volume_key)
-                        r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
+                        r = measured_crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
                 else
-                        r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
+                        r = measured_crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
                 if (r >= 0)
                         break;
         }
index 25272c5e1b9ceb44d23fcf345325a4af268370b2..f435932815d10f98f06695db1550f1f2826fd109 100644 (file)
@@ -601,9 +601,13 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
         else if (arg_json_format_flags & JSON_FORMAT_OFF)
                 printf("      Size: %s\n", FORMAT_BYTES(size));
 
+        printf(" Sec. Size: %" PRIu32 "\n", m->sector_size);
+
         if (arg_json_format_flags & JSON_FORMAT_OFF)
                 putc('\n', stdout);
 
+        fflush(stdout);
+
         r = dissected_image_acquire_metadata(m, 0);
         if (r == -ENXIO)
                 return log_error_errno(r, "No root partition discovered.");
@@ -704,6 +708,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
                                                JSON_BUILD_PAIR("name", JSON_BUILD_STRING(bn)),
                                                JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->image_uuid), "imageUuid", JSON_BUILD_UUID(m->image_uuid)),
                                                JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)),
+                                               JSON_BUILD_PAIR("sectorSize", JSON_BUILD_INTEGER(m->sector_size)),
                                                JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)),
                                                JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)),
                                                JSON_BUILD_PAIR_CONDITION(mi, "machineInfo", JSON_BUILD_VARIANT(mi)),
@@ -1437,9 +1442,9 @@ static int run(int argc, char *argv[]) {
         loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN;
 
         if (arg_in_memory)
-                r = loop_device_make_by_path_memory(arg_image, open_flags, loop_flags, LOCK_SH, &d);
+                r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
         else
-                r = loop_device_make_by_path(arg_image, open_flags, loop_flags, LOCK_SH, &d);
+                r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
         if (r < 0)
                 return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
 
index b0ea536eb29778ca7f1f4a770210cb7572e1e612..ed34e0a32fc5ea4294e8a868f2f794adaaaa72a2 100644 (file)
@@ -8,6 +8,7 @@
 #include "bus-error.h"
 #include "bus-locator.h"
 #include "chase-symlinks.h"
+#include "efi-loader.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fstab-util.h"
@@ -40,6 +41,7 @@ typedef enum MountPointFlags {
         MOUNT_MAKEFS    = 1 << 3,
         MOUNT_GROWFS    = 1 << 4,
         MOUNT_RW_ONLY   = 1 << 5,
+        MOUNT_PCRFS     = 1 << 6,
 } MountPointFlags;
 
 static bool arg_sysroot_check = false;
@@ -176,6 +178,8 @@ static int add_swap(
         if (flags & MOUNT_GROWFS)
                 /* TODO: swap devices must be wiped and recreated */
                 log_warning("%s: growing swap devices is currently unsupported.", what);
+        if (flags & MOUNT_PCRFS)
+                log_warning("%s: measuring swap devices is currently unsupported.", what);
 
         if (!(flags & MOUNT_NOAUTO)) {
                 r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET,
@@ -525,6 +529,19 @@ static int add_mount(
                         return r;
         }
 
+        if (flags & MOUNT_PCRFS) {
+                r = efi_stub_measured();
+                if (r < 0)
+                        log_warning_errno(r, "Failed to detect if we are running on a kernel image with TPM measurement enabled, assuming not: %m");
+                else if (r == 0)
+                        log_debug("Kernel stub did not measure kernel image into PCR, skipping userspace measurement, too.");
+                else {
+                        r = generator_hook_up_pcrfs(dest, where, target_unit);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
         if (!FLAGS_SET(flags, MOUNT_AUTOMOUNT)) {
                 if (!FLAGS_SET(flags, MOUNT_NOAUTO) && strv_isempty(wanted_by) && strv_isempty(required_by)) {
                         r = generator_add_symlink(dest, target_unit,
@@ -658,7 +675,7 @@ static int parse_fstab(bool initrd) {
 
         while ((me = getmntent(f))) {
                 _cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL;
-                bool makefs, growfs, noauto, nofail;
+                bool makefs, growfs, pcrfs, noauto, nofail;
                 MountPointFlags flags;
                 int k;
 
@@ -718,16 +735,18 @@ static int parse_fstab(bool initrd) {
 
                 makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0");
                 growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0");
+                pcrfs = fstab_test_option(me->mnt_opts, "x-systemd.pcrfs\0");
                 noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
                 nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
 
-                log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s noauto=%s nofail=%s",
+                log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s",
                           what, where, me->mnt_type,
-                          yes_no(makefs), yes_no(growfs),
+                          yes_no(makefs), yes_no(growfs), yes_no(pcrfs),
                           yes_no(noauto), yes_no(nofail));
 
                 flags = makefs * MOUNT_MAKEFS |
                         growfs * MOUNT_GROWFS |
+                        pcrfs * MOUNT_PCRFS |
                         noauto * MOUNT_NOAUTO |
                         nofail * MOUNT_NOFAIL;
 
@@ -911,7 +930,7 @@ static int add_sysroot_mount(void) {
                          fstype,
                          opts,
                          is_device_path(what) ? 1 : 0, /* passno */
-                         flags,                        /* makefs, growfs off, noauto off, nofail off, automount off */
+                         flags,                        /* makefs off, pcrfs off, noauto off, nofail off, automount off */
                          SPECIAL_INITRD_ROOT_FS_TARGET);
 }
 
index 71c6283e10d68a3afbc4f53b187d9a3453354ed6..b2bc435bd35072798aa960edbd32e919a3a5f32f 100644 (file)
                 #define assert(expr)
                 #define assert_not_reached() __builtin_unreachable()
         #else
-                #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
-                #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+                #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
+                #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__)
         #endif
         #define static_assert _Static_assert
-        #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
+        #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
 #endif
 
 /* This passes the argument through after (if asserts are enabled) checking that it is not null. */
index 67621fdb4243f51dcf74c4fa3cc9527ee61d8998..78e2dbec5985f0ca7d715e533ab3412c407ec8db 100644 (file)
@@ -29,6 +29,8 @@ static inline void *explicit_bzero_safe(void *p, size_t l) {
 #endif
 
 struct VarEraser {
+        /* NB: This is a pointer to memory to erase in case of CLEANUP_ERASE(). Pointer to pointer to memory
+         * to erase in case of CLEANUP_ERASE_PTR() */
         void *p;
         size_t size;
 };
@@ -38,5 +40,27 @@ static inline void erase_var(struct VarEraser *e) {
 }
 
 /* Mark var to be erased when leaving scope. */
-#define CLEANUP_ERASE(var) \
-        _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { .p = &var, .size = sizeof(var) }
+#define CLEANUP_ERASE(var)                                              \
+        _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \
+                .p = &(var),                                            \
+                .size = sizeof(var),                                    \
+        }
+
+static inline void erase_varp(struct VarEraser *e) {
+
+        /* Very similar to erase_var(), but assumes `p` is a pointer to a pointer whose memory shall be destructed. */
+        if (!e->p)
+                return;
+
+        explicit_bzero_safe(*(void**) e->p, e->size);
+}
+
+/* Mark pointer so that memory pointed to is erased when leaving scope. Note: this takes a pointer to the
+ * specified pointer, instead of just a copy of it. This is to allow callers to invalidate the pointer after
+ * use, if they like, disabling our automatic erasure (for example because they succeeded with whatever they
+ * wanted to do and now intend to return the allocated buffer to their caller without it being erased). */
+#define CLEANUP_ERASE_PTR(ptr, sz)                                      \
+        _cleanup_(erase_varp) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \
+                .p = (ptr),                                             \
+                .size = (sz),                                           \
+        }
index d57291328dd408faf0765a8c4ac8caaec5495426..e12b4ff6077cfa8fa8c3375b5314a2c858c770a6 100644 (file)
@@ -17,6 +17,9 @@
 /* This TPM PCR is where we extend the initrd sysext images into which we pass to the booted kernel */
 #define TPM_PCR_INDEX_INITRD_SYSEXTS 13U
 
+/* This TPM PCR is where we measure the root fs volume key (and maybe /var/'s) if it is split off */
+#define TPM_PCR_INDEX_VOLUME_KEY 15U
+
 /* List of PE sections that have special meaning for us in unified kernels. This is the canonical order in
  * which we measure the sections into TPM PCR 11 (see above). PLEASE DO NOT REORDER! */
 typedef enum UnifiedSection {
index 458fd054ef7a4fd34f0d1ae28847d1a21367cfb4..dcf95a3ee1e27edfcda35ad1239792204f8bdca6 100644 (file)
@@ -52,10 +52,11 @@ static int add_cryptsetup(
                 const char *what,
                 bool rw,
                 bool require,
+                bool measure,
                 char **ret_device) {
 
 #if HAVE_LIBCRYPTSETUP
-        _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL;
+        _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL, *options = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
 
@@ -89,7 +90,28 @@ static int add_cryptsetup(
                 "After=%s\n",
                 d, d);
 
-        r = generator_write_cryptsetup_service_section(f, id, what, NULL, rw ? NULL : "read-only");
+        if (!rw) {
+                options = strdup("read-only");
+                if (!options)
+                        return log_oom();
+        }
+
+        if (measure) {
+                /* We only measure the root volume key into PCR 15 if we are booted with sd-stub (i.e. in a
+                 * UKI), and sd-stub measured the UKI. We do this in order not to step into people's own PCR
+                 * assignment, under the assumption that people who are fine to use sd-stub with its PCR
+                 * assignments are also OK with our PCR 15 use here. */
+
+                r = efi_stub_measured();
+                if (r < 0)
+                        log_warning_errno(r, "Failed to determine whether booted via systemd-stub with measurements enabled, ignoring: %m");
+                else if (r == 0)
+                        log_debug("Will not measure volume key of volume '%s', because not booted via systemd-stub with measurements enabled.", id);
+                else if (!strextend_with_separator(&options, ",", "tpm2-measure-pcr=yes"))
+                        return log_oom();
+        }
+
+        r = generator_write_cryptsetup_service_section(f, id, what, NULL, options);
         if (r < 0)
                 return r;
 
@@ -144,6 +166,7 @@ static int add_mount(
                 const char *fstype,
                 bool rw,
                 bool growfs,
+                bool measure,
                 const char *options,
                 const char *description,
                 const char *post) {
@@ -164,7 +187,7 @@ static int add_mount(
         log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)");
 
         if (streq_ptr(fstype, "crypto_LUKS")) {
-                r = add_cryptsetup(id, what, rw, true, &crypto_what);
+                r = add_cryptsetup(id, what, rw, /* require= */ true, measure, &crypto_what);
                 if (r < 0)
                         return r;
 
@@ -236,6 +259,12 @@ static int add_mount(
                         return r;
         }
 
+        if (measure) {
+                r = generator_hook_up_pcrfs(arg_dest, where, post);
+                if (r < 0)
+                        return r;
+        }
+
         if (post) {
                 r = generator_add_symlink(arg_dest, post, "requires", unit);
                 if (r < 0)
@@ -291,6 +320,7 @@ static int add_partition_mount(
                         p->fstype,
                         p->rw,
                         p->growfs,
+                        /* measure= */ STR_IN_SET(id, "root", "var"), /* by default measure rootfs and /var, since they contain the "identity" of the system */
                         NULL,
                         description,
                         SPECIAL_LOCAL_FS_TARGET);
@@ -315,7 +345,7 @@ static int add_partition_swap(DissectedPartition *p) {
         }
 
         if (streq_ptr(p->fstype, "crypto_LUKS")) {
-                r = add_cryptsetup("swap", p->node, true, true, &crypto_what);
+                r = add_cryptsetup("swap", p->node, /* rw= */ true, /* require= */ true, /* measure= */ false, &crypto_what);
                 if (r < 0)
                         return r;
                 what = crypto_what;
@@ -384,6 +414,7 @@ static int add_automount(
                       fstype,
                       rw,
                       growfs,
+                      /* measure= */ false,
                       options,
                       description,
                       NULL);
@@ -424,14 +455,14 @@ static int add_automount(
 static const char *esp_or_xbootldr_options(const DissectedPartition *p) {
         assert(p);
 
-        /* if we probed vfat or have no idea about the file system then assume these file systems are vfat
-         * and thus understand "umask=0077". If we detected something else then don't specify any options and
-         * use kernel defaults. */
+        /* Discovered ESP and XBOOTLDR partition are always hardened with "noexec,nosuid,nodev".
+         * If we probed vfat or have no idea about the file system then assume these file systems are vfat
+         * and thus understand "umask=0077". */
 
         if (!p->fstype || streq(p->fstype, "vfat"))
-                return "umask=0077";
+                return "umask=0077,noexec,nosuid,nodev";
 
-        return NULL;
+        return "noexec,nosuid,nodev";
 }
 
 static int add_partition_xbootldr(DissectedPartition *p) {
@@ -592,7 +623,7 @@ static int add_root_cryptsetup(void) {
         /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
          * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
 
-        return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
+        return add_cryptsetup("root", "/dev/gpt-auto-root-luks", /* rw= */ true, /* require= */ false, /* measure= */ true, NULL);
 #else
         return 0;
 #endif
@@ -639,6 +670,7 @@ static int add_root_mount(void) {
                         arg_root_fstype,
                         /* rw= */ arg_root_rw > 0,
                         /* growfs= */ false,
+                        /* measure= */ true,
                         arg_root_options,
                         "Root Partition",
                         in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
index 61f0d081a3732ebc20c3bbd9b5c416419b68d6e8..3cbdf912aae08f5d2d08e2c44a75dcc9e7f4513c 100644 (file)
@@ -26,14 +26,15 @@ static int add_fido2_credential_id(
         _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
         _cleanup_strv_free_ char **l = NULL;
         _cleanup_free_ char *escaped = NULL;
+        ssize_t escaped_size;
         int r;
 
         assert(v);
         assert(cid);
 
-        r = base64mem(cid, cid_size, &escaped);
-        if (r < 0)
-                return log_error_errno(r, "Failed to base64 encode FIDO2 credential ID: %m");
+        escaped_size = base64mem(cid, cid_size, &escaped);
+        if (escaped_size < 0)
+                return log_error_errno(escaped_size, "Failed to base64 encode FIDO2 credential ID: %m");
 
         w = json_variant_ref(json_variant_by_key(*v, "fido2HmacCredential"));
         if (w) {
@@ -73,13 +74,14 @@ static int add_fido2_salt(
 
         _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
         _cleanup_(erase_and_freep) char *base64_encoded = NULL, *hashed = NULL;
+        ssize_t base64_encoded_size;
         int r;
 
         /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
          * expect a NUL terminated string, and we use a binary key */
-        r = base64mem(secret, secret_size, &base64_encoded);
-        if (r < 0)
-                return log_error_errno(r, "Failed to base64 encode secret key: %m");
+        base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+        if (base64_encoded_size < 0)
+                return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
 
         r = hash_password(base64_encoded, &hashed);
         if (r < 0)
index 69c9d97acada63cd9507a88723533ec5f6c3e950..dc6ecf166562778477506ed76bac43f1bbe37493 100644 (file)
@@ -19,6 +19,7 @@ static int add_pkcs11_encrypted_key(
 
         _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
         _cleanup_(erase_and_freep) char *base64_encoded = NULL, *hashed = NULL;
+        ssize_t base64_encoded_size;
         int r;
 
         assert(v);
@@ -30,9 +31,9 @@ static int add_pkcs11_encrypted_key(
 
         /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
          * expect a NUL terminated string, and we use a binary key */
-        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-        if (r < 0)
-                return log_error_errno(r, "Failed to base64 encode secret key: %m");
+        base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        if (base64_encoded_size < 0)
+                return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
 
         r = hash_password(base64_encoded, &hashed);
         if (r < 0)
index 0a4c01834aa3465c7d1a0da15e5f9048fab1b532..fd71e98eef6dab4d5bc9face0508dc17b9eeb680 100644 (file)
@@ -1798,26 +1798,6 @@ static int parse_disk_size(const char *t, uint64_t *ret) {
         return 0;
 }
 
-static int parse_sector_size(const char *t, uint64_t *ret) {
-        int r;
-
-        assert(t);
-        assert(ret);
-
-        uint64_t ss;
-
-        r = safe_atou64(t, &ss);
-        if (r < 0)
-                return log_error_errno(r, "Failed to parse sector size parameter %s", t);
-        if (ss < 512 || ss > 4096) /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */
-                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Sector size not between 512 and 4096: %s", t);
-        if (!ISPOWEROF2(ss))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size not power of 2: %s", t);
-
-        *ret = ss;
-        return 0;
-}
-
 static int resize_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
index 23fda4a355af227354933d7351b331a8f437936d..5c7cd52e1b8869b04c76269fa29ee85e8ef8bea1 100644 (file)
@@ -17,6 +17,7 @@ int fido2_use_token(
         _cleanup_(erase_and_freep) void *hmac = NULL;
         size_t hmac_size;
         Fido2EnrollFlags flags = 0;
+        ssize_t ss;
         int r;
 
         assert(h);
@@ -65,9 +66,9 @@ int fido2_use_token(
         if (r < 0)
                 return r;
 
-        r = base64mem(hmac, hmac_size, ret);
-        if (r < 0)
-                return log_error_errno(r, "Failed to base64 encode HMAC secret: %m");
+        ss = base64mem(hmac, hmac_size, ret);
+        if (ss < 0)
+                return log_error_errno(ss, "Failed to base64 encode HMAC secret: %m");
 
         return 0;
 }
index f8f9f462bbca967766f640fcb5e8b35c377c9008..8b7fdda5b1adbefc8466dec365c1ca35017e48fd 100644 (file)
@@ -58,10 +58,10 @@ static int fscrypt_upload_volume_key(
         };
         memcpy(key.raw, volume_key, volume_key_size);
 
+        CLEANUP_ERASE(key);
+
         /* Upload to the kernel */
         serial = add_key("logon", description, &key, sizeof(key), where);
-        explicit_bzero_safe(&key, sizeof(key));
-
         if (serial < 0)
                 return log_error_errno(errno, "Failed to install master key in keyring: %m");
 
@@ -124,20 +124,18 @@ static int fscrypt_slot_try_one(
          *      resulting hash.
          */
 
+        CLEANUP_ERASE(derived);
+
         if (PKCS5_PBKDF2_HMAC(
                             password, strlen(password),
                             salt, salt_size,
                             0xFFFF, EVP_sha512(),
-                            sizeof(derived), derived) != 1) {
-                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
-                goto finish;
-        }
+                            sizeof(derived), derived) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
 
         context = EVP_CIPHER_CTX_new();
-        if (!context) {
-                r = log_oom();
-                goto finish;
-        }
+        if (!context)
+                return log_oom();
 
         /* We use AES256 in counter mode */
         assert_se(cc = EVP_aes_256_ctr());
@@ -145,13 +143,8 @@ static int fscrypt_slot_try_one(
         /* We only use the first half of the derived key */
         assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc));
 
-        if (EVP_DecryptInit_ex(context, cc, NULL, derived, NULL) != 1)  {
-                r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context.");
-                goto finish;
-        }
-
-        /* Flush out the derived key now, we don't need it anymore */
-        explicit_bzero_safe(derived, sizeof(derived));
+        if (EVP_DecryptInit_ex(context, cc, NULL, derived, NULL) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context.");
 
         decrypted_size = encrypted_size + EVP_CIPHER_key_length(cc) * 2;
         decrypted = malloc(decrypted_size);
@@ -184,10 +177,6 @@ static int fscrypt_slot_try_one(
                 *ret_decrypted_size = decrypted_size;
 
         return 0;
-
-finish:
-        explicit_bzero_safe(derived, sizeof(derived));
-        return r;
 }
 
 static int fscrypt_slot_try_many(
@@ -407,25 +396,24 @@ static int fscrypt_slot_set(
         _cleanup_free_ void *encrypted = NULL;
         const EVP_CIPHER *cc;
         size_t encrypted_size;
+        ssize_t ss;
 
         r = crypto_random_bytes(salt, sizeof(salt));
         if (r < 0)
                 return log_error_errno(r, "Failed to generate salt: %m");
 
+        CLEANUP_ERASE(derived);
+
         if (PKCS5_PBKDF2_HMAC(
                             password, strlen(password),
                             salt, sizeof(salt),
                             0xFFFF, EVP_sha512(),
-                            sizeof(derived), derived) != 1) {
-                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
-                goto finish;
-        }
+                            sizeof(derived), derived) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
 
         context = EVP_CIPHER_CTX_new();
-        if (!context) {
-                r = log_oom();
-                goto finish;
-        }
+        if (!context)
+                return log_oom();
 
         /* We use AES256 in counter mode */
         cc = EVP_aes_256_ctr();
@@ -433,13 +421,8 @@ static int fscrypt_slot_set(
         /* We only use the first half of the derived key */
         assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc));
 
-        if (EVP_EncryptInit_ex(context, cc, NULL, derived, NULL) != 1)  {
-                r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context.");
-                goto finish;
-        }
-
-        /* Flush out the derived key now, we don't need it anymore */
-        explicit_bzero_safe(derived, sizeof(derived));
+        if (EVP_EncryptInit_ex(context, cc, NULL, derived, NULL) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context.");
 
         encrypted_size = volume_key_size + EVP_CIPHER_key_length(cc) * 2;
         encrypted = malloc(encrypted_size);
@@ -457,12 +440,12 @@ static int fscrypt_slot_set(
         assert((size_t) encrypted_size_out1 + (size_t) encrypted_size_out2 < encrypted_size);
         encrypted_size = (size_t) encrypted_size_out1 + (size_t) encrypted_size_out2;
 
-        r = base64mem(salt, sizeof(salt), &salt_base64);
-        if (r < 0)
+        ss = base64mem(salt, sizeof(salt), &salt_base64);
+        if (ss < 0)
                 return log_oom();
 
-        r = base64mem(encrypted, encrypted_size, &encrypted_base64);
-        if (r < 0)
+        ss = base64mem(encrypted, encrypted_size, &encrypted_base64);
+        if (ss < 0)
                 return log_oom();
 
         joined = strjoin(salt_base64, ":", encrypted_base64);
@@ -476,10 +459,6 @@ static int fscrypt_slot_set(
         log_info("Written key slot %s.", label);
 
         return 0;
-
-finish:
-        explicit_bzero_safe(derived, sizeof(derived));
-        return r;
 }
 
 int home_create_fscrypt(
index f215558ae04b0a4d1779aa1b40568201398d57fd..2ea9887853737b052b09df193dfe53f092c5be6b 100644 (file)
@@ -1378,7 +1378,15 @@ int home_setup_luks(
                                 return r;
                 }
 
-                r = loop_device_make(setup->image_fd, O_RDWR, offset, size, user_record_luks_sector_size(h), 0, LOCK_UN, &setup->loop);
+                r = loop_device_make(
+                                setup->image_fd,
+                                O_RDWR,
+                                offset,
+                                size,
+                                h->luks_sector_size == UINT64_MAX ? UINT32_MAX : user_record_luks_sector_size(h), /* if sector size is not specified, select UINT32_MAX, i.e. auto-probe */
+                                /* loop_flags= */ 0,
+                                LOCK_UN,
+                                &setup->loop);
                 if (r == -ENOENT) {
                         log_error_errno(r, "Loopback block device support is not available on this system.");
                         return -ENOLINK; /* make recognizable */
@@ -1832,6 +1840,7 @@ static int luks_format(
 
 static int make_partition_table(
                 int fd,
+                uint32_t sector_size,
                 const char *label,
                 sd_id128_t uuid,
                 uint64_t *ret_offset,
@@ -1859,7 +1868,7 @@ static int make_partition_table(
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize partition type: %m");
 
-        r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
+        r = fdisk_new_context_fd(fd, /* read_only= */ false, sector_size, &c);
         if (r < 0)
                 return log_error_errno(r, "Failed to open device: %m");
 
@@ -2309,6 +2318,7 @@ int home_create_luks(
 
         r = make_partition_table(
                         setup->image_fd,
+                        user_record_luks_sector_size(h),
                         user_record_user_name_and_realm(h),
                         partition_uuid,
                         &partition_offset,
@@ -2319,7 +2329,15 @@ int home_create_luks(
 
         log_info("Writing of partition table completed.");
 
-        r = loop_device_make(setup->image_fd, O_RDWR, partition_offset, partition_size, user_record_luks_sector_size(h), 0, LOCK_EX, &setup->loop);
+        r = loop_device_make(
+                        setup->image_fd,
+                        O_RDWR,
+                        partition_offset,
+                        partition_size,
+                        user_record_luks_sector_size(h),
+                        0,
+                        LOCK_EX,
+                        &setup->loop);
         if (r < 0) {
                 if (r == -ENOENT) { /* this means /dev/loop-control doesn't exist, i.e. we are in a container
                                      * or similar and loopback bock devices are not available, return a
@@ -2356,7 +2374,7 @@ int home_create_luks(
         r = mkfs_options_for_fstype(fstype, &extra_mkfs_options);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine mkfs command line options for '%s': %m", fstype);
-        r = make_filesystem(setup->dm_node, fstype, user_record_user_name_and_realm(h), NULL, fs_uuid, user_record_luks_discard(h), extra_mkfs_options);
+        r = make_filesystem(setup->dm_node, fstype, user_record_user_name_and_realm(h), NULL, fs_uuid, user_record_luks_discard(h), 0, extra_mkfs_options);
         if (r < 0)
                 return r;
 
@@ -2688,7 +2706,7 @@ static int prepare_resize_partition(
                 return 0;
         }
 
-        r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
+        r = fdisk_new_context_fd(fd, /* read_only= */ false, UINT32_MAX, &c);
         if (r < 0)
                 return log_error_errno(r, "Failed to open device: %m");
 
@@ -2772,6 +2790,7 @@ static int apply_resize_partition(
 
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         _cleanup_free_ void *two_zero_lbas = NULL;
+        uint32_t ssz;
         ssize_t n;
         int r;
 
@@ -2792,18 +2811,22 @@ static int apply_resize_partition(
         if (r < 0)
                 return log_error_errno(r, "Failed to change partition size: %m");
 
-        two_zero_lbas = malloc0(1024U);
+        r = probe_sector_size(fd, &ssz);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine current sector size: %m");
+
+        two_zero_lbas = malloc0(ssz * 2);
         if (!two_zero_lbas)
                 return log_oom();
 
         /* libfdisk appears to get confused by the existing PMBR. Let's explicitly flush it out. */
-        n = pwrite(fd, two_zero_lbas, 1024U, 0);
+        n = pwrite(fd, two_zero_lbas, ssz * 2, 0);
         if (n < 0)
                 return log_error_errno(errno, "Failed to wipe partition table: %m");
-        if (n != 1024)
+        if ((size_t) n != ssz * 2)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while wiping partition table.");
 
-        r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
+        r = fdisk_new_context_fd(fd, /* read_only= */ false, ssz, &c);
         if (r < 0)
                 return log_error_errno(r, "Failed to open device: %m");
 
index 52606d2f4046094ba374147c14cc4be4c3283067..c77e2e8fd9a2d84d9a0fd4291a078dd47b02a704 100644 (file)
@@ -3,6 +3,7 @@
 #include <getopt.h>
 #include <locale.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -52,6 +53,7 @@ typedef struct StatusInfo {
         const char *hardware_vendor;
         const char *hardware_model;
         const char *firmware_version;
+        usec_t firmware_date;
 } StatusInfo;
 
 static const char* chassis_string_to_glyph(const char *chassis) {
@@ -239,6 +241,14 @@ static int print_status_info(StatusInfo *i) {
                         return table_log_add_error(r);
         }
 
+        if (timestamp_is_set(i->firmware_date)) {
+                r = table_add_many(table,
+                                   TABLE_FIELD, "Firmware Date",
+                                   TABLE_TIMESTAMP_DATE, i->firmware_date);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
         r = table_print(table, NULL);
         if (r < 0)
                 return table_log_print_error(r);
@@ -304,6 +314,7 @@ static int show_all_names(sd_bus *bus) {
                 { "HardwareVendor",            "s", NULL, offsetof(StatusInfo, hardware_vendor)  },
                 { "HardwareModel",             "s", NULL, offsetof(StatusInfo, hardware_model)   },
                 { "FirmwareVersion",           "s", NULL, offsetof(StatusInfo, firmware_version) },
+                { "FirmwareDate",              "t", NULL, offsetof(StatusInfo, firmware_date)    },
                 {}
         };
 
index ea19dfbb0412b397e8158ac133b87424b7ce19ba..a9782de4ff9a5396966e3eb7295535f69776d6a6 100644 (file)
@@ -250,8 +250,64 @@ static int get_firmware_vendor(char **ret) {
          return get_hardware_firmware_data("bios_vendor", ret);
 }
 
-static int get_firmware_date(char **ret) {
-         return get_hardware_firmware_data("bios_date", ret);
+static int get_firmware_date(usec_t *ret) {
+         _cleanup_free_ char *bios_date = NULL, *month = NULL, *day = NULL, *year = NULL;
+         int r;
+
+         assert(ret);
+
+         r = get_hardware_firmware_data("bios_date", &bios_date);
+         if (r < 0)
+                return r;
+         if (r == 0) {
+                *ret = USEC_INFINITY;
+                return 0;
+         }
+
+         const char *p = bios_date;
+         r = extract_many_words(&p, "/", EXTRACT_DONT_COALESCE_SEPARATORS, &month, &day, &year, NULL);
+         if (r < 0)
+                return r;
+         if (r != 3) /* less than three args read? */
+                return -EINVAL;
+         if (!isempty(p)) /* more left in the string? */
+                return -EINVAL;
+
+         unsigned m, d, y;
+         r = safe_atou(month, &m);
+         if (r < 0)
+                return r;
+         if (m < 1 || m > 12)
+                return -EINVAL;
+         m -= 1;
+
+         r = safe_atou(day, &d);
+         if (r < 0)
+                return r;
+         if (d < 1 || d > 31)
+                return -EINVAL;
+
+         r = safe_atou(year, &y);
+         if (r < 0)
+                return r;
+         if (y < 1970 || y > (unsigned) INT_MAX)
+                return -EINVAL;
+         y -= 1900;
+
+         struct tm tm = {
+                .tm_mday = d,
+                .tm_mon = m,
+                .tm_year = y,
+         };
+         time_t v = timegm(&tm);
+         if (v == (time_t) -1)
+                return -errno;
+         if (tm.tm_mday != (int) d || tm.tm_mon != (int) m || tm.tm_year != (int) y)
+                return -EINVAL; /* date was not normalized? (e.g. "30th of feb") */
+
+         *ret = (usec_t) v * USEC_PER_SEC;
+
+         return 0;
 }
 
 static const char* valid_chassis(const char *chassis) {
@@ -661,11 +717,11 @@ static int property_get_firmware_date(
                 void *userdata,
                 sd_bus_error *error) {
 
-        _cleanup_free_ char *firmware_date = NULL;
+        usec_t firmware_date = USEC_INFINITY;
 
         (void) get_firmware_date(&firmware_date);
 
-        return sd_bus_message_append(reply, "s", firmware_date);
+        return sd_bus_message_append(reply, "t", firmware_date);
 }
 static int property_get_hostname(
                 sd_bus *bus,
@@ -1189,7 +1245,8 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_
 static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
                 *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL,
-                *firmware_vendor = NULL, *firmware_date = NULL;
+                *firmware_vendor = NULL;
+        usec_t firmware_date = USEC_INFINITY;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         sd_id128_t product_uuid = SD_ID128_NULL;
@@ -1277,7 +1334,7 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
                                        JSON_BUILD_PAIR("HardwareSerial", JSON_BUILD_STRING(serial)),
                                        JSON_BUILD_PAIR("FirmwareVersion", JSON_BUILD_STRING(firmware_version)),
                                        JSON_BUILD_PAIR("FirmwareVendor", JSON_BUILD_STRING(firmware_vendor)),
-                                       JSON_BUILD_PAIR("FirmwareDate", JSON_BUILD_STRING(firmware_date)),
+                                       JSON_BUILD_PAIR_FINITE_USEC("FirmwareDate", firmware_date),
                                        JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
                                        JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL)));
 
@@ -1320,7 +1377,7 @@ static const sd_bus_vtable hostname_vtable[] = {
         SD_BUS_PROPERTY("HardwareModel", "s", property_get_hardware_model, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("FirmwareVersion", "s", property_get_firmware_version, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("FirmwareVendor", "s", property_get_firmware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("FirmwareDate", "s", property_get_firmware_date, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("FirmwareDate", "t", property_get_firmware_date, 0, SD_BUS_VTABLE_PROPERTY_CONST),
 
         SD_BUS_METHOD_WITH_ARGS("SetHostname",
                                 SD_BUS_ARGS("s", hostname, "b", interactive),
index dd285179247fd4baaaae531a3cd0c1bd163b41fa..b2883ff177ba091f76c063e043c1bd47ed5e242f 100644 (file)
@@ -129,7 +129,7 @@ static const char *arg_namespace = NULL;
 static uint64_t arg_vacuum_size = 0;
 static uint64_t arg_vacuum_n_files = 0;
 static usec_t arg_vacuum_time = 0;
-static char **arg_output_fields = NULL;
+static Set *arg_output_fields = NULL;
 static const char *arg_pattern = NULL;
 static pcre2_code *arg_compiled_pattern = NULL;
 static PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO;
@@ -142,7 +142,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_system_units, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_user_units, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
-STATIC_DESTRUCTOR_REGISTER(arg_output_fields, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_output_fields, set_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, pattern_freep);
 
 static enum {
@@ -1026,13 +1026,10 @@ static int parse_argv(int argc, char *argv[]) {
                         if (!v)
                                 return log_oom();
 
-                        if (!arg_output_fields)
-                                arg_output_fields = TAKE_PTR(v);
-                        else {
-                                r = strv_extend_strv(&arg_output_fields, v, true);
-                                if (r < 0)
-                                        return log_oom();
-                        }
+                        r = set_put_strdupv(&arg_output_fields, v);
+                        if (r < 0)
+                                return log_oom();
+
                         break;
                 }
 
index b59f42c4b8dc93fefdcd35c71d38b049a95f72d7..d31da2d129519f0b20e57356e3fa65af58a6e2e9 100644 (file)
@@ -416,9 +416,44 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
         return r;
 }
 
-static ManagedJournalFile* find_journal(Server *s, uid_t uid) {
+static int find_user_journal(Server *s, uid_t uid, ManagedJournalFile **ret) {
+        _cleanup_(managed_journal_file_closep) ManagedJournalFile *f = NULL;
         _cleanup_free_ char *p = NULL;
-        ManagedJournalFile *f;
+        int r;
+
+        assert(!uid_for_system_journal(uid));
+
+        f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(uid));
+        if (f)
+                goto found;
+
+        if (asprintf(&p, "%s/user-" UID_FMT ".journal", s->system_storage.path, uid) < 0)
+                return log_oom();
+
+        /* Too many open? Then let's close one (or more) */
+        while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
+                ManagedJournalFile *first;
+
+                assert_se(first = ordered_hashmap_steal_first(s->user_journals));
+                (void) managed_journal_file_close(first);
+        }
+
+        r = open_journal(s, true, p, O_RDWR|O_CREAT, s->seal, &s->system_storage.metrics, &f);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f);
+        if (r < 0)
+                return r;
+
+        server_add_acls(f, uid);
+
+found:
+        *ret = TAKE_PTR(f);
+        return 0;
+}
+
+static ManagedJournalFile* find_journal(Server *s, uid_t uid) {
         int r;
 
         assert(s);
@@ -446,36 +481,17 @@ static ManagedJournalFile* find_journal(Server *s, uid_t uid) {
         if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
                 return NULL;
 
-        if (uid_for_system_journal(uid))
-                return s->system_journal;
-
-        f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(uid));
-        if (f)
-                return f;
-
-        if (asprintf(&p, "%s/user-" UID_FMT ".journal", s->system_storage.path, uid) < 0) {
-                log_oom();
-                return s->system_journal;
-        }
+        if (!uid_for_system_journal(uid)) {
+                ManagedJournalFile *f = NULL;
 
-        /* Too many open? Then let's close one (or more) */
-        while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
-                assert_se(f = ordered_hashmap_steal_first(s->user_journals));
-                (void) managed_journal_file_close(f);
-        }
-
-        r = open_journal(s, true, p, O_RDWR|O_CREAT, s->seal, &s->system_storage.metrics, &f);
-        if (r < 0)
-                return s->system_journal;
+                r = find_user_journal(s, uid, &f);
+                if (r >= 0)
+                        return ASSERT_PTR(f);
 
-        r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f);
-        if (r < 0) {
-                (void) managed_journal_file_close(f);
-                return s->system_journal;
+                log_warning_errno(r, "Failed to open user journal file, falling back to system journal: %m");
         }
 
-        server_add_acls(f, uid);
-        return f;
+        return s->system_journal;
 }
 
 static int do_rotate(
@@ -543,7 +559,7 @@ static void server_vacuum_deferred_closes(Server *s) {
         }
 }
 
-static int vacuum_offline_user_journals(Server *s) {
+static int server_archive_offline_user_journals(Server *s) {
         _cleanup_closedir_ DIR *d = NULL;
         int r;
 
@@ -559,9 +575,8 @@ static int vacuum_offline_user_journals(Server *s) {
         }
 
         for (;;) {
-                _cleanup_free_ char *u = NULL, *full = NULL;
+                _cleanup_free_ char *full = NULL;
                 _cleanup_close_ int fd = -EBADF;
-                const char *a, *b;
                 struct dirent *de;
                 ManagedJournalFile *f;
                 uid_t uid;
@@ -573,24 +588,14 @@ static int vacuum_offline_user_journals(Server *s) {
                                 log_ratelimit_warning_errno(errno, JOURNAL_LOG_RATELIMIT,
                                                             "Failed to enumerate %s, ignoring: %m",
                                                             s->system_storage.path);
-
                         break;
                 }
 
-                a = startswith(de->d_name, "user-");
-                if (!a)
-                        continue;
-                b = endswith(de->d_name, ".journal");
-                if (!b)
-                        continue;
-
-                u = strndup(a, b-a);
-                if (!u)
-                        return log_oom();
-
-                r = parse_uid(u, &uid);
+                r = journal_file_parse_uid_from_filename(de->d_name, &uid);
                 if (r < 0) {
-                        log_debug_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name);
+                        /* Don't warn if the file is not an online or offline user journal. */
+                        if (r != -EREMOTE)
+                                log_warning_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name);
                         continue;
                 }
 
@@ -680,7 +685,7 @@ void server_rotate(Server *s) {
         /* Finally, also rotate all user journals we currently do not have open. (But do so only if we
          * actually have access to /var, i.e. are not in the log-to-runtime-journal mode). */
         if (!s->runtime_journal)
-                (void) vacuum_offline_user_journals(s);
+                (void) server_archive_offline_user_journals(s);
 
         server_process_deferred_closes(s);
 }
@@ -2504,10 +2509,13 @@ int server_init(Server *s, const char *namespace) {
 
         /* Unless we got *some* sockets and not audit, open audit socket */
         if (s->audit_fd >= 0 || no_sockets) {
+                log_info("Collecting audit messages is enabled.");
+
                 r = server_open_audit(s);
                 if (r < 0)
                         return r;
-        }
+        } else
+                log_info("Collecting audit messages is disabled.");
 
         r = server_open_varlink(s, varlink_socket, varlink_fd);
         if (r < 0)
index 379a0c8caf99e3415e11bdd503e815d7ad5495e9..67552de8fe8344943c5264da9cec72a202be5510 100644 (file)
@@ -30,7 +30,7 @@ _noreturn_ static void log_assert_errno(const char *text, int error, const char
         do {                                                            \
                 int _r_ = (expr);                                       \
                 if (_unlikely_(_r_ < 0))                                \
-                        log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
+                        log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __func__); \
         } while (false)
 
 static ManagedJournalFile *test_open(const char *name) {
index 0992c641f0361608a52b3a0c74fd09d9aaca160e..e8e8cf37c36e34c0d70b631bf57fcbe7c5b5c4ae 100755 (executable)
@@ -32,7 +32,7 @@ MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
 ENTRY_TOKEN="$KERNEL_INSTALL_ENTRY_TOKEN"
 BOOT_ROOT="$KERNEL_INSTALL_BOOT_ROOT"
 
-BOOT_MNT="$(stat -c %m "$BOOT_ROOT")"
+[ -n "$BOOT_MNT" ] || BOOT_MNT="$(stat -c %m "$BOOT_ROOT")"
 if [ "$BOOT_MNT" = '/' ]; then
     ENTRY_DIR="$ENTRY_DIR_ABS"
 else
index 2e440636680161319af264380d3e9adf471a3349..f16bb9f50f9d4d328c942b9365bbf54b3c5b54b1 100755 (executable)
@@ -9,6 +9,8 @@ plugin="${2:?}"
 
 D="$(mktemp --tmpdir --directory "test-kernel-install.XXXXXXXXXX")"
 
+export _KERNEL_INSTALL_BOOTCTL="$PROJECT_BUILD_ROOT/bootctl"
+
 # shellcheck disable=SC2064
 trap "rm -rf '$D'" EXIT INT QUIT PIPE
 mkdir -p "$D/boot"
@@ -31,6 +33,7 @@ EOF
 export KERNEL_INSTALL_CONF_ROOT="$D/sources"
 export KERNEL_INSTALL_PLUGINS="$plugin"
 export BOOT_ROOT="$D/boot"
+export BOOT_MNT="$D/boot"
 export MACHINE_ID='3e0484f3634a418b8e6a39e8828b03e3'
 
 "$kernel_install" -v add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
@@ -82,3 +85,32 @@ grep -qE '^initrd .*/the-token/1.1.1/initrd' "$entry"
 
 grep -qE 'image' "$BOOT_ROOT/the-token/1.1.1/linux"
 grep -qE 'initrd' "$BOOT_ROOT/the-token/1.1.1/initrd"
+
+if test -x "$_KERNEL_INSTALL_BOOTCTL"; then
+    echo "Testing bootctl"
+    e2="${entry%+*}_2.conf"
+    cp "$entry" "$e2"
+    export SYSTEMD_ESP_PATH=/
+
+    # create file that is not referenced. Check if cleanup removes
+    # it but leaves the rest alone
+    :> "$BOOT_ROOT/the-token/1.1.2/initrd"
+    "$_KERNEL_INSTALL_BOOTCTL" --root="$BOOT_ROOT" cleanup
+    test ! -e "$BOOT_ROOT/the-token/1.1.2/initrd"
+    test -e "$BOOT_ROOT/the-token/1.1.2/linux"
+    test -e "$BOOT_ROOT/the-token/1.1.1/linux"
+    test -e "$BOOT_ROOT/the-token/1.1.1/initrd"
+    # now remove duplicated entry and make sure files are left over
+    "$_KERNEL_INSTALL_BOOTCTL" --root="$BOOT_ROOT" unlink "${e2##*/}"
+    test -e "$BOOT_ROOT/the-token/1.1.1/linux"
+    test -e "$BOOT_ROOT/the-token/1.1.1/initrd"
+    test -e "$entry"
+    test ! -e "$e2"
+    # remove last entry referencing those files
+    entry_id="${entry##*/}"
+    entry_id="${entry_id%+*}.conf"
+    "$_KERNEL_INSTALL_BOOTCTL" --root="$BOOT_ROOT" unlink "$entry_id"
+    test ! -e "$entry"
+    test ! -e "$BOOT_ROOT/the-token/1.1.1/linux"
+    test ! -e "$BOOT_ROOT/the-token/1.1.1/initrd"
+fi
index a311d1d5b9129d3d150ee4338e7b66292d33d26f..5a7308c5ac3968ccbd4ae955fe4dcdf26951fce2 100644 (file)
@@ -40,7 +40,9 @@ int dhcp_network_bind_raw_socket(
                 const struct hw_addr_data *hw_addr,
                 const struct hw_addr_data *bcast_addr,
                 uint16_t arp_type,
-                uint16_t port);
+                uint16_t port,
+                bool so_priority_set,
+                int so_priority);
 int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type);
 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
                                  const void *packet, size_t len);
index 4e4b1ccb751b097c5a89ce56a3f36962c058807a..9137efa3ee1f36ddb3e0e883da3f30f2fc1084d3 100644 (file)
@@ -25,7 +25,9 @@ static int _bind_raw_socket(
                 const struct hw_addr_data *hw_addr,
                 const struct hw_addr_data *bcast_addr,
                 uint16_t arp_type,
-                uint16_t port) {
+                uint16_t port,
+                bool so_priority_set,
+                int so_priority) {
 
         assert(ifindex > 0);
         assert(link);
@@ -113,6 +115,12 @@ static int _bind_raw_socket(
         if (r < 0)
                 return -errno;
 
+        if (so_priority_set) {
+                r = setsockopt_int(s, SOL_SOCKET, SO_PRIORITY, so_priority);
+                if (r < 0)
+                        return r;
+        }
+
         link->ll = (struct sockaddr_ll) {
                 .sll_family = AF_PACKET,
                 .sll_protocol = htobe16(ETH_P_IP),
@@ -137,7 +145,9 @@ int dhcp_network_bind_raw_socket(
                 const struct hw_addr_data *hw_addr,
                 const struct hw_addr_data *bcast_addr,
                 uint16_t arp_type,
-                uint16_t port) {
+                uint16_t port,
+                bool so_priority_set,
+                int so_priority) {
 
         static struct hw_addr_data default_eth_bcast = {
                 .length = ETH_ALEN,
@@ -160,13 +170,13 @@ int dhcp_network_bind_raw_socket(
                 return _bind_raw_socket(ifindex, link, xid,
                                         hw_addr,
                                         (bcast_addr && !hw_addr_is_null(bcast_addr)) ? bcast_addr : &default_eth_bcast,
-                                        arp_type, port);
+                                        arp_type, port, so_priority_set, so_priority);
 
         case ARPHRD_INFINIBAND:
                 return _bind_raw_socket(ifindex, link, xid,
                                         &HW_ADDR_NULL,
                                         (bcast_addr && !hw_addr_is_null(bcast_addr)) ? bcast_addr : &default_ib_bcast,
-                                        arp_type, port);
+                                        arp_type, port, so_priority_set, so_priority);
         default:
                 return -EINVAL;
         }
index 7f49da7c2d7189c826756a7e3d5066256d9f3e5b..a52259c238a480c64f1b3d348675d62460937e36 100644 (file)
@@ -279,6 +279,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
         uint8_t code, len;
         const uint8_t *option;
         size_t offset = 0;
+        int r;
 
         while (offset < buflen) {
                 code = options[offset ++];
@@ -318,13 +319,9 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
                         if (error_message) {
                                 _cleanup_free_ char *string = NULL;
 
-                                /* Accept a trailing NUL byte */
-                                if (memchr(option, 0, len - 1))
-                                        return -EINVAL;
-
-                                string = memdup_suffix0((const char *) option, len);
-                                if (!string)
-                                        return -ENOMEM;
+                                r = make_cstring((const char*) option, len, MAKE_CSTRING_ALLOW_TRAILING_NUL, &string);
+                                if (r < 0)
+                                        return r;
 
                                 if (!ascii_is_valid(string))
                                         return -EINVAL;
index 2afcda3eec13ed820835520b1de32b56c7f8d57b..fa43f28eb55e09e92ce332e004235c0f215b3a86 100644 (file)
@@ -79,6 +79,7 @@ struct sd_dhcp6_client {
 
         sd_dhcp6_client_callback_t callback;
         void *userdata;
+        bool send_release;
 
         /* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */
         bool test_mode;
index 62873e011124d86b2e121682bc08ffa04dcadcdd..a6b74e07b287c9efcc278895dc61db4d0b723d39 100644 (file)
@@ -495,13 +495,18 @@ int dhcp6_option_parse(
 }
 
 int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) {
+        DHCP6Status status;
+
         assert(data || data_len == 0);
 
         if (data_len < sizeof(uint16_t))
                 return -EBADMSG;
 
+        status = unaligned_read_be16(data);
+
         if (ret_status_message) {
-                char *msg;
+                _cleanup_free_ char *msg = NULL;
+                const char *s;
 
                 /* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415.
                  * Let's escape unsafe characters for safety. */
@@ -509,10 +514,14 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s
                 if (!msg)
                         return -ENOMEM;
 
-                *ret_status_message = msg;
+                s = dhcp6_message_status_to_string(status);
+                if (s && !strextend_with_separator(&msg, ": ", s))
+                        return -ENOMEM;
+
+                *ret_status_message = TAKE_PTR(msg);
         }
 
-        return unaligned_read_be16(data);
+        return status;
 }
 
 static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
@@ -538,9 +547,8 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
                                 return r;
                         if (r > 0)
                                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
-                                                              "Received an IA address or PD prefix option with non-zero status: %s%s%s",
-                                                              strempty(msg), isempty(msg) ? "" : ": ",
-                                                              dhcp6_message_status_to_string(r));
+                                                              "Received an IA address or PD prefix option with non-zero status%s%s",
+                                                              isempty(msg) ? "." : ": ", strempty(msg));
                         if (r < 0)
                                 /* Let's log but ignore the invalid status option. */
                                 log_dhcp6_client_errno(client, r,
@@ -746,9 +754,8 @@ int dhcp6_option_parse_ia(
                                 return r;
                         if (r > 0)
                                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
-                                                              "Received an IA option with non-zero status: %s%s%s",
-                                                              strempty(msg), isempty(msg) ? "" : ": ",
-                                                              dhcp6_message_status_to_string(r));
+                                                              "Received an IA option with non-zero status%s%s",
+                                                              isempty(msg) ? "." : ": ", strempty(msg));
                         if (r < 0)
                                 log_dhcp6_client_errno(client, r,
                                                        "Received an IA option with an invalid status sub option, ignoring: %m");
index f965ea40fea9c882ccc6f0c68f441d154ede7a23..be0f651f1ab4ac92260d6c7c400f85b46b8ade8d 100644 (file)
@@ -11,6 +11,7 @@ static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = {
         [DHCP6_STATE_BOUND]               = "bound",
         [DHCP6_STATE_RENEW]               = "renew",
         [DHCP6_STATE_REBIND]              = "rebind",
+        [DHCP6_STATE_STOPPING]            = "stopping",
 };
 
 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State);
index 18217691b7511afd8ccce48dd420bb1c4e42c89b..c70f93203d199b985311af2334a9d3c598f273e9 100644 (file)
@@ -58,6 +58,7 @@ typedef enum DHCP6State {
         DHCP6_STATE_BOUND,
         DHCP6_STATE_RENEW,
         DHCP6_STATE_REBIND,
+        DHCP6_STATE_STOPPING,
         _DHCP6_STATE_MAX,
         _DHCP6_STATE_INVALID = -EINVAL,
 } DHCP6State;
index a9cfba90e3d769f9242b44c6523ab96769dab192..d2bbf660b0d1af7fac4950f02629be96d661f5f8 100644 (file)
@@ -16,7 +16,10 @@ int dhcp_network_bind_raw_socket(
                 uint32_t id,
                 const struct hw_addr_data *hw_addr,
                 const struct hw_addr_data *bcast_addr,
-                uint16_t arp_type, uint16_t port) {
+                uint16_t arp_type,
+                uint16_t port,
+                bool so_priority_set,
+                int so_priority) {
 
         int fd;
         fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
index 48174e7c4b0f9848c300192d2faa5444a8b14944..e482450cd4d7a28fbf2b0afe34b8dc5731d387dc 100644 (file)
@@ -121,6 +121,8 @@ struct sd_dhcp_client {
         sd_dhcp_lease *lease;
         usec_t start_delay;
         int ip_service_type;
+        int socket_priority;
+        bool socket_priority_set;
 
         /* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */
         bool test_mode;
@@ -193,35 +195,33 @@ int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
                         r = asprintf(&t, "DATA");
                 break;
         case 1:
-                if (len != sizeof_field(sd_dhcp_client_id, eth))
-                        return -EINVAL;
-
-                r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
-                             client_id->eth.haddr[0],
-                             client_id->eth.haddr[1],
-                             client_id->eth.haddr[2],
-                             client_id->eth.haddr[3],
-                             client_id->eth.haddr[4],
-                             client_id->eth.haddr[5]);
+                if (len == sizeof_field(sd_dhcp_client_id, eth))
+                        r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
+                                     client_id->eth.haddr[0],
+                                     client_id->eth.haddr[1],
+                                     client_id->eth.haddr[2],
+                                     client_id->eth.haddr[3],
+                                     client_id->eth.haddr[4],
+                                     client_id->eth.haddr[5]);
+                else
+                        r = asprintf(&t, "ETHER");
                 break;
         case 2 ... 254:
                 r = asprintf(&t, "ARP/LL");
                 break;
         case 255:
-                if (len < 6)
-                        return -EINVAL;
-
-                uint32_t iaid = be32toh(client_id->ns.iaid);
-                uint16_t duid_type = be16toh(client_id->ns.duid.type);
-                if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0)
-                        return -EINVAL;
-
-                r = asprintf(&t, "IAID:0x%x/DUID", iaid);
+                if (len < sizeof(uint32_t))
+                        r = asprintf(&t, "IAID/DUID");
+                else {
+                        uint32_t iaid = be32toh(client_id->ns.iaid);
+                        /* TODO: check and stringify DUID */
+                        r = asprintf(&t, "IAID:0x%x/DUID", iaid);
+                }
                 break;
         }
-
         if (r < 0)
                 return -ENOMEM;
+
         *ret = TAKE_PTR(t);
         return 0;
 }
@@ -647,6 +647,16 @@ int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
         return 0;
 }
 
+int sd_dhcp_client_set_socket_priority(sd_dhcp_client *client, int socket_priority) {
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+        client->socket_priority_set = true;
+        client->socket_priority = socket_priority;
+
+        return 0;
+}
+
 int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
@@ -1381,7 +1391,8 @@ static int client_start_delayed(sd_dhcp_client *client) {
 
         r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
                                          &client->hw_addr, &client->bcast_addr,
-                                         client->arp_type, client->port);
+                                         client->arp_type, client->port,
+                                         client->socket_priority_set, client->socket_priority);
         if (r < 0) {
                 client_stop(client, r);
                 return r;
@@ -1429,7 +1440,8 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
 
         r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
                                          &client->hw_addr, &client->bcast_addr,
-                                         client->arp_type, client->port);
+                                         client->arp_type, client->port,
+                                         client->socket_priority_set, client->socket_priority);
         if (r < 0) {
                 client_stop(client, r);
                 return 0;
index b14ad57071dfecb814fac5a511ff4abe906fce59..02c6a6e0d79133e90eb65c5c7dd65563e87bc981 100644 (file)
@@ -376,6 +376,8 @@ static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
 }
 
 static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
+        int r;
+
         assert(option);
         assert(ret);
 
@@ -388,12 +390,9 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
                  * One trailing NUL byte is OK, we don't mind. See:
                  * https://github.com/systemd/systemd/issues/1337
                  */
-                if (memchr(option, 0, len - 1))
-                        return -EINVAL;
-
-                string = memdup_suffix0((const char *) option, len);
-                if (!string)
-                        return -ENOMEM;
+                r = make_cstring((const char*) option, len, MAKE_CSTRING_ALLOW_TRAILING_NUL, &string);
+                if (r < 0)
+                        return r;
 
                 free_and_replace(*ret, string);
         }
index 29cd0035063000c638d98f7cd668d4d06ac4d187..57dd91f81f3c45798c8053aef5fe893580cc1d98 100644 (file)
@@ -498,6 +498,14 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
         return 0;
 }
 
+int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) {
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+        client->send_release = enable;
+        return 0;
+}
+
 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
         assert_return(client, -EINVAL);
 
@@ -586,7 +594,8 @@ static int client_append_common_options_in_managed_mode(
                       DHCP6_STATE_SOLICITATION,
                       DHCP6_STATE_REQUEST,
                       DHCP6_STATE_RENEW,
-                      DHCP6_STATE_REBIND));
+                      DHCP6_STATE_REBIND,
+                      DHCP6_STATE_STOPPING));
         assert(buf);
         assert(*buf);
         assert(offset);
@@ -603,9 +612,11 @@ static int client_append_common_options_in_managed_mode(
                         return r;
         }
 
-        r = dhcp6_option_append_fqdn(buf, offset, client->fqdn);
-        if (r < 0)
-                return r;
+        if (client->state != DHCP6_STATE_STOPPING) {
+                r = dhcp6_option_append_fqdn(buf, offset, client->fqdn);
+                if (r < 0)
+                        return r;
+        }
 
         r = dhcp6_option_append_user_class(buf, offset, client->user_class);
         if (r < 0)
@@ -636,6 +647,8 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client)
                 return DHCP6_MESSAGE_RENEW;
         case DHCP6_STATE_REBIND:
                 return DHCP6_MESSAGE_REBIND;
+        case DHCP6_STATE_STOPPING:
+                return DHCP6_MESSAGE_RELEASE;
         default:
                 assert_not_reached();
         }
@@ -679,6 +692,9 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off
                 req_opts = p;
                 break;
 
+        case DHCP6_STATE_STOPPING:
+                return 0;
+
         default:
                 n = client->n_req_opts;
                 req_opts = client->req_opts;
@@ -690,6 +706,22 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off
         return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
 }
 
+static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) {
+        assert(client);
+        assert(buf);
+        assert(*buf);
+        assert(offset);
+
+        if (!client->mudurl)
+                return 0;
+
+        if (client->state == DHCP6_STATE_STOPPING)
+                return 0;
+
+        return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_MUD_URL_V6,
+                                   strlen(client->mudurl), client->mudurl);
+}
+
 int dhcp6_client_send_message(sd_dhcp6_client *client) {
         _cleanup_free_ uint8_t *buf = NULL;
         struct in6_addr all_servers =
@@ -735,7 +767,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
 
         case DHCP6_STATE_REQUEST:
         case DHCP6_STATE_RENEW:
-
+        case DHCP6_STATE_STOPPING:
                 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID,
                                         client->lease->serverid_len,
                                         client->lease->serverid);
@@ -753,18 +785,15 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
                         return r;
                 break;
 
-        case DHCP6_STATE_STOPPED:
         case DHCP6_STATE_BOUND:
+        case DHCP6_STATE_STOPPED:
         default:
                 assert_not_reached();
         }
 
-        if (client->mudurl) {
-                r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_MUD_URL_V6,
-                                        strlen(client->mudurl), client->mudurl);
-                if (r < 0)
-                        return r;
-        }
+        r = client_append_mudurl(client, &buf, &offset);
+        if (r < 0)
+                return r;
 
         r = client_append_oro(client, &buf, &offset);
         if (r < 0)
@@ -856,6 +885,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
                 break;
 
         case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_STOPPING:
         case DHCP6_STATE_BOUND:
         default:
                 assert_not_reached();
@@ -911,6 +941,7 @@ static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) {
                 assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW));
                 break;
         case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_STOPPING:
         case DHCP6_STATE_BOUND:
         default:
                 assert_not_reached();
@@ -1319,6 +1350,7 @@ static int client_receive_message(
 
         case DHCP6_STATE_BOUND:
         case DHCP6_STATE_STOPPED:
+        case DHCP6_STATE_STOPPING:
         default:
                 assert_not_reached();
         }
@@ -1326,10 +1358,37 @@ static int client_receive_message(
         return 0;
 }
 
+static int client_send_release(sd_dhcp6_client *client) {
+        sd_dhcp6_lease *lease;
+
+        assert(client);
+
+        if (!client->send_release)
+                return 0;
+
+        if (sd_dhcp6_client_get_lease(client, &lease) < 0)
+                return 0;
+
+        if (!lease->ia_na && !lease->ia_pd)
+                return 0;
+
+        client_set_state(client, DHCP6_STATE_STOPPING);
+        return dhcp6_client_send_message(client);
+}
+
 int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
+        int r;
+
         if (!client)
                 return 0;
 
+        /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client
+         * engine is about to release its UDP socket unconditionally. */
+        r = client_send_release(client);
+        if (r < 0)
+                log_dhcp6_client_errno(client, r,
+                                       "Failed to send DHCP6 release message, ignoring: %m");
+
         client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
 
         client->receive_message = sd_event_source_unref(client->receive_message);
index 3dcccf1729e6186157a68d49acd0d87e430e16a7..d14c412c1fb8057a8fcf9b29962ffc5865de9be2 100644 (file)
@@ -510,13 +510,11 @@ static int dhcp6_lease_parse_message(
                         r = dhcp6_option_parse_status(optval, optlen, &msg);
                         if (r < 0)
                                 return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
-
                         if (r > 0)
                                 return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r),
-                                                              "Received %s message with non-zero status: %s%s%s",
+                                                              "Received %s message with non-zero status%s%s",
                                                               dhcp6_message_type_to_string(message->type),
-                                                              strempty(msg), isempty(msg) ? "" : ": ",
-                                                              dhcp6_message_status_to_string(r));
+                                                              isempty(msg) ? "." : ": ", strempty(msg));
                         break;
                 }
                 case SD_DHCP6_OPTION_IA_NA: {
index 92b9b5b9bc558dbd0370294d13b66fea534cac0c..863649f6df5e77e7c0cb88ac1e4ec3cc9227fc71 100644 (file)
@@ -235,7 +235,10 @@ int dhcp_network_bind_raw_socket(
                 uint32_t id,
                 const struct hw_addr_data *_hw_addr,
                 const struct hw_addr_data *_bcast_addr,
-                uint16_t arp_type, uint16_t port) {
+                uint16_t arp_type,
+                uint16_t port,
+                bool so_priority_set,
+                int so_priority) {
 
         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
                 return -errno;
index 3e1c52a306136fe386250ad8e24a358ed5d5ba51..00c11909c92e9c95ca3df4daf2d04e7ee70ad0b2 100644 (file)
@@ -602,6 +602,62 @@ static const uint8_t msg_request[] = {
         0x00, 0x00,
 };
 
+/* RFC 3315 section 18.1.6. The DHCP6 Release message must include:
+    - transaction id
+    - server identifier
+    - client identifier
+    - all released IA with addresses included
+    - elapsed time (required for all messages).
+    All other options aren't required. */
+static const uint8_t msg_release[] = {
+        /* Message type */
+        DHCP6_MESSAGE_RELEASE,
+        /* Transaction ID */
+        0x00, 0x00, 0x00,
+        /* Server ID */
+        0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e,
+        SERVER_ID_BYTES,
+        /* IA_NA */
+        0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x44,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x00, /* lifetime T2 */
+        /* IA_NA (IAADDR suboption) */
+        0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18,
+        IA_NA_ADDRESS1_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0x00, /* valid lifetime */
+        /* IA_NA (IAADDR suboption) */
+        0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18,
+        IA_NA_ADDRESS2_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0x00, /* valid lifetime */
+        /* IA_PD */
+        0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x00, /* lifetime T2 */
+        /* IA_PD (IA_PD_PREFIX suboption) */
+        0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19,
+        0x00, 0x00, 0x00, 0x00, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0x00, /* valid lifetime */
+        0x40, /* prefixlen */
+        IA_PD_PREFIX1_BYTES,
+        /* IA_PD (IA_PD_PREFIX suboption) */
+        0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19,
+        0x00, 0x00, 0x00, 0x00, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0x00, /* valid lifetime */
+        0x40, /* prefixlen */
+        IA_PD_PREFIX2_BYTES,
+        /* Client ID */
+        0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e,
+        CLIENT_ID_BYTES,
+        /* Extra options */
+        /* Elapsed time */
+        0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02,
+        0x00, 0x00,
+};
+
 static const uint8_t msg_reply[] = {
         /* Message type */
         DHCP6_MESSAGE_REPLY,
@@ -775,13 +831,24 @@ static void test_client_verify_solicit(const DHCP6Message *msg, size_t len) {
         assert_se(memcmp(msg, msg_solicit, len - sizeof(be16_t)) == 0);
 }
 
+static void test_client_verify_release(const DHCP6Message *msg, size_t len) {
+        log_debug("/* %s */", __func__);
+
+        assert_se(len == sizeof(msg_release));
+        assert_se(msg->type == DHCP6_MESSAGE_RELEASE);
+        /* The transaction ID and elapsed time value are not deterministic. Skip them. */
+        assert_se(memcmp(msg->options, msg_release + offsetof(DHCP6Message, options),
+                         len - offsetof(DHCP6Message, options) - sizeof(be16_t)) == 0);
+}
+
 static void test_client_verify_request(const DHCP6Message *msg, size_t len) {
         log_debug("/* %s */", __func__);
 
         assert_se(len == sizeof(msg_request));
         assert_se(msg->type == DHCP6_MESSAGE_REQUEST);
         /* The transaction ID and elapsed time value are not deterministic. Skip them. */
-        assert_se(memcmp(msg->options, msg_request + offsetof(DHCP6Message, options), len - offsetof(DHCP6Message, options) - sizeof(be16_t)) == 0);
+        assert_se(memcmp(msg->options, msg_request + offsetof(DHCP6Message, options),
+                         len - offsetof(DHCP6Message, options) - sizeof(be16_t)) == 0);
 }
 
 static void test_lease_common(sd_dhcp6_client *client) {
@@ -905,7 +972,7 @@ static void test_client_callback(sd_dhcp6_client *client, int event, void *userd
         case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
                 log_debug("/* %s (event=ip-acquire) */", __func__);
 
-                assert_se(IN_SET(test_client_sent_message_count, 3, 4));
+                assert_se(IN_SET(test_client_sent_message_count, 3, 5));
 
                 test_lease_managed(client);
 
@@ -916,7 +983,7 @@ static void test_client_callback(sd_dhcp6_client *client, int event, void *userd
                         assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0);
                         break;
 
-                case 4:
+                case 5:
                         assert_se(sd_event_exit(sd_dhcp6_client_get_event(client), 0) >= 0);
                         break;
 
@@ -974,6 +1041,12 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet,
                 break;
 
         case 3:
+                test_client_verify_release(packet, len);
+                /* when stopping, dhcp6 client doesn't wait for release server reply */
+                assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply));
+                break;
+
+        case 4:
                 test_client_verify_solicit(packet, len);
                 assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply));
                 break;
@@ -1010,6 +1083,7 @@ TEST(dhcp6_client) {
         assert_se(sd_dhcp6_client_set_local_address(client, &local_address) >= 0);
         assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") >= 0);
         assert_se(sd_dhcp6_client_set_iaid(client, unaligned_read_be32((uint8_t[]) { IA_ID_BYTES })) >= 0);
+        assert_se(sd_dhcp6_client_set_send_release(client, true) >= 0);
         dhcp6_client_set_test_mode(client, true);
 
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVER) >= 0);
@@ -1028,7 +1102,7 @@ TEST(dhcp6_client) {
 
         assert_se(sd_event_loop(e) >= 0);
 
-        assert_se(test_client_sent_message_count == 4);
+        assert_se(test_client_sent_message_count == 5);
 
         assert_se(!sd_dhcp6_client_unref(client_ref));
         test_fd[1] = safe_close(test_fd[1]);
index 001df4d473915a039561ae240efaf58dc2d69805..bd8c0fd426a1dcbd115fc883778e0f1fdb3cddeb 100644 (file)
@@ -53,7 +53,6 @@ static uint8_t advertisement[] = {
 
 static bool test_stopped;
 static int test_fd[2];
-static sd_event_source *recv_router_advertisement;
 static struct {
         struct in6_addr address;
         unsigned char prefixlen;
@@ -281,9 +280,9 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat
 }
 
 TEST(ra) {
-        sd_event *e;
-        sd_radv *ra;
-        unsigned i;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *recv_router_advertisement = NULL;
+        _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
 
         assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
 
@@ -303,7 +302,7 @@ TEST(ra) {
         assert_se(sd_radv_set_rdnss(ra, 60, &test_rdnss, 1) >= 0);
         assert_se(sd_radv_set_dnssl(ra, 60, (char **)test_dnssl) >= 0);
 
-        for (i = 0; i < ELEMENTSOF(prefix); i++) {
+        for (unsigned i = 0; i < ELEMENTSOF(prefix); i++) {
                 sd_radv_prefix *p;
 
                 printf("Test prefix %u\n", i);
@@ -324,8 +323,8 @@ TEST(ra) {
                 assert_se(!p);
         }
 
-        assert_se(sd_event_add_io(e, &recv_router_advertisement, test_fd[0],
-                                  EPOLLIN, radv_recv, ra) >= 0);
+        assert_se(sd_event_add_io(e, &recv_router_advertisement, test_fd[0], EPOLLIN, radv_recv, ra) >= 0);
+        assert_se(sd_event_source_set_io_fd_own(recv_router_advertisement, true) >= 0);
 
         assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
                                              2 * USEC_PER_SEC, 0,
@@ -334,13 +333,6 @@ TEST(ra) {
         assert_se(sd_radv_start(ra) >= 0);
 
         assert_se(sd_event_loop(e) >= 0);
-
-        ra = sd_radv_unref(ra);
-        assert_se(!ra);
-
-        close(test_fd[0]);
-
-        sd_event_unref(e);
 }
 
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 3c679f60b547c91b07921d0b88c2ec4456d5f34c..e501b6437744bc21edc16e5c99f362b0d2460d8e 100644 (file)
@@ -10,6 +10,7 @@
 #include "sd-ndisc.h"
 
 #include "alloc-util.h"
+#include "fd-util.h"
 #include "hexdecoct.h"
 #include "icmp6-util.h"
 #include "socket-util.h"
@@ -255,8 +256,8 @@ static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router
 }
 
 TEST(rs) {
-        sd_event *e;
-        sd_ndisc *nd;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
 
         send_ra_function = send_ra;
 
@@ -279,17 +280,13 @@ TEST(rs) {
         assert_se(sd_ndisc_start(nd) >= 0);
         assert_se(sd_ndisc_start(nd) >= 0);
         assert_se(sd_ndisc_stop(nd) >= 0);
+        test_fd[1] = safe_close(test_fd[1]);
 
         assert_se(sd_ndisc_start(nd) >= 0);
 
         assert_se(sd_event_loop(e) >= 0);
 
-        nd = sd_ndisc_unref(nd);
-        assert_se(!nd);
-
-        close(test_fd[1]);
-
-        sd_event_unref(e);
+        test_fd[1] = safe_close(test_fd[1]);
 }
 
 static int test_timeout_value(uint8_t flags) {
@@ -342,8 +339,8 @@ static int test_timeout_value(uint8_t flags) {
 }
 
 TEST(timeout) {
-        sd_event *e;
-        sd_ndisc *nd;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
 
         send_ra_function = test_timeout_value;
 
@@ -367,9 +364,7 @@ TEST(timeout) {
 
         assert_se(sd_event_loop(e) >= 0);
 
-        nd = sd_ndisc_unref(nd);
-
-        sd_event_unref(e);
+        test_fd[1] = safe_close(test_fd[1]);
 }
 
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 07acb992717e59b4f6dd6aa67e149264f67c62d0..0f2230a42f97afbb09dc4a4e463ea41f856181d9 100644 (file)
@@ -802,4 +802,12 @@ global:
         sd_bus_emit_signal_to;
         sd_bus_emit_signal_tov;
         sd_bus_message_new_signal_to;
+        sd_pidfd_get_cgroup;
+        sd_pidfd_get_machine_name;
+        sd_pidfd_get_owner_uid;
+        sd_pidfd_get_session;
+        sd_pidfd_get_slice;
+        sd_pidfd_get_unit;
+        sd_pidfd_get_user_slice;
+        sd_pidfd_get_user_unit;
 } LIBSYSTEMD_252;
index 9238e32980613e41e44e3e0fc4816bad26aa1cd7..7fdffec2ea07640a11c83c2d8d7af0720ac1e5e5 100644 (file)
@@ -191,6 +191,8 @@ libsystemd_pc = custom_target(
 ############################################################
 
 tests += [
+        [files('sd-journal/test-journal-file.c')],
+
         [files('sd-journal/test-journal-send.c')],
 
         [files('sd-journal/test-journal-match.c')],
index 07168f660b8af907744ec210fb86a1d181f1b081..9719f97c028b82654e2822f7584795b6f7e9f7b8 100644 (file)
@@ -4667,3 +4667,26 @@ _public_ int sd_bus_message_sensitive(sd_bus_message *m) {
         m->sensitive = true;
         return 0;
 }
+
+char** bus_message_make_log_fields(sd_bus_message *m) {
+        _cleanup_strv_free_ char **strv = NULL;
+
+        assert(m);
+
+        (void) strv_extend_assignment(&strv, "DBUS_MESSAGE_TYPE", bus_message_type_to_string(m->header->type));
+        (void) strv_extend_assignment(&strv, "DBUS_SENDER", sd_bus_message_get_sender(m));
+        (void) strv_extend_assignment(&strv, "DBUS_DESTINATION", sd_bus_message_get_destination(m));
+        (void) strv_extend_assignment(&strv, "DBUS_PATH", sd_bus_message_get_path(m));
+        (void) strv_extend_assignment(&strv, "DBUS_INTERFACE", sd_bus_message_get_interface(m));
+        (void) strv_extend_assignment(&strv, "DBUS_MEMBER", sd_bus_message_get_member(m));
+
+        (void) strv_extendf(&strv, "DBUS_MESSAGE_COOKIE=%" PRIu64, BUS_MESSAGE_COOKIE(m));
+        if (m->reply_cookie != 0)
+                (void) strv_extendf(&strv, "DBUS_MESSAGE_REPLY_COOKIE=%" PRIu64, m->reply_cookie);
+
+        (void) strv_extend_assignment(&strv, "DBUS_SIGNATURE", m->root_container.signature);
+        (void) strv_extend_assignment(&strv, "DBUS_ERROR_NAME", m->error.name);
+        (void) strv_extend_assignment(&strv, "DBUS_ERROR_MESSAGE", m->error.message);
+
+        return TAKE_PTR(strv);
+}
index 0439fbd45a8b18ee3443d2c171fa7ebb8cacdba2..76f0d853d36100099d4f43a76dd807fc5008306d 100644 (file)
@@ -187,3 +187,5 @@ void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m);
 
 sd_bus_message* bus_message_ref_queued(sd_bus_message *m, sd_bus *bus);
 sd_bus_message* bus_message_unref_queued(sd_bus_message *m, sd_bus *bus);
+
+char** bus_message_make_log_fields(sd_bus_message *m);
index d85dcc52c17d47705f5ee8c14a9f4b54a9ae1375..10796e25c7479a94d7182bf5452bcdede55cb191 100644 (file)
@@ -2928,6 +2928,7 @@ static int process_fd_check(sd_bus *bus, sd_bus_message *m) {
 }
 
 static int process_message(sd_bus *bus, sd_bus_message *m) {
+        _unused_ _cleanup_(log_context_freep) LogContext *c = NULL;
         int r;
 
         assert(bus);
@@ -2936,6 +2937,9 @@ static int process_message(sd_bus *bus, sd_bus_message *m) {
         bus->current_message = m;
         bus->iteration_counter++;
 
+        if (log_context_enabled())
+                c = log_context_new_consume(bus_message_make_log_fields(m));
+
         log_debug_bus_message(m);
 
         r = process_hello(bus, m);
index 049b3aa97f2e85a72da5fc8e9646b8296902987c..0d2eea5f59104d31b34b9b92f1f33bdf89bdc0f7 100644 (file)
@@ -242,11 +242,15 @@ _public_ int sd_device_monitor_stop(sd_device_monitor *m) {
 
 static int device_monitor_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+        _unused_ _cleanup_(log_context_freep) LogContext *c = NULL;
         sd_device_monitor *m = ASSERT_PTR(userdata);
 
         if (device_monitor_receive_device(m, &device) <= 0)
                 return 0;
 
+        if (log_context_enabled())
+                c = log_context_new_consume(device_make_log_fields(device));
+
         if (m->callback)
                 return m->callback(m, device, m->userdata);
 
index d9a519a4d98f225ac13701e4c2e291c8681a3b9e..740c58438cfcf672ff85ce6293a3a767880204e2 100644 (file)
@@ -38,6 +38,7 @@ void device_set_db_persist(sd_device *device);
 void device_set_devlink_priority(sd_device *device, int priority);
 int device_ensure_usec_initialized(sd_device *device, sd_device *device_old);
 int device_add_devlink(sd_device *device, const char *devlink);
+void device_remove_devlink(sd_device *device, const char *devlink);
 bool device_has_devlink(sd_device *device, const char *devlink);
 int device_add_property(sd_device *device, const char *property, const char *value);
 int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) _printf_(3, 4);
index 56acec67f6de2a2a4b536b685ecef60c0366fcec..47c5d98896313d1b986d27b37be41e2bda0c7e7e 100644 (file)
@@ -5,6 +5,7 @@
 #include "devnum-util.h"
 #include "fd-util.h"
 #include "string-util.h"
+#include "strv.h"
 
 int devname_from_devnum(mode_t mode, dev_t devnum, char **ret) {
         _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
@@ -63,3 +64,78 @@ int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret) {
 
         return TAKE_FD(fd);
 }
+
+static int add_string_field(
+                sd_device *device,
+                const char *field,
+                int (*func)(sd_device *dev, const char **s),
+                char ***strv) {
+
+        const char *s;
+        int r;
+
+        assert(device);
+        assert(field);
+        assert(func);
+        assert(strv);
+
+        r = func(device, &s);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"%s\" property, ignoring: %m", field);
+        if (r >= 0)
+                (void) strv_extend_assignment(strv, field, s);
+
+        return 0;
+}
+
+char** device_make_log_fields(sd_device *device) {
+        _cleanup_strv_free_ char **strv = NULL;
+        dev_t devnum;
+        int ifindex;
+        sd_device_action_t action;
+        uint64_t seqnum, diskseq;
+        int r;
+
+        assert(device);
+
+        (void) add_string_field(device, "SYSPATH", sd_device_get_syspath, &strv);
+        (void) add_string_field(device, "SUBSYSTEM", sd_device_get_subsystem, &strv);
+        (void) add_string_field(device, "DEVTYPE", sd_device_get_devtype, &strv);
+        (void) add_string_field(device, "DRIVER", sd_device_get_driver, &strv);
+        (void) add_string_field(device, "DEVPATH", sd_device_get_devpath, &strv);
+        (void) add_string_field(device, "DEVNAME", sd_device_get_devname, &strv);
+        (void) add_string_field(device, "SYSNAME", sd_device_get_sysname, &strv);
+        (void) add_string_field(device, "SYSNUM", sd_device_get_sysnum, &strv);
+
+        r = sd_device_get_devnum(device, &devnum);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"DEVNUM\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "DEVNUM="DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(devnum));
+
+        r = sd_device_get_ifindex(device, &ifindex);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"IFINDEX\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "IFINDEX=%i", ifindex);
+
+        r = sd_device_get_action(device, &action);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"ACTION\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "ACTION=%s", device_action_to_string(action));
+
+        r = sd_device_get_seqnum(device, &seqnum);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"SEQNUM\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "SEQNUM=%"PRIu64, seqnum);
+
+        r = sd_device_get_diskseq(device, &diskseq);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"DISKSEQ\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "DISKSEQ=%"PRIu64, diskseq);
+
+        return TAKE_PTR(strv);
+}
index f139e114a8fa78896318dcbcc91bb6ce2773d803..a1b5e91edf664276feef8634b3daf8be49e3f02c 100644 (file)
@@ -99,3 +99,5 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
         return devname_from_devnum(st->st_mode, st->st_rdev, ret);
 }
 int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
+
+char** device_make_log_fields(sd_device *device);
index c3160b04bb7e44e4a791fe9175ee4d2727743551..8c65ee346984aa50189b40e313fb970d760df2cc 100644 (file)
@@ -1489,6 +1489,20 @@ int device_add_devlink(sd_device *device, const char *devlink) {
         return 0;
 }
 
+void device_remove_devlink(sd_device *device, const char *devlink) {
+        _cleanup_free_ char *s = NULL;
+
+        assert(device);
+        assert(devlink);
+
+        s = set_remove(device->devlinks, devlink);
+        if (!s)
+                return;
+
+        device->devlinks_generation++;
+        device->property_devlinks_outdated = true;
+}
+
 bool device_has_devlink(sd_device *device, const char *devlink) {
         assert(device);
         assert(devlink);
index a27f4bf7427d799c024caa4a4c890a25c054d3f2..a009a110a96fdfb58ba67443eb508a75fb59276b 100644 (file)
@@ -173,6 +173,7 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
 }
 
 DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free);
 
 int id128_get_product(sd_id128_t *ret) {
         sd_id128_t uuid;
index 4b91a16bd163f21c58a160b2a72d2df96b72e5a1..887f443d6939cfe098fc57d5e1d9fae8f55552bf 100644 (file)
@@ -27,6 +27,7 @@ int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id);
 void id128_hash_func(const sd_id128_t *p, struct siphash *state);
 int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
 extern const struct hash_ops id128_hash_ops;
+extern const struct hash_ops id128_hash_ops_free;
 
 sd_id128_t id128_make_v4_uuid(sd_id128_t id);
 
index 805773474c30b90060135bd3984cd14d7f756d21..32dd4cfeb807436da24ff6f83bafc59d2645646f 100644 (file)
@@ -35,6 +35,7 @@
 #include "string-util.h"
 #include "strv.h"
 #include "sync-util.h"
+#include "user-util.h"
 #include "xattr-util.h"
 
 #define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
@@ -3932,6 +3933,41 @@ fail:
         return r;
 }
 
+int journal_file_parse_uid_from_filename(const char *path, uid_t *ret_uid) {
+        _cleanup_free_ char *buf = NULL, *p = NULL;
+        const char *a, *b, *at;
+        int r;
+
+        /* This helper returns -EREMOTE when the filename doesn't match user online/offline journal
+         * pattern. Hence it currently doesn't parse archived or disposed user journals. */
+
+        assert(path);
+        assert(ret_uid);
+
+        r = path_extract_filename(path, &p);
+        if (r < 0)
+                return r;
+        if (r == O_DIRECTORY)
+                return -EISDIR;
+
+        a = startswith(p, "user-");
+        if (!a)
+                return -EREMOTE;
+        b = endswith(p, ".journal");
+        if (!b)
+                return -EREMOTE;
+
+        at = strchr(a, '@');
+        if (at)
+                return -EREMOTE;
+
+        buf = strndup(a, b-a);
+        if (!buf)
+                return -ENOMEM;
+
+        return parse_uid(buf, ret_uid);
+}
+
 int journal_file_archive(JournalFile *f, char **ret_previous_path) {
         _cleanup_free_ char *p = NULL;
 
index 1f3c80c9125d70b65b6e662248c553b777252f73..a1154a010727883a9280ae090df4f7574eca8b5d 100644 (file)
@@ -288,6 +288,7 @@ void journal_file_dump(JournalFile *f);
 void journal_file_print_header(JournalFile *f);
 
 int journal_file_archive(JournalFile *f, char **ret_previous_path);
+int journal_file_parse_uid_from_filename(const char *path, uid_t *uid);
 JournalFile* journal_initiate_close(JournalFile *f, Set *deferred_closes);
 
 int journal_file_dispose(int dir_fd, const char *fname);
index 204886b1e43d2dae289a1b19fbd189fb4c7b5164..523e23925de46ad4adcbb573f90c2347266eb4f5 100644 (file)
@@ -367,7 +367,7 @@ _public_ int sd_journal_add_disjunction(sd_journal *j) {
 }
 
 static char *match_make_string(Match *m) {
-        char *p = NULL, *r;
+        _cleanup_free_ char *p = NULL;
         bool enclose = false;
 
         if (!m)
@@ -377,34 +377,25 @@ static char *match_make_string(Match *m) {
                 return cescape_length(m->data, m->size);
 
         LIST_FOREACH(matches, i, m->matches) {
-                char *t, *k;
+                _cleanup_free_ char *t = NULL;
 
                 t = match_make_string(i);
                 if (!t)
-                        return mfree(p);
+                        return NULL;
 
                 if (p) {
-                        k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t);
-                        free(p);
-                        free(t);
-
-                        if (!k)
+                        if (!strextend(&p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t))
                                 return NULL;
 
-                        p = k;
-
                         enclose = true;
                 } else
-                        p = t;
+                        p = TAKE_PTR(t);
         }
 
-        if (enclose) {
-                r = strjoin("(", p, ")");
-                free(p);
-                return r;
-        }
+        if (enclose)
+                return strjoin("(", p, ")");
 
-        return p;
+        return TAKE_PTR(p);
 }
 
 char *journal_make_match_string(sd_journal *j) {
@@ -1883,15 +1874,17 @@ static int allocate_inotify(sd_journal *j) {
 static sd_journal *journal_new(int flags, const char *path, const char *namespace) {
         _cleanup_(sd_journal_closep) sd_journal *j = NULL;
 
-        j = new0(sd_journal, 1);
+        j = new(sd_journal, 1);
         if (!j)
                 return NULL;
 
-        j->original_pid = getpid_cached();
-        j->toplevel_fd = -EBADF;
-        j->inotify_fd = -EBADF;
-        j->flags = flags;
-        j->data_threshold = DEFAULT_DATA_THRESHOLD;
+        *j = (sd_journal) {
+                .original_pid = getpid_cached(),
+                .toplevel_fd = -EBADF,
+                .inotify_fd = -EBADF,
+                .flags = flags,
+                .data_threshold = DEFAULT_DATA_THRESHOLD,
+        };
 
         if (path) {
                 char *t;
@@ -2197,8 +2190,8 @@ _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
 }
 
 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
-        Object *o;
         JournalFile *f;
+        Object *o;
         int r;
 
         assert_return(j, -EINVAL);
@@ -2207,7 +2200,6 @@ _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id12
         f = j->current_file;
         if (!f)
                 return -EADDRNOTAVAIL;
-
         if (f->current_offset <= 0)
                 return -EADDRNOTAVAIL;
 
@@ -2228,8 +2220,12 @@ _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id12
                         return -ESTALE;
         }
 
+        uint64_t t = le64toh(o->entry.monotonic);
+        if (!VALID_MONOTONIC(t))
+                return -EBADMSG;
+
         if (ret)
-                *ret = le64toh(o->entry.monotonic);
+                *ret = t;
 
         return 0;
 }
diff --git a/src/libsystemd/sd-journal/test-journal-file.c b/src/libsystemd/sd-journal/test-journal-file.c
new file mode 100644 (file)
index 0000000..729de1f
--- /dev/null
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "journal-file.h"
+#include "tests.h"
+#include "user-util.h"
+
+static void test_journal_file_parse_uid_from_filename_simple(
+                const char *path,
+                uid_t expected_uid,
+                int expected_error) {
+
+        uid_t uid = UID_INVALID;
+        int r;
+
+        log_info("testing %s", path);
+
+        r = journal_file_parse_uid_from_filename(path, &uid);
+        assert_se(r == expected_error);
+        if (r < 0)
+                assert_se(uid == UID_INVALID);
+        else
+                assert_se(uid == expected_uid);
+}
+
+TEST(journal_file_parse_uid_from_filename) {
+
+        test_journal_file_parse_uid_from_filename_simple("/var/log/journal/", 0, -EISDIR);
+
+        /* The helper should return -EREMOTE for any filenames that don't look like an online or offline user
+         * journals. This includes archived and disposed journal files. */
+        test_journal_file_parse_uid_from_filename_simple("/etc/password", 0, -EREMOTE);
+        test_journal_file_parse_uid_from_filename_simple("system.journal", 0, -EREMOTE);
+        test_journal_file_parse_uid_from_filename_simple("user-1000@0005d26980bdce6e-2f2a4939583822ef.journal~", 0, -EREMOTE);
+        test_journal_file_parse_uid_from_filename_simple("user-1000@xxx-yyy-zzz.journal", 0, -EREMOTE);
+
+        test_journal_file_parse_uid_from_filename_simple("user-1000.journal", 1000, 0);
+        test_journal_file_parse_uid_from_filename_simple("user-foo.journal", 0, -EINVAL);
+        test_journal_file_parse_uid_from_filename_simple("user-65535.journal", 0, -ENXIO);
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);
index 90b5ebb4b7f9a056f898a6c376dd4f912fdaf4e3..d483889fd18531c8bc8d3dadd2fa65a6531d2041 100644 (file)
@@ -22,6 +22,7 @@
 #include "macro.h"
 #include "parse-util.h"
 #include "path-util.h"
+#include "process-util.h"
 #include "socket-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
@@ -134,6 +135,206 @@ _public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) {
         return 0;
 }
 
+_public_ int sd_pidfd_get_session(int pidfd, char **ret_session) {
+        _cleanup_free_ char *session = NULL;
+        pid_t pid;
+        int r;
+
+        assert_return(pidfd >= 0, -EBADF);
+        assert_return(ret_session, -EINVAL);
+
+        r = pidfd_get_pid(pidfd, &pid);
+        if (r < 0)
+                return r;
+
+        r = sd_pid_get_session(pid, &session);
+        if (r < 0)
+                return r;
+
+        r = pidfd_verify_pid(pidfd, pid);
+        if (r < 0)
+                return r;
+
+        *ret_session = TAKE_PTR(session);
+
+        return 0;
+}
+
+_public_ int sd_pidfd_get_unit(int pidfd, char **ret_unit) {
+        _cleanup_free_ char *unit = NULL;
+        pid_t pid;
+        int r;
+
+        assert_return(pidfd >= 0, -EBADF);
+        assert_return(ret_unit, -EINVAL);
+
+        r = pidfd_get_pid(pidfd, &pid);
+        if (r < 0)
+                return r;
+
+        r = sd_pid_get_unit(pid, &unit);
+        if (r < 0)
+                return r;
+
+        r = pidfd_verify_pid(pidfd, pid);
+        if (r < 0)
+                return r;
+
+        *ret_unit = TAKE_PTR(unit);
+
+        return 0;
+}
+
+_public_ int sd_pidfd_get_user_unit(int pidfd, char **ret_unit) {
+        _cleanup_free_ char *unit = NULL;
+        pid_t pid;
+        int r;
+
+        assert_return(pidfd >= 0, -EBADF);
+        assert_return(ret_unit, -EINVAL);
+
+        r = pidfd_get_pid(pidfd, &pid);
+        if (r < 0)
+                return r;
+
+        r = sd_pid_get_user_unit(pid, &unit);
+        if (r < 0)
+                return r;
+
+        r = pidfd_verify_pid(pidfd, pid);
+        if (r < 0)
+                return r;
+
+        *ret_unit = TAKE_PTR(unit);
+
+        return 0;
+}
+
+_public_ int sd_pidfd_get_machine_name(int pidfd, char **ret_name) {
+        _cleanup_free_ char *name = NULL;
+        pid_t pid;
+        int r;
+
+        assert_return(pidfd >= 0, -EBADF);
+        assert_return(ret_name, -EINVAL);
+
+        r = pidfd_get_pid(pidfd, &pid);
+        if (r < 0)
+                return r;
+
+        r = sd_pid_get_machine_name(pid, &name);
+        if (r < 0)
+                return r;
+
+        r = pidfd_verify_pid(pidfd, pid);
+        if (r < 0)
+                return r;
+
+        *ret_name = TAKE_PTR(name);
+
+        return 0;
+}
+
+_public_ int sd_pidfd_get_slice(int pidfd, char **ret_slice) {
+        _cleanup_free_ char *slice = NULL;
+        pid_t pid;
+        int r;
+
+        assert_return(pidfd >= 0, -EBADF);
+        assert_return(ret_slice, -EINVAL);
+
+        r = pidfd_get_pid(pidfd, &pid);
+        if (r < 0)
+                return r;
+
+        r = sd_pid_get_slice(pid, &slice);
+        if (r < 0)
+                return r;
+
+        r = pidfd_verify_pid(pidfd, pid);
+        if (r < 0)
+                return r;
+
+        *ret_slice = TAKE_PTR(slice);
+
+        return 0;
+}
+
+_public_ int sd_pidfd_get_user_slice(int pidfd, char **ret_slice) {
+        _cleanup_free_ char *slice = NULL;
+        pid_t pid;
+        int r;
+
+        assert_return(pidfd >= 0, -EBADF);
+        assert_return(ret_slice, -EINVAL);
+
+        r = pidfd_get_pid(pidfd, &pid);
+        if (r < 0)
+                return r;
+
+        r = sd_pid_get_user_slice(pid, &slice);
+        if (r < 0)
+                return r;
+
+        r = pidfd_verify_pid(pidfd, pid);
+        if (r < 0)
+                return r;
+
+        *ret_slice = TAKE_PTR(slice);
+
+        return 0;
+}
+
+_public_ int sd_pidfd_get_owner_uid(int pidfd, uid_t *ret_uid) {
+        uid_t uid;
+        pid_t pid;
+        int r;
+
+        assert_return(pidfd >= 0, -EINVAL);
+        assert_return(ret_uid, -EINVAL);
+
+        r = pidfd_get_pid(pidfd, &pid);
+        if (r < 0)
+                return r;
+
+        r = sd_pid_get_owner_uid(pid, &uid);
+        if (r < 0)
+                return r;
+
+        r = pidfd_verify_pid(pidfd, pid);
+        if (r < 0)
+                return r;
+
+        *ret_uid = uid;
+
+        return 0;
+}
+
+_public_ int sd_pidfd_get_cgroup(int pidfd, char **ret_cgroup) {
+        _cleanup_free_ char *cgroup = NULL;
+        pid_t pid;
+        int r;
+
+        assert_return(pidfd >= 0, -EBADF);
+        assert_return(ret_cgroup, -EINVAL);
+
+        r = pidfd_get_pid(pidfd, &pid);
+        if (r < 0)
+                return r;
+
+        r = sd_pid_get_cgroup(pid, &cgroup);
+        if (r < 0)
+                return r;
+
+        r = pidfd_verify_pid(pidfd, pid);
+        if (r < 0)
+                return r;
+
+        *ret_cgroup = TAKE_PTR(cgroup);
+
+        return 0;
+}
+
 _public_ int sd_peer_get_session(int fd, char **session) {
         struct ucred ucred = UCRED_INVALID;
         int r;
index 96a8c567fc7654204a7f05902a1ed79c2de693ea..fe4b52f6c684bc5ee30e91faa4a97a02770cc432 100644 (file)
@@ -9,6 +9,8 @@
 #include "fd-util.h"
 #include "format-util.h"
 #include "log.h"
+#include "missing_syscall.h"
+#include "process-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tests.h"
@@ -44,6 +46,7 @@ TEST(login) {
                 *type = NULL, *class = NULL, *state = NULL, *state2 = NULL,
                 *seat = NULL, *session = NULL,
                 *unit = NULL, *user_unit = NULL, *slice = NULL;
+        _cleanup_close_ int pidfd = -EBADF;
         int r;
         uid_t u, u2 = UID_INVALID;
         char *t, **seats = NULL, **sessions = NULL;
@@ -71,6 +74,35 @@ TEST(login) {
         log_info("sd_pid_get_cgroup(0, …) → %s / \"%s\"", e(r), strnull(cgroup));
         assert_se(IN_SET(r, 0, -ENOMEDIUM));
 
+        pidfd = pidfd_open(getpid_cached(), 0);
+        if (pidfd >= 0) {
+                _cleanup_free_ char *cgroup2 = NULL, *session2 = NULL,
+                        *unit2 = NULL, *user_unit2 = NULL, *slice2 = NULL;
+
+                r = sd_pidfd_get_unit(pidfd, &unit2);
+                log_info("sd_pidfd_get_unit(pidfd, …) → %s / \"%s\"", e(r), strnull(unit2));
+                assert_se(IN_SET(r, 0, -ENODATA));
+
+                r = sd_pidfd_get_user_unit(pidfd, &user_unit2);
+                log_info("sd_pidfd_get_user_unit(pidfd, …) → %s / \"%s\"", e(r), strnull(user_unit2));
+                assert_se(IN_SET(r, 0, -ENODATA));
+
+                r = sd_pidfd_get_slice(pidfd, &slice2);
+                log_info("sd_pidfd_get_slice(pidfd, …) → %s / \"%s\"", e(r), strnull(slice2));
+                assert_se(IN_SET(r, 0, -ENODATA));
+
+                r = sd_pidfd_get_owner_uid(pidfd, &u2);
+                log_info("sd_pidfd_get_owner_uid(pidfd, …) → %s / "UID_FMT, e(r), u2);
+                assert_se(IN_SET(r, 0, -ENODATA));
+
+                r = sd_pidfd_get_session(pidfd, &session2);
+                log_info("sd_pidfd_get_session(pidfd, …) → %s / \"%s\"", e(r), strnull(session2));
+
+                r = sd_pidfd_get_cgroup(pidfd, &cgroup2);
+                log_info("sd_pidfd_get_cgroup(pidfd, …) → %s / \"%s\"", e(r), strnull(cgroup2));
+                assert_se(IN_SET(r, 0, -ENOMEDIUM));
+        }
+
         r = sd_uid_get_display(u2, &display_session);
         log_info("sd_uid_get_display("UID_FMT", …) → %s / \"%s\"", u2, e(r), strnull(display_session));
         if (u2 == UID_INVALID)
index 56de3f965c4a05e0a1513cd193f9485137623f86..dd440a5d17d924feaa9e0db4753988ebafe542cf 100644 (file)
@@ -159,6 +159,25 @@ int sd_network_link_get_network_file(int ifindex, char **ret) {
         return network_link_get_string(ifindex, "NETWORK_FILE", ret);
 }
 
+int sd_network_link_get_network_file_dropins(int ifindex, char ***ret) {
+        _cleanup_free_ char **sv = NULL, *joined = NULL;
+        int r;
+
+        assert_return(ifindex > 0, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = network_link_get_string(ifindex, "NETWORK_FILE_DROPINS", &joined);
+        if (r < 0)
+                return r;
+
+        r = strv_split_full(&sv, joined, ":", EXTRACT_CUNESCAPE);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(sv);
+        return 0;
+}
+
 int sd_network_link_get_operational_state(int ifindex, char **ret) {
         return network_link_get_string(ifindex, "OPER_STATE", ret);
 }
index ded64a66efad22e7b367b752a616047f6c084b3f..1af3a36d1d93acd2d08a5e3e39c80bd38c92177d 100644 (file)
@@ -380,6 +380,14 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
         case SD_PATH_CATALOG:
                 *ret = "/usr/lib/systemd/catalog";
                 return 0;
+
+        case SD_PATH_SYSTEMD_SYSTEM_ENVIRONMENT_GENERATOR:
+                *ret = SYSTEM_ENV_GENERATOR_DIR;
+                return 0;
+
+        case SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR:
+                *ret = USER_ENV_GENERATOR_DIR;
+                return 0;
         }
 
         return -EOPNOTSUPP;
@@ -626,6 +634,18 @@ static int get_search(uint64_t type, char ***list) {
                 return 0;
         }
 
+        case SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR:
+        case SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR: {
+                char **t;
+
+                t = env_generator_binary_paths(type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR);
+                if (!t)
+                        return -ENOMEM;
+
+                *list = t;
+                return 0;
+        }
+
         case SD_PATH_SYSTEMD_SEARCH_NETWORK:
                 return strv_from_nulstr(list, NETWORK_DIRS_NULSTR);
 
index ddca11d908ef281c2a362d3add945f2734991f17..4f2d3aaeb868305962810afa4a7b9ed673ff0324 100644 (file)
@@ -505,7 +505,9 @@ int find_legacy_keymap(Context *c, char **ret) {
 
                                 /* If that didn't work, strip off the
                                  * other layouts from the entry, too */
-                                x = strndup(a[1], strcspn(a[1], ","));
+                                x = strdupcspn(a[1], ",");
+                                if (!x)
+                                        return -ENOMEM;
                                 if (startswith_comma(c->x11_layout, x))
                                         matching = 1;
                         }
index 7d51a619bd59b982a1ae6be51ab5306a5fba1455..547de2418c498d8ec5b5bbf74cf613bb9b83c08e 100644 (file)
@@ -521,7 +521,7 @@ int config_parse_l2tp_tunnel_local_address(
                         return log_oom();
         }
 
-        type = l2tp_local_address_type_from_string(rvalue);
+        type = l2tp_local_address_type_from_string(addr_or_type);
         if (type >= 0) {
                 free_and_replace(t->local_ifname, ifname);
                 t->local_address_type = type;
@@ -534,15 +534,15 @@ int config_parse_l2tp_tunnel_local_address(
                 return 0;
         }
 
-        r = in_addr_from_string_auto(rvalue, &f, &a);
+        r = in_addr_from_string_auto(addr_or_type, &f, &a);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Invalid L2TP Tunnel local address specified, ignoring assignment: %s", rvalue);
+                           "Invalid L2TP Tunnel local address \"%s\" specified, ignoring assignment: %s", addr_or_type, rvalue);
                 return 0;
         }
 
         if (in_addr_is_null(f, &a)) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "L2TP Tunnel local address cannot be null, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -598,7 +598,7 @@ int config_parse_l2tp_tunnel_remote_address(
         }
 
         if (in_addr_is_null(f, &a)) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "L2TP Tunnel remote address cannot be null, ignoring assignment: %s", rvalue);
                 return 0;
         }
index 357d38f5ea64518765aed3d93fd4b8987859fe8b..65015b132f0322f61c7082af26ffcd4407a9588d 100644 (file)
@@ -1536,13 +1536,29 @@ static int table_add_string_line(Table *table, const char *key, const char *valu
         return 0;
 }
 
+static int format_dropins(char **dropins) {
+        STRV_FOREACH(d, dropins) {
+                _cleanup_free_ char *s = NULL;
+                int glyph = *(d + 1) == NULL ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH;
+
+                s = strjoin(special_glyph(glyph), *d);
+                if (!s)
+                        return log_oom();
+
+                free_and_replace(*d, s);
+        }
+
+        return 0;
+}
+
 static int link_status_one(
                 sd_bus *bus,
                 sd_netlink *rtnl,
                 sd_hwdb *hwdb,
                 const LinkInfo *info) {
 
-        _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, **route_domains = NULL;
+        _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL,
+                **route_domains = NULL, **link_dropins = NULL, **network_dropins = NULL;
         _cleanup_free_ char *t = NULL, *network = NULL, *iaid = NULL, *duid = NULL,
                 *setup_state = NULL, *operational_state = NULL, *online_state = NULL, *activation_policy = NULL;
         const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL,
@@ -1571,12 +1587,22 @@ static int link_status_one(
         (void) sd_network_link_get_ntp(info->ifindex, &ntp);
         (void) sd_network_link_get_sip(info->ifindex, &sip);
         (void) sd_network_link_get_network_file(info->ifindex, &network);
+        (void) sd_network_link_get_network_file_dropins(info->ifindex, &network_dropins);
         (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
         (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
         (void) sd_network_link_get_activation_policy(info->ifindex, &activation_policy);
 
         if (info->sd_device) {
+                const char *joined;
+
                 (void) sd_device_get_property_value(info->sd_device, "ID_NET_LINK_FILE", &link);
+
+                if (sd_device_get_property_value(info->sd_device, "ID_NET_LINK_FILE_DROPINS", &joined) >= 0) {
+                        r = strv_split_full(&link_dropins, joined, ":", EXTRACT_CUNESCAPE);
+                        if (r < 0)
+                                return r;
+                }
+
                 (void) sd_device_get_property_value(info->sd_device, "ID_NET_DRIVER", &driver);
                 (void) sd_device_get_property_value(info->sd_device, "ID_PATH", &path);
 
@@ -1596,6 +1622,20 @@ static int link_status_one(
 
         (void) dhcp_lease_load(&lease, lease_file);
 
+        r = format_dropins(network_dropins);
+        if (r < 0)
+                return r;
+
+        if (strv_prepend(&network_dropins, network) < 0)
+                return log_oom();
+
+        r = format_dropins(link_dropins);
+        if (r < 0)
+                return r;
+
+        if (strv_prepend(&link_dropins, link) < 0)
+                return log_oom();
+
         table = table_new("dot", "key", "value");
         if (!table)
                 return log_oom();
@@ -1631,10 +1671,10 @@ static int link_status_one(
                            TABLE_EMPTY,
                            TABLE_STRING, "Link File:",
                            TABLE_SET_ALIGN_PERCENT, 100,
-                           TABLE_STRING, strna(link),
+                           TABLE_STRV, link_dropins ?: STRV_MAKE("n/a"),
                            TABLE_EMPTY,
                            TABLE_STRING, "Network File:",
-                           TABLE_STRING, strna(network),
+                           TABLE_STRV, network_dropins ?: STRV_MAKE("n/a"),
                            TABLE_EMPTY,
                            TABLE_STRING, "State:");
         if (r < 0)
index 5b3b7d128aaef8f0e556a4d59418d7f46ef92ca5..b8b9dc153b088a68b1cad66ae71b773568a6dc06 100644 (file)
@@ -1216,9 +1216,13 @@ int link_request_address(
 
         (void) address_get(link, address, &existing);
 
-        if (address->lifetime_valid_usec == 0)
+        if (address->lifetime_valid_usec == 0) {
+                if (consume_object)
+                        address_free(address);
+
                 /* The requested address is outdated. Let's remove it. */
                 return address_remove_and_drop(existing);
+        }
 
         if (!existing) {
                 _cleanup_(address_freep) Address *tmp = NULL;
@@ -1679,12 +1683,11 @@ int config_parse_address(
         /* Address=address/prefixlen */
         r = in_addr_prefix_from_string_auto_internal(rvalue, PREFIXLEN_REFUSE, &f, &buffer, &prefixlen);
         if (r == -ENOANO) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "An address '%s' is specified without prefix length. "
-                           "The behavior of parsing addresses without prefix length will be changed in the future release. "
-                           "Please specify prefix length explicitly.", rvalue);
-
-                r = in_addr_prefix_from_string_auto_internal(rvalue, PREFIXLEN_LEGACY, &f, &buffer, &prefixlen);
+                r = in_addr_prefix_from_string_auto(rvalue, &f, &buffer, &prefixlen);
+                if (r >= 0)
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "An address '%s' is specified without prefix length. Assuming the prefix length is %u."
+                                   "Please specify the prefix length explicitly.", rvalue, prefixlen);
         }
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid address '%s', ignoring assignment: %m", rvalue);
index e4005719d05065068e7c435f0a1a231e09494801..1d5e2975a8c5e33bfb0f115937a7368ed0f709fa 100644 (file)
@@ -183,6 +183,7 @@ static int dhcp4_request_route(Route *in, Link *link) {
 
         assert(route);
         assert(link);
+        assert(link->network);
         assert(link->dhcp_lease);
 
         r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &server);
@@ -200,6 +201,8 @@ static int dhcp4_request_route(Route *in, Link *link) {
                 route->table = link_get_dhcp4_route_table(link);
         if (route->mtu == 0)
                 route->mtu = link->network->dhcp_route_mtu;
+        if (route->quickack < 0)
+                route->quickack = link->network->dhcp_quickack;
 
         if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
                 link->dhcp4_configured = false;
@@ -1499,6 +1502,12 @@ static int dhcp4_configure(Link *link) {
                         return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set IP service type: %m");
         }
 
+        if (link->network->dhcp_socket_priority_set) {
+                r = sd_dhcp_client_set_socket_priority(link->dhcp_client, link->network->dhcp_socket_priority);
+                if (r < 0)
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set socket priority: %m");
+        }
+
         if (link->network->dhcp_fallback_lease_lifetime > 0) {
                 r = sd_dhcp_client_set_fallback_lease_lifetime(link->dhcp_client, link->network->dhcp_fallback_lease_lifetime);
                 if (r < 0)
@@ -1701,6 +1710,42 @@ int config_parse_dhcp_ip_service_type(
         return 0;
 }
 
+int config_parse_dhcp_socket_priority(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = ASSERT_PTR(data);
+        int a, r;
+
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                network->dhcp_socket_priority_set = false;
+                return 0;
+        }
+
+        r = safe_atoi(rvalue, &a);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse socket priority, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        network->dhcp_socket_priority_set = true;
+        network->dhcp_socket_priority = a;
+
+        return 0;
+}
+
 int config_parse_dhcp_fallback_lease_lifetime(
                 const char *unit,
                 const char *filename,
index 1d30cd15dfba04ba9c65eb6f9c4d494c6eae55fe..0f14e0794333c28bd69225d5278312512b071010 100644 (file)
@@ -29,6 +29,7 @@ int link_request_dhcp4_client(Link *link);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_ip_service_type);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_socket_priority);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_mud_url);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_fallback_lease_lifetime);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_label);
index c44c37f3aa4c9b0057266210c9c36b049ff07dd5..43be98837768393c836795fdb7f66ab694fc0431 100644 (file)
@@ -707,6 +707,12 @@ static int dhcp6_configure(Link *link) {
                                             "DHCPv6 CLIENT: Failed to %s rapid commit: %m",
                                             enable_disable(link->network->dhcp6_use_rapid_commit));
 
+        r = sd_dhcp6_client_set_send_release(client, link->network->dhcp6_send_release);
+        if (r < 0)
+                return log_link_debug_errno(link, r,
+                                            "DHCPv6 CLIENT: Failed to %s sending release message on stop: %m",
+                                            enable_disable(link->network->dhcp6_send_release));
+
         link->dhcp6_client = TAKE_PTR(client);
 
         return 0;
index 9e2ff33d970ce2560f136fc2ebbd556d248778b4..c37f734d619428abcf3257dc7c178284874b2e95 100644 (file)
@@ -464,6 +464,7 @@ static int network_build_json(Network *network, JsonVariant **ret) {
 
         return json_build(ret, JSON_BUILD_OBJECT(
                                 JSON_BUILD_PAIR_STRING("NetworkFile", network->filename),
+                                JSON_BUILD_PAIR_STRV("NetworkFileDropins", network->dropins),
                                 JSON_BUILD_PAIR_BOOLEAN("RequiredForOnline", network->required_for_online),
                                 JSON_BUILD_PAIR("RequiredOperationalStateForOnline",
                                                 JSON_BUILD_ARRAY(JSON_BUILD_STRING(link_operstate_to_string(network->required_operstate_for_online.min)),
@@ -475,7 +476,9 @@ static int network_build_json(Network *network, JsonVariant **ret) {
 }
 
 static int device_build_json(sd_device *device, JsonVariant **ret) {
-        const char *link = NULL, *path = NULL, *vendor = NULL, *model = NULL;
+        _cleanup_strv_free_ char **link_dropins = NULL;
+        const char *link = NULL, *path = NULL, *vendor = NULL, *model = NULL, *joined;
+        int r;
 
         assert(ret);
 
@@ -485,6 +488,13 @@ static int device_build_json(sd_device *device, JsonVariant **ret) {
         }
 
         (void) sd_device_get_property_value(device, "ID_NET_LINK_FILE", &link);
+
+        if (sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &joined) >= 0) {
+                 r = strv_split_full(&link_dropins, joined, ":", EXTRACT_CUNESCAPE);
+                 if (r < 0)
+                        return r;
+        }
+
         (void) sd_device_get_property_value(device, "ID_PATH", &path);
 
         if (sd_device_get_property_value(device, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
@@ -495,6 +505,7 @@ static int device_build_json(sd_device *device, JsonVariant **ret) {
 
         return json_build(ret, JSON_BUILD_OBJECT(
                                 JSON_BUILD_PAIR_STRING_NON_EMPTY("LinkFile", link),
+                                JSON_BUILD_PAIR_STRV_NON_EMPTY("LinkFileDropins", link_dropins),
                                 JSON_BUILD_PAIR_STRING_NON_EMPTY("Path", path),
                                 JSON_BUILD_PAIR_STRING_NON_EMPTY("Vendor", vendor),
                                 JSON_BUILD_PAIR_STRING_NON_EMPTY("Model", model)));
index 95a7d75e53c4777b906870ba52dd88de4b2e1377..e43c04567a4a390ee27e57164ebb4cfa7ffdf446 100644 (file)
@@ -2515,6 +2515,15 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
         log_link_debug(link, "Saved new link: ifindex=%i, iftype=%s(%u), kind=%s",
                        link->ifindex, strna(arphrd_to_name(link->iftype)), link->iftype, strna(link->kind));
 
+        /* If contained in this set, the link is wireless and the corresponding NL80211_CMD_NEW_INTERFACE
+         * message arrived too early. Request the wireless link information again.
+         */
+        if (set_remove(manager->new_wlan_ifindices, INT_TO_PTR(link->ifindex))) {
+                r = link_get_wlan_interface(link);
+                if (r < 0)
+                        log_link_warning_errno(link, r, "Failed to get wireless interface, ignoring: %m");
+        }
+
         *ret = TAKE_PTR(link);
         return 0;
 }
index f82117cc1e6f8d525e545f0dd1746be742f97e09..ad668215e7bb09df1f5271f2ce78cd79e321f1bb 100644 (file)
@@ -614,6 +614,7 @@ Manager* manager_free(Manager *m) {
         m->request_queue = ordered_set_free(m->request_queue);
 
         m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
+        m->new_wlan_ifindices = set_free(m->new_wlan_ifindices);
         m->links_by_name = hashmap_free(m->links_by_name);
         m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr);
         m->links_by_dhcp_pd_subnet_prefix = hashmap_free(m->links_by_dhcp_pd_subnet_prefix);
index e6183af0e4ba3899bc7487a59391698aa2941420..c9cbcf928994d5b3090be8aa930f76e1cd1289aa 100644 (file)
@@ -38,6 +38,7 @@ struct Manager {
         bool manage_foreign_rules;
 
         Set *dirty_links;
+        Set *new_wlan_ifindices;
 
         char *state_file;
         LinkOperationalState operational_state;
index c7ed5fcfe1a2b5da6898e7386565ed9e4e63c3d5..99a07e16fcf8981c41ba85c43de3299a720d855e 100644 (file)
@@ -173,6 +173,7 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
 
         assert(route);
         assert(link);
+        assert(link->network);
         assert(rt);
 
         r = sd_ndisc_router_get_address(rt, &router);
@@ -186,6 +187,8 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
         ndisc_set_route_priority(link, route);
         if (!route->protocol_set)
                 route->protocol = RTPROT_RA;
+        if (route->quickack < 0)
+                route->quickack = link->network->ipv6_accept_ra_quickack;
 
         is_new = route_get(NULL, link, route, NULL) < 0;
 
@@ -814,31 +817,24 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
                         return log_link_error_errno(link, r, "Failed to get RA option type: %m");
 
                 switch (type) {
-
                 case SD_NDISC_OPTION_PREFIX_INFORMATION:
                         r = ndisc_router_process_prefix(link, rt);
-                        if (r < 0)
-                                return r;
                         break;
 
                 case SD_NDISC_OPTION_ROUTE_INFORMATION:
                         r = ndisc_router_process_route(link, rt);
-                        if (r < 0)
-                                return r;
                         break;
 
                 case SD_NDISC_OPTION_RDNSS:
                         r = ndisc_router_process_rdnss(link, rt);
-                        if (r < 0)
-                                return r;
                         break;
 
                 case SD_NDISC_OPTION_DNSSL:
                         r = ndisc_router_process_dnssl(link, rt);
-                        if (r < 0)
-                                return r;
                         break;
                 }
+                if (r < 0 && r != -EBADMSG)
+                        return r;
         }
 }
 
@@ -1021,6 +1017,10 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         assert(rt);
 
         r = sd_ndisc_router_get_address(rt, &router);
+        if (r == -ENODATA) {
+                log_link_debug(link, "Received RA without router address, ignoring.");
+                return 0;
+        }
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
 
@@ -1035,6 +1035,10 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         }
 
         r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &timestamp_usec);
+        if (r == -ENODATA) {
+                log_link_debug(link, "Received RA without timestamp, ignoring.");
+                return 0;
+        }
         if (r < 0)
                 return r;
 
@@ -1081,7 +1085,7 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router
 
         case SD_NDISC_EVENT_ROUTER:
                 r = ndisc_router_handler(link, rt);
-                if (r < 0) {
+                if (r < 0 && r != -EBADMSG) {
                         link_enter_failed(link);
                         return;
                 }
index 762eef5b915332b2ad5be386a44a348a66caddcc..716904cc3446afed1c12e4416e64fa70bf65afc7 100644 (file)
@@ -221,6 +221,7 @@ DHCPv4.UseHostname,                          config_parse_bool,
 DHCPv4.UseDomains,                           config_parse_dhcp_use_domains,                            AF_INET,                       0
 DHCPv4.UseRoutes,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_routes)
 DHCPv4.UseGateway,                           config_parse_tristate,                                    0,                             offsetof(Network, dhcp_use_gateway)
+DHCPv4.QuickAck,                             config_parse_bool,                                        0,                             offsetof(Network, dhcp_quickack)
 DHCPv4.RequestOptions,                       config_parse_dhcp_request_options,                        AF_INET,                       0
 DHCPv4.Anonymize,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_anonymize)
 DHCPv4.SendHostname,                         config_parse_bool,                                        0,                             offsetof(Network, dhcp_send_hostname)
@@ -243,6 +244,7 @@ DHCPv4.SendDecline,                          config_parse_bool,
 DHCPv4.DenyList,                             config_parse_in_addr_prefixes,                            AF_INET,                       offsetof(Network, dhcp_deny_listed_ip)
 DHCPv4.AllowList,                            config_parse_in_addr_prefixes,                            AF_INET,                       offsetof(Network, dhcp_allow_listed_ip)
 DHCPv4.IPServiceType,                        config_parse_dhcp_ip_service_type,                        0,                             offsetof(Network, dhcp_ip_service_type)
+DHCPv4.SocketPriority,                       config_parse_dhcp_socket_priority,                        0,                             0
 DHCPv4.SendOption,                           config_parse_dhcp_send_option,                            AF_INET,                       offsetof(Network, dhcp_client_send_options)
 DHCPv4.SendVendorOption,                     config_parse_dhcp_send_option,                            0,                             offsetof(Network, dhcp_client_send_vendor_options)
 DHCPv4.RouteMTUBytes,                        config_parse_mtu,                                         AF_INET,                       offsetof(Network, dhcp_route_mtu)
@@ -268,6 +270,7 @@ DHCPv6.DUIDType,                             config_parse_duid_type,
 DHCPv6.DUIDRawData,                          config_parse_duid_rawdata,                                0,                             offsetof(Network, dhcp6_duid)
 DHCPv6.RapidCommit,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_rapid_commit)
 DHCPv6.NetLabel,                             config_parse_string,                                      CONFIG_PARSE_STRING_SAFE,      offsetof(Network, dhcp6_netlabel)
+DHCPv6.SendRelease,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp6_send_release)
 IPv6AcceptRA.UseGateway,                     config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_gateway)
 IPv6AcceptRA.UseRoutePrefix,                 config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_route_prefix)
 IPv6AcceptRA.UseAutonomousPrefix,            config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
@@ -278,6 +281,7 @@ IPv6AcceptRA.UseMTU,                         config_parse_bool,
 IPv6AcceptRA.DHCPv6Client,                   config_parse_ipv6_accept_ra_start_dhcp6_client,           0,                             offsetof(Network, ipv6_accept_ra_start_dhcp6_client)
 IPv6AcceptRA.RouteTable,                     config_parse_dhcp_or_ra_route_table,                      AF_INET6,                      0
 IPv6AcceptRA.RouteMetric,                    config_parse_ipv6_accept_ra_route_metric,                 0,                             0
+IPv6AcceptRA.QuickAck,                       config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_quickack)
 IPv6AcceptRA.RouterAllowList,                config_parse_in_addr_prefixes,                            AF_INET6,                      offsetof(Network, ndisc_allow_listed_router)
 IPv6AcceptRA.RouterDenyList,                 config_parse_in_addr_prefixes,                            AF_INET6,                      offsetof(Network, ndisc_deny_listed_router)
 IPv6AcceptRA.PrefixAllowList,                config_parse_in_addr_prefixes,                            AF_INET6,                      offsetof(Network, ndisc_allow_listed_prefix)
index d516918c786e6e2b3ec5a6b805447df36c007dfb..7c5d691afab1771dea1737291e877d66e7d3ea5c 100644 (file)
@@ -416,6 +416,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .dhcp6_use_rapid_commit = true,
                 .dhcp6_duid.type = _DUID_TYPE_INVALID,
                 .dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID,
+                .dhcp6_send_release = true,
 
                 .dhcp_pd = -1,
                 .dhcp_pd_announce = true,
@@ -552,7 +553,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                         CONFIG_PARSE_WARN,
                         network,
                         &network->stats_by_path,
-                        NULL);
+                        &network->dropins);
         if (r < 0)
                 return r; /* config_parse_many() logs internally. */
 
@@ -670,6 +671,7 @@ static Network *network_free(Network *network) {
         free(network->name);
         free(network->filename);
         free(network->description);
+        strv_free(network->dropins);
         hashmap_free(network->stats_by_path);
 
         /* conditions */
index 79e8ceb06b41d038155434152c1a2d0244f586f7..7685c98f652901b6f8b71c43463eeac5672236bf 100644 (file)
@@ -65,6 +65,7 @@ struct Network {
 
         char *name;
         char *filename;
+        char **dropins;
         Hashmap *stats_by_path;
         char *description;
 
@@ -130,6 +131,8 @@ struct Network {
         uint16_t dhcp_client_port;
         int dhcp_critical;
         int dhcp_ip_service_type;
+        int dhcp_socket_priority;
+        bool dhcp_socket_priority_set;
         bool dhcp_anonymize;
         bool dhcp_send_hostname;
         int dhcp_broadcast;
@@ -143,6 +146,7 @@ struct Network {
         bool dhcp_use_mtu;
         bool dhcp_use_routes;
         int dhcp_use_gateway;
+        bool dhcp_quickack;
         bool dhcp_use_timezone;
         bool dhcp_use_hostname;
         bool dhcp_use_6rd;
@@ -182,6 +186,7 @@ struct Network {
         OrderedHashmap *dhcp6_client_send_vendor_options;
         Set *dhcp6_request_options;
         char *dhcp6_netlabel;
+        bool dhcp6_send_release;
 
         /* DHCP Server Support */
         bool dhcp_server;
@@ -309,6 +314,7 @@ struct Network {
         bool ipv6_accept_ra_use_autonomous_prefix;
         bool ipv6_accept_ra_use_onlink_prefix;
         bool ipv6_accept_ra_use_mtu;
+        bool ipv6_accept_ra_quickack;
         bool active_slave;
         bool primary_slave;
         DHCPUseDomains ipv6_accept_ra_use_domains;
index d1f3bab0922e05b181d314e6d5cefa16c61297f0..5214a8ad2c91cd789723059add21846fad6eceeb 100644 (file)
@@ -1437,9 +1437,13 @@ int link_request_route(
 
         (void) route_get(link->manager, link, route, &existing);
 
-        if (route->lifetime_usec == 0)
+        if (route->lifetime_usec == 0) {
+                if (consume_object)
+                        route_free(route);
+
                 /* The requested route is outdated. Let's remove it. */
                 return route_remove_and_drop(existing);
+        }
 
         if (!existing) {
                 _cleanup_(route_freep) Route *tmp = NULL;
index 0800e91202b4d23f058f88c4ca04d92da8d5817e..6e962c03f64eb8d045a252b38957ba806437c129 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "alloc-util.h"
 #include "dns-domain.h"
+#include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -515,7 +516,7 @@ int link_save(Link *link) {
 
         if (link->network) {
                 const char *online_state;
-                bool space;
+                bool space = false;
 
                 online_state = link_online_state_to_string(link->online_state);
                 if (online_state)
@@ -538,6 +539,18 @@ int link_save(Link *link) {
 
                 fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
 
+                fputs("NETWORK_FILE_DROPINS=\"", f);
+                STRV_FOREACH(d, link->network->dropins) {
+                        _cleanup_free_ char *escaped = NULL;
+
+                        escaped = xescape(*d, ":");
+                        if (!escaped)
+                                return -ENOMEM;
+
+                        fputs_with_space(f, escaped, ":", &space);
+                }
+                fputs("\"\n", f);
+
                 /************************************************************/
 
                 fputs("DNS=", f);
index e35857261e07a01b7b38a1579dca799867ca8df7..1a3754b29ce1b85b34655113ebfacaa53f1c2a8a 100644 (file)
@@ -92,6 +92,19 @@ int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *me
         if (r < 0) {
                 log_debug_errno(r, "nl80211: received %s(%u) message for link '%"PRIu32"' we don't know about, ignoring.",
                                 strna(nl80211_cmd_to_string(cmd)), cmd, ifindex);
+
+                /* The NL80211_CMD_NEW_INTERFACE message might arrive before RTM_NEWLINK, in which case a
+                 * link will not have been created yet. Store the interface index such that the wireless
+                 * properties of the link (such as wireless interface type) are queried again after the link
+                 * is created.
+                 */
+                if (cmd == NL80211_CMD_NEW_INTERFACE) {
+                        r = set_ensure_put(&manager->new_wlan_ifindices, NULL, INT_TO_PTR(ifindex));
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to add new wireless interface index to set, ignoring: %m");
+                } else if (cmd == NL80211_CMD_DEL_INTERFACE)
+                        set_remove(manager->new_wlan_ifindices, INT_TO_PTR(ifindex));
+
                 return 0;
         }
 
index 2e4ca0cb5f87d7af87da08375741c78fcf30f43b..f6f068706a81926f86e6c5ce50cc8d8574090692 100644 (file)
@@ -197,7 +197,7 @@ TEST(config_parse_address) {
         test_config_parse_address_one("", AF_INET, 0, NULL, 0);
         test_config_parse_address_one("/", AF_INET, 0, NULL, 0);
         test_config_parse_address_one("/8", AF_INET, 0, NULL, 0);
-        test_config_parse_address_one("1.2.3.4", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 8);
+        test_config_parse_address_one("1.2.3.4", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32);
         test_config_parse_address_one("1.2.3.4/0", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 0);
         test_config_parse_address_one("1.2.3.4/1", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 1);
         test_config_parse_address_one("1.2.3.4/2", AF_INET, 1, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 2);
@@ -208,7 +208,7 @@ TEST(config_parse_address) {
         test_config_parse_address_one("", AF_INET6, 0, NULL, 0);
         test_config_parse_address_one("/", AF_INET6, 0, NULL, 0);
         test_config_parse_address_one("/8", AF_INET6, 0, NULL, 0);
-        test_config_parse_address_one("::1", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0);
+        test_config_parse_address_one("::1", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128);
         test_config_parse_address_one("::1/0", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0);
         test_config_parse_address_one("::1/1", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 1);
         test_config_parse_address_one("::1/2", AF_INET6, 1, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 2);
index 551f8f975c30e00c6408972977781c46e959f773..9f747a72b1968c94d5011297cd2cb767722f2b00 100644 (file)
@@ -5726,6 +5726,7 @@ static int run(int argc, char *argv[]) {
                 r = loop_device_make_by_path(
                                 arg_image,
                                 arg_read_only ? O_RDONLY : O_RDWR,
+                                /* sector_size= */ UINT32_MAX,
                                 FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
                                 LOCK_SH,
                                 &loop);
index 2733763eb2fa43346a9530bd0a54724bb0f355ab..b37a3b90081e8209e2946397f5ecda13c1b379a5 100644 (file)
@@ -70,7 +70,7 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to extract file name from '%s': %m", device);
 
-        return make_filesystem(device, fstype, label, NULL, uuid, true, NULL);
+        return make_filesystem(device, fstype, label, NULL, uuid, true, 0, NULL);
 }
 
 DEFINE_MAIN_FUNCTION(run);
index 6802a9c8740b9bd8fbfd4d88c376d7b7e1b4973f..7c4a1ee01c7543cd22ff1aa46592e2194b9205eb 100644 (file)
@@ -147,6 +147,7 @@ static size_t arg_n_filter_partitions = 0;
 static FilterPartitionsType arg_filter_partitions_type = FILTER_PARTITIONS_NONE;
 static sd_id128_t *arg_defer_partitions = NULL;
 static size_t arg_n_defer_partitions = 0;
+static uint64_t arg_sector_size = 0;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -1973,17 +1974,37 @@ static int context_load_partition_table(Context *context) {
         assert(context->end == UINT64_MAX);
         assert(context->total == UINT64_MAX);
 
-        /* libfdisk doesn't have an API to operate on arbitrary fds, hence reopen the fd going via the
-         * /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
-        if (context->backing_fd < 0) {
-                c = fdisk_new_context();
-                if (!c)
-                        return log_oom();
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
 
-                r = fdisk_assign_device(c, context->node, arg_dry_run);
-        } else
-                r = fdisk_new_context_fd(context->backing_fd, arg_dry_run, &c);
+        if (arg_sector_size > 0)
+                r = fdisk_save_user_sector_size(c, /* phy= */ 0, arg_sector_size);
+        else {
+                uint32_t ssz;
+
+                if (context->backing_fd < 0) {
+                        context->backing_fd = open(context->node, O_RDONLY|O_CLOEXEC);
+                        if (context->backing_fd < 0)
+                                return log_error_errno(errno, "Failed to open device '%s': %m", context->node);
+                }
+
+                /* Auto-detect sector size if not specified. */
+                r = probe_sector_size_prefer_ioctl(context->backing_fd, &ssz);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to probe sector size of '%s': %m", context->node);
 
+                r = fdisk_save_user_sector_size(c, /* phy= */ 0, ssz);
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to set sector size: %m");
+
+        /* libfdisk doesn't have an API to operate on arbitrary fds, hence reopen the fd going via the
+         * /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
+        r = fdisk_assign_device(
+                        c,
+                        context->backing_fd >= 0 ? FORMAT_PROC_FD_PATH(context->backing_fd) : context->node,
+                        arg_dry_run);
         if (r == -EINVAL && arg_size_auto) {
                 struct stat st;
 
@@ -1999,7 +2020,7 @@ static int context_load_partition_table(Context *context) {
 
                 if (S_ISREG(st.st_mode) && st.st_size == 0) {
                         /* User the fallback values if we have no better idea */
-                        context->sector_size = 512;
+                        context->sector_size = arg_sector_size ?: 512;
                         context->grain_size = 4096;
                         return /* from_scratch = */ true;
                 }
@@ -3314,6 +3335,7 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
                 _cleanup_free_ void *pubkey = NULL;
                 _cleanup_free_ void *blob = NULL, *hash = NULL;
                 size_t secret_size, blob_size, hash_size, pubkey_size = 0;
+                ssize_t base64_encoded_size;
                 uint16_t pcr_bank, primary_alg;
                 int keyslot;
 
@@ -3341,9 +3363,9 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to seal to TPM2: %m");
 
-                r = base64mem(secret, secret_size, &base64_encoded);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to base64 encode secret key: %m");
+                base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+                if (base64_encoded_size < 0)
+                        return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
 
                 r = cryptsetup_set_minimal_pbkdf(cd);
                 if (r < 0)
@@ -3355,7 +3377,7 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
                                 NULL,
                                 VOLUME_KEY_SIZE,
                                 base64_encoded,
-                                strlen(base64_encoded));
+                                base64_encoded_size);
                 if (keyslot < 0)
                         return log_error_errno(keyslot, "Failed to add new TPM2 key: %m");
 
@@ -3368,6 +3390,7 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
                                 primary_alg,
                                 blob, blob_size,
                                 hash, hash_size,
+                                NULL, 0, /* no salt because tpm2_seal has no pin */
                                 0,
                                 &v);
                 if (r < 0)
@@ -4030,7 +4053,7 @@ static int context_mkfs(Context *context) {
                 }
 
                 r = make_filesystem(partition_target_path(t), p->format, strempty(p->new_label), root,
-                                    p->fs_uuid, arg_discard, NULL);
+                                    p->fs_uuid, arg_discard, context->sector_size, NULL);
                 if (r < 0)
                         return r;
 
@@ -5360,7 +5383,8 @@ static int context_minimize(Context *context) {
                                 return r;
                 }
 
-                r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, fs_uuid, arg_discard, NULL);
+                r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, fs_uuid,
+                                    arg_discard, context->sector_size, NULL);
                 if (r < 0)
                         return r;
 
@@ -5413,7 +5437,8 @@ static int context_minimize(Context *context) {
                 if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
                         return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
 
-                r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, p->fs_uuid, arg_discard, NULL);
+                r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, p->fs_uuid,
+                                    arg_discard, context->sector_size, NULL);
                 if (r < 0)
                         return r;
 
@@ -5510,6 +5535,7 @@ static int help(void) {
                "     --defer-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
                "                          Take partitions of the specified types into account\n"
                "                          but don't populate them yet\n"
+               "     --sector-size=SIZE   Set the logical sector size for the image\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -5548,6 +5574,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_INCLUDE_PARTITIONS,
                 ARG_EXCLUDE_PARTITIONS,
                 ARG_DEFER_PARTITIONS,
+                ARG_SECTOR_SIZE,
         };
 
         static const struct option options[] = {
@@ -5578,6 +5605,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "include-partitions",   required_argument, NULL, ARG_INCLUDE_PARTITIONS   },
                 { "exclude-partitions",   required_argument, NULL, ARG_EXCLUDE_PARTITIONS   },
                 { "defer-partitions",     required_argument, NULL, ARG_DEFER_PARTITIONS     },
+                { "sector-size",          required_argument, NULL, ARG_SECTOR_SIZE          },
                 {}
         };
 
@@ -5865,6 +5893,13 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_SECTOR_SIZE:
+                        r = parse_sector_size(optarg, &arg_sector_size);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -6140,7 +6175,7 @@ static int find_root(Context *context) {
         return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device.");
 }
 
-static int resize_pt(int fd) {
+static int resize_pt(int fd, uint64_t sector_size) {
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         int r;
 
@@ -6148,7 +6183,7 @@ static int resize_pt(int fd) {
          * possession of the enlarged backing file. For this it suffices to open the device with libfdisk and
          * immediately write it again, with no changes. */
 
-        r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
+        r = fdisk_new_context_fd(fd, /* read_only= */ false, sector_size, &c);
         if (r < 0)
                 return log_error_errno(r, "Failed to open device '%s': %m", FORMAT_PROC_FD_PATH(fd));
 
@@ -6172,7 +6207,8 @@ static int resize_backing_fd(
                 const char *node,           /* The primary way we access the disk image to operate on */
                 int *fd,                    /* An O_RDONLY fd referring to that inode */
                 const char *backing_file,   /* If the above refers to a loopback device, the backing regular file for that, which we can grow */
-                LoopDevice *loop_device) {
+                LoopDevice *loop_device,
+                uint64_t sector_size) {
 
         _cleanup_close_ int writable_fd = -EBADF;
         uint64_t current_size;
@@ -6284,7 +6320,7 @@ static int resize_backing_fd(
                          node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
 
 done:
-        r = resize_pt(writable_fd);
+        r = resize_pt(writable_fd, sector_size);
         if (r < 0)
                 return r;
 
@@ -6409,7 +6445,8 @@ static int run(int argc, char *argv[]) {
                                 context->node,
                                 &context->backing_fd,
                                 node_is_our_loop ? arg_image : NULL,
-                                node_is_our_loop ? loop_device : NULL);
+                                node_is_our_loop ? loop_device : NULL,
+                                context->sector_size);
                 if (r < 0)
                         return r;
         }
@@ -6483,7 +6520,8 @@ static int run(int argc, char *argv[]) {
                                 context->node,
                                 &context->backing_fd,
                                 node_is_our_loop ? arg_image : NULL,
-                                node_is_our_loop ? loop_device : NULL);
+                                node_is_our_loop ? loop_device : NULL,
+                                context->sector_size);
                 if (r < 0)
                         return r;
 
index d3348ce04fd8a33837a56ebab779e45b2f1fa774..52662402474154ed60e09422ca07e25d96e0dbd6 100644 (file)
 static const char *arg_suffix = NULL;
 
 static const char* const path_table[_SD_PATH_MAX] = {
-        [SD_PATH_TEMPORARY]                       = "temporary",
-        [SD_PATH_TEMPORARY_LARGE]                 = "temporary-large",
-        [SD_PATH_SYSTEM_BINARIES]                 = "system-binaries",
-        [SD_PATH_SYSTEM_INCLUDE]                  = "system-include",
-        [SD_PATH_SYSTEM_LIBRARY_PRIVATE]          = "system-library-private",
-        [SD_PATH_SYSTEM_LIBRARY_ARCH]             = "system-library-arch",
-        [SD_PATH_SYSTEM_SHARED]                   = "system-shared",
-        [SD_PATH_SYSTEM_CONFIGURATION_FACTORY]    = "system-configuration-factory",
-        [SD_PATH_SYSTEM_STATE_FACTORY]            = "system-state-factory",
-        [SD_PATH_SYSTEM_CONFIGURATION]            = "system-configuration",
-        [SD_PATH_SYSTEM_RUNTIME]                  = "system-runtime",
-        [SD_PATH_SYSTEM_RUNTIME_LOGS]             = "system-runtime-logs",
-        [SD_PATH_SYSTEM_STATE_PRIVATE]            = "system-state-private",
-        [SD_PATH_SYSTEM_STATE_LOGS]               = "system-state-logs",
-        [SD_PATH_SYSTEM_STATE_CACHE]              = "system-state-cache",
-        [SD_PATH_SYSTEM_STATE_SPOOL]              = "system-state-spool",
-        [SD_PATH_USER_BINARIES]                   = "user-binaries",
-        [SD_PATH_USER_LIBRARY_PRIVATE]            = "user-library-private",
-        [SD_PATH_USER_LIBRARY_ARCH]               = "user-library-arch",
-        [SD_PATH_USER_SHARED]                     = "user-shared",
-        [SD_PATH_USER_CONFIGURATION]              = "user-configuration",
-        [SD_PATH_USER_RUNTIME]                    = "user-runtime",
-        [SD_PATH_USER_STATE_CACHE]                = "user-state-cache",
-        [SD_PATH_USER]                            = "user",
-        [SD_PATH_USER_DOCUMENTS]                  = "user-documents",
-        [SD_PATH_USER_MUSIC]                      = "user-music",
-        [SD_PATH_USER_PICTURES]                   = "user-pictures",
-        [SD_PATH_USER_VIDEOS]                     = "user-videos",
-        [SD_PATH_USER_DOWNLOAD]                   = "user-download",
-        [SD_PATH_USER_PUBLIC]                     = "user-public",
-        [SD_PATH_USER_TEMPLATES]                  = "user-templates",
-        [SD_PATH_USER_DESKTOP]                    = "user-desktop",
-        [SD_PATH_SEARCH_BINARIES]                 = "search-binaries",
-        [SD_PATH_SEARCH_BINARIES_DEFAULT]         = "search-binaries-default",
-        [SD_PATH_SEARCH_LIBRARY_PRIVATE]          = "search-library-private",
-        [SD_PATH_SEARCH_LIBRARY_ARCH]             = "search-library-arch",
-        [SD_PATH_SEARCH_SHARED]                   = "search-shared",
-        [SD_PATH_SEARCH_CONFIGURATION_FACTORY]    = "search-configuration-factory",
-        [SD_PATH_SEARCH_STATE_FACTORY]            = "search-state-factory",
-        [SD_PATH_SEARCH_CONFIGURATION]            = "search-configuration",
-
-        [SD_PATH_SYSTEMD_UTIL]                    = "systemd-util",
-        [SD_PATH_SYSTEMD_SYSTEM_UNIT]             = "systemd-system-unit",
-        [SD_PATH_SYSTEMD_SYSTEM_PRESET]           = "systemd-system-preset",
-        [SD_PATH_SYSTEMD_SYSTEM_CONF]             = "systemd-system-conf",
-        [SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT]      = "systemd-search-system-unit",
-        [SD_PATH_SYSTEMD_SYSTEM_GENERATOR]        = "systemd-system-generator",
-        [SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR] = "systemd-search-system-generator",
-        [SD_PATH_SYSTEMD_USER_UNIT]               = "systemd-user-unit",
-        [SD_PATH_SYSTEMD_USER_PRESET]             = "systemd-user-preset",
-        [SD_PATH_SYSTEMD_USER_CONF]               = "systemd-user-conf",
-        [SD_PATH_SYSTEMD_SEARCH_USER_UNIT]        = "systemd-search-user-unit",
-        [SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR]   = "systemd-search-user-generator",
-        [SD_PATH_SYSTEMD_USER_GENERATOR]          = "systemd-user-generator",
-        [SD_PATH_SYSTEMD_SLEEP]                   = "systemd-sleep",
-        [SD_PATH_SYSTEMD_SHUTDOWN]                = "systemd-shutdown",
-
-        [SD_PATH_TMPFILES]                        = "tmpfiles",
-        [SD_PATH_SYSUSERS]                        = "sysusers",
-        [SD_PATH_SYSCTL]                          = "sysctl",
-        [SD_PATH_BINFMT]                          = "binfmt",
-        [SD_PATH_MODULES_LOAD]                    = "modules-load",
-        [SD_PATH_CATALOG]                         = "catalog",
-
-        [SD_PATH_SYSTEMD_SEARCH_NETWORK]          = "systemd-search-network",
+        [SD_PATH_TEMPORARY]                                   = "temporary",
+        [SD_PATH_TEMPORARY_LARGE]                             = "temporary-large",
+        [SD_PATH_SYSTEM_BINARIES]                             = "system-binaries",
+        [SD_PATH_SYSTEM_INCLUDE]                              = "system-include",
+        [SD_PATH_SYSTEM_LIBRARY_PRIVATE]                      = "system-library-private",
+        [SD_PATH_SYSTEM_LIBRARY_ARCH]                         = "system-library-arch",
+        [SD_PATH_SYSTEM_SHARED]                               = "system-shared",
+        [SD_PATH_SYSTEM_CONFIGURATION_FACTORY]                = "system-configuration-factory",
+        [SD_PATH_SYSTEM_STATE_FACTORY]                        = "system-state-factory",
+        [SD_PATH_SYSTEM_CONFIGURATION]                        = "system-configuration",
+        [SD_PATH_SYSTEM_RUNTIME]                              = "system-runtime",
+        [SD_PATH_SYSTEM_RUNTIME_LOGS]                         = "system-runtime-logs",
+        [SD_PATH_SYSTEM_STATE_PRIVATE]                        = "system-state-private",
+        [SD_PATH_SYSTEM_STATE_LOGS]                           = "system-state-logs",
+        [SD_PATH_SYSTEM_STATE_CACHE]                          = "system-state-cache",
+        [SD_PATH_SYSTEM_STATE_SPOOL]                          = "system-state-spool",
+        [SD_PATH_USER_BINARIES]                               = "user-binaries",
+        [SD_PATH_USER_LIBRARY_PRIVATE]                        = "user-library-private",
+        [SD_PATH_USER_LIBRARY_ARCH]                           = "user-library-arch",
+        [SD_PATH_USER_SHARED]                                 = "user-shared",
+        [SD_PATH_USER_CONFIGURATION]                          = "user-configuration",
+        [SD_PATH_USER_RUNTIME]                                = "user-runtime",
+        [SD_PATH_USER_STATE_CACHE]                            = "user-state-cache",
+        [SD_PATH_USER]                                        = "user",
+        [SD_PATH_USER_DOCUMENTS]                              = "user-documents",
+        [SD_PATH_USER_MUSIC]                                  = "user-music",
+        [SD_PATH_USER_PICTURES]                               = "user-pictures",
+        [SD_PATH_USER_VIDEOS]                                 = "user-videos",
+        [SD_PATH_USER_DOWNLOAD]                               = "user-download",
+        [SD_PATH_USER_PUBLIC]                                 = "user-public",
+        [SD_PATH_USER_TEMPLATES]                              = "user-templates",
+        [SD_PATH_USER_DESKTOP]                                = "user-desktop",
+        [SD_PATH_SEARCH_BINARIES]                             = "search-binaries",
+        [SD_PATH_SEARCH_BINARIES_DEFAULT]                     = "search-binaries-default",
+        [SD_PATH_SEARCH_LIBRARY_PRIVATE]                      = "search-library-private",
+        [SD_PATH_SEARCH_LIBRARY_ARCH]                         = "search-library-arch",
+        [SD_PATH_SEARCH_SHARED]                               = "search-shared",
+        [SD_PATH_SEARCH_CONFIGURATION_FACTORY]                = "search-configuration-factory",
+        [SD_PATH_SEARCH_STATE_FACTORY]                        = "search-state-factory",
+        [SD_PATH_SEARCH_CONFIGURATION]                        = "search-configuration",
+
+        [SD_PATH_SYSTEMD_UTIL]                                = "systemd-util",
+        [SD_PATH_SYSTEMD_SYSTEM_UNIT]                         = "systemd-system-unit",
+        [SD_PATH_SYSTEMD_SYSTEM_PRESET]                       = "systemd-system-preset",
+        [SD_PATH_SYSTEMD_SYSTEM_CONF]                         = "systemd-system-conf",
+        [SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT]                  = "systemd-search-system-unit",
+        [SD_PATH_SYSTEMD_SYSTEM_GENERATOR]                    = "systemd-system-generator",
+        [SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR]             = "systemd-search-system-generator",
+        [SD_PATH_SYSTEMD_USER_UNIT]                           = "systemd-user-unit",
+        [SD_PATH_SYSTEMD_USER_PRESET]                         = "systemd-user-preset",
+        [SD_PATH_SYSTEMD_USER_CONF]                           = "systemd-user-conf",
+        [SD_PATH_SYSTEMD_SEARCH_USER_UNIT]                    = "systemd-search-user-unit",
+        [SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR]               = "systemd-search-user-generator",
+        [SD_PATH_SYSTEMD_USER_GENERATOR]                      = "systemd-user-generator",
+        [SD_PATH_SYSTEMD_SLEEP]                               = "systemd-sleep",
+        [SD_PATH_SYSTEMD_SHUTDOWN]                            = "systemd-shutdown",
+
+        [SD_PATH_TMPFILES]                                    = "tmpfiles",
+        [SD_PATH_SYSUSERS]                                    = "sysusers",
+        [SD_PATH_SYSCTL]                                      = "sysctl",
+        [SD_PATH_BINFMT]                                      = "binfmt",
+        [SD_PATH_MODULES_LOAD]                                = "modules-load",
+        [SD_PATH_CATALOG]                                     = "catalog",
+
+        [SD_PATH_SYSTEMD_SEARCH_NETWORK]                      = "systemd-search-network",
+
+        [SD_PATH_SYSTEMD_SYSTEM_ENVIRONMENT_GENERATOR]        = "systemd-system-environment-generator",
+        [SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR]          = "systemd-user-environment-generator",
+        [SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR] = "systemd-search-system-environment-generator",
+        [SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR]   = "systemd-search-user-environment-generator",
 };
 
 static int list_homes(void) {
index 7be1afbcc4bd8d4969bfc6265019945b1c44c62e..7811833fac0a49fb18142418db1c7e1e32571faf 100644 (file)
@@ -335,7 +335,7 @@ static int portable_extract_by_path(
 
         assert(path);
 
-        r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
+        r = loop_device_make_by_path(path, O_RDONLY, /* sector_size= */ UINT32_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
         if (r == -EISDIR) {
                 _cleanup_free_ char *image_name = NULL;
 
index f90e5974c4dc32031854a5d0269ce4c2ba082897..574a1a4be92bb62144bdc380d1b5e46ff83cf5b6 100644 (file)
@@ -1371,14 +1371,14 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
 }
 
 int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
-        assert(p);
-
         _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
+        _cleanup_free_ char *t = NULL;
         const void *d;
-        char *t;
         uint8_t c;
         int r;
 
+        assert(p);
+
         r = dns_packet_read_uint8(p, &c, NULL);
         if (r < 0)
                 return r;
@@ -1387,19 +1387,14 @@ int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
         if (r < 0)
                 return r;
 
-        if (memchr(d, 0, c))
-                return -EBADMSG;
-
-        t = memdup_suffix0(d, c);
-        if (!t)
-                return -ENOMEM;
+        r = make_cstring(d, c, MAKE_CSTRING_REFUSE_TRAILING_NUL, &t);
+        if (r < 0)
+                return r;
 
-        if (!utf8_is_valid(t)) {
-                free(t);
+        if (!utf8_is_valid(t))
                 return -EBADMSG;
-        }
 
-        *ret = t;
+        *ret = TAKE_PTR(t);
 
         if (start)
                 *start = rewinder.saved_rindex;
index 647c0bd1f96c69169b424b05e4e265ee81dfc74e..a11b21350a57ab43286df6ad8246a25c7f087b57 100644 (file)
@@ -123,13 +123,13 @@ void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) {
 
         case DNS_SEARCH_DOMAIN_LINK:
                 assert(d->link);
-                LIST_FIND_TAIL(domains, d, tail);
+                tail = LIST_FIND_TAIL(domains, d);
                 LIST_REMOVE(domains, d->link->search_domains, d);
                 LIST_INSERT_AFTER(domains, d->link->search_domains, tail, d);
                 break;
 
         case DNS_SEARCH_DOMAIN_SYSTEM:
-                LIST_FIND_TAIL(domains, d, tail);
+                tail = LIST_FIND_TAIL(domains, d);
                 LIST_REMOVE(domains, d->manager->search_domains, d);
                 LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d);
                 break;
index 8ff513fa33f08c62d9790138961d2d3a9d976185..342a90abcb697b7ecdaeb7bf1d785c5b12b8182b 100644 (file)
@@ -195,19 +195,19 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
 
         case DNS_SERVER_LINK:
                 assert(s->link);
-                LIST_FIND_TAIL(servers, s, tail);
+                tail = LIST_FIND_TAIL(servers, s);
                 LIST_REMOVE(servers, s->link->dns_servers, s);
                 LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s);
                 break;
 
         case DNS_SERVER_SYSTEM:
-                LIST_FIND_TAIL(servers, s, tail);
+                tail = LIST_FIND_TAIL(servers, s);
                 LIST_REMOVE(servers, s->manager->dns_servers, s);
                 LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s);
                 break;
 
         case DNS_SERVER_FALLBACK:
-                LIST_FIND_TAIL(servers, s, tail);
+                tail = LIST_FIND_TAIL(servers, s);
                 LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
                 LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
                 break;
index dc3d70bf1f37362faf3203a5e2ef96656f053fc7..fe06f418144d4c067af5e2d7e88cf752f741c3a5 100644 (file)
@@ -258,6 +258,8 @@ int ask_password_plymouth(
         if (r < 0)
                 return r;
 
+        CLEANUP_ERASE(buffer);
+
         pollfd[POLL_SOCKET].fd = fd;
         pollfd[POLL_SOCKET].events = POLLIN;
         pollfd[POLL_INOTIFY].fd = notify;
@@ -271,20 +273,16 @@ int ask_password_plymouth(
                 else
                         timeout = USEC_INFINITY;
 
-                if (flag_file && access(flag_file, F_OK) < 0) {
-                        r = -errno;
-                        goto finish;
-                }
+                if (flag_file && access(flag_file, F_OK) < 0)
+                        return -errno;
 
                 r = ppoll_usec(pollfd, notify >= 0 ? 2 : 1, timeout);
                 if (r == -EINTR)
                         continue;
                 if (r < 0)
-                        goto finish;
-                if (r == 0) {
-                        r = -ETIME;
-                        goto finish;
-                }
+                        return r;
+                if (r == 0)
+                        return -ETIME;
 
                 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
                         (void) flush_fd(notify);
@@ -297,13 +295,10 @@ int ask_password_plymouth(
                         if (ERRNO_IS_TRANSIENT(errno))
                                 continue;
 
-                        r = -errno;
-                        goto finish;
-                }
-                if (k == 0) {
-                        r = -EIO;
-                        goto finish;
+                        return -errno;
                 }
+                if (k == 0)
+                        return -EIO;
 
                 p += k;
 
@@ -315,14 +310,12 @@ int ask_password_plymouth(
                                  * with a normal password request */
                                 packet = mfree(packet);
 
-                                if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
-                                        r = -ENOMEM;
-                                        goto finish;
-                                }
+                                if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
+                                        return -ENOMEM;
 
                                 r = loop_write(fd, packet, n+1, true);
                                 if (r < 0)
-                                        goto finish;
+                                        return r;
 
                                 flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
                                 p = 0;
@@ -330,8 +323,7 @@ int ask_password_plymouth(
                         }
 
                         /* No password, because UI not shown */
-                        r = -ENOENT;
-                        goto finish;
+                        return -ENOENT;
 
                 } else if (IN_SET(buffer[0], 2, 9)) {
                         uint32_t size;
@@ -343,35 +335,25 @@ int ask_password_plymouth(
 
                         memcpy(&size, buffer+1, sizeof(size));
                         size = le32toh(size);
-                        if (size + 5 > sizeof(buffer)) {
-                                r = -EIO;
-                                goto finish;
-                        }
+                        if (size + 5 > sizeof(buffer))
+                                return -EIO;
 
                         if (p-5 < size)
                                 continue;
 
                         l = strv_parse_nulstr(buffer + 5, size);
-                        if (!l) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
+                        if (!l)
+                                return -ENOMEM;
 
                         *ret = l;
                         break;
 
-                } else {
+                } else
                         /* Unknown packet */
-                        r = -EIO;
-                        goto finish;
-                }
+                        return -EIO;
         }
 
-        r = 0;
-
-finish:
-        explicit_bzero_safe(buffer, sizeof(buffer));
-        return r;
+        return 0;
 }
 
 #define NO_ECHO "(no echo) "
@@ -433,6 +415,8 @@ int ask_password_tty(
                         return -errno;
         }
 
+        CLEANUP_ERASE(passphrase);
+
         /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
         if (ttyfd < 0)
                 ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
@@ -636,7 +620,6 @@ int ask_password_tty(
         }
 
         x = strndup(passphrase, p);
-        explicit_bzero_safe(passphrase, sizeof(passphrase));
         if (!x) {
                 r = -ENOMEM;
                 goto finish;
@@ -896,6 +879,8 @@ int ask_password_agent(
                         goto finish;
                 }
 
+                CLEANUP_ERASE(passphrase);
+
                 cmsg_close_all(&msghdr);
 
                 if (n == 0) {
@@ -920,7 +905,6 @@ int ask_password_agent(
                                 l = strv_new("");
                         else
                                 l = strv_parse_nulstr(passphrase+1, n-1);
-                        explicit_bzero_safe(passphrase, n);
                         if (!l) {
                                 r = -ENOMEM;
                                 goto finish;
index 85ae8ff8078bc424e041ed3cbf0318dbf45d998d..ee134146a152695e21407b0556dbac09c71d61d7 100644 (file)
@@ -707,6 +707,10 @@ int block_device_remove_all_partitions(sd_device *dev, int fd) {
                 if (r < 0)
                         return r;
 
+                r = btrfs_forget_device(devname);
+                if (r < 0 && r != -ENOENT)
+                        log_debug_errno(r, "Failed to forget btrfs device %s, ignoring: %m", devname);
+
                 r = block_device_remove_partition(fd, devname, nr);
                 if (r == -ENODEV) {
                         log_debug("Kernel removed partition %s before us, ignoring", devname);
@@ -758,3 +762,18 @@ int blockdev_reread_partition_table(sd_device *dev) {
 
         return 0;
 }
+
+int blockdev_get_sector_size(int fd, uint32_t *ret) {
+        int ssz = 0;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        if (ioctl(fd, BLKSSZGET, &ssz) < 0)
+                return -errno;
+        if (ssz <= 0) /* make sure the field is initialized */
+                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Block device reported invalid sector size %i.", ssz);
+
+        *ret = ssz;
+        return 0;
+}
index b2c14102ae18f352aa3a2314422b20c2a7e07c0f..5b27d23e8ae77230c2ac7cb8ca5153fa1e8c8c78 100644 (file)
@@ -54,3 +54,5 @@ int partition_enumerator_new(sd_device *dev, sd_device_enumerator **ret);
 int block_device_remove_all_partitions(sd_device *dev, int fd);
 int block_device_has_partitions(sd_device *dev);
 int blockdev_reread_partition_table(sd_device *dev);
+
+int blockdev_get_sector_size(int fd, uint32_t *ret);
index 06f42ab61d70bb0d8cd51759134356456bb61200..49fb34b4d644bfd6b4ebadd3e6de5997a889b41e 100644 (file)
@@ -1362,6 +1362,8 @@ int show_boot_entry(
                 printf(" architecture: %s\n", e->architecture);
         if (e->kernel)
                 boot_entry_file_list("linux", e->root, e->kernel, &status);
+        if (e->efi)
+                boot_entry_file_list("efi", e->root, e->efi, &status);
 
         STRV_FOREACH(s, e->initrd)
                 boot_entry_file_list(s == e->initrd ? "initrd" : NULL,
index 0ef3c608bbeebbac107d521b24a59e23840e3f1b..c68405754152805e825b2b40d64434beb60d6bf0 100644 (file)
@@ -1986,3 +1986,21 @@ int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
 
         return -ENXIO;
 }
+
+int btrfs_forget_device(const char *path) {
+        _cleanup_close_ int control_fd = -EBADF;
+        struct btrfs_ioctl_vol_args args = {};
+
+        assert(path);
+
+        if (strlen(path) > BTRFS_PATH_NAME_MAX)
+                return -E2BIG;
+
+        strcpy(args.name, path);
+
+        control_fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC);
+        if (control_fd < 0)
+                return -errno;
+
+        return RET_NERRNO(ioctl(control_fd, BTRFS_IOC_FORGET_DEV, &args));
+}
index 5f568f23528d3f84957b205ef867eceaa8017cc9..080d5a607d8d5c5e64a8deaa2f9aee85f4aee65a 100644 (file)
@@ -136,3 +136,5 @@ static inline bool btrfs_might_be_subvol(const struct stat *st) {
 
         return S_ISDIR(st->st_mode) && st->st_ino == 256;
 }
+
+int btrfs_forget_device(const char *path);
index a68837b70bdd1f9007cf822f066a2275f7ae0d69..074440e99b91b710b90bf405bc5cde88b2eb8eae 100644 (file)
@@ -9,6 +9,7 @@
 #include "sd-id128.h"
 
 #include "blockdev-util.h"
+#include "capability-util.h"
 #include "chattr-util.h"
 #include "constants.h"
 #include "creds-util.h"
@@ -215,7 +216,6 @@ static int make_credential_host_secret(
                 void **ret_data,
                 size_t *ret_size) {
 
-        struct credential_host_secret_format buf;
         _cleanup_free_ char *t = NULL;
         _cleanup_close_ int fd = -EBADF;
         int r;
@@ -223,10 +223,15 @@ static int make_credential_host_secret(
         assert(dfd >= 0);
         assert(fn);
 
-        fd = openat(dfd, ".", O_CLOEXEC|O_WRONLY|O_TMPFILE, 0400);
+        /* For non-root users creating a temporary file using the openat(2) over "." will fail later, in the
+         * linkat(2) step at the end.  The reason is that linkat(2) requires the CAP_DAC_READ_SEARCH
+         * capability when it uses the AT_EMPTY_PATH flag. */
+        if (have_effective_cap(CAP_DAC_READ_SEARCH) > 0) {
+                fd = openat(dfd, ".", O_CLOEXEC|O_WRONLY|O_TMPFILE, 0400);
+                if (fd < 0)
+                        log_debug_errno(errno, "Failed to create temporary credential file with O_TMPFILE, proceeding without: %m");
+        }
         if (fd < 0) {
-                log_debug_errno(errno, "Failed to create temporary credential file with O_TMPFILE, proceeding without: %m");
-
                 if (asprintf(&t, "credential.secret.%016" PRIx64, random_u64()) < 0)
                         return -ENOMEM;
 
@@ -239,21 +244,23 @@ static int make_credential_host_secret(
         if (r < 0)
                 log_debug_errno(r, "Failed to set file attributes for secrets file, ignoring: %m");
 
-        buf = (struct credential_host_secret_format) {
+        struct credential_host_secret_format buf = {
                 .machine_id = machine_id,
         };
 
+        CLEANUP_ERASE(buf);
+
         r = crypto_random_bytes(buf.data, sizeof(buf.data));
         if (r < 0)
-                goto finish;
+                goto fail;
 
         r = loop_write(fd, &buf, sizeof(buf), false);
         if (r < 0)
-                goto finish;
+                goto fail;
 
         if (fsync(fd) < 0) {
                 r = -errno;
-                goto finish;
+                goto fail;
         }
 
         warn_not_encrypted(fd, flags, dirname, fn);
@@ -261,17 +268,17 @@ static int make_credential_host_secret(
         if (t) {
                 r = rename_noreplace(dfd, t, dfd, fn);
                 if (r < 0)
-                        goto finish;
+                        goto fail;
 
                 t = mfree(t);
         } else if (linkat(fd, "", dfd, fn, AT_EMPTY_PATH) < 0) {
                 r = -errno;
-                goto finish;
+                goto fail;
         }
 
         if (fsync(dfd) < 0) {
                 r = -errno;
-                goto finish;
+                goto fail;
         }
 
         if (ret_data) {
@@ -280,7 +287,7 @@ static int make_credential_host_secret(
                 copy = memdup(buf.data, sizeof(buf.data));
                 if (!copy) {
                         r = -ENOMEM;
-                        goto finish;
+                        goto fail;
                 }
 
                 *ret_data = copy;
@@ -289,13 +296,12 @@ static int make_credential_host_secret(
         if (ret_size)
                 *ret_size = sizeof(buf.data);
 
-        r = 0;
+        return 0;
 
-finish:
+fail:
         if (t && unlinkat(dfd, t, 0) < 0)
                 log_debug_errno(errno, "Failed to remove temporary credential key: %m");
 
-        explicit_bzero_safe(&buf, sizeof(buf));
         return r;
 }
 
@@ -652,24 +658,14 @@ int encrypt_credential_and_warn(
 
 #if HAVE_TPM2
         bool try_tpm2;
-        if (sd_id128_equal(with_key, _CRED_AUTO)) {
-                /* If automatic mode is selected and we are running in a container, let's not try TPM2. OTOH
-                 * if user picks TPM2 explicitly, let's always honour the request and try. */
-
-                r = detect_container();
-                if (r < 0)
-                        log_debug_errno(r, "Failed to determine whether we are running in a container, ignoring: %m");
-                else if (r > 0)
-                        log_debug("Running in container, not attempting to use TPM2.");
-
-                try_tpm2 = r <= 0;
-        } else if (sd_id128_equal(with_key, _CRED_AUTO_INITRD)) {
-                /* If automatic mode for initrds is selected, we'll use the TPM2 key if the firmware does it,
-                 * otherwise we'll use a fixed key */
+        if (sd_id128_in_set(with_key, _CRED_AUTO, _CRED_AUTO_INITRD)) {
+                /* If automatic mode is selected lets see if a TPM2 it is present. If we are running in a
+                 * container tpm2_support will detect this, and will return a different flag combination of
+                 * TPM2_SUPPORT_FULL, effectively skipping the use of TPM2 when inside one. */
 
-                try_tpm2 = efi_has_tpm2();
+                try_tpm2 = tpm2_support() == TPM2_SUPPORT_FULL;
                 if (!try_tpm2)
-                        log_debug("Firmware lacks TPM2 support, not attempting to use TPM2.");
+                        log_debug("System lacks TPM2 support or running in a container, not attempting to use TPM2.");
         } else
                 try_tpm2 = sd_id128_in_set(with_key,
                                            CRED_AES256_GCM_BY_TPM2_HMAC,
@@ -710,7 +706,7 @@ int encrypt_credential_and_warn(
                               &tpm2_primary_alg);
                 if (r < 0) {
                         if (sd_id128_equal(with_key, _CRED_AUTO_INITRD))
-                                log_warning("Firmware reported a TPM2 being present and used, but we didn't manage to talk to it. Credential will be refused if SecureBoot is enabled.");
+                                log_warning("TPM2 present and used, but we didn't manage to talk to it. Credential will be refused if SecureBoot is enabled.");
                         else if (!sd_id128_equal(with_key, _CRED_AUTO))
                                 return r;
 
@@ -1150,12 +1146,9 @@ int decrypt_credential_and_warn(
         if (le32toh(m->name_size) > 0) {
                 _cleanup_free_ char *embedded_name = NULL;
 
-                if (memchr(m->name, 0, le32toh(m->name_size)))
-                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Embedded credential name contains NUL byte, refusing.");
-
-                embedded_name = memdup_suffix0(m->name, le32toh(m->name_size));
-                if (!embedded_name)
-                        return log_oom();
+                r = make_cstring(m->name, le32toh(m->name_size), MAKE_CSTRING_REFUSE_TRAILING_NUL, &embedded_name);
+                if (r < 0)
+                        return log_error_errno(r, "Unable to convert embedded credential name to C string: %m");
 
                 if (!credential_name_valid(embedded_name))
                         return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Embedded credential name is not valid, refusing.");
index 1f0f7ca7da0d02235d6cabab78533dff9fed925c..a7d5267696f9ef0d19872a2d1cb8ed46dd131176 100644 (file)
@@ -175,15 +175,13 @@ static int image_new(
 static int extract_pretty(const char *path, const char *suffix, char **ret) {
         _cleanup_free_ char *name = NULL;
         const char *p;
-        size_t n;
 
         assert(path);
         assert(ret);
 
         p = last_path_component(path);
-        n = strcspn(p, "/");
 
-        name = strndup(p, n);
+        name = strdupcspn(p, "/");
         if (!name)
                 return -ENOMEM;
 
@@ -1197,7 +1195,7 @@ int image_read_metadata(Image *i) {
                 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
                 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
 
-                r = loop_device_make_by_path(i->path, O_RDONLY, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
+                r = loop_device_make_by_path(i->path, O_RDONLY, /* sector_size= */ UINT32_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
                 if (r < 0)
                         return r;
 
index 3eefe3efa4e39b2eefefa0422538ca3898220cf0..c0d052e0ea36dd9da9363593491f0a6a05e9c659 100644 (file)
@@ -25,6 +25,7 @@
 #include "ask-password-api.h"
 #include "blkid-util.h"
 #include "blockdev-util.h"
+#include "btrfs-util.h"
 #include "chase-symlinks.h"
 #include "conf-files.h"
 #include "constants.h"
@@ -61,6 +62,7 @@
 #include "raw-clone.h"
 #include "resize-fs.h"
 #include "signal-util.h"
+#include "sparse-endian.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-table.h"
@@ -106,6 +108,105 @@ int dissect_fstype_ok(const char *fstype) {
         return false;
 }
 
+int probe_sector_size(int fd, uint32_t *ret) {
+
+        struct gpt_header {
+                char signature[8];
+                le32_t revision;
+                le32_t header_size;
+                le32_t crc32;
+                le32_t reserved;
+                le64_t my_lba;
+                le64_t alternate_lba;
+                le64_t first_usable_lba;
+                le64_t last_usable_lba;
+                sd_id128_t disk_guid;
+                le64_t partition_entry_lba;
+                le32_t number_of_partition_entries;
+                le32_t size_of_partition_entry;
+                le32_t partition_entry_array_crc32;
+        } _packed_;
+
+        /* Disk images might be for 512B or for 4096 sector sizes, let's try to auto-detect that by searching
+         * for the GPT headers at the relevant byte offsets */
+
+        assert_cc(sizeof(struct gpt_header) == 92);
+
+        /* We expect a sector size in the range 512…4096. The GPT header is located in the second
+         * sector. Hence it could be at byte 512 at the earliest, and at byte 4096 at the latest. And we must
+         * read with granularity of the largest sector size we care about. Which means 8K. */
+        uint8_t sectors[2 * 4096];
+        uint32_t found = 0;
+        ssize_t n;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        n = pread(fd, sectors, sizeof(sectors), 0);
+        if (n < 0)
+                return -errno;
+        if (n != sizeof(sectors)) /* too short? */
+                goto not_found;
+
+        /* Let's see if we find the GPT partition header with various expected sector sizes */
+        for (uint32_t sz = 512; sz <= 4096; sz <<= 1) {
+                struct gpt_header *p;
+
+                assert(sizeof(sectors) >= sz * 2);
+                p = (struct gpt_header*) (sectors + sz);
+
+                if (memcmp(p->signature, (const char[8]) { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }, 8) != 0)
+                        continue;
+
+                if (le32toh(p->revision) != UINT32_C(0x00010000)) /* the only known revision of the spec: 1.0 */
+                        continue;
+
+                if (le32toh(p->header_size) < sizeof(struct gpt_header))
+                        continue;
+
+                if (le32toh(p->header_size) > 4096) /* larger than a sector? something is off… */
+                        continue;
+
+                if (le64toh(p->my_lba) != 1) /* this sector must claim to be at sector offset 1 */
+                        continue;
+
+                if (found != 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
+                                               "Detected valid partition table at offsets matching multiple sector sizes, refusing.");
+
+                found = sz;
+        }
+
+        if (found != 0) {
+                log_debug("Determined sector size %" PRIu32 " based on discovered partition table.", found);
+                *ret = found;
+                return 1; /* indicate we *did* find it */
+        }
+
+not_found:
+        log_debug("Couldn't find any partition table to derive sector size of.");
+        *ret = 512; /* pick the traditional default */
+        return 0;   /* indicate we didn't find it */
+}
+
+int probe_sector_size_prefer_ioctl(int fd, uint32_t *ret) {
+        struct stat st;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        /* Just like probe_sector_size(), but if we are looking at a block device, will use the already
+         * configured sector size rather than probing by contents */
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (S_ISBLK(st.st_mode))
+                return blockdev_get_sector_size(fd, ret);
+
+        return probe_sector_size(fd, ret);
+}
+
 int probe_filesystem_full(
                 int fd,
                 const char *path,
@@ -463,6 +564,7 @@ static int dissect_image(
         assert(!verity || verity->root_hash_sig || verity->root_hash_sig_size == 0);
         assert(!verity || (verity->root_hash || !verity->root_hash_sig));
         assert(!((flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)));
+        assert(m->sector_size > 0);
 
         /* Probes a disk image, and returns information about what it found in *ret.
          *
@@ -511,6 +613,11 @@ static int dissect_image(
         if (r != 0)
                 return errno_or_else(ENOMEM);
 
+        errno = 0;
+        r = blkid_probe_set_sectorsize(b, m->sector_size);
+        if (r != 0)
+                return errno_or_else(EIO);
+
         if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
                 /* Look for file system superblocks, unless we only shall look for GPT partition tables */
                 blkid_probe_enable_superblocks(b, 1);
@@ -1243,6 +1350,10 @@ int dissect_image_file(
         if (r < 0)
                 return r;
 
+        r = probe_sector_size(fd, &m->sector_size);
+        if (r < 0)
+                return r;
+
         r = dissect_image(m, fd, path, verity, mount_options, flags);
         if (r < 0)
                 return r;
@@ -1739,6 +1850,16 @@ static DecryptedImage* decrypted_image_free(DecryptedImage *d) {
                 DecryptedPartition *p = d->decrypted + i;
 
                 if (p->device && p->name && !p->relinquished) {
+                        _cleanup_free_ char *node = NULL;
+
+                        node = path_join("/dev/mapper", p->name);
+                        if (node) {
+                                r = btrfs_forget_device(node);
+                                if (r < 0 && r != -ENOENT)
+                                        log_debug_errno(r, "Failed to forget btrfs device %s, ignoring: %m", node);
+                        } else
+                                log_oom_debug();
+
                         /* Let's deactivate lazily, as the dm volume may be already/still used by other processes. */
                         r = sym_crypt_deactivate_by_name(p->device, p->name, CRYPT_DEACTIVATE_DEFERRED);
                         if (r < 0)
@@ -3050,6 +3171,7 @@ int dissect_loop_device(
                 return r;
 
         m->loop = loop_device_ref(loop);
+        m->sector_size = m->loop->sector_size;
 
         r = dissect_image(m, loop->fd, loop->node, verity, mount_options, flags);
         if (r < 0)
@@ -3225,6 +3347,7 @@ int mount_image_privately_interactively(
         r = loop_device_make_by_path(
                         image,
                         FLAGS_SET(flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
+                        /* sector_size= */ UINT32_MAX,
                         FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
                         LOCK_SH,
                         &d);
@@ -3316,7 +3439,8 @@ int verity_dissect_and_mount(
          * accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */
         r = loop_device_make_by_path(
                         src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
-                        -1,
+                        /* open_flags= */ -1,
+                        /* sector_size= */ UINT32_MAX,
                         verity.data_path ? 0 : LO_FLAGS_PARTSCAN,
                         LOCK_SH,
                         &loop_device);
index e9ab1f89a364051f4be8be25670ec97de19553e5..143d6ff501e386ed3ccf3303ba0ad662d55569d7 100644 (file)
@@ -93,6 +93,8 @@ struct DissectedImage {
         DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
         DecryptedImage *decrypted_image;
 
+        uint32_t sector_size;
+
         /* Meta information extracted from /etc/os-release and similar */
         char *image_name;
         sd_id128_t image_uuid;
@@ -186,3 +188,6 @@ int mount_image_privately_interactively(const char *path, DissectImageFlags flag
 int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_sysext_scope);
 
 int dissect_fstype_ok(const char *fstype);
+
+int probe_sector_size(int fd, uint32_t *ret);
+int probe_sector_size_prefer_ioctl(int fd, uint32_t *ret);
index 1340412cdae10c03d0ccb7a2dac2b93d59805aa0..621fa082baae29defe8dfd5b16d406f558fb6492 100644 (file)
@@ -2,10 +2,12 @@
 
 #include "alloc-util.h"
 #include "efi-loader.h"
+#include "env-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "stat-util.h"
 #include "strv.h"
+#include "tpm-pcr.h"
 #include "utf8.h"
 
 #if ENABLE_EFI
@@ -236,6 +238,43 @@ int efi_stub_get_features(uint64_t *ret) {
         return 0;
 }
 
+int efi_stub_measured(void) {
+        _cleanup_free_ char *pcr_string = NULL;
+        unsigned pcr_nr;
+        int r;
+
+        /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11. Or in
+         * other words, if we are running on a TPM enabled UKI.
+         *
+         * Returns == 0 and > 0 depending on the result of the test. Returns -EREMOTE if we detected a stub
+         * being used, but it measured things into a different PCR than we are configured for in
+         * userspace. (i.e. we expect PCR 11 being used for this by both sd-stub and us) */
+
+        r = getenv_bool_secure("SYSTEMD_FORCE_MEASURE"); /* Give user a chance to override the variable test,
+                                                          * for debugging purposes */
+        if (r >= 0)
+                return r;
+        if (r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $SYSTEMD_FORCE_MEASURE, ignoring: %m");
+
+        if (!is_efi_boot())
+                return 0;
+
+        r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return r;
+
+        r = safe_atou(pcr_string, &pcr_nr);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
+        if (pcr_nr != TPM_PCR_INDEX_KERNEL_IMAGE)
+                return log_debug_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
+
+        return 1;
+}
+
 int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
         _cleanup_free_ char *v = NULL;
         static struct stat cache_stat = {};
index 84968869abe2732a8cd64f5b2a4ed89774ea7542..56ccdee9c1e14c500552c70ed4e3a11d649a4c97 100644 (file)
@@ -18,6 +18,8 @@ int efi_loader_get_entries(char ***ret);
 int efi_loader_get_features(uint64_t *ret);
 int efi_stub_get_features(uint64_t *ret);
 
+int efi_stub_measured(void);
+
 int efi_loader_get_config_timeout_one_shot(usec_t *ret);
 int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat);
 
index e2b2ca235298b23af3b47f77bb60b5d21bd88c81..e978b8bf7ea34871e04c083ef2f833047bcfc9d7 100644 (file)
@@ -434,6 +434,8 @@ int ethtool_set_wol(
 
         strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
 
+        CLEANUP_ERASE(ecmd);
+
         if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
                 return -errno;
 
@@ -466,16 +468,11 @@ int ethtool_set_wol(
                 need_update = true;
         }
 
-        if (!need_update) {
-                explicit_bzero_safe(&ecmd, sizeof(ecmd));
+        if (!need_update)
                 return 0;
-        }
 
         ecmd.cmd = ETHTOOL_SWOL;
-        r = RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
-
-        explicit_bzero_safe(&ecmd, sizeof(ecmd));
-        return r;
+        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
 }
 
 int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
index eeed1840aab93dd09edcb1a0522a0725f819460e..e88adb2d4338e07f239fb277e16f5e924b6d3a3b 100644 (file)
@@ -1,11 +1,17 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "dissect-image.h"
 #include "fd-util.h"
 #include "fdisk-util.h"
 
 #if HAVE_LIBFDISK
 
-int fdisk_new_context_fd(int fd, bool read_only, struct fdisk_context **ret) {
+int fdisk_new_context_fd(
+                int fd,
+                bool read_only,
+                uint32_t sector_size,
+                struct fdisk_context **ret) {
+
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         int r;
 
@@ -18,6 +24,18 @@ int fdisk_new_context_fd(int fd, bool read_only, struct fdisk_context **ret) {
         if (!c)
                 return -ENOMEM;
 
+        if (sector_size == UINT32_MAX) {
+                r = probe_sector_size_prefer_ioctl(fd, &sector_size);
+                if (r < 0)
+                        return r;
+        }
+
+        if (sector_size != 0) {
+                r = fdisk_save_user_sector_size(c, /* phy= */ 0, sector_size);
+                if (r < 0)
+                        return r;
+        }
+
         r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(fd), read_only);
         if (r < 0)
                 return r;
index 7f34a042ec52fe12062569e3367e5a1a44ea6ff4..48451329275bdf39e5da42d158aba09a15c13f6e 100644 (file)
@@ -14,7 +14,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_partition*, fdisk_unref_partition,
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_parttype*, fdisk_unref_parttype, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_table*, fdisk_unref_table, NULL);
 
-int fdisk_new_context_fd(int fd, bool read_only, struct fdisk_context **ret);
+int fdisk_new_context_fd(int fd, bool read_only, uint32_t sector_size, struct fdisk_context **ret);
 
 int fdisk_partition_get_uuid_as_id128(struct fdisk_partition *p, sd_id128_t *ret);
 int fdisk_partition_get_type_as_id128(struct fdisk_partition *p, sd_id128_t *ret);
index 4795e1ad808f3a0bbe8b88f9fbb7f62ba3a73a4f..fbac0f749880502d9aec124c328e1158acb59d70 100644 (file)
@@ -297,6 +297,7 @@ static size_t table_data_size(TableDataType type, const void *data) {
         case TABLE_TIMESTAMP:
         case TABLE_TIMESTAMP_UTC:
         case TABLE_TIMESTAMP_RELATIVE:
+        case TABLE_TIMESTAMP_DATE:
         case TABLE_TIMESPAN:
         case TABLE_TIMESPAN_MSEC:
                 return sizeof(usec_t);
@@ -894,6 +895,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                 case TABLE_TIMESTAMP:
                 case TABLE_TIMESTAMP_UTC:
                 case TABLE_TIMESTAMP_RELATIVE:
+                case TABLE_TIMESTAMP_DATE:
                 case TABLE_TIMESPAN:
                 case TABLE_TIMESPAN_MSEC:
                         buffer.usec = va_arg(ap, usec_t);
@@ -1300,6 +1302,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                 case TABLE_TIMESTAMP:
                 case TABLE_TIMESTAMP_UTC:
                 case TABLE_TIMESTAMP_RELATIVE:
+                case TABLE_TIMESTAMP_DATE:
                         return CMP(a->timestamp, b->timestamp);
 
                 case TABLE_TIMESPAN:
@@ -1530,7 +1533,8 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
 
         case TABLE_TIMESTAMP:
         case TABLE_TIMESTAMP_UTC:
-        case TABLE_TIMESTAMP_RELATIVE: {
+        case TABLE_TIMESTAMP_RELATIVE:
+        case TABLE_TIMESTAMP_DATE: {
                 _cleanup_free_ char *p = NULL;
                 char *ret;
 
@@ -1542,6 +1546,8 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
                         ret = format_timestamp(p, FORMAT_TIMESTAMP_MAX, d->timestamp);
                 else if (d->type == TABLE_TIMESTAMP_UTC)
                         ret = format_timestamp_style(p, FORMAT_TIMESTAMP_MAX, d->timestamp, TIMESTAMP_UTC);
+                else if (d->type == TABLE_TIMESTAMP_DATE)
+                        ret = format_timestamp_style(p, FORMAT_TIMESTAMP_MAX, d->timestamp, TIMESTAMP_DATE);
                 else
                         ret = format_timestamp_relative(p, FORMAT_TIMESTAMP_RELATIVE_MAX, d->timestamp);
                 if (!ret)
@@ -2578,6 +2584,7 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
         case TABLE_TIMESTAMP:
         case TABLE_TIMESTAMP_UTC:
         case TABLE_TIMESTAMP_RELATIVE:
+        case TABLE_TIMESTAMP_DATE:
                 if (d->timestamp == USEC_INFINITY)
                         return json_variant_new_null(ret);
 
index 0e8aecffe6bd93b0fafcf75bae3864c293d19c89..0817846be22dffe68cf8cafb008a7b27c0554ed3 100644 (file)
@@ -23,6 +23,7 @@ typedef enum TableDataType {
         TABLE_TIMESTAMP,
         TABLE_TIMESTAMP_UTC,
         TABLE_TIMESTAMP_RELATIVE,
+        TABLE_TIMESTAMP_DATE,
         TABLE_TIMESPAN,
         TABLE_TIMESPAN_MSEC,
         TABLE_SIZE,
index 6d95aa72f8788533441e2876ae76271445455c26..64f4a2741c324ad56cd03fde89a7425a2cd9f0fd 100644 (file)
@@ -59,25 +59,111 @@ int generator_open_unit_file(
         return 0;
 }
 
-int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src) {
-        _cleanup_free_ char *bn = NULL;
-        const char *from, *to;
+
+int generator_add_symlink_full(
+                const char *dir,
+                const char *dst,
+                const char *dep_type,
+                const char *src,
+                const char *instance) {
+
+        _cleanup_free_ char *dn = NULL, *fn = NULL, *instantiated = NULL, *to = NULL, *from = NULL;
         int r;
 
-        /* Adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute)
-         * or ../<src> (otherwise). */
+        assert(dir);
+        assert(dst);
+        assert(dep_type);
+        assert(src);
 
-        r = path_extract_filename(src, &bn);
+        /* Adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise). If
+         * <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
+
+        r = path_extract_directory(src, &dn);
+        if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → just a file name was passed */
+                return log_error_errno(r, "Failed to extract directory name from '%s': %m", src);
+
+        r = path_extract_filename(src, &fn);
         if (r < 0)
-                return log_error_errno(r, "Failed to extract filename from '%s': %m", src);
+                return log_error_errno(r, "Failed to extract file name from '%s': %m", src);
+        if (r == O_DIRECTORY)
+                return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src);
 
-        from = path_is_absolute(src) ? src : strjoina("../", src);
-        to = strjoina(dir, "/", dst, ".", dep_type, "/", bn);
+        if (instance) {
+                r = unit_name_replace_instance(fn, instance, &instantiated);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance);
+        }
+
+        from = path_join(dn ?: "..", fn);
+        if (!from)
+                return log_oom();
+
+        to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn);
+        if (!to)
+                return log_oom();
 
         (void) mkdir_parents_label(to, 0755);
-        if (symlink(from, to) < 0)
-                if (errno != EEXIST)
-                        return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
+
+        if (symlink(from, to) < 0 && errno != EEXIST)
+                return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
+
+        return 0;
+}
+
+static int generator_add_ordering(
+                const char *dir,
+                const char *src,
+                const char *order,
+                const char *dst,
+                const char *instance) {
+
+        _cleanup_free_ char *instantiated = NULL, *p = NULL, *fn = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        const char *to;
+        int r;
+
+        assert(dir);
+        assert(src);
+        assert(order);
+        assert(dst);
+
+        /* Adds in an explicit ordering dependency of type <order> from <src> to <dst>. If <instance> is
+         * specified, it is inserted into <dst>. */
+
+        if (instance) {
+                r = unit_name_replace_instance(dst, instance, &instantiated);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", dst, instance);
+
+                to = instantiated;
+        } else
+                to = dst;
+
+        fn = strjoin(src, ".d/50-order-", to, ".conf");
+        if (!fn)
+                return log_oom();
+
+        p = path_join(dir, fn);
+        if (!p)
+                return log_oom();
+
+        (void) mkdir_parents_label(p, 0755);
+
+        r = fopen_unlocked(p, "wxe", &f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create '%s': %m", p);
+
+        fprintf(f,
+                "# Automatically generated by %s\n\n"
+                "[Unit]\n"
+                "%s=%s\n",
+                program_invocation_short_name,
+                order,
+                to);
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write drop-in '%s': %m", p);
 
         return 0;
 }
@@ -532,66 +618,73 @@ int generator_hook_up_growfs(
                 const char *where,
                 const char *target) {
 
-        _cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL, *unit_file = NULL;
-        _cleanup_fclose_ FILE *f = NULL;
+        const char *growfs_unit, *growfs_unit_path;
+        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
         int r;
 
         assert(dir);
         assert(where);
 
-        escaped = cescape(where);
-        if (!escaped)
-                return log_oom();
-
-        r = unit_name_from_path_instance("systemd-growfs", where, ".service", &unit);
-        if (r < 0)
-                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
-                                       where);
-
         r = unit_name_from_path(where, ".mount", &where_unit);
         if (r < 0)
-                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
-                                       where);
+                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
 
-        unit_file = path_join(dir, unit);
-        if (!unit_file)
-                return log_oom();
+        if (empty_or_root(where)) {
+                growfs_unit = SPECIAL_GROWFS_ROOT_SERVICE;
+                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_ROOT_SERVICE;
+        } else {
+                growfs_unit = SPECIAL_GROWFS_SERVICE;
+                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_SERVICE;
 
-        log_debug("Creating %s", unit_file);
+                r = unit_name_path_escape(where, &instance);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
+        }
 
-        f = fopen(unit_file, "wxe");
-        if (!f)
-                return log_error_errno(errno, "Failed to create unit file %s: %m",
-                                       unit_file);
+        if (target) {
+                r = generator_add_ordering(dir, target, "After", growfs_unit, instance);
+                if (r < 0)
+                        return r;
+        }
 
-        fprintf(f,
-                "# Automatically generated by %s\n\n"
-                "[Unit]\n"
-                "Description=Grow File System on %%f\n"
-                "Documentation=man:systemd-growfs@.service(8)\n"
-                "DefaultDependencies=no\n"
-                "BindsTo=%%i.mount\n"
-                "Conflicts=shutdown.target\n"
-                "After=systemd-repart.service %%i.mount\n"
-                "Before=shutdown.target%s%s\n",
-                program_invocation_short_name,
-                target ? " " : "",
-                strempty(target));
+        return generator_add_symlink_full(dir, where_unit, "wants", growfs_unit_path, instance);
+}
 
-        if (empty_or_root(where)) /* Make sure the root fs is actually writable before we resize it */
-                fprintf(f,
-                        "After=systemd-remount-fs.service\n");
+int generator_hook_up_pcrfs(
+                const char *dir,
+                const char *where,
+                const char *target) {
 
-        fprintf(f,
-                "\n"
-                "[Service]\n"
-                "Type=oneshot\n"
-                "RemainAfterExit=yes\n"
-                "ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
-                "TimeoutSec=0\n",
-                escaped);
+        const char *pcrfs_unit, *pcrfs_unit_path;
+        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
+        int r;
+
+        assert(dir);
+        assert(where);
+
+        r = unit_name_from_path(where, ".mount", &where_unit);
+        if (r < 0)
+                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
+
+        if (empty_or_root(where)) {
+                pcrfs_unit = SPECIAL_PCRFS_ROOT_SERVICE;
+                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_ROOT_SERVICE;
+        } else {
+                pcrfs_unit = SPECIAL_PCRFS_SERVICE;
+                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_SERVICE;
+
+                r = unit_name_path_escape(where, &instance);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
+        }
+
+        if (target) {
+                r = generator_add_ordering(dir, target, "After", pcrfs_unit, instance);
+                if (r < 0)
+                        return r;
+        }
 
-        return generator_add_symlink(dir, where_unit, "wants", unit);
+        return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
 }
 
 int generator_enable_remount_fs_service(const char *dir) {
index 1b4f36ac53c6796713690dad7e122d0c36287355..111900fd45143d6c24694fd2bccbb32633974b09 100644 (file)
@@ -12,7 +12,11 @@ int generator_open_unit_file(
         const char *name,
         FILE **file);
 
-int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src);
+int generator_add_symlink_full(const char *dir, const char *dst, const char *dep_type, const char *src, const char *instance);
+
+static inline int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src) {
+        return generator_add_symlink_full(dir, dst, dep_type, src, NULL);
+}
 
 int generator_write_fsck_deps(
         FILE *f,
@@ -77,6 +81,10 @@ int generator_hook_up_growfs(
         const char *dir,
         const char *where,
         const char *target);
+int generator_hook_up_pcrfs(
+        const char *dir,
+        const char *where,
+        const char *target);
 
 int generator_enable_remount_fs_service(const char *dir);
 
index dc6e12fd06ffacd9f6889d30a23acfc60794945c..51cae3b5a2c11cff3b6e60ed748a22caab7fc605 100644 (file)
@@ -284,6 +284,9 @@ InstallChangeType install_changes_add(
         assert(!changes == !n_changes);
         assert(INSTALL_CHANGE_TYPE_VALID(type));
 
+        /* Message formatting requires <path> to be set. */
+        assert(path);
+
         /* Register a change or error. Note that the return value may be the error
          * that was passed in, or -ENOMEM generated internally. */
 
@@ -339,7 +342,9 @@ void install_changes_dump(int r, const char *verb, const InstallChange *changes,
         assert(verb || r >= 0);
 
         for (size_t i = 0; i < n_changes; i++) {
-                assert(verb || changes[i].type >= 0);
+                if (changes[i].type < 0)
+                        assert(verb);
+                assert(changes[i].path);
 
                 /* When making changes here, make sure to also change install_error() in dbus-manager.c. */
 
@@ -376,7 +381,7 @@ void install_changes_dump(int r, const char *verb, const InstallChange *changes,
                         break;
                 case INSTALL_CHANGE_AUXILIARY_FAILED:
                         if (!quiet)
-                                log_warning("Failed to enable auxiliary unit %s, ignoring.", changes[i].source);
+                                log_warning("Failed to enable auxiliary unit %s, ignoring.", changes[i].path);
                         break;
                 case -EEXIST:
                         if (changes[i].source)
@@ -2134,7 +2139,7 @@ static int install_context_apply(
                 q = install_info_traverse(ctx, lp, i, flags, NULL);
                 if (q < 0) {
                         if (i->auxiliary) {
-                                q = install_changes_add(changes, n_changes, INSTALL_CHANGE_AUXILIARY_FAILED, NULL, i->name);
+                                q = install_changes_add(changes, n_changes, INSTALL_CHANGE_AUXILIARY_FAILED, i->name, NULL);
                                 if (q < 0)
                                         return q;
                                 continue;
index 5c9dd61898a63efb5b7c0d21ee6e1821f83982d0..8ddda6582ebea564b5a18043bf8edda031b0b9c1 100644 (file)
@@ -73,9 +73,7 @@ static int print_catalog(FILE *f, sd_journal *j) {
                 return log_oom();
 
         fprintf(f, "%s%s %s%s", ansi_grey(), prefix, ansi_normal(), ansi_green());
-
         fputs(z, f);
-
         fprintf(f, "%s\n", ansi_normal());
 
         return 1;
@@ -108,7 +106,7 @@ static int url_from_catalog(sd_journal *j, char **ret) {
         weblink += strspn(weblink, " \t");
 
         /* Cut out till next whitespace/newline */
-        url = strndup(weblink, strcspn(weblink, WHITESPACE));
+        url = strdupcspn(weblink, WHITESPACE);
         if (!url)
                 return log_oom();
 
@@ -123,7 +121,14 @@ notfound:
         return 0;
 }
 
-static int parse_field(const void *data, size_t length, const char *field, size_t field_len, char **target, size_t *target_len) {
+static int parse_field(
+                const void *data,
+                size_t length,
+                const char *field,
+                size_t field_len,
+                char **target,
+                size_t *target_len) {
+
         size_t nl;
         char *buf;
 
@@ -143,8 +148,7 @@ static int parse_field(const void *data, size_t length, const char *field, size_
         if (!buf)
                 return log_oom();
 
-        free(*target);
-        *target = buf;
+        free_and_replace(*target, buf);
 
         if (target_len)
                 *target_len = nl;
@@ -166,17 +170,21 @@ typedef struct ParseFieldVec {
                 .target_len = _target_len                               \
         }
 
-static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fields, unsigned n_fields) {
-        unsigned i;
+static int parse_fieldv(
+                const void *data,
+                size_t length,
+                const ParseFieldVec *fields,
+                size_t n_fields) {
+
+        int r;
 
-        for (i = 0; i < n_fields; i++) {
+        for (size_t i = 0; i < n_fields; i++) {
                 const ParseFieldVec *f = &fields[i];
-                int r;
 
                 r = parse_field(data, length, f->field, f->field_len, f->target, f->target_len);
                 if (r < 0)
                         return r;
-                else if (r > 0)
+                if (r > 0)
                         break;
         }
 
@@ -317,7 +325,8 @@ static bool print_multiline(
 }
 
 static int output_timestamp_monotonic(
-                FILE *f, OutputMode mode,
+                FILE *f,
+                OutputMode mode,
                 const dual_timestamp *ts,
                 const sd_id128_t *boot_id,
                 const dual_timestamp *previous_ts,
@@ -355,7 +364,6 @@ static int output_timestamp_monotonic(
 
 finish:
         written_chars += fprintf(f, "%s", "]");
-
         return written_chars;
 }
 
@@ -495,13 +503,11 @@ static int output_short(
         assert(previous_ts);
         assert(previous_boot_id);
 
-        /* Set the threshold to one bigger than the actual print
-         * threshold, so that if the line is actually longer than what
-         * we're willing to print, ellipsization will occur. This way
-         * we won't output a misleading line without any indication of
-         * truncation.
+        /* Set the threshold to one bigger than the actual print threshold, so that if the line is actually
+         * longer than what we're willing to print, ellipsization will occur. This way we won't output a
+         * misleading line without any indication of truncation.
          */
-        sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
+        (void) sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
 
         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
                 r = parse_fieldv(data, length, fields, ELEMENTSOF(fields));
@@ -682,7 +688,7 @@ static int output_verbose(
         assert(previous_ts);
         assert(previous_boot_id);
 
-        sd_journal_set_data_threshold(j, 0);
+        (void) sd_journal_set_data_threshold(j, 0);
 
         if (!VALID_REALTIME(ts->realtime))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available");
@@ -693,16 +699,19 @@ static int output_verbose(
 
         timestamp = format_timestamp_style(buf, sizeof buf, ts->realtime,
                                            flags & OUTPUT_UTC ? TIMESTAMP_US_UTC : TIMESTAMP_US);
-        fprintf(f, "%s [%s]\n",
+        fprintf(f, "%s%s%s %s[%s]%s\n",
+                timestamp && (flags & OUTPUT_COLOR) ? ANSI_UNDERLINE : "",
                 timestamp ?: "(no timestamp)",
-                cursor);
+                timestamp && (flags & OUTPUT_COLOR) ? ANSI_NORMAL : "",
+                (flags & OUTPUT_COLOR) ? ANSI_GREY : "",
+                cursor,
+                (flags & OUTPUT_COLOR) ? ANSI_NORMAL : "");
 
         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
-                const char *c, *p;
-                int fieldlen;
-                const char *on = "", *off = "";
                 _cleanup_free_ char *urlified = NULL;
-                size_t valuelen;
+                const char *on = "", *off = "";
+                const char *c, *p = NULL;
+                size_t fieldlen, valuelen;
 
                 c = memchr(data, '=', length);
                 if (!c)
@@ -719,22 +728,35 @@ static int output_verbose(
                         continue;
 
                 valuelen = length - 1 - fieldlen;
+                p = c + 1;
+
+                if (flags & OUTPUT_COLOR) {
+                        if (startswith(data, "MESSAGE=")) {
+                                on = ANSI_HIGHLIGHT;
+                                off = ANSI_NORMAL;
+                        } else if (startswith(data, "CONFIG_FILE=")) {
+                                _cleanup_free_ char *u = NULL;
+
+                                u = memdup_suffix0(p, valuelen);
+                                if (!u)
+                                        return log_oom();
+
+                                if (terminal_urlify_path(u, NULL, &urlified) >= 0) {
+                                        p = urlified;
+                                        valuelen = strlen(urlified);
+                                }
 
-                if ((flags & OUTPUT_COLOR) && (p = startswith(data, "MESSAGE="))) {
-                        on = ANSI_HIGHLIGHT;
-                        off = ANSI_NORMAL;
-                } else if ((p = startswith(data, "CONFIG_FILE="))) {
-                        if (terminal_urlify_path(p, NULL, &urlified) >= 0) {
-                                p = urlified;
-                                valuelen = strlen(urlified);
+                        } else if (startswith(data, "_")) {
+                                /* Highlight trusted data as such */
+                                on = ANSI_GREEN;
+                                off = ANSI_NORMAL;
                         }
-                } else
-                        p = c + 1;
+                }
 
                 if ((flags & OUTPUT_SHOW_ALL) ||
                     (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
                      && utf8_is_printable(data, length))) {
-                        fprintf(f, "    %s%.*s=", on, fieldlen, (const char*)data);
+                        fprintf(f, "    %s%.*s=", on, (int) fieldlen, (const char*)data);
                         print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, false,
                                         p, valuelen,
                                         NULL);
@@ -747,7 +769,6 @@ static int output_verbose(
                                 FORMAT_BYTES(length - (c - (const char *) data) - 1),
                                 off);
         }
-
         if (r < 0)
                 return r;
 
@@ -783,7 +804,7 @@ static int output_export(
         assert(previous_ts);
         assert(previous_boot_id);
 
-        sd_journal_set_data_threshold(j, 0);
+        (void) sd_journal_set_data_threshold(j, 0);
 
         r = sd_journal_get_cursor(j, &cursor);
         if (r < 0)
@@ -925,6 +946,12 @@ static int update_json_data(
         struct json_data *d;
         int r;
 
+        assert(name);
+        assert(value);
+
+        if (size == SIZE_MAX)
+                size = strlen(value);
+
         if (!(flags & OUTPUT_SHOW_ALL) && strlen(name) + 1 + size >= JSON_THRESHOLD)
                 r = json_variant_new_null(&v);
         else if (utf8_is_printable(value, size))
@@ -1013,7 +1040,7 @@ static int output_json(
                 const dual_timestamp *previous_ts,
                 const sd_id128_t *previous_boot_id) {
 
-        char sid[SD_ID128_STRING_MAX], usecbuf[DECIMAL_STR_MAX(usec_t)];
+        char usecbuf[DECIMAL_STR_MAX(usec_t)];
         _cleanup_(json_variant_unrefp) JsonVariant *object = NULL;
         _cleanup_free_ char *cursor = NULL;
         JsonVariant **array = NULL;
@@ -1048,22 +1075,21 @@ static int output_json(
         if (!h)
                 return log_oom();
 
-        r = update_json_data(h, flags, "__CURSOR", cursor, strlen(cursor));
+        r = update_json_data(h, flags, "__CURSOR", cursor, SIZE_MAX);
         if (r < 0)
                 goto finish;
 
         xsprintf(usecbuf, USEC_FMT, realtime);
-        r = update_json_data(h, flags, "__REALTIME_TIMESTAMP", usecbuf, strlen(usecbuf));
+        r = update_json_data(h, flags, "__REALTIME_TIMESTAMP", usecbuf, SIZE_MAX);
         if (r < 0)
                 goto finish;
 
         xsprintf(usecbuf, USEC_FMT, monotonic);
-        r = update_json_data(h, flags, "__MONOTONIC_TIMESTAMP", usecbuf, strlen(usecbuf));
+        r = update_json_data(h, flags, "__MONOTONIC_TIMESTAMP", usecbuf, SIZE_MAX);
         if (r < 0)
                 goto finish;
 
-        sd_id128_to_string(journal_boot_id, sid);
-        r = update_json_data(h, flags, "_BOOT_ID", sid, strlen(sid));
+        r = update_json_data(h, flags, "_BOOT_ID", SD_ID128_TO_STRING(journal_boot_id), SIZE_MAX);
         if (r < 0)
                 goto finish;
 
@@ -1130,12 +1156,8 @@ static int output_json(
 
 finish:
         while ((d = hashmap_steal_first(h))) {
-                size_t k;
-
                 json_variant_unref(d->name);
-                for (k = 0; k < d->n_values; k++)
-                        json_variant_unref(d->values[k]);
-
+                json_variant_unref_many(d->values, d->n_values);
                 free(d);
         }
 
@@ -1355,13 +1377,12 @@ int show_journal_entry(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                char **output_fields,
+                Set *output_fields,
                 const size_t highlight[2],
                 bool *ellipsized,
                 dual_timestamp *previous_ts,
                 sd_id128_t *previous_boot_id) {
 
-        _cleanup_set_free_ Set *fields = NULL;
         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
         sd_id128_t boot_id = SD_ID128_NULL;
         int r;
@@ -1374,10 +1395,6 @@ int show_journal_entry(
         if (n_columns <= 0)
                 n_columns = columns();
 
-        r = set_put_strdupv(&fields, output_fields);
-        if (r < 0)
-                return r;
-
         r = get_dual_timestamp(j, &ts, &boot_id);
         if (r == -EBADMSG) {
                 log_debug_errno(r, "Skipping message we can't read: %m");
@@ -1386,7 +1403,7 @@ int show_journal_entry(
         if (r < 0)
                 return log_error_errno(r, "Failed to get journal fields: %m");
 
-        r = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight, &ts, &boot_id, previous_ts, previous_boot_id);
+        r = output_funcs[mode](f, j, mode, n_columns, flags, output_fields, highlight, &ts, &boot_id, previous_ts, previous_boot_id);
 
         /* Store timestamp and boot ID for next iteration */
         *previous_ts = ts;
@@ -1467,7 +1484,7 @@ int show_journal(
                         /* -ESTALE is returned if the timestamp is not from this boot */
                         if (r == -ESTALE)
                                 continue;
-                        else if (r < 0)
+                        if (r < 0)
                                 return log_error_errno(r, "Failed to get journal time: %m");
 
                         if (usec < not_before)
index 0800b55c03d10f0a6db9c6ef28b22ff30a06641e..a15bda688616933a37b39792164b3e8cb1d1deb2 100644 (file)
@@ -18,7 +18,7 @@ int show_journal_entry(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                char **output_fields,
+                Set *output_fields,
                 const size_t highlight[2],
                 bool *ellipsized,
                 dual_timestamp *previous_ts,
index 3224286186c8f5c69fdc1a0e165089ecc1b13c67..5945ba2ad459638748f15b5e6fa49c5bc98a6524 100644 (file)
@@ -20,6 +20,7 @@
 #include "data-fd-util.h"
 #include "device-util.h"
 #include "devnum-util.h"
+#include "dissect-image.h"
 #include "env-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
@@ -125,14 +126,16 @@ static int loop_configure_verify(int fd, const struct loop_config *c) {
         assert(c);
 
         if (c->block_size != 0) {
-                int z;
+                uint32_t ssz;
 
-                if (ioctl(fd, BLKSSZGET, &z) < 0)
-                        return -errno;
+                r = blockdev_get_sector_size(fd, &ssz);
+                if (r < 0)
+                        return r;
 
-                assert(z >= 0);
-                if ((uint32_t) z != c->block_size)
-                        log_debug("LOOP_CONFIGURE didn't honour requested block size %u, got %i instead. Ignoring.", c->block_size, z);
+                if (ssz != c->block_size) {
+                        log_debug("LOOP_CONFIGURE didn't honour requested block size %" PRIu32 ", got %" PRIu32 " instead. Ignoring.", c->block_size, ssz);
+                        broken = true;
+                }
         }
 
         if (c->info.lo_sizelimit != 0) {
@@ -173,6 +176,7 @@ static int loop_configure_verify(int fd, const struct loop_config *c) {
 
 static int loop_configure_fallback(int fd, const struct loop_config *c) {
         struct loop_info64 info_copy;
+        int r;
 
         assert(fd >= 0);
         assert(c);
@@ -220,6 +224,21 @@ static int loop_configure_fallback(int fd, const struct loop_config *c) {
                 if (ioctl(fd, BLKFLSBUF, 0) < 0)
                         log_debug_errno(errno, "Failed to issue BLKFLSBUF ioctl, ignoring: %m");
 
+        /* If a block size is requested then try to configure it. If that doesn't work, ignore errors, but
+         * afterwards, let's validate what is in effect, and if it doesn't match what we want, fail */
+        if (c->block_size != 0) {
+                uint32_t ssz;
+
+                if (ioctl(fd, LOOP_SET_BLOCK_SIZE, (unsigned long) c->block_size) < 0)
+                        log_debug_errno(errno, "Failed to set sector size, ignoring: %m");
+
+                r = blockdev_get_sector_size(fd, &ssz);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to read sector size: %m");
+                if (ssz != c->block_size)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Sector size of loopback device doesn't match what we requested, refusing.");
+        }
+
         /* LO_FLAGS_DIRECT_IO is a flags we need to configure via explicit ioctls. */
         if (FLAGS_SET(c->info.lo_flags, LO_FLAGS_DIRECT_IO))
                 if (ioctl(fd, LOOP_SET_DIRECT_IO, 1UL) < 0)
@@ -391,6 +410,7 @@ static int loop_configure(
                 .diskseq = diskseq,
                 .uevent_seqnum_not_before = seqnum,
                 .timestamp_not_before = timestamp,
+                .sector_size = c->block_size,
         };
 
         *ret = TAKE_PTR(d);
@@ -403,7 +423,7 @@ static int loop_device_make_internal(
                 int open_flags,
                 uint64_t offset,
                 uint64_t size,
-                uint32_t block_size,
+                uint32_t sector_size,
                 uint32_t loop_flags,
                 int lock_op,
                 LoopDevice **ret) {
@@ -474,9 +494,50 @@ static int loop_device_make_internal(
         if (control < 0)
                 return -errno;
 
+        if (sector_size == 0)
+                /* If no sector size is specified, default to the classic default */
+                sector_size = 512;
+        else if (sector_size == UINT32_MAX) {
+
+                if (S_ISBLK(st.st_mode))
+                        /* If the sector size is specified as UINT32_MAX we'll propagate the sector size of
+                         * the underlying block device. */
+                        r = blockdev_get_sector_size(fd, &sector_size);
+                else {
+                        _cleanup_close_ int non_direct_io_fd = -1;
+                        int probe_fd;
+
+                        assert(S_ISREG(st.st_mode));
+
+                        /* If sector size is specified as UINT32_MAX, we'll try to probe the right sector
+                         * size of the image in question by looking for the GPT partition header at various
+                         * offsets. This of course only works if the image already has a disk label.
+                         *
+                         * So here we actually want to read the file contents ourselves. This is quite likely
+                         * not going to work if we managed to enable O_DIRECT, because in such a case there
+                         * are some pretty strict alignment requirements to offset, size and target, but
+                         * there's no way to query what alignment specifically is actually required. Hence,
+                         * let's avoid the mess, and temporarily open an fd without O_DIRECT for the probing
+                         * logic. */
+
+                        if (FLAGS_SET(loop_flags, LO_FLAGS_DIRECT_IO)) {
+                                non_direct_io_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+                                if (non_direct_io_fd < 0)
+                                        return non_direct_io_fd;
+
+                                probe_fd = non_direct_io_fd;
+                        } else
+                                probe_fd = fd;
+
+                        r = probe_sector_size(probe_fd, &sector_size);
+                }
+                if (r < 0)
+                        return r;
+        }
+
         config = (struct loop_config) {
                 .fd = fd,
-                .block_size = block_size,
+                .block_size = sector_size,
                 .info = {
                         /* Use the specified flags, but configure the read-only flag from the open flags, and force autoclear */
                         .lo_flags = (loop_flags & ~LO_FLAGS_READ_ONLY) | ((open_flags & O_ACCMODE) == O_RDONLY ? LO_FLAGS_READ_ONLY : 0) | LO_FLAGS_AUTOCLEAR,
@@ -560,7 +621,7 @@ int loop_device_make(
                 int open_flags,
                 uint64_t offset,
                 uint64_t size,
-                uint32_t block_size,
+                uint32_t sector_size,
                 uint32_t loop_flags,
                 int lock_op,
                 LoopDevice **ret) {
@@ -574,7 +635,7 @@ int loop_device_make(
                         open_flags,
                         offset,
                         size,
-                        block_size,
+                        sector_size,
                         loop_flags_mangle(loop_flags),
                         lock_op,
                         ret);
@@ -583,6 +644,7 @@ int loop_device_make(
 int loop_device_make_by_path(
                 const char *path,
                 int open_flags,
+                uint32_t sector_size,
                 uint32_t loop_flags,
                 int lock_op,
                 LoopDevice **ret) {
@@ -638,12 +700,13 @@ int loop_device_make_by_path(
                   direct ? "enabled" : "disabled",
                   direct != (direct_flags != 0) ? " (O_DIRECT was requested but not supported)" : "");
 
-        return loop_device_make_internal(path, fd, open_flags, 0, 0, 0, loop_flags, lock_op, ret);
+        return loop_device_make_internal(path, fd, open_flags, 0, 0, sector_size, loop_flags, lock_op, ret);
 }
 
 int loop_device_make_by_path_memory(
                 const char *path,
                 int open_flags,
+                uint32_t sector_size,
                 uint32_t loop_flags,
                 int lock_op,
                 LoopDevice **ret) {
@@ -679,7 +742,7 @@ int loop_device_make_by_path_memory(
 
         fd = safe_close(fd); /* Let's close the original early */
 
-        return loop_device_make_internal(NULL, mfd, open_flags, 0, 0, 0, loop_flags, lock_op, ret);
+        return loop_device_make_internal(NULL, mfd, open_flags, 0, 0, sector_size, loop_flags, lock_op, ret);
 }
 
 static LoopDevice* loop_device_free(LoopDevice *d) {
@@ -821,6 +884,11 @@ int loop_device_open(
         if (r < 0 && r != -EOPNOTSUPP)
                 return r;
 
+        uint32_t sector_size;
+        r = blockdev_get_sector_size(fd, &sector_size);
+        if (r < 0)
+                return r;
+
         r = sd_device_get_devnum(dev, &devnum);
         if (r < 0)
                 return r;
@@ -850,6 +918,7 @@ int loop_device_open(
                 .diskseq = diskseq,
                 .uevent_seqnum_not_before = UINT64_MAX,
                 .timestamp_not_before = USEC_INFINITY,
+                .sector_size = sector_size,
         };
 
         *ret = d;
index 512b5ab4a22d6510b5388bf07a0fb42130d06995..5c79ed7ccee90710b8b0185d5948e948f5ce51ac 100644 (file)
@@ -23,14 +23,15 @@ struct LoopDevice {
         uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach, or 0 if we don't know */
         uint64_t uevent_seqnum_not_before; /* uevent sequm right before we attached the loopback device, or UINT64_MAX if we don't know */
         usec_t timestamp_not_before; /* CLOCK_MONOTONIC timestamp taken immediately before attaching the loopback device, or USEC_INFINITY if we don't know */
+        uint32_t sector_size;
 };
 
 /* Returns true if LoopDevice object is not actually a loopback device but some other block device we just wrap */
 #define LOOP_DEVICE_IS_FOREIGN(d) ((d)->nr < 0)
 
-int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t block_size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
-int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, int lock_op, LoopDevice **ret);
-int loop_device_make_by_path_memory(const char *path, int open_flags, uint32_t loop_flags, int lock_op, LoopDevice **ret);
+int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t sector_size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
+int loop_device_make_by_path(const char *path, int open_flags, uint32_t sector_size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
+int loop_device_make_by_path_memory(const char *path, int open_flags, uint32_t sector_size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
 int loop_device_open(sd_device *dev, int open_flags, int lock_op, LoopDevice **ret);
 int loop_device_open_from_fd(int fd, int open_flags, int lock_op, LoopDevice **ret);
 int loop_device_open_from_path(const char *path, int open_flags, int lock_op, LoopDevice **ret);
index 9e240d41f4305b51baead46048d3c45168949336..11ae92290d71eeca9a514f5712ca0205fac2a3b4 100644 (file)
@@ -175,13 +175,11 @@ static int do_mcopy(const char *node, const char *root) {
                         continue;
                 }
 
-                r = strv_consume(&argv, TAKE_PTR(p));
-                if (r < 0)
+                if (strv_consume(&argv, TAKE_PTR(p)) < 0)
                         return log_oom();
         }
 
-        r = strv_extend(&argv, "::");
-        if (r < 0)
+        if (strv_extend(&argv, "::") < 0)
                 return log_oom();
 
         if (fstat(rfd, &st) < 0)
@@ -303,6 +301,7 @@ int make_filesystem(
                 const char *root,
                 sd_id128_t uuid,
                 bool discard,
+                uint64_t sector_size,
                 char * const *extra_mkfs_args) {
 
         _cleanup_free_ char *mkfs = NULL, *mangled_label = NULL;
@@ -399,15 +398,13 @@ int make_filesystem(
                                 "-I", "256",
                                 "-m", "0",
                                 "-E", discard ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1",
+                                "-b", "4096",
                                 node);
                 if (!argv)
                         return log_oom();
 
-                if (root) {
-                        r = strv_extend_strv(&argv, STRV_MAKE("-d", root), false);
-                        if (r < 0)
-                                return log_oom();
-                }
+                if (root && strv_extend_strv(&argv, STRV_MAKE("-d", root), false) < 0)
+                        return log_oom();
 
         } else if (STR_IN_SET(fstype, "ext3", "ext4")) {
                 argv = strv_new(mkfs,
@@ -418,13 +415,11 @@ int make_filesystem(
                                 "-O", "has_journal",
                                 "-m", "0",
                                 "-E", discard ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1",
+                                "-b", "4096",
                                 node);
 
-                if (root) {
-                        r = strv_extend_strv(&argv, STRV_MAKE("-d", root), false);
-                        if (r < 0)
-                                return log_oom();
-                }
+                if (root && strv_extend_strv(&argv, STRV_MAKE("-d", root), false) < 0)
+                        return log_oom();
 
         } else if (streq(fstype, "btrfs")) {
                 argv = strv_new(mkfs,
@@ -435,17 +430,11 @@ int make_filesystem(
                 if (!argv)
                         return log_oom();
 
-                if (!discard) {
-                        r = strv_extend(&argv, "--nodiscard");
-                        if (r < 0)
-                                return log_oom();
-                }
+                if (!discard && strv_extend(&argv, "--nodiscard") < 0)
+                        return log_oom();
 
-                if (root) {
-                        r = strv_extend_strv(&argv, STRV_MAKE("-r", root), false);
-                        if (r < 0)
-                                return log_oom();
-                }
+                if (root && strv_extend_strv(&argv, STRV_MAKE("-r", root), false) < 0)
+                        return log_oom();
 
         } else if (streq(fstype, "f2fs")) {
                 argv = strv_new(mkfs,
@@ -471,23 +460,27 @@ int make_filesystem(
                 if (!argv)
                         return log_oom();
 
-                if (!discard) {
-                        r = strv_extend(&argv, "-K");
-                        if (r < 0)
-                                return log_oom();
-                }
+                if (!discard && strv_extend(&argv, "-K") < 0)
+                        return log_oom();
 
                 if (root) {
                         r = make_protofile(root, &protofile);
                         if (r < 0)
                                 return r;
 
-                        r = strv_extend_strv(&argv, STRV_MAKE("-p", protofile), false);
-                        if (r < 0)
+                        if (strv_extend_strv(&argv, STRV_MAKE("-p", protofile), false) < 0)
+                                return log_oom();
+                }
+
+                if (sector_size > 0) {
+                        if (strv_extend(&argv, "-s") < 0)
+                                return log_oom();
+
+                        if (strv_extendf(&argv, "size=%"PRIu64, sector_size) < 0)
                                 return log_oom();
                 }
 
-        } else if (streq(fstype, "vfat"))
+        } else if (streq(fstype, "vfat")) {
 
                 argv = strv_new(mkfs,
                                 "-i", vol_id,
@@ -495,7 +488,15 @@ int make_filesystem(
                                 "-F", "32",  /* yes, we force FAT32 here */
                                 node);
 
-        else if (streq(fstype, "swap"))
+                if (sector_size > 0) {
+                        if (strv_extend(&argv, "-S") < 0)
+                                return log_oom();
+
+                        if (strv_extendf(&argv, "%"PRIu64, sector_size) < 0)
+                                return log_oom();
+                }
+
+        } else if (streq(fstype, "swap"))
                 /* TODO: add --quiet here if
                  * https://github.com/util-linux/util-linux/issues/1499 resolved. */
 
@@ -523,11 +524,8 @@ int make_filesystem(
         if (!argv)
                 return log_oom();
 
-        if (extra_mkfs_args) {
-                r = strv_extend_strv(&argv, extra_mkfs_args, false);
-                if (r < 0)
-                        return log_oom();
-        }
+        if (extra_mkfs_args && strv_extend_strv(&argv, extra_mkfs_args, false) < 0)
+                return log_oom();
 
         if (root && stat(root, &st) < 0)
                 return log_error_errno(errno, "Failed to stat %s: %m", root);
index 7f72016cca08e6615a254336d6ddb8beadf34de1..b99ec3c0ad3a5cf8338defb3a6221d8f4d2f4537 100644 (file)
@@ -11,4 +11,12 @@ int mkfs_exists(const char *fstype);
 
 int mkfs_supports_root_option(const char *fstype);
 
-int make_filesystem(const char *node, const char *fstype, const char *label, const char *root, sd_id128_t uuid, bool discard, char * const *extra_mkfs_args);
+int make_filesystem(
+                const char *node,
+                const char *fstype,
+                const char *label,
+                const char *root,
+                sd_id128_t uuid,
+                bool discard,
+                uint64_t sector_size,
+                char * const *extra_mkfs_args);
index dedf067c4c148cee66d24d9b29b75b84c1986195..3402518555b3589a36c676086a2512cad3ed09bd 100644 (file)
@@ -54,7 +54,7 @@ int open_file_parse(const char *v, OpenFile **ret) {
         }
 
         if (isempty(of->fdname)) {
-                free(of->fdname);
+                of->fdname = mfree(of->fdname);
                 r = path_extract_filename(of->path, &of->fdname);
                 if (r < 0)
                         return r;
index ba8dfb041d8b07ffb2e177b19cc6326c8359b159..3870f0401be73716c487a4707c33ac82aa0fe042 100644 (file)
@@ -12,6 +12,7 @@
 #include "format-table.h"
 #include "fs-util.h"
 #include "hexdecoct.h"
+#include "hmac.h"
 #include "memory-util.h"
 #include "openssl-util.h"
 #include "parse-util.h"
@@ -730,18 +731,62 @@ int tpm2_get_good_pcr_banks(
         return 0;
 }
 
+int tpm2_get_good_pcr_banks_strv(
+                ESYS_CONTEXT *c,
+                uint32_t pcr_mask,
+                char ***ret) {
+
+        _cleanup_free_ TPMI_ALG_HASH *algs = NULL;
+        _cleanup_strv_free_ char **l = NULL;
+        int n_algs;
+
+        assert(c);
+        assert(ret);
+
+        n_algs = tpm2_get_good_pcr_banks(c, pcr_mask, &algs);
+        if (n_algs < 0)
+                return n_algs;
+
+        for (int i = 0; i < n_algs; i++) {
+                _cleanup_free_ char *n = NULL;
+                const EVP_MD *implementation;
+                const char *salg;
+
+                salg = tpm2_pcr_bank_to_string(algs[i]);
+                if (!salg)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 operates with unknown PCR algorithm, can't measure.");
+
+                implementation = EVP_get_digestbyname(salg);
+                if (!implementation)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 operates with unsupported PCR algorithm, can't measure.");
+
+                n = strdup(ASSERT_PTR(EVP_MD_name(implementation)));
+                if (!n)
+                        return log_oom();
+
+                ascii_strlower(n); /* OpenSSL uses uppercase digest names, we prefer them lower case. */
+
+                if (strv_consume(&l, TAKE_PTR(n)) < 0)
+                        return log_oom();
+        }
+
+        *ret = TAKE_PTR(l);
+        return 0;
+}
+
 static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
         struct sha256_ctx hash;
 
         assert(auth);
         assert(pin);
+
         auth->size = SHA256_DIGEST_SIZE;
 
+        CLEANUP_ERASE(hash);
+
         sha256_init_ctx(&hash);
         sha256_process_bytes(pin, len, &hash);
         sha256_finish_ctx(&hash, auth->buffer);
-
-        explicit_bzero_safe(&hash, sizeof(hash));
 }
 
 static int tpm2_make_encryption_session(
@@ -773,11 +818,11 @@ static int tpm2_make_encryption_session(
         if (pin) {
                 TPM2B_AUTH auth = {};
 
+                CLEANUP_ERASE(auth);
+
                 hash_pin(pin, strlen(pin), &auth);
 
                 rc = sym_Esys_TR_SetAuth(c, bind_key, &auth);
-                /* ESAPI knows about it, so clear it from our memory */
-                explicit_bzero_safe(&auth, sizeof(auth));
                 if (rc != TSS2_RC_SUCCESS)
                         return log_error_errno(
                                                SYNTHETIC_ERRNO(ENOTRECOVERABLE),
@@ -1369,8 +1414,8 @@ int tpm2_seal(const char *device,
         static const TPML_PCR_SELECTION creation_pcr = {};
         _cleanup_(erase_and_freep) void *secret = NULL;
         _cleanup_free_ void *blob = NULL, *hash = NULL;
-        TPM2B_SENSITIVE_CREATE hmac_sensitive;
         ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE;
+        TPM2B_SENSITIVE_CREATE hmac_sensitive;
         TPMI_ALG_PUBLIC primary_alg;
         TPM2B_PUBLIC hmac_template;
         TPMI_ALG_HASH pcr_bank;
@@ -1410,6 +1455,8 @@ int tpm2_seal(const char *device,
 
         start = now(CLOCK_MONOTONIC);
 
+        CLEANUP_ERASE(hmac_sensitive);
+
         r = tpm2_context_init(device, &c);
         if (r < 0)
                 return r;
@@ -1450,7 +1497,7 @@ int tpm2_seal(const char *device,
                         .nameAlg = TPM2_ALG_SHA256,
                         .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
                         .parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
-                        .unique.keyedHash.size = 32,
+                        .unique.keyedHash.size = SHA256_DIGEST_SIZE,
                         .authPolicy = *policy_digest,
                 },
         };
@@ -1498,7 +1545,6 @@ int tpm2_seal(const char *device,
         }
 
         secret = memdup(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
-        explicit_bzero_safe(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
         if (!secret) {
                 r = log_oom();
                 goto finish;
@@ -1559,7 +1605,6 @@ int tpm2_seal(const char *device,
         r = 0;
 
 finish:
-        explicit_bzero_safe(&hmac_sensitive, sizeof(hmac_sensitive));
         primary = tpm2_flush_context_verbose(c.esys_context, primary);
         session = tpm2_flush_context_verbose(c.esys_context, session);
         return r;
@@ -1875,6 +1920,90 @@ int tpm2_find_device_auto(
 #endif
 }
 
+#if HAVE_TPM2
+int tpm2_extend_bytes(
+                ESYS_CONTEXT *c,
+                char **banks,
+                unsigned pcr_index,
+                const void *data,
+                size_t data_size,
+                const void *secret,
+                size_t secret_size) {
+
+#if HAVE_OPENSSL
+        TPML_DIGEST_VALUES values = {};
+        TSS2_RC rc;
+
+        assert(c);
+        assert(data || data_size == 0);
+        assert(secret || secret_size == 0);
+
+        if (data_size == SIZE_MAX)
+                data_size = strlen(data);
+        if (secret_size == SIZE_MAX)
+                secret_size = strlen(secret);
+
+        if (pcr_index >= TPM2_PCRS_MAX)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Can't measure into unsupported PCR %u, refusing.", pcr_index);
+
+        if (strv_isempty(banks))
+                return 0;
+
+        STRV_FOREACH(bank, banks) {
+                const EVP_MD *implementation;
+                int id;
+
+                assert_se(implementation = EVP_get_digestbyname(*bank));
+
+                if (values.count >= ELEMENTSOF(values.digests))
+                        return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Too many banks selected.");
+
+                if ((size_t) EVP_MD_size(implementation) > sizeof(values.digests[values.count].digest))
+                        return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Hash result too large for TPM2.");
+
+                id = tpm2_pcr_bank_from_string(EVP_MD_name(implementation));
+                if (id < 0)
+                        return log_error_errno(id, "Can't map hash name to TPM2.");
+
+                values.digests[values.count].hashAlg = id;
+
+                /* So here's a twist: sometimes we want to measure secrets (e.g. root file system volume
+                 * key), but we'd rather not leak a literal hash of the secret to the TPM (given that the
+                 * wire is unprotected, and some other subsystem might use the simple, literal hash of the
+                 * secret for other purposes, maybe because it needs a shorter secret derived from it for
+                 * some unrelated purpose, who knows). Hence we instead measure an HMAC signature of a
+                 * private non-secret string instead. */
+                if (secret_size > 0) {
+                        if (!HMAC(implementation, secret, secret_size, data, data_size, (unsigned char*) &values.digests[values.count].digest, NULL))
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to calculate HMAC of data to measure.");
+                } else if (EVP_Digest(data, data_size, (unsigned char*) &values.digests[values.count].digest, NULL, implementation, NULL) != 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to hash data to measure.");
+
+                values.count++;
+        }
+
+        rc = sym_Esys_PCR_Extend(
+                        c,
+                        ESYS_TR_PCR0 + pcr_index,
+                        ESYS_TR_PASSWORD,
+                        ESYS_TR_NONE,
+                        ESYS_TR_NONE,
+                        &values);
+        if (rc != TSS2_RC_SUCCESS)
+                return log_error_errno(
+                                SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                "Failed to measure into PCR %u: %s",
+                                pcr_index,
+                                sym_Tss2_RC_Decode(rc));
+
+        return 0;
+#else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                               "OpenSSL not supported on this build.");
+#endif
+}
+#endif
+
 int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
         const char *p = ASSERT_PTR(s);
         uint32_t mask = 0;
@@ -1982,6 +2111,8 @@ int tpm2_make_luks2_json(
                 size_t blob_size,
                 const void *policy_hash,
                 size_t policy_hash_size,
+                const void *salt,
+                size_t salt_size,
                 TPM2Flags flags,
                 JsonVariant **ret) {
 
@@ -2021,7 +2152,8 @@ int tpm2_make_luks2_json(
                                        JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size)),
                                        JSON_BUILD_PAIR("tpm2-pin", JSON_BUILD_BOOLEAN(flags & TPM2_FLAGS_USE_PIN)),
                                        JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey_pcrs", JSON_BUILD_VARIANT(pkmj)),
-                                       JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey", JSON_BUILD_BASE64(pubkey, pubkey_size))));
+                                       JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey", JSON_BUILD_BASE64(pubkey, pubkey_size)),
+                                       JSON_BUILD_PAIR_CONDITION(salt, "tpm2_salt", JSON_BUILD_BASE64(salt, salt_size))));
         if (r < 0)
                 return r;
 
@@ -2044,10 +2176,12 @@ int tpm2_parse_luks2_json(
                 size_t *ret_blob_size,
                 void **ret_policy_hash,
                 size_t *ret_policy_hash_size,
+                void **ret_salt,
+                size_t *ret_salt_size,
                 TPM2Flags *ret_flags) {
 
-        _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL;
-        size_t blob_size = 0, policy_hash_size = 0, pubkey_size = 0;
+        _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL, *salt = NULL;
+        size_t blob_size = 0, policy_hash_size = 0, pubkey_size = 0, salt_size = 0;
         uint32_t hash_pcr_mask = 0, pubkey_pcr_mask = 0;
         uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */
         uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
@@ -2132,6 +2266,13 @@ int tpm2_parse_luks2_json(
                 SET_FLAG(flags, TPM2_FLAGS_USE_PIN, json_variant_boolean(w));
         }
 
+        w = json_variant_by_key(v, "tpm2_salt");
+        if (w) {
+                r = json_variant_unbase64(w, &salt, &salt_size);
+                if (r < 0)
+                        return log_debug_errno(r, "Invalid base64 data in 'tpm2_salt' field.");
+        }
+
         w = json_variant_by_key(v, "tpm2_pubkey_pcrs");
         if (w) {
                 r = tpm2_parse_pcr_json_array(w, &pubkey_pcr_mask);
@@ -2169,6 +2310,10 @@ int tpm2_parse_luks2_json(
                 *ret_policy_hash = TAKE_PTR(policy_hash);
         if (ret_policy_hash_size)
                 *ret_policy_hash_size = policy_hash_size;
+        if (ret_salt)
+                *ret_salt = TAKE_PTR(salt);
+        if (ret_salt_size)
+                *ret_salt_size = salt_size;
         if (ret_flags)
                 *ret_flags = flags;
 
@@ -2333,3 +2478,60 @@ int pcr_mask_to_string(uint32_t mask, char **ret) {
         *ret = TAKE_PTR(buf);
         return 0;
 }
+
+#define PBKDF2_HMAC_SHA256_ITERATIONS 10000
+
+/*
+ * Implements PBKDF2 HMAC SHA256 for a derived keylen of 32
+ * bytes and for PBKDF2_HMAC_SHA256_ITERATIONS count.
+ * I found the wikipedia entry relevant and it contains links to
+ * relevant RFCs:
+ *   - https://en.wikipedia.org/wiki/PBKDF2
+ *   - https://www.rfc-editor.org/rfc/rfc2898#section-5.2
+ */
+int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
+                    size_t passlen,
+                    const void *salt,
+                    size_t saltlen,
+                    uint8_t ret_key[static SHA256_DIGEST_SIZE]) {
+
+        uint8_t _cleanup_(erase_and_freep) *buffer = NULL;
+        uint8_t u[SHA256_DIGEST_SIZE];
+
+        /* To keep this simple, since derived KeyLen (dkLen in docs)
+         * Is the same as the hash output, we don't need multiple
+         * blocks. Part of the algorithm is to add the block count
+         * in, but this can be hardcoded to 1.
+         */
+        static const uint8_t block_cnt[] = { 0, 0, 0, 1 };
+
+        assert (saltlen > 0);
+        assert (saltlen <= (SIZE_MAX - sizeof(block_cnt)));
+        assert (passlen > 0);
+
+        /*
+         * Build a buffer of salt + block_cnt and hmac_sha256 it we
+         * do this as we don't have a context builder for HMAC_SHA256.
+         */
+        buffer = malloc(saltlen + sizeof(block_cnt));
+        if (!buffer)
+                return -ENOMEM;
+
+        memcpy(buffer, salt, saltlen);
+        memcpy(&buffer[saltlen], block_cnt, sizeof(block_cnt));
+
+        hmac_sha256(pass, passlen, buffer, saltlen + sizeof(block_cnt), u);
+
+        /* dk needs to be an unmodified u as u gets modified in the loop */
+        memcpy(ret_key, u, SHA256_DIGEST_SIZE);
+        uint8_t *dk = ret_key;
+
+        for (size_t i = 1; i < PBKDF2_HMAC_SHA256_ITERATIONS; i++) {
+                hmac_sha256(pass, passlen, u, sizeof(u), u);
+
+                for (size_t j=0; j < sizeof(u); j++)
+                        dk[j] ^= u[j];
+        }
+
+        return 0;
+}
index c240335ae619d611e6e35c16d0b5201bf5c7fd3d..04efd786386700152bcfba46843de29c763e6df5 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "json.h"
 #include "macro.h"
+#include "sha256.h"
 
 typedef enum TPM2Flags {
         TPM2_FLAGS_USE_PIN = 1 << 0,
@@ -68,6 +69,9 @@ static inline void Esys_Freep(void *p) {
 }
 
 int tpm2_get_good_pcr_banks(ESYS_CONTEXT *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
+int tpm2_get_good_pcr_banks_strv(ESYS_CONTEXT *c, uint32_t pcr_mask, char ***ret);
+
+int tpm2_extend_bytes(ESYS_CONTEXT *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size);
 
 #else
 struct tpm2_context;
@@ -84,8 +88,8 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret);
 int tpm2_make_pcr_json_array(uint32_t pcr_mask, JsonVariant **ret);
 int tpm2_parse_pcr_json_array(JsonVariant *v, uint32_t *ret);
 
-int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, TPM2Flags flags, JsonVariant **ret);
-int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, void **ret_pubkey, size_t *ret_pubkey_size, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, size_t *ret_policy_hash_size, TPM2Flags *ret_flags);
+int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *salt, size_t salt_size, TPM2Flags flags, JsonVariant **ret);
+int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, void **ret_pubkey, size_t *ret_pubkey_size, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, size_t *ret_policy_hash_size, void **ret_salt, size_t *ret_salt_size, TPM2Flags *ret_flags);
 
 #define TPM2_PCRS_MAX 24U
 
@@ -153,3 +157,9 @@ int tpm2_load_pcr_signature(const char *path, JsonVariant **ret);
 int tpm2_load_pcr_public_key(const char *path, void **ret_pubkey, size_t *ret_pubkey_size);
 
 int pcr_mask_to_string(uint32_t mask, char **ret);
+
+int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
+                    size_t passlen,
+                    const void *salt,
+                    size_t saltlen,
+                    uint8_t res[static SHA256_DIGEST_SIZE]);
index 4fbc5e877e813d0470447e73e6d8f839c18a61cb..0c4391991be4fa7ef6caa163c8e9dad0a2c6ffdd 100644 (file)
@@ -540,6 +540,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
                         r = loop_device_make_by_path(
                                         img->path,
                                         O_RDONLY,
+                                        /* sector_size= */ UINT32_MAX,
                                         FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
                                         LOCK_SH,
                                         &d);
index 86d1c5b7c299d41c3a3cfbce6112005ea3fca2b5..6878954268dd129f92f14ad492d331faa8771738 100644 (file)
@@ -80,24 +80,44 @@ static int list_dependencies_one(
         typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
 
         STRV_FOREACH(c, deps) {
+                _cleanup_free_ char *load_state = NULL, *sub_state = NULL;
+                UnitActiveState active_state;
+
                 if (strv_contains(*units, *c)) {
                         if (!arg_plain) {
                                 printf("  ");
-                                r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1);
+                                r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), /* last = */ true);
                                 if (r < 0)
                                         return r;
                         }
                         continue;
                 }
 
+                if (arg_types && !strv_contains(arg_types, unit_type_suffix(*c)))
+                        continue;
+
+                r = get_state_one_unit(bus, *c, &active_state);
+                if (r < 0)
+                        return r;
+
+                if (arg_states) {
+                        r = unit_load_state(bus, *c, &load_state);
+                        if (r < 0)
+                                return r;
+
+                        r = get_sub_state_one_unit(bus, *c, &sub_state);
+                        if (r < 0)
+                                return r;
+
+                        if (!strv_overlap(arg_states, STRV_MAKE(unit_active_state_to_string(active_state), load_state, sub_state)))
+                                continue;
+                }
+
                 if (arg_plain)
                         printf("  ");
                 else {
-                        UnitActiveState active_state = _UNIT_ACTIVE_STATE_INVALID;
                         const char *on;
 
-                        (void) get_state_one_unit(bus, *c, &active_state);
-
                         switch (active_state) {
                         case UNIT_ACTIVE:
                         case UNIT_RELOADING:
@@ -141,6 +161,9 @@ int verb_list_dependencies(int argc, char *argv[], void *userdata) {
         sd_bus *bus;
         int r;
 
+        /* We won't be able to preserve the tree structure if --type= or --state= is used */
+        arg_plain = arg_plain || arg_types || arg_states;
+
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
                 return r;
index 4166b361ddab8bcaf536aff1828f467765423954..281cf62b2043724eb2ed22d28fc93d718ff827cf 100644 (file)
@@ -13,6 +13,7 @@
 #include "errno-util.h"
 #include "exec-util.h"
 #include "exit-status.h"
+#include "fd-util.h"
 #include "format-util.h"
 #include "hexdecoct.h"
 #include "hostname-util.h"
@@ -953,7 +954,7 @@ static int map_exec(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_e
         if (!info)
                 return -ENOMEM;
 
-        LIST_FIND_TAIL(exec_status_info_list, i->exec_status_info_list, last);
+        last = LIST_FIND_TAIL(exec_status_info_list, i->exec_status_info_list);
 
         while ((r = exec_status_info_deserialize(m, info, is_ex_prop)) > 0) {
 
@@ -2100,29 +2101,93 @@ static int show_one(
         return 0;
 }
 
-static int get_unit_dbus_path_by_pid(
+static int get_unit_dbus_path_by_pid_fallback(
                 sd_bus *bus,
                 uint32_t pid,
-                char **unit) {
+                char **ret_path,
+                char **ret_unit) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        char *u;
+        _cleanup_free_ char *path = NULL, *unit = NULL;
+        char *p;
         int r;
 
+        assert(bus);
+        assert(ret_path);
+        assert(ret_unit);
+
         r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", pid);
         if (r < 0)
                 return log_error_errno(r, "Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r));
 
-        r = sd_bus_message_read(reply, "o", &u);
+        r = sd_bus_message_read(reply, "o", &p);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        path = strdup(p);
+        if (!path)
+                return log_oom();
+
+        r = unit_name_from_dbus_path(path, &unit);
+        if (r < 0)
+                return log_oom();
+
+        *ret_unit = TAKE_PTR(unit);
+        *ret_path = TAKE_PTR(path);
+
+        return 0;
+}
+
+static int get_unit_dbus_path_by_pid(
+                sd_bus *bus,
+                uint32_t pid,
+                char **ret_path,
+                char **ret_unit) {
+
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_free_ char *path = NULL, *unit = NULL;
+        _cleanup_close_ int pidfd = -EBADF;
+        char *p, *u;
+        int r;
+
+        assert(bus);
+        assert(ret_path);
+        assert(ret_unit);
+
+        /* First, try to send a PIDFD across the wire, so that we can pin the process and there's no race
+         * condition possible while we wait for the D-Bus reply. If we either don't have PIDFD support in
+         * the kernel or the new D-Bus method is not available, then fallback to the older method that
+         * sends the numeric PID. */
+
+        pidfd = pidfd_open(pid, 0);
+        if (pidfd < 0 && ERRNO_IS_NOT_SUPPORTED(errno))
+                return get_unit_dbus_path_by_pid_fallback(bus, pid, ret_path, ret_unit);
+        if (pidfd < 0)
+                return log_error_errno(errno, "Failed to open PID %"PRIu32": %m", pid);
+
+        r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPIDFD", &error, &reply, "h", pidfd);
+        if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
+                return get_unit_dbus_path_by_pid_fallback(bus, pid, ret_path, ret_unit);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "os", &p, &u);
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        u = strdup(u);
-        if (!u)
+        path = strdup(p);
+        if (!path)
+                return log_oom();
+
+        unit = strdup(u);
+        if (!unit)
                 return log_oom();
 
-        *unit = u;
+        *ret_unit = TAKE_PTR(unit);
+        *ret_path = TAKE_PTR(path);
+
         return 0;
 }
 
@@ -2297,15 +2362,11 @@ int verb_show(int argc, char *argv[], void *userdata) {
 
                         } else {
                                 /* Interpret as PID */
-                                r = get_unit_dbus_path_by_pid(bus, id, &path);
+                                r = get_unit_dbus_path_by_pid(bus, id, &path, &unit);
                                 if (r < 0) {
                                         ret = r;
                                         continue;
                                 }
-
-                                r = unit_name_from_dbus_path(path, &unit);
-                                if (r < 0)
-                                        return log_oom();
                         }
 
                         r = show_one(bus, path, unit, show_mode, &new_line, &ellipsized);
index b333850bec8e0e9171049d379829dfdc45dad877..bcad65f8dc2e55efd058bf56afe4fd606f05421e 100644 (file)
@@ -122,6 +122,7 @@ int get_state_one_unit(sd_bus *bus, const char *unit, UnitActiveState *ret_activ
         UnitActiveState state;
         int r;
 
+        assert(bus);
         assert(unit);
         assert(ret_active_state);
 
@@ -148,6 +149,34 @@ int get_state_one_unit(sd_bus *bus, const char *unit, UnitActiveState *ret_activ
         return 0;
 }
 
+int get_sub_state_one_unit(sd_bus *bus, const char *unit, char **ret_sub_state) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_free_ char *sub_state = NULL, *dbus_path = NULL;
+        int r;
+
+        assert(bus);
+        assert(unit);
+        assert(ret_sub_state);
+
+        dbus_path = unit_dbus_path_from_name(unit);
+        if (!dbus_path)
+                return log_oom();
+
+        r = sd_bus_get_property_string(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        dbus_path,
+                        "org.freedesktop.systemd1.Unit",
+                        "SubState",
+                        &error,
+                        &sub_state);
+        if (r < 0)
+                return log_error_errno(r, "Failed to retrieve unit sub state: %s", bus_error_message(&error, r));
+
+        *ret_sub_state = TAKE_PTR(sub_state);
+        return 0;
+}
+
 int get_unit_list(
                 sd_bus *bus,
                 const char *machine,
index 6445bb488741b23039e4d5e90211cbf784baf8f6..317bab75b73fbd1cccbf7a64152d8f974999e465 100644 (file)
@@ -21,7 +21,8 @@ void polkit_agent_open_maybe(void);
 
 int translate_bus_error_to_exit_status(int r, const sd_bus_error *error);
 
-int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *ret_active_state);
+int get_state_one_unit(sd_bus *bus, const char *unit, UnitActiveState *ret_active_state);
+int get_sub_state_one_unit(sd_bus *bus, const char *unit, char **ret_sub_state);
 int get_unit_list(sd_bus *bus, const char *machine, char **patterns, UnitInfo **unit_infos, int c, sd_bus_message **ret_reply);
 int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret, bool *ret_expanded);
 
index ae9b95620e59ea8fd6fca51f2976bb1d18d0174f..4e7fd0460c78eebcc3e74354098be239469811b1 100644 (file)
@@ -281,8 +281,7 @@ static int systemctl_help(void) {
                "                             kexec, suspend, hibernate, suspend-then-hibernate,\n"
                "                             hybrid-sleep, default, rescue, emergency, and exit.\n"
                "  -q --quiet             Suppress output\n"
-               "     --no-warn           Don't generate warning when trying to enable/disable\n"
-               "                         units without install information\n"
+               "     --no-warn           Suppress several warnings shown by default\n"
                "     --wait              For (re)start, wait until service stopped again\n"
                "                         For is-system-running, wait until startup is completed\n"
                "     --no-block          Do not wait until operation finished\n"
@@ -1168,11 +1167,12 @@ static int run(int argc, char *argv[]) {
                 goto finish;
 
         if (proc_mounted() == 0)
-                log_warning("%s%s/proc/ is not mounted. This is not a supported mode of operation. Please fix\n"
-                            "your invocation environment to mount /proc/ and /sys/ properly. Proceeding anyway.\n"
-                            "Your mileage may vary.",
-                            emoji_enabled() ? special_glyph(SPECIAL_GLYPH_WARNING_SIGN) : "",
-                            emoji_enabled() ? " " : "");
+                log_full(arg_no_warn ? LOG_DEBUG : LOG_WARNING,
+                         "%s%s/proc/ is not mounted. This is not a supported mode of operation. Please fix\n"
+                         "your invocation environment to mount /proc/ and /sys/ properly. Proceeding anyway.\n"
+                         "Your mileage may vary.",
+                         emoji_enabled() ? special_glyph(SPECIAL_GLYPH_WARNING_SIGN) : "",
+                         emoji_enabled() ? " " : "");
 
         if (arg_action != ACTION_SYSTEMCTL && running_in_chroot() > 0) {
                 if (!arg_quiet)
index b200f17cbdd5dde0744216dd9e13cc60723c2826..2b3f995769837e23bfaea63b3e5d2d50180b84e8 100644 (file)
@@ -309,6 +309,9 @@ int sd_dhcp_client_get_lease(
 int sd_dhcp_client_set_service_type(
                 sd_dhcp_client *client,
                 int type);
+int sd_dhcp_client_set_socket_priority(
+                sd_dhcp_client *client,
+                int so_priority);
 int sd_dhcp_client_set_fallback_lease_lifetime(
                 sd_dhcp_client *client,
                 uint32_t fallback_lease_lifetime);
index 497b2afb2f097f630b786ab477e8fcaba3282a34..a9fa78569d22097b7a353989216c181ed444ba82 100644 (file)
@@ -264,6 +264,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
                                       sd_dhcp6_option *v);
 int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable);
+int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable);
 
 int sd_dhcp6_client_get_lease(
                 sd_dhcp6_client *client,
index 54970532eca8a63394090763176bbeec9cc0ce3c..85dd086e2b94e384887d849a166bc9256c89bf78 100644 (file)
@@ -80,6 +80,18 @@ int sd_pid_get_machine_name(pid_t pid, char **machine);
  * hierarchy. */
 int sd_pid_get_cgroup(pid_t pid, char **cgroup);
 
+/* Equivalent to the corresponding sd_pid_get* functions, but take a
+ * PIDFD instead of a PID, to ensure there can be no possible PID
+ * recycle issues before/after the calls. */
+int sd_pidfd_get_session(pid_t pid, char **session);
+int sd_pidfd_get_owner_uid(pid_t pid, uid_t *uid);
+int sd_pidfd_get_unit(pid_t pid, char **unit);
+int sd_pidfd_get_user_unit(pid_t pid, char **unit);
+int sd_pidfd_get_slice(pid_t pid, char **slice);
+int sd_pidfd_get_user_slice(pid_t pid, char **slice);
+int sd_pidfd_get_machine_name(pid_t pid, char **machine);
+int sd_pidfd_get_cgroup(pid_t pid, char **cgroup);
+
 /* Similar to sd_pid_get_session(), but retrieves data about the peer
  * of a connected AF_UNIX socket */
 int sd_peer_get_session(int fd, char **session);
index 51241c942602dbe868b7b51065c5e9acd20465a7..00fdbad2c56e91112977b17742fe2c83adf25b05 100644 (file)
@@ -183,6 +183,9 @@ _SD_BEGIN_DECLARATIONS;
 #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)
 
+#define SD_MESSAGE_TIME_BUMP                          SD_ID128_MAKE(7d,b7,3c,8a,f0,d9,4e,eb,82,2a,e0,43,23,fe,6a,b6)
+#define SD_MESSAGE_TIME_BUMP_STR                      SD_ID128_MAKE_STR(7d,b7,3c,8a,f0,d9,4e,eb,82,2a,e0,43,23,fe,6a,b6)
+
 #define SD_MESSAGE_SHUTDOWN_SCHEDULED                 SD_ID128_MAKE(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
 #define SD_MESSAGE_SHUTDOWN_SCHEDULED_STR             SD_ID128_MAKE_STR(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
 
index b89e035fd64779d4a9be8743947425f11c62a238..9cc2cbaa6ec0ba125f52eeeabc43dcfd6fd82696 100644 (file)
@@ -119,6 +119,9 @@ int sd_network_link_get_activation_policy(int ifindex, char **ret);
 /* Get path to .network file applied to link */
 int sd_network_link_get_network_file(int ifindex, char **ret);
 
+/* Get paths to .network file dropins applied to link */
+int sd_network_link_get_network_file_dropins(int ifindex, char ***ret);
+
 /* Get DNS entries for a given link. These are string representations of
  * IP addresses */
 int sd_network_link_get_dns(int ifindex, char ***ret);
index 161a8e0c8d1014869ba267cc0f7ad059dd19db1f..0b61484f5e62815f3c0ad42b06134851ca1d7e00 100644 (file)
@@ -110,6 +110,12 @@ enum {
         /* systemd-networkd search paths */
         SD_PATH_SYSTEMD_SEARCH_NETWORK,
 
+        /* systemd environment generators */
+        SD_PATH_SYSTEMD_SYSTEM_ENVIRONMENT_GENERATOR,
+        SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR,
+        SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR,
+        SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR,
+
         _SD_PATH_MAX
 };
 
index 3aa98e1ffdffa01a4a7ff7d72ba5f25dd745f799..8dfc97c9e8a403ff74afa54a56a89cc219607748 100644 (file)
@@ -15,6 +15,7 @@ path = run_command(sh, '-c', 'echo "$PATH"', check: true).stdout().strip()
 test_env = environment()
 test_env.set('SYSTEMD_LANGUAGE_FALLBACK_MAP', language_fallback_map)
 test_env.set('PATH', project_build_root + ':' + path)
+test_env.set('PROJECT_BUILD_ROOT', project_build_root)
 
 ############################################################
 
index 38299ce7298291f5f458be7e1547393b1351e9c6..668a44733bc93a65b525e34d94336131bd9c152e 100644 (file)
@@ -330,6 +330,13 @@ TEST(chase_symlinks) {
                 assert_se(sd_id128_equal(a, b));
         }
 
+        assert_se(lstat(p, &st) >= 0);
+        r = chase_symlinks_and_unlink(p, NULL, 0, 0,  &result);
+        assert_se(path_equal(result, p));
+        result = mfree(result);
+        assert_se(r == 0);
+        assert_se(lstat(p, &st) == -1 && errno == ENOENT);
+
         /* Test CHASE_NOFOLLOW */
 
         p = strjoina(temp, "/target");
index afdc3b543681e37f351d2bca548abb2dd0148b6d..9d71db6ae19126d6793d9d6ee1395ee00ce60941 100644 (file)
@@ -322,6 +322,13 @@ TEST(base64mem_linebreak) {
                 assert_se(decoded_size == n);
                 assert_se(memcmp(data, decoded, n) == 0);
 
+                /* Also try in secure mode */
+                decoded = mfree(decoded);
+                decoded_size = 0;
+                assert_se(unbase64mem_full(encoded, SIZE_MAX, /* secure= */ true, &decoded, &decoded_size) >= 0);
+                assert_se(decoded_size == n);
+                assert_se(memcmp(data, decoded, n) == 0);
+
                 for (size_t j = 0; j < (size_t) l; j++)
                         assert_se((encoded[j] == '\n') == (j % (m + 1) == m));
         }
@@ -446,7 +453,17 @@ static void test_unbase64mem_one(const char *input, const char *output, int ret)
         size_t size = 0;
 
         assert_se(unbase64mem(input, SIZE_MAX, &buffer, &size) == ret);
+        if (ret >= 0) {
+                assert_se(size == strlen(output));
+                assert_se(memcmp(buffer, output, size) == 0);
+                assert_se(((char*) buffer)[size] == 0);
+        }
+
+        /* also try in secure mode */
+        buffer = mfree(buffer);
+        size = 0;
 
+        assert_se(unbase64mem_full(input, SIZE_MAX, /* secure=*/ true, &buffer, &size) == ret);
         if (ret >= 0) {
                 assert_se(size == strlen(output));
                 assert_se(memcmp(buffer, output, size) == 0);
index 31d767e41d423360490a7d0ce1dc191e609caae6..93ab1c5d48feee1b2286adced1ba4cff18ca478a 100644 (file)
@@ -14,9 +14,7 @@ static void test_in_addr_prefix_from_string_one(
                 const union in_addr_union *u,
                 unsigned char prefixlen,
                 int ret_refuse,
-                unsigned char prefixlen_refuse,
-                int ret_legacy,
-                unsigned char prefixlen_legacy) {
+                unsigned char prefixlen_refuse) {
 
         union in_addr_union q;
         unsigned char l;
@@ -46,43 +44,34 @@ static void test_in_addr_prefix_from_string_one(
                 assert_se(in_addr_equal(family, &q, u));
                 assert_se(l == prefixlen_refuse);
         }
-
-        r = in_addr_prefix_from_string_auto_internal(p, PREFIXLEN_LEGACY, &f, &q, &l);
-        assert_se(r == ret_legacy);
-
-        if (r >= 0) {
-                assert_se(f == family);
-                assert_se(in_addr_equal(family, &q, u));
-                assert_se(l == prefixlen_legacy);
-        }
 }
 
 TEST(in_addr_prefix_from_string) {
-        test_in_addr_prefix_from_string_one("", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
-        test_in_addr_prefix_from_string_one("/", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
-        test_in_addr_prefix_from_string_one("/8", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
-        test_in_addr_prefix_from_string_one("1.2.3.4", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32, -ENOANO, 0, 0, 8);
-        test_in_addr_prefix_from_string_one("1.2.3.4/0", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 0, 0, 0, 0, 0);
-        test_in_addr_prefix_from_string_one("1.2.3.4/1", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 1, 0, 1, 0, 1);
-        test_in_addr_prefix_from_string_one("1.2.3.4/2", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 2, 0, 2, 0, 2);
-        test_in_addr_prefix_from_string_one("1.2.3.4/32", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32, 0, 32, 0, 32);
-        test_in_addr_prefix_from_string_one("1.2.3.4/33", AF_INET, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0);
-        test_in_addr_prefix_from_string_one("1.2.3.4/-1", AF_INET, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0);
-        test_in_addr_prefix_from_string_one("::1", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
-
-        test_in_addr_prefix_from_string_one("", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
-        test_in_addr_prefix_from_string_one("/", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
-        test_in_addr_prefix_from_string_one("/8", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
-        test_in_addr_prefix_from_string_one("::1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, -ENOANO, 0, 0, 0);
-        test_in_addr_prefix_from_string_one("::1/0", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0, 0, 0, 0, 0);
-        test_in_addr_prefix_from_string_one("::1/1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 1, 0, 1, 0, 1);
-        test_in_addr_prefix_from_string_one("::1/2", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 2, 0, 2, 0, 2);
-        test_in_addr_prefix_from_string_one("::1/32", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 32, 0, 32, 0, 32);
-        test_in_addr_prefix_from_string_one("::1/33", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 33, 0, 33, 0, 33);
-        test_in_addr_prefix_from_string_one("::1/64", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 64, 0, 64, 0, 64);
-        test_in_addr_prefix_from_string_one("::1/128", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, 0, 128, 0, 128);
-        test_in_addr_prefix_from_string_one("::1/129", AF_INET6, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0);
-        test_in_addr_prefix_from_string_one("::1/-1", AF_INET6, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0);
+        test_in_addr_prefix_from_string_one("", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0);
+        test_in_addr_prefix_from_string_one("/", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0);
+        test_in_addr_prefix_from_string_one("/8", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0);
+        test_in_addr_prefix_from_string_one("1.2.3.4", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32, -ENOANO, 0);
+        test_in_addr_prefix_from_string_one("1.2.3.4/0", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 0, 0, 0);
+        test_in_addr_prefix_from_string_one("1.2.3.4/1", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 1, 0, 1);
+        test_in_addr_prefix_from_string_one("1.2.3.4/2", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 2, 0, 2);
+        test_in_addr_prefix_from_string_one("1.2.3.4/32", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32, 0, 32);
+        test_in_addr_prefix_from_string_one("1.2.3.4/33", AF_INET, -ERANGE, NULL, 0, -ERANGE, 0);
+        test_in_addr_prefix_from_string_one("1.2.3.4/-1", AF_INET, -ERANGE, NULL, 0, -ERANGE, 0);
+        test_in_addr_prefix_from_string_one("::1", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0);
+
+        test_in_addr_prefix_from_string_one("", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0);
+        test_in_addr_prefix_from_string_one("/", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0);
+        test_in_addr_prefix_from_string_one("/8", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0);
+        test_in_addr_prefix_from_string_one("::1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, -ENOANO, 0);
+        test_in_addr_prefix_from_string_one("::1/0", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0, 0, 0);
+        test_in_addr_prefix_from_string_one("::1/1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 1, 0, 1);
+        test_in_addr_prefix_from_string_one("::1/2", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 2, 0, 2);
+        test_in_addr_prefix_from_string_one("::1/32", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 32, 0, 32);
+        test_in_addr_prefix_from_string_one("::1/33", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 33, 0, 33);
+        test_in_addr_prefix_from_string_one("::1/64", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 64, 0, 64);
+        test_in_addr_prefix_from_string_one("::1/128", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, 0, 128);
+        test_in_addr_prefix_from_string_one("::1/129", AF_INET6, -ERANGE, NULL, 0, -ERANGE, 0);
+        test_in_addr_prefix_from_string_one("::1/-1", AF_INET6, -ERANGE, NULL, 0, -ERANGE, 0);
 }
 
 static void test_in_addr_prefix_to_string_valid(int family, const char *p) {
index ea45f5b95cf0cd8e4004fa569859037639cbc0a6..307c1bf9361197529f768b8fc7ebca1925030303 100644 (file)
@@ -19,7 +19,7 @@ int main(int argc, const char *argv[]) {
         for (i = 0; i < ELEMENTSOF(items); i++) {
                 LIST_INIT(item_list, &items[i]);
                 assert_se(LIST_JUST_US(item_list, &items[i]));
-                LIST_PREPEND(item_list, head, &items[i]);
+                assert_se(LIST_PREPEND(item_list, head, &items[i]) == &items[i]);
         }
 
         i = 0;
@@ -55,14 +55,13 @@ int main(int argc, const char *argv[]) {
         assert_se(items[2].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        list_item *cursor;
-        LIST_FIND_HEAD(item_list, &items[0], cursor);
+        list_item *cursor = LIST_FIND_HEAD(item_list, &items[0]);
         assert_se(cursor == &items[3]);
 
-        LIST_FIND_TAIL(item_list, &items[3], cursor);
+        cursor = LIST_FIND_TAIL(item_list, &items[3]);
         assert_se(cursor == &items[0]);
 
-        LIST_REMOVE(item_list, head, &items[1]);
+        assert_se(LIST_REMOVE(item_list, head, &items[1]) == &items[1]);
         assert_se(LIST_JUST_US(item_list, &items[1]));
 
         assert_se(items[0].item_list_next == NULL);
@@ -73,7 +72,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[2].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_INSERT_AFTER(item_list, head, &items[3], &items[1]);
+        assert_se(LIST_INSERT_AFTER(item_list, head, &items[3], &items[1]) == &items[1]);
         assert_se(items[0].item_list_next == NULL);
         assert_se(items[2].item_list_next == &items[0]);
         assert_se(items[1].item_list_next == &items[2]);
@@ -84,7 +83,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[1].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_REMOVE(item_list, head, &items[1]);
+        assert_se(LIST_REMOVE(item_list, head, &items[1]) == &items[1]);
         assert_se(LIST_JUST_US(item_list, &items[1]));
 
         assert_se(items[0].item_list_next == NULL);
@@ -95,7 +94,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[2].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_INSERT_BEFORE(item_list, head, &items[2], &items[1]);
+        assert_se(LIST_INSERT_BEFORE(item_list, head, &items[2], &items[1]) == &items[1]);
         assert_se(items[0].item_list_next == NULL);
         assert_se(items[2].item_list_next == &items[0]);
         assert_se(items[1].item_list_next == &items[2]);
@@ -106,7 +105,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[1].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_REMOVE(item_list, head, &items[0]);
+        assert_se(LIST_REMOVE(item_list, head, &items[0]) == &items[0]);
         assert_se(LIST_JUST_US(item_list, &items[0]));
 
         assert_se(items[2].item_list_next == NULL);
@@ -117,7 +116,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[1].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_INSERT_BEFORE(item_list, head, &items[3], &items[0]);
+        assert_se(LIST_INSERT_BEFORE(item_list, head, &items[3], &items[0]) == &items[0]);
         assert_se(items[2].item_list_next == NULL);
         assert_se(items[1].item_list_next == &items[2]);
         assert_se(items[3].item_list_next == &items[1]);
@@ -129,7 +128,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[0].item_list_prev == NULL);
         assert_se(head == &items[0]);
 
-        LIST_REMOVE(item_list, head, &items[0]);
+        assert_se(LIST_REMOVE(item_list, head, &items[0]) == &items[0]);
         assert_se(LIST_JUST_US(item_list, &items[0]));
 
         assert_se(items[2].item_list_next == NULL);
@@ -140,7 +139,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[1].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_INSERT_BEFORE(item_list, head, NULL, &items[0]);
+        assert_se(LIST_INSERT_BEFORE(item_list, head, NULL, &items[0]) == &items[0]);
         assert_se(items[0].item_list_next == NULL);
         assert_se(items[2].item_list_next == &items[0]);
         assert_se(items[1].item_list_next == &items[2]);
@@ -151,7 +150,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[1].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_REMOVE(item_list, head, &items[0]);
+        assert_se(LIST_REMOVE(item_list, head, &items[0]) == &items[0]);
         assert_se(LIST_JUST_US(item_list, &items[0]));
 
         assert_se(items[2].item_list_next == NULL);
@@ -162,7 +161,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[1].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_REMOVE(item_list, head, &items[1]);
+        assert_se(LIST_REMOVE(item_list, head, &items[1]) == &items[1]);
         assert_se(LIST_JUST_US(item_list, &items[1]));
 
         assert_se(items[2].item_list_next == NULL);
@@ -171,18 +170,18 @@ int main(int argc, const char *argv[]) {
         assert_se(items[2].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_REMOVE(item_list, head, &items[2]);
+        assert_se(LIST_REMOVE(item_list, head, &items[2]) == &items[2]);
         assert_se(LIST_JUST_US(item_list, &items[2]));
         assert_se(LIST_JUST_US(item_list, head));
 
-        LIST_REMOVE(item_list, head, &items[3]);
+        assert_se(LIST_REMOVE(item_list, head, &items[3]) == &items[3]);
         assert_se(LIST_JUST_US(item_list, &items[3]));
 
         assert_se(head == NULL);
 
         for (i = 0; i < ELEMENTSOF(items); i++) {
                 assert_se(LIST_JUST_US(item_list, &items[i]));
-                LIST_APPEND(item_list, head, &items[i]);
+                assert_se(LIST_APPEND(item_list, head, &items[i]) == &items[i]);
         }
 
         assert_se(!LIST_JUST_US(item_list, head));
@@ -198,20 +197,20 @@ int main(int argc, const char *argv[]) {
         assert_se(items[3].item_list_prev == &items[2]);
 
         for (i = 0; i < ELEMENTSOF(items); i++)
-                LIST_REMOVE(item_list, head, &items[i]);
+                assert_se(LIST_REMOVE(item_list, head, &items[i]) == &items[i]);
 
         assert_se(head == NULL);
 
         for (i = 0; i < ELEMENTSOF(items) / 2; i++) {
                 LIST_INIT(item_list, &items[i]);
                 assert_se(LIST_JUST_US(item_list, &items[i]));
-                LIST_PREPEND(item_list, head, &items[i]);
+                assert_se(LIST_PREPEND(item_list, head, &items[i]) == &items[i]);
         }
 
         for (i = ELEMENTSOF(items) / 2; i < ELEMENTSOF(items); i++) {
                 LIST_INIT(item_list, &items[i]);
                 assert_se(LIST_JUST_US(item_list, &items[i]));
-                LIST_PREPEND(item_list, head2, &items[i]);
+                assert_se(LIST_PREPEND(item_list, head2, &items[i]) == &items[i]);
         }
 
         assert_se(items[0].item_list_next == NULL);
@@ -224,7 +223,7 @@ int main(int argc, const char *argv[]) {
         assert_se(items[2].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_JOIN(item_list, head2, head);
+        assert_se(LIST_JOIN(item_list, head2, head) == head2);
         assert_se(head == NULL);
 
         assert_se(items[0].item_list_next == NULL);
@@ -237,18 +236,18 @@ int main(int argc, const char *argv[]) {
         assert_se(items[2].item_list_prev == &items[3]);
         assert_se(items[3].item_list_prev == NULL);
 
-        LIST_JOIN(item_list, head, head2);
+        assert_se(LIST_JOIN(item_list, head, head2) == head);
         assert_se(head2 == NULL);
         assert_se(head);
 
         for (i = 0; i < ELEMENTSOF(items); i++)
-                LIST_REMOVE(item_list, head, &items[i]);
+                assert_se(LIST_REMOVE(item_list, head, &items[i]) == &items[i]);
 
         assert_se(head == NULL);
 
-        LIST_PREPEND(item_list, head, items + 0);
-        LIST_PREPEND(item_list, head, items + 1);
-        LIST_PREPEND(item_list, head, items + 2);
+        assert_se(LIST_PREPEND(item_list, head, items + 0) == items + 0);
+        assert_se(LIST_PREPEND(item_list, head, items + 1) == items + 1);
+        assert_se(LIST_PREPEND(item_list, head, items + 2) == items + 2);
 
         assert_se(LIST_POP(item_list, head) == items + 2);
         assert_se(LIST_POP(item_list, head) == items + 1);
index 446437313224b4dbb6ec7edfe4c5d92dba3a70d4..f22fa42ce354ffa75476be3a7700c793794ca389 100644 (file)
@@ -1008,7 +1008,6 @@ TEST(unit_is_recursive_template_dependency) {
 
 TEST(config_parse_log_filter_patterns) {
         ExecContext c = {};
-        int r;
 
         static const struct {
                 const char *regex;
@@ -1033,9 +1032,8 @@ TEST(config_parse_log_filter_patterns) {
                 return (void) log_tests_skipped("PCRE2 support is not available");
 
         for (size_t i = 0; i < ELEMENTSOF(regex_tests); i++) {
-                r = config_parse_log_filter_patterns(NULL, "fake", 1, "section", 1, "LogFilterPatterns", 1,
-                                                     regex_tests[i].regex, &c, NULL);
-                assert_se(r >= 0);
+                assert_se(config_parse_log_filter_patterns(NULL, "fake", 1, "section", 1, "LogFilterPatterns", 1,
+                                                           regex_tests[i].regex, &c, NULL) >= 0);
 
                 assert_se(set_size(c.log_filter_allowed_patterns) == regex_tests[i].allowed_patterns_count);
                 assert_se(set_size(c.log_filter_denied_patterns) == regex_tests[i].denied_patterns_count);
@@ -1047,6 +1045,8 @@ TEST(config_parse_log_filter_patterns) {
                 SET_FOREACH(p, c.log_filter_denied_patterns)
                         assert_se(p && p[0] != '~');
         }
+
+        exec_context_done(&c);
 }
 
 TEST(config_parse_open_file) {
index f21d88f39ab7231f54bcf9544b73401c492b90cb..30c536d5c7c9dcd220265027e1ca7d4ad83b3984 100644 (file)
@@ -7,6 +7,7 @@
 #include "log.h"
 #include "process-util.h"
 #include "string-util.h"
+#include "strv.h"
 
 assert_cc(IS_SYNTHETIC_ERRNO(SYNTHETIC_ERRNO(EINVAL)));
 assert_cc(!IS_SYNTHETIC_ERRNO(EINVAL));
@@ -69,6 +70,75 @@ static void test_log_syntax(void) {
         assert_se(log_syntax("unit", LOG_ERR, "filename", 10, SYNTHETIC_ERRNO(ENOTTY), "ENOTTY: %s: %m", "hogehoge") == -ENOTTY);
 }
 
+static void test_log_context(void) {
+        {
+                char **strv = STRV_MAKE("FIRST=abc", "SECOND=qrs");
+
+                LOG_CONTEXT_PUSH("THIRD=pfs");
+                LOG_CONTEXT_PUSH("FOURTH=def");
+                LOG_CONTEXT_PUSH_STRV(strv);
+                LOG_CONTEXT_PUSH_STRV(strv);
+
+                /* Test that the log context was set up correctly. */
+                assert_se(log_context_num_contexts() == 4);
+                assert_se(log_context_num_fields() == 6);
+
+                /* Test that everything still works with modifications to the log context. */
+                test_log_struct();
+                test_long_lines();
+                test_log_syntax();
+
+                {
+                        LOG_CONTEXT_PUSH("FIFTH=123");
+                        LOG_CONTEXT_PUSH_STRV(strv);
+
+                        /* Check that our nested fields got added correctly. */
+                        assert_se(log_context_num_contexts() == 6);
+                        assert_se(log_context_num_fields() == 9);
+
+                        /* Test that everything still works in a nested block. */
+                        test_log_struct();
+                        test_long_lines();
+                        test_log_syntax();
+                }
+
+                /* Check that only the fields from the nested block got removed. */
+                assert_se(log_context_num_contexts() == 4);
+                assert_se(log_context_num_fields() == 6);
+        }
+
+        assert_se(log_context_num_contexts() == 0);
+        assert_se(log_context_num_fields() == 0);
+
+        {
+                _cleanup_(log_context_freep) LogContext *ctx = NULL;
+
+                char **strv = STRV_MAKE("SIXTH=ijn", "SEVENTH=PRP");
+                assert_se(ctx = log_context_new(strv, /*owned=*/ false));
+
+                assert_se(log_context_num_contexts() == 1);
+                assert_se(log_context_num_fields() == 2);
+
+                /* Test that everything still works with a manually configured log context. */
+                test_log_struct();
+                test_long_lines();
+                test_log_syntax();
+        }
+
+        {
+                char **strv = NULL;
+
+                assert_se(strv = strv_new("ABC", "DEF"));
+                LOG_CONTEXT_CONSUME_STRV(strv);
+
+                assert_se(log_context_num_contexts() == 1);
+                assert_se(log_context_num_fields() == 2);
+        }
+
+        assert_se(log_context_num_contexts() == 0);
+        assert_se(log_context_num_fields() == 0);
+}
+
 int main(int argc, char* argv[]) {
         test_file();
 
@@ -81,6 +151,7 @@ int main(int argc, char* argv[]) {
                 test_log_struct();
                 test_long_lines();
                 test_log_syntax();
+                test_log_context();
         }
 
         return 0;
index bfc327264368d010e186b03905df1a13d50cc217..97c2f66ac99f5fed4ef7ee2f242ecb7312190a82 100644 (file)
@@ -245,16 +245,16 @@ static int run(int argc, char *argv[]) {
         assert_se(r >= 0);
 
         assert_se(sd_id128_randomize(&id) >= 0);
-        assert_se(make_filesystem(dissected->partitions[PARTITION_ESP].node, "vfat", "EFI", NULL, id, true, NULL) >= 0);
+        assert_se(make_filesystem(dissected->partitions[PARTITION_ESP].node, "vfat", "EFI", NULL, id, true, 0, NULL) >= 0);
 
         assert_se(sd_id128_randomize(&id) >= 0);
-        assert_se(make_filesystem(dissected->partitions[PARTITION_XBOOTLDR].node, "vfat", "xbootldr", NULL, id, true, NULL) >= 0);
+        assert_se(make_filesystem(dissected->partitions[PARTITION_XBOOTLDR].node, "vfat", "xbootldr", NULL, id, true, 0, NULL) >= 0);
 
         assert_se(sd_id128_randomize(&id) >= 0);
-        assert_se(make_filesystem(dissected->partitions[PARTITION_ROOT].node, "ext4", "root", NULL, id, true, NULL) >= 0);
+        assert_se(make_filesystem(dissected->partitions[PARTITION_ROOT].node, "ext4", "root", NULL, id, true, 0, NULL) >= 0);
 
         assert_se(sd_id128_randomize(&id) >= 0);
-        assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", NULL, id, true, NULL) >= 0);
+        assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", NULL, id, true, 0, NULL) >= 0);
 
         dissected = dissected_image_unref(dissected);
 
index 4047139c269a1881b11659d150eb6954b5701c18..b3ff7d65c1cf1ad853ba19b42686097886f66d40 100644 (file)
@@ -1168,4 +1168,54 @@ TEST(streq_skip_trailing_chars) {
         assert_se(!streq_skip_trailing_chars("", "f", NULL));
 }
 
+#define TEST_MAKE_CSTRING_ONE(x, ret, mode, expect)                     \
+        do {                                                            \
+                _cleanup_free_ char *b = NULL;                          \
+                assert_se(make_cstring((x), ELEMENTSOF(x), (mode), &b) == (ret)); \
+                assert_se(streq_ptr(b, (expect)));                      \
+        } while(false)
+
+TEST(make_cstring) {
+        static const char test1[] = "this is a test",
+                test2[] = "",
+                test3[] = "a",
+                test4[] = "aa\0aa",
+                test5[] = { 'b', 'b', 0, 'b' , 'b' },
+                test6[] = {},
+                test7[] = { 'x' },
+                test8[] = { 'x', 'y', 'z' };
+
+        TEST_MAKE_CSTRING_ONE(test1, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
+        TEST_MAKE_CSTRING_ONE(test1, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "this is a test");
+        TEST_MAKE_CSTRING_ONE(test1, 0, MAKE_CSTRING_REQUIRE_TRAILING_NUL, "this is a test");
+
+        TEST_MAKE_CSTRING_ONE(test2, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
+        TEST_MAKE_CSTRING_ONE(test2, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "");
+        TEST_MAKE_CSTRING_ONE(test2, 0, MAKE_CSTRING_REQUIRE_TRAILING_NUL, "");
+
+        TEST_MAKE_CSTRING_ONE(test3, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
+        TEST_MAKE_CSTRING_ONE(test3, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "a");
+        TEST_MAKE_CSTRING_ONE(test3, 0, MAKE_CSTRING_REQUIRE_TRAILING_NUL, "a");
+
+        TEST_MAKE_CSTRING_ONE(test4, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
+        TEST_MAKE_CSTRING_ONE(test4, -EINVAL, MAKE_CSTRING_ALLOW_TRAILING_NUL, NULL);
+        TEST_MAKE_CSTRING_ONE(test4, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
+
+        TEST_MAKE_CSTRING_ONE(test5, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
+        TEST_MAKE_CSTRING_ONE(test5, -EINVAL, MAKE_CSTRING_ALLOW_TRAILING_NUL, NULL);
+        TEST_MAKE_CSTRING_ONE(test5, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
+
+        TEST_MAKE_CSTRING_ONE(test6, 0, MAKE_CSTRING_REFUSE_TRAILING_NUL, "");
+        TEST_MAKE_CSTRING_ONE(test6, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "");
+        TEST_MAKE_CSTRING_ONE(test6, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
+
+        TEST_MAKE_CSTRING_ONE(test7, 0, MAKE_CSTRING_REFUSE_TRAILING_NUL, "x");
+        TEST_MAKE_CSTRING_ONE(test7, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "x");
+        TEST_MAKE_CSTRING_ONE(test7, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
+
+        TEST_MAKE_CSTRING_ONE(test8, 0, MAKE_CSTRING_REFUSE_TRAILING_NUL, "xyz");
+        TEST_MAKE_CSTRING_ONE(test8, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "xyz");
+        TEST_MAKE_CSTRING_ONE(test8, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 9208faafa47d22c482090a71d0be038a97e01955..5c03eaa960b54c8df9330544a898d03ae474134c 100644 (file)
@@ -942,4 +942,16 @@ TEST(strv_fnmatch) {
         assert_se(pos == 1);
 }
 
+TEST(strv_extend_join) {
+        _cleanup_strv_free_ char **v = NULL;
+
+        assert_se(strv_extend_assignment(&v, "MESSAGE", "ABC") >= 0);
+        assert_se(strv_extend_assignment(&v, "ABC", "QER") >= 0);
+        assert_se(strv_extend_assignment(&v, "MISSING", NULL) >= 0);
+
+        assert_se(strv_length(v) == 2);
+        assert_se(streq(v[0], "MESSAGE=ABC"));
+        assert_se(streq(v[1], "ABC=QER"));
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index 6b546fb9f572daf185262cfbe5df80fd5bdc40c9..33d5b89729041f84bdf6618fd2e226cd63c1a23c 100644 (file)
@@ -365,6 +365,11 @@ TEST(format_timestamp) {
                 assert_se(parse_timestamp(buf, &y) >= 0);
                 assert_se(x == y);
 
+                assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_DATE));
+                log_debug("%s", buf);
+                assert_se(parse_timestamp(buf, &y) >= 0);
+                assert_se(y > usec_sub_unsigned(x, 2 * USEC_PER_DAY) && y < usec_add(x, 2* USEC_PER_DAY));
+
                 assert_se(format_timestamp_relative(buf, sizeof(buf), x));
                 log_debug("%s", buf);
                 assert_se(parse_timestamp(buf, &y) >= 0);
@@ -466,28 +471,42 @@ TEST(format_timestamp_relative) {
         assert_se(streq(buf, "2 weeks 2 days ago"));
 }
 
-static void test_format_timestamp_utc_one(usec_t val, const char *result) {
+static void test_format_timestamp_one(usec_t val, TimestampStyle style, const char *result) {
         char buf[FORMAT_TIMESTAMP_MAX];
         const char *t;
 
-        t = format_timestamp_style(buf, sizeof(buf), val, TIMESTAMP_UTC);
+        t = format_timestamp_style(buf, sizeof(buf), val, style);
         assert_se(streq_ptr(t, result));
 }
 
-TEST(format_timestamp_utc) {
-        test_format_timestamp_utc_one(0, NULL);
-        test_format_timestamp_utc_one(1, "Thu 1970-01-01 00:00:00 UTC");
-        test_format_timestamp_utc_one(USEC_PER_SEC, "Thu 1970-01-01 00:00:01 UTC");
+TEST(format_timestamp_range) {
+        test_format_timestamp_one(0, TIMESTAMP_UTC, NULL);
+        test_format_timestamp_one(0, TIMESTAMP_DATE, NULL);
+        test_format_timestamp_one(0, TIMESTAMP_US_UTC, NULL);
+
+        test_format_timestamp_one(1, TIMESTAMP_UTC, "Thu 1970-01-01 00:00:00 UTC");
+        test_format_timestamp_one(1, TIMESTAMP_DATE, "Thu 1970-01-01");
+        test_format_timestamp_one(1, TIMESTAMP_US_UTC, "Thu 1970-01-01 00:00:00.000001 UTC");
+
+        test_format_timestamp_one(USEC_PER_SEC, TIMESTAMP_UTC, "Thu 1970-01-01 00:00:01 UTC");
+        test_format_timestamp_one(USEC_PER_SEC, TIMESTAMP_DATE, "Thu 1970-01-01");
+        test_format_timestamp_one(USEC_PER_SEC, TIMESTAMP_US_UTC, "Thu 1970-01-01 00:00:01.000000 UTC");
 
 #if SIZEOF_TIME_T == 8
-        test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX, "Thu 9999-12-30 23:59:59 UTC");
-        test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, "--- XXXX-XX-XX XX:XX:XX");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX, TIMESTAMP_UTC, "Thu 9999-12-30 23:59:59 UTC");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX, TIMESTAMP_DATE, "Thu 9999-12-30");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_UTC, "--- XXXX-XX-XX XX:XX:XX UTC");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_US_UTC, "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_DATE, "--- XXXX-XX-XX");
 #elif SIZEOF_TIME_T == 4
-        test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX, "Tue 2038-01-19 03:14:07 UTC");
-        test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, "--- XXXX-XX-XX XX:XX:XX");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX, TIMESTAMP_UTC, "Tue 2038-01-19 03:14:07 UTC");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX, TIMESTAMP_DATE, "Tue 2038-01-19");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_UTC, "--- XXXX-XX-XX XX:XX:XX UTC");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_US_UTC, "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC");
+        test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_DATE, "--- XXXX-XX-XX");
 #endif
 
-        test_format_timestamp_utc_one(USEC_INFINITY, NULL);
+        test_format_timestamp_one(USEC_INFINITY, TIMESTAMP_UTC, NULL);
 }
 
 TEST(deserialize_dual_timestamp) {
index c5f3d41da9a5854dac5c76470c0cbaf9ce081c42..04e08490b36a6a5b4be3d16fd1ea16175e2b8ebd 100644 (file)
@@ -28,4 +28,45 @@ TEST(tpm2_parse_pcrs) {
         test_tpm2_parse_pcrs_one("foo", 0, -EINVAL);
 }
 
+TEST(tpm2_util_pbkdf2_hmac_sha256) {
+
+        /*
+         * The test vectors from RFC 6070 [1] are for dkLen of 20 as it's SHA1
+         * other RFCs I bumped into had various differing dkLen and iter counts,
+         * so this was generated using Python's hmacmodule.
+         *
+         * 1. https://www.rfc-editor.org/rfc/rfc6070.html#page-2
+         */
+        static const struct {
+                const uint8_t pass[256];
+                size_t passlen;
+                const uint8_t salt[256];
+                size_t saltlen;
+                uint8_t expected[SHA256_DIGEST_SIZE];
+        } test_vectors[] = {
+                { .pass={'f', 'o', 'o', 'p', 'a', 's', 's'},                                                                        .passlen=7,  .salt={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'}, .saltlen=16, .expected={0xCB, 0xEA, 0x27, 0x23, 0x9A, 0x65, 0x99, 0xF6, 0x8C, 0x26, 0x54, 0x80, 0x5C, 0x63, 0x61, 0xD2, 0x91, 0x0A, 0x60, 0x3F, 0xC2, 0xF5, 0xF0, 0xAB, 0x55, 0x8B, 0x46, 0x07, 0x60, 0x93, 0xAB, 0xCB} },
+                { .pass={'f', 'o', 'o', 'p', 'a', 's', 's'},                                                                        .passlen=7,  .salt={0x00, 'h', 'f', 's', 'd', 'j', 'h', 'f', 'd', 'j', 'h', 'j', 'd', 'f', 's'},     .saltlen=15, .expected={0x2B, 0xDF, 0x52, 0x29, 0x48, 0x3F, 0x98, 0x25, 0x01, 0x19, 0xB4, 0x42, 0xBC, 0xA7, 0x38, 0x5D, 0xCD, 0x08, 0xBD, 0xDC, 0x33, 0xBF, 0x32, 0x5E, 0x31, 0x87, 0x54, 0xFF, 0x2C, 0x23, 0x68, 0xFF} },
+                { .pass={'f', 'o', 'o', 'p', 'a', 's', 's'},                                                                        .passlen=7,  .salt={'m', 'y', 's', 'a', 0x00, 'l', 't'},                                             .saltlen=7,  .expected={0x7C, 0x24, 0xB4, 0x4D, 0x30, 0x11, 0x53, 0x24, 0x87, 0x56, 0x24, 0x10, 0xBA, 0x9F, 0xF2, 0x4E, 0xBB, 0xF5, 0x03, 0x56, 0x2B, 0xB1, 0xA1, 0x92, 0x8B, 0x5F, 0x32, 0x02, 0x23, 0x1F, 0x79, 0xE6} },
+                { .pass={'p', 'a', 's', 's', 'w', 'i', 't', 'h', 'n', 'u', 'l', 'l', 0x00, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}, .passlen=21, .salt={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'}, .saltlen=16, .expected={0xE9, 0x53, 0xB7, 0x1D, 0xAB, 0xD1, 0xC1, 0xF3, 0xC4, 0x7F, 0x18, 0x96, 0xDD, 0xD7, 0x6B, 0xC6, 0x6A, 0xBD, 0xFB, 0x12, 0x7C, 0xF8, 0x68, 0xDC, 0x6E, 0xEF, 0x29, 0xCC, 0x1B, 0x30, 0x5B, 0x74} },
+                { .pass={'p', 'a', 's', 's', 'w', 'i', 't', 'h', 'n', 'u', 'l', 'l', 0x00, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}, .passlen=21, .salt={0x00, 'h', 'f', 's', 'd', 'j', 'h', 'f', 'd', 'j', 'h', 'j', 'd', 'f', 's'},     .saltlen=15, .expected={0x51, 0xA3, 0x82, 0xA5, 0x2F, 0x48, 0x84, 0xB3, 0x02, 0x0D, 0xC2, 0x42, 0x9A, 0x8F, 0x86, 0xCC, 0x66, 0xFD, 0x65, 0x87, 0x89, 0x07, 0x2B, 0x07, 0x82, 0x42, 0xD6, 0x6D, 0x43, 0xB8, 0xFD, 0xCF} },
+                { .pass={'p', 'a', 's', 's', 'w', 'i', 't', 'h', 'n', 'u', 'l', 'l', 0x00, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}, .passlen=21, .salt={'m', 'y', 's', 'a', 0x00, 'l', 't'},                                             .saltlen=7,  .expected={0xEC, 0xFB, 0x5D, 0x5F, 0xF6, 0xA6, 0xE0, 0x79, 0x50, 0x64, 0x36, 0x64, 0xA3, 0x9A, 0x5C, 0xF3, 0x7A, 0x87, 0x0B, 0x64, 0x51, 0x59, 0x75, 0x64, 0x8B, 0x78, 0x2B, 0x62, 0x8F, 0x68, 0xD9, 0xCC} },
+                { .pass={0x00, 'p', 'a', 's', 's'},                                                                                 .passlen=5,  .salt={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'}, .saltlen=16, .expected={0x8A, 0x9A, 0x47, 0x9A, 0x91, 0x22, 0x2F, 0x56, 0x29, 0x4F, 0x26, 0x00, 0xE7, 0xB3, 0xEB, 0x63, 0x6D, 0x51, 0xF2, 0x60, 0x17, 0x08, 0x20, 0x70, 0x82, 0x8F, 0xA3, 0xD7, 0xBE, 0x2B, 0xD5, 0x5D} },
+                { .pass={0x00, 'p', 'a', 's', 's'},                                                                                 .passlen=5,  .salt={0x00, 'h', 'f', 's', 'd', 'j', 'h', 'f', 'd', 'j', 'h', 'j', 'd', 'f', 's'},     .saltlen=15, .expected={0x72, 0x3A, 0xF5, 0xF7, 0xCD, 0x6C, 0x12, 0xDD, 0x53, 0x28, 0x46, 0x0C, 0x19, 0x0E, 0xF2, 0x91, 0xDE, 0xEA, 0xF9, 0x6F, 0x74, 0x32, 0x34, 0x3F, 0x84, 0xED, 0x8D, 0x2A, 0xDE, 0xC9, 0xC6, 0x34} },
+                { .pass={0x00, 'p', 'a', 's', 's'},                                                                                 .passlen=5,  .salt={'m', 'y', 's', 'a', 0x00, 'l', 't'},                                             .saltlen=7,  .expected={0xE3, 0x07, 0x12, 0xBE, 0xEE, 0xF5, 0x5D, 0x18, 0x72, 0xF4, 0xCF, 0xF1, 0x20, 0x6B, 0xD6, 0x66, 0xCD, 0x7C, 0xE7, 0x4F, 0xC2, 0x16, 0x70, 0x5B, 0x9B, 0x2F, 0x7D, 0xE2, 0x3B, 0x42, 0x3A, 0x1B} },
+        };
+
+        uint8_t res[SHA256_DIGEST_SIZE];
+        for(size_t i = 0; i < sizeof(test_vectors)/sizeof(test_vectors[0]); i++) {
+
+                int rc = tpm2_util_pbkdf2_hmac_sha256(
+                                test_vectors[i].pass,
+                                test_vectors[i].passlen,
+                                test_vectors[i].salt,
+                                test_vectors[i].saltlen,
+                                res);
+                assert_se(rc == 0);
+                assert_se(memcmp(test_vectors[i].expected, res, SHA256_DIGEST_SIZE) == 0);
+        }
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index c6428e844f8ecbbfc7558da6d2e57ed3f95560a7..3904126a875f4bf0ec957be1e85e8c41942a52dd 100644 (file)
@@ -242,11 +242,13 @@ TEST_RET(unit_printf, .sd_booted = true) {
                 *user, *group, *uid, *gid, *home, *shell,
                 *tmp_dir, *var_tmp_dir;
         _cleanup_(manager_freep) Manager *m = NULL;
+        _cleanup_close_ int fd = -EBADF;
         Unit *u;
         int r;
 
         _cleanup_(unlink_tempfilep) char filename[] = "/tmp/test-unit_printf.XXXXXX";
-        assert_se(mkostemp_safe(filename) >= 0);
+        fd = mkostemp_safe(filename);
+        assert_se(fd >= 0);
 
         /* Using the specifier functions is admittedly a bit circular, but we don't want to reimplement the
          * logic a second time. We're at least testing that the hookup works. */
index 3b7d79323fee443e8892f43aea087d815009c2bc..7aa1551baf149e7d6993d14958aaf557057c4a31 100644 (file)
@@ -37,7 +37,7 @@ int server_address_new(
 
         memcpy(&a->sockaddr, sockaddr, socklen);
 
-        LIST_FIND_TAIL(addresses, n->addresses, tail);
+        tail = LIST_FIND_TAIL(addresses, n->addresses);
         LIST_INSERT_AFTER(addresses, n->addresses, tail, a);
 
         if (ret)
index 887b323d96a5373f0c0f770df993a765598f5184..e60742c39b704d7b81429fc484b9e1a03ec6933e 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "sd-daemon.h"
 #include "sd-event.h"
+#include "sd-messages.h"
 
 #include "capability-util.h"
 #include "clock-util.h"
 #include "timesyncd-manager.h"
 #include "user-util.h"
 
+static int advance_tstamp(int fd, const struct stat *st) {
+        assert_se(fd >= 0);
+        assert_se(st);
+
+        /* So here's the problem: whenever we read the timestamp we'd like to ensure the next time we won't
+         * restore the exact same time again, but one at least one step further (so that comparing mtimes of
+         * the timestamp file is a reliable check that timesync did its thing). But file systems have
+         * different timestamp accuracy: traditional fat has 2s granularity, and even ext2 and friends expose
+         * different granularity depending on selected inode size during formatting! Hence, to ensure the
+         * timestamp definitely is increased, here's what we'll do: we'll first try to increase the timestamp
+         * by 1µs, write that and read it back. If it was updated, great. But if it was not, we'll instead
+         * increase the timestamp by 10µs, and do the same, then 100µs, then 1ms, and so on, until it works,
+         * or we reach 10s. If it still didn't work then, the fs is just broken and we give up. */
+
+        usec_t target = MAX3(now(CLOCK_REALTIME),
+                             TIME_EPOCH * USEC_PER_SEC,
+                             timespec_load(&st->st_mtim));
+
+        for (usec_t a = 1; a <= 10 * USEC_PER_SEC; a *= 10) { /* 1µs, 10µs, 100µs, 1ms, … 10s */
+                struct timespec ts[2];
+                struct stat new_st;
+
+                /* Bump to the maximum of the old timestamp advanced by the specified unit,  */
+                usec_t c = usec_add(target, a);
+
+                timespec_store(&ts[0], c);
+                ts[1] = ts[0];
+
+                if (futimens(fd, ts) < 0) {
+                        /* If this doesn't work at all, log, don't fail but give up */
+                        log_warning_errno(errno, "Unable to update mtime of timestamp file, ignoring: %m");
+                        return 0;
+                }
+
+                if (fstat(fd, &new_st) < 0)
+                        return log_error_errno(errno, "Failed to stat timestamp file: %m");
+
+                if (timespec_load(&new_st.st_mtim) > target) {
+                        log_debug("Successfully bumped timestamp file.");
+                        return 1;
+                }
+
+                log_debug("Tried to advance timestamp file by " USEC_FMT ", but this didn't work, file system timestamp granularity too coarse?", a);
+        }
+
+        log_debug("Gave up trying to advance timestamp file.");
+        return 0;
+}
+
 static int load_clock_timestamp(uid_t uid, gid_t gid) {
         usec_t min = TIME_EPOCH * USEC_PER_SEC, ct;
         _cleanup_close_ int fd = -EBADF;
         int r;
 
-        /* Let's try to make sure that the clock is always
-         * monotonically increasing, by saving the clock whenever we
-         * have a new NTP time, or when we shut down, and restoring it
-         * when we start again. This is particularly helpful on
-         * systems lacking a battery backed RTC. We also will adjust
-         * the time to at least the build time of systemd. */
+        /* Let's try to make sure that the clock is always monotonically increasing, by saving the clock
+         * whenever we have a new NTP time, or when we shut down, and restoring it when we start again. This
+         * is particularly helpful on systems lacking a battery backed RTC. We also will adjust the time to
+         * at least the build time of systemd. */
 
         fd = open(CLOCK_FILE, O_RDWR|O_CLOEXEC, 0644);
-        if (fd >= 0) {
-                struct stat st;
-                usec_t stamp;
+        if (fd < 0) {
+                if (errno != ENOENT)
+                        log_debug_errno(errno, "Unable to open timestamp file '" CLOCK_FILE "', ignoring: %m");
 
-                /* check if the recorded time is later than the compiled-in one */
-                if (fstat(fd, &st) >= 0) {
-                        stamp = timespec_load(&st.st_mtim);
-                        if (stamp > min)
-                                min = stamp;
-                }
-
-                if (geteuid() == 0) {
-                        /* Try to fix the access mode, so that we can still
-                           touch the file after dropping privileges */
-                        r = fchmod_and_chown(fd, 0644, uid, gid);
-                        if (r < 0)
-                                log_warning_errno(r, "Failed to chmod or chown %s, ignoring: %m", CLOCK_FILE);
-                }
-
-        } else {
                 r = mkdir_safe_label(STATE_DIR, 0755, uid, gid,
                                      MKDIR_FOLLOW_SYMLINK | MKDIR_WARN_MODE);
-                if (r < 0) {
+                if (r < 0)
                         log_debug_errno(r, "Failed to create state directory, ignoring: %m");
-                        goto settime;
-                }
 
                 /* create stamp file with the compiled-in date */
                 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);
-        }
+        } else {
+                struct stat st;
+                usec_t stamp;
 
-settime:
-        ct = now(CLOCK_REALTIME);
-        if (ct < min) {
-                char date[FORMAT_TIMESTAMP_MAX];
+                /* check if the recorded time is later than the compiled-in one */
+                if (fstat(fd, &st) < 0)
+                        return log_error_errno(errno, "Unable to stat timestamp file '" CLOCK_FILE "': %m");
 
-                log_info("System clock time unset or jumped backwards, restoring from recorded timestamp: %s",
-                         format_timestamp(date, sizeof(date), min));
+                stamp = timespec_load(&st.st_mtim);
+                if (stamp > min)
+                        min = stamp;
+
+                /* Try to fix the access mode, so that we can still touch the file after dropping
+                 * privileges */
+                r = fchmod_and_chown(fd, 0644, uid, gid);
+                if (r < 0)
+                        log_full_errno(ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_WARNING, r,
+                                       "Failed to chmod or chown %s, ignoring: %m", CLOCK_FILE);
 
-                if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(min)) < 0)
-                        log_error_errno(errno, "Failed to restore system clock, ignoring: %m");
+                (void) advance_tstamp(fd, &st);
+        }
+
+        ct = now(CLOCK_REALTIME);
+        if (ct > min)
+                return 0;
+
+        /* Not that it matters much, but we actually restore the clock to n+1 here rather than n, simply
+         * because we read n as time previously already and we want to progress here, i.e. not report the
+         * same time again. */
+        if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(min+1)) < 0) {
+                log_warning_errno(errno, "Failed to restore system clock, ignoring: %m");
+                return 0;
         }
 
+        log_struct(LOG_INFO,
+                   "MESSAGE_ID=" SD_MESSAGE_TIME_BUMP_STR,
+                   "REALTIME_USEC=" USEC_FMT, min+1,
+                   LOG_MESSAGE("System clock time unset or jumped backwards, restored from recorded timestamp: %s",
+                               FORMAT_TIMESTAMP(min+1)));
         return 0;
 }
 
index 5f18ba35fc38b4ae9ea9564e9e8b17b96a6e4b3b..9efdb6000accc6383834f4eab2052f34a9d719df 100644 (file)
@@ -47,6 +47,7 @@ static LinkConfig* link_config_free(LinkConfig *config) {
                 return NULL;
 
         free(config->filename);
+        strv_free(config->dropins);
 
         net_match_clear(&config->match);
         condition_free_list(config->conditions);
@@ -264,7 +265,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
                         "SR-IOV\0",
                         config_item_perf_lookup, link_config_gperf_lookup,
                         CONFIG_PARSE_WARN, config, &stats_by_path,
-                        NULL);
+                        &config->dropins);
         if (r < 0)
                 return r; /* config_parse_many() logs internally. */
 
index ea9f560f45ee6c3bbb0a772d7eb9701a7e635f92..ed0896e9dee83b4b8ea085b9de0326a492ca82c4 100644 (file)
@@ -44,6 +44,7 @@ typedef struct Link {
 
 struct LinkConfig {
         char *filename;
+        char **dropins;
 
         NetMatch match;
         LIST_HEAD(Condition, conditions);
index ea7b1c5f602260a22c1cc67c6d93a19df0adfc5e..18450536b5caefb3d0c39460d88efcc6b652978b 100644 (file)
@@ -2,16 +2,19 @@
 
 #include "alloc-util.h"
 #include "device-util.h"
+#include "escape.h"
 #include "errno-util.h"
 #include "link-config.h"
 #include "log.h"
 #include "string-util.h"
+#include "strv.h"
 #include "udev-builtin.h"
 
 static LinkConfigContext *ctx = NULL;
 
 static int builtin_net_setup_link(sd_device *dev, sd_netlink **rtnl, int argc, char **argv, bool test) {
         _cleanup_(link_freep) Link *link = NULL;
+        _cleanup_free_ char *joined = NULL;
         int r;
 
         if (argc > 1)
@@ -48,6 +51,19 @@ static int builtin_net_setup_link(sd_device *dev, sd_netlink **rtnl, int argc, c
         if (link->new_name)
                 udev_builtin_add_property(dev, test, "ID_NET_NAME", link->new_name);
 
+        STRV_FOREACH(d, link->config->dropins) {
+                _cleanup_free_ char *escaped = NULL;
+
+                escaped = xescape(*d, ":");
+                if (!escaped)
+                        return log_oom();
+
+                if (!strextend_with_separator(&joined, ":", escaped))
+                        return log_oom();
+        }
+
+        udev_builtin_add_property(dev, test, "ID_NET_LINK_FILE_DROPINS", joined);
+
         return 0;
 }
 
index 1990282c75427e6678836f6485f8f7a2a1b3d6c2..1596497da893a11675d2f2258536b283eddfde0f 100644 (file)
@@ -11,6 +11,7 @@
 #include "dirent-util.h"
 #include "escape.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "format-util.h"
 #include "fs-util.h"
 #include "hexdecoct.h"
@@ -103,7 +104,8 @@ static int node_symlink(sd_device *dev, const char *devnode, const char *slink)
         return 0;
 }
 
-static int stack_directory_read_one(int dirfd, const char *id, bool is_symlink, char **devnode, int *priority) {
+static int stack_directory_read_one(int dirfd, const char *id, char **devnode, int *priority) {
+        _cleanup_free_ char *buf = NULL;
         int tmp_prio, r;
 
         assert(dirfd >= 0);
@@ -111,15 +113,14 @@ static int stack_directory_read_one(int dirfd, const char *id, bool is_symlink,
         assert(devnode);
         assert(priority);
 
-        if (is_symlink) {
-                _cleanup_free_ char *buf = NULL;
-                char *colon;
+        /* First, let's try to read the entry with the new format, which should replace the old format pretty
+         * quickly. */
 
-                /* New format. The devnode and priority can be obtained from symlink. */
+        r = readlinkat_malloc(dirfd, id, &buf);
+        if (r >= 0) {
+                char *colon;
 
-                r = readlinkat_malloc(dirfd, id, &buf);
-                if (r < 0)
-                        return r;
+                /* With the new format, the devnode and priority can be obtained from symlink itself. */
 
                 colon = strchr(buf, ':');
                 if (!colon || colon == buf)
@@ -132,7 +133,7 @@ static int stack_directory_read_one(int dirfd, const char *id, bool is_symlink,
                  * symlink will be removed during processing the event. The check is just for shortening the
                  * timespan that the symlink points to a non-existing device node. */
                 if (access(colon + 1, F_OK) < 0)
-                        return -errno;
+                        return -ENODEV;
 
                 r = safe_atoi(buf, &tmp_prio);
                 if (r < 0)
@@ -145,7 +146,7 @@ static int stack_directory_read_one(int dirfd, const char *id, bool is_symlink,
                 if (r < 0)
                         return r;
 
-        } else {
+        } else if (r == -EINVAL) { /* Not a symlink ? try the old format */
                 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
                 const char *val;
 
@@ -169,20 +170,22 @@ static int stack_directory_read_one(int dirfd, const char *id, bool is_symlink,
                 r = free_and_strdup(devnode, val);
                 if (r < 0)
                         return r;
-        }
+
+        } else
+                return r == -ENOENT ? -ENODEV : r;
 
         *priority = tmp_prio;
         return 1; /* Updated */
 }
 
-static int stack_directory_find_prioritized_devnode(sd_device *dev, const char *dirname, bool add, char **ret) {
+static int stack_directory_find_prioritized_devnode(sd_device *dev, int dirfd, bool add, char **ret) {
         _cleanup_closedir_ DIR *dir = NULL;
         _cleanup_free_ char *devnode = NULL;
         int r, priority = 0;
         const char *id;
 
         assert(dev);
-        assert(dirname);
+        assert(dirfd >= 0);
         assert(ret);
 
         /* Find device node of device with highest priority. This returns 1 if a device found, 0 if no
@@ -204,7 +207,7 @@ static int stack_directory_find_prioritized_devnode(sd_device *dev, const char *
                         return -ENOMEM;
         }
 
-        dir = opendir(dirname);
+        dir = xopendirat(dirfd, ".", O_NOFOLLOW);
         if (!dir)
                 return -errno;
 
@@ -212,22 +215,15 @@ static int stack_directory_find_prioritized_devnode(sd_device *dev, const char *
         if (r < 0)
                 return r;
 
-        FOREACH_DIRENT_ALL(de, dir, break) {
-                if (de->d_name[0] == '.')
-                        continue;
+        FOREACH_DIRENT(de, dir, break) {
 
                 /* skip ourself */
                 if (streq(de->d_name, id))
                         continue;
 
-                if (!IN_SET(de->d_type, DT_LNK, DT_REG))
-                        continue;
-
-                r = stack_directory_read_one(dirfd(dir), de->d_name, /* is_symlink = */ de->d_type == DT_LNK, &devnode, &priority);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to read '%s/%s', ignoring: %m", dirname, de->d_name);
-                        continue;
-                }
+                r = stack_directory_read_one(dirfd, de->d_name, &devnode, &priority);
+                if (r < 0 && r != -ENODEV)
+                        log_debug_errno(r, "Failed to read '%s', ignoring: %m", de->d_name);
         }
 
         *ret = TAKE_PTR(devnode);
@@ -280,38 +276,6 @@ static int stack_directory_update(sd_device *dev, int fd, bool add) {
         return 1; /* Updated. */
 }
 
-static int stack_directory_open(const char *dirname) {
-        _cleanup_close_ int fd = -EBADF;
-        int r;
-
-        assert(dirname);
-
-        r = mkdir_parents(dirname, 0755);
-        if (r < 0)
-                return r;
-
-        fd = open_mkdir_at(AT_FDCWD, dirname, O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW | O_RDONLY, 0755);
-        if (fd < 0)
-                return fd;
-
-        return TAKE_FD(fd);
-}
-
-static int stack_directory_lock(int dirfd) {
-        _cleanup_close_ int fd = -EBADF;
-
-        assert(dirfd >= 0);
-
-        fd = openat(dirfd, ".lock", O_CLOEXEC | O_NOFOLLOW | O_RDONLY | O_CREAT, 0600);
-        if (fd < 0)
-                return -errno;
-
-        if (flock(fd, LOCK_EX) < 0)
-                return -errno;
-
-        return TAKE_FD(fd);
-}
-
 size_t udev_node_escape_path(const char *src, char *dest, size_t size) {
         size_t i, j;
         uint64_t h;
@@ -385,31 +349,57 @@ static int stack_directory_get_name(const char *slink, char **ret) {
         return 0;
 }
 
-static int link_update(sd_device *dev, const char *slink, bool add) {
-        _cleanup_free_ char *dirname = NULL, *devnode = NULL;
+static int stack_directory_open(sd_device *dev, const char *slink, int *ret_dirfd, int *ret_lockfd) {
         _cleanup_close_ int dirfd = -EBADF, lockfd = -EBADF;
+        _cleanup_free_ char *dirname = NULL;
         int r;
 
         assert(dev);
         assert(slink);
+        assert(ret_dirfd);
+        assert(ret_lockfd);
 
         r = stack_directory_get_name(slink, &dirname);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to build stack directory name for '%s': %m", slink);
 
-        dirfd = stack_directory_open(dirname);
+        r = mkdir_parents(dirname, 0755);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to create stack directory '%s': %m", dirname);
+
+        dirfd = open_mkdir_at(AT_FDCWD, dirname, O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW | O_RDONLY, 0755);
         if (dirfd < 0)
                 return log_device_debug_errno(dev, dirfd, "Failed to open stack directory '%s': %m", dirname);
 
-        lockfd = stack_directory_lock(dirfd);
+        lockfd = openat(dirfd, ".lock", O_CLOEXEC | O_NOFOLLOW | O_RDONLY | O_CREAT, 0600);
         if (lockfd < 0)
-                return log_device_debug_errno(dev, lockfd, "Failed to lock stack directory '%s': %m", dirname);
+                return log_device_debug_errno(dev, errno, "Failed to create lock file for stack directory '%s': %m", dirname);
+
+        if (flock(lockfd, LOCK_EX) < 0)
+                return log_device_debug_errno(dev, errno, "Failed to place a lock on lock file for %s: %m", dirname);
+
+        *ret_dirfd = TAKE_FD(dirfd);
+        *ret_lockfd = TAKE_FD(lockfd);
+        return 0;
+}
+
+static int link_update(sd_device *dev, const char *slink, bool add) {
+        _cleanup_close_ int dirfd = -EBADF, lockfd = -EBADF;
+        _cleanup_free_ char *devnode = NULL;
+        int r;
+
+        assert(dev);
+        assert(slink);
+
+        r = stack_directory_open(dev, slink, &dirfd, &lockfd);
+        if (r < 0)
+                return r;
 
         r = stack_directory_update(dev, dirfd, add);
         if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to update stack directory '%s': %m", dirname);
+                return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink);
 
-        r = stack_directory_find_prioritized_devnode(dev, dirname, add, &devnode);
+        r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink);
         if (r > 0)
index 52bf66ab0b935d5bd3f7590a7a15a461dbe99b3c..5bd09a64d1d268e5a5cf89b656eb10ef0e4347b6 100644 (file)
@@ -579,9 +579,6 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
         } else if (streq(key, "SYMLINK")) {
                 if (attr)
                         return log_token_invalid_attr(rules, key);
-                if (op == OP_REMOVE)
-                        return log_token_invalid_op(rules, key);
-
                 if (!is_match) {
                         check_value_format_and_warn(rules, key, value, false);
                         r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL);
@@ -2313,11 +2310,17 @@ static int udev_rule_apply_token_to_event(
                         if (truncated)
                                 continue;
 
-                        r = device_add_devlink(dev, filename);
-                        if (r < 0)
-                                return log_rule_error_errno(dev, rules, r, "Failed to add devlink '%s': %m", filename);
+                        if (token->op == OP_REMOVE) {
+                                device_remove_devlink(dev, filename);
+                                log_rule_debug(dev, rules, "Dropped SYMLINK '%s'", p);
+                        } else {
+                                r = device_add_devlink(dev, filename);
+                                if (r < 0)
+                                        return log_rule_error_errno(dev, rules, r, "Failed to add devlink '%s': %m", filename);
+
+                                log_rule_debug(dev, rules, "Added SYMLINK '%s'", p);
+                        }
 
-                        log_rule_debug(dev, rules, "LINK '%s'", p);
                         p = next;
                 }
                 break;
index 1d421064d78f080ea94a26ba49b2d4f9273b814a..40ee5085a0b992a2c766715cbf2380ec24254c89 100644 (file)
 #include "device-util.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "id128-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
 #include "set.h"
+#include "static-destruct.h"
 #include "string-util.h"
 #include "strv.h"
 #include "udevadm.h"
@@ -26,17 +28,21 @@ static bool arg_verbose = false;
 static bool arg_dry_run = false;
 static bool arg_quiet = false;
 static bool arg_uuid = false;
+static bool arg_settle = false;
 
 static int exec_list(
                 sd_device_enumerator *e,
                 sd_device_action_t action,
-                Hashmap *settle_hashmap) {
+                Set **ret_settle_path_or_ids) {
 
-        bool skip_uuid_logic = false;
+        _cleanup_set_free_ Set *settle_path_or_ids = NULL;
+        int uuid_supported = -1;
         const char *action_str;
         sd_device *d;
         int r, ret = 0;
 
+        assert(e);
+
         action_str = device_action_to_string(action);
 
         FOREACH_DEVICE_AND_SUBSYSTEM(e, d) {
@@ -57,14 +63,14 @@ static int exec_list(
 
                 /* Use the UUID mode if the user explicitly asked for it, or if --settle has been specified,
                  * so that we can recognize our own uevent. */
-                r = sd_device_trigger_with_uuid(d, action, (arg_uuid || settle_hashmap) && !skip_uuid_logic ? &id : NULL);
-                if (r == -EINVAL && !arg_uuid && settle_hashmap && !skip_uuid_logic) {
+                r = sd_device_trigger_with_uuid(d, action, (arg_uuid || arg_settle) && uuid_supported != 0 ? &id : NULL);
+                if (r == -EINVAL && !arg_uuid && arg_settle && uuid_supported < 0) {
                         /* If we specified a UUID because of the settling logic, and we got EINVAL this might
                          * be caused by an old kernel which doesn't know the UUID logic (pre-4.13). Let's try
                          * if it works without the UUID logic then. */
                         r = sd_device_trigger(d, action);
                         if (r != -EINVAL)
-                                skip_uuid_logic = true; /* dropping the uuid stuff changed the return code,
+                                uuid_supported = false; /* dropping the uuid stuff changed the return code,
                                                          * hence don't bother next time */
                 }
                 if (r < 0) {
@@ -108,65 +114,96 @@ static int exec_list(
                         continue;
                 }
 
+                if (uuid_supported < 0)
+                        uuid_supported = true;
+
                 /* If the user asked for it, write event UUID to stdout */
                 if (arg_uuid)
                         printf(SD_ID128_UUID_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
 
-                if (settle_hashmap) {
-                        _cleanup_free_ sd_id128_t *mid = NULL;
-                        _cleanup_free_ char *sp = NULL;
+                if (arg_settle) {
+                        if (uuid_supported) {
+                                sd_id128_t *dup;
 
-                        sp = strdup(syspath);
-                        if (!sp)
-                                return log_oom();
+                                dup = newdup(sd_id128_t, &id, 1);
+                                if (!dup)
+                                        return log_oom();
 
-                        mid = newdup(sd_id128_t, &id, 1);
-                        if (!d)
-                                return log_oom();
+                                r = set_ensure_consume(&settle_path_or_ids, &id128_hash_ops_free, dup);
+                        } else {
+                                char *dup;
 
-                        r = hashmap_put(settle_hashmap, sp, mid);
+                                dup = strdup(syspath);
+                                if (!dup)
+                                        return log_oom();
+
+                                r = set_ensure_consume(&settle_path_or_ids, &path_hash_ops_free, dup);
+                        }
                         if (r < 0)
                                 return log_oom();
-
-                        TAKE_PTR(sp);
-                        TAKE_PTR(mid);
                 }
         }
 
+        if (ret_settle_path_or_ids)
+                *ret_settle_path_or_ids = TAKE_PTR(settle_path_or_ids);
+
         return ret;
 }
 
 static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *userdata) {
-        Hashmap *settle_hashmap = ASSERT_PTR(userdata);
-        sd_id128_t *settle_id;
+        Set *settle_path_or_ids = * (Set**) ASSERT_PTR(userdata);
         const char *syspath;
-        char *k;
+        sd_id128_t id;
         int r;
 
         assert(dev);
 
         r = sd_device_get_syspath(dev, &syspath);
         if (r < 0) {
-                log_debug_errno(r, "Failed to get syspath of device event, ignoring: %m");
+                log_device_debug_errno(dev, r, "Failed to get syspath of device event, ignoring: %m");
                 return 0;
         }
 
-        settle_id = hashmap_get2(settle_hashmap, syspath, (void**) &k);
-        if (!settle_id) {
-                log_debug("Got uevent for unexpected device '%s', ignoring.", syspath);
-                return 0;
-        }
-        if (!sd_id128_is_null(*settle_id)) { /* If this is SD_ID128_NULL then we are on pre-4.13 and have no UUID to check, hence don't */
-                sd_id128_t event_id;
+        if (sd_device_get_trigger_uuid(dev, &id) >= 0) {
+                _cleanup_free_ sd_id128_t *saved = NULL;
 
-                r = sd_device_get_trigger_uuid(dev, &event_id);
-                if (r < 0) {
-                        log_debug_errno(r, "Got uevent without synthetic UUID for device '%s', ignoring: %m", syspath);
+                saved = set_remove(settle_path_or_ids, &id);
+                if (!saved) {
+                        log_device_debug(dev, "Got uevent not matching expected UUID, ignoring.");
                         return 0;
                 }
-
-                if (!sd_id128_equal(event_id, *settle_id)) {
-                        log_debug("Got uevent not matching expected UUID for device '%s', ignoring.", syspath);
+        } else {
+                _cleanup_free_ char *saved = NULL;
+
+                saved = set_remove(settle_path_or_ids, syspath);
+                if (!saved) {
+                        const char *old_sysname;
+
+                        /* When the device is renamed, the new name is broadcast, and the old name is saved
+                         * in INTERFACE_OLD. */
+
+                        if (sd_device_get_property_value(dev, "INTERFACE_OLD", &old_sysname) >= 0) {
+                                _cleanup_free_ char *dir = NULL, *old_syspath = NULL;
+
+                                r = path_extract_directory(syspath, &dir);
+                                if (r < 0) {
+                                        log_device_debug_errno(dev, r,
+                                                               "Failed to extract directory from '%s', ignoring: %m",
+                                                               syspath);
+                                        return 0;
+                                }
+
+                                old_syspath = path_join(dir, old_sysname);
+                                if (!old_syspath) {
+                                        log_oom_debug();
+                                        return 0;
+                                }
+
+                                saved = set_remove(settle_path_or_ids, old_syspath);
+                        }
+                }
+                if (!saved) {
+                        log_device_debug(dev, "Got uevent for unexpected device, ignoring.");
                         return 0;
                 }
         }
@@ -175,12 +212,9 @@ static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *us
                 printf("settle %s\n", syspath);
 
         if (arg_uuid)
-                printf("settle " SD_ID128_UUID_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(*settle_id));
+                printf("settle " SD_ID128_UUID_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id));
 
-        free(hashmap_remove(settle_hashmap, syspath));
-        free(k);
-
-        if (hashmap_isempty(settle_hashmap))
+        if (set_isempty(settle_path_or_ids))
                 return sd_event_exit(sd_device_monitor_get_event(m), 0);
 
         return 0;
@@ -283,9 +317,9 @@ int trigger_main(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
         _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
-        _cleanup_hashmap_free_ Hashmap *settle_hashmap = NULL;
+        _cleanup_set_free_ Set *settle_path_or_ids = NULL;
         usec_t ping_timeout_usec = 5 * USEC_PER_SEC;
-        bool settle = false, ping = false;
+        bool ping = false;
         int c, r;
 
         if (running_in_chroot() > 0) {
@@ -389,7 +423,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                         break;
                 }
                 case 'w':
-                        settle = true;
+                        arg_settle = true;
                         break;
 
                 case ARG_NAME: {
@@ -477,11 +511,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                         return log_error_errno(r, "Failed to add parent match '%s': %m", argv[optind]);
         }
 
-        if (settle) {
-                settle_hashmap = hashmap_new(&path_hash_ops_free_free);
-                if (!settle_hashmap)
-                        return log_oom();
-
+        if (arg_settle) {
                 r = sd_event_default(&event);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get default event: %m");
@@ -494,7 +524,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to attach event to device monitor: %m");
 
-                r = sd_device_monitor_start(m, device_monitor_handler, settle_hashmap);
+                r = sd_device_monitor_start(m, device_monitor_handler, &settle_path_or_ids);
                 if (r < 0)
                         return log_error_errno(r, "Failed to start device monitor: %m");
         }
@@ -519,11 +549,11 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                 assert_not_reached();
         }
 
-        r = exec_list(e, action, settle_hashmap);
+        r = exec_list(e, action, arg_settle ? &settle_path_or_ids : NULL);
         if (r < 0)
                 return r;
 
-        if (event && !hashmap_isempty(settle_hashmap)) {
+        if (!set_isempty(settle_path_or_ids)) {
                 r = sd_event_loop(event);
                 if (r < 0)
                         return log_error_errno(r, "Event loop failed: %m");
index 296521aa56eb17b73b5cd54c97762ab0960e1fa4..b00b9fd9001596eae570150aa19c2ea74449a287 100755 (executable)
@@ -22,7 +22,7 @@ import tempfile
 import typing
 
 
-__version__ = '{{GIT_VERSION}}'
+__version__ = '{{PROJECT_VERSION}} ({{GIT_VERSION}})'
 
 EFI_ARCH_MAP = {
         # host_arch glob : [efi_arch, 32_bit_efi_arch if mixed mode is supported]
@@ -65,7 +65,7 @@ def shell_join(cmd):
     return ' '.join(shlex.quote(str(x)) for x in cmd)
 
 
-def path_is_readable(s: str | None) -> pathlib.Path | None:
+def path_is_readable(s: typing.Optional[str]) -> typing.Optional[pathlib.Path]:
     """Convert a filename string to a Path and verify access."""
     if s is None:
         return None
@@ -215,14 +215,14 @@ class Uname:
 class Section:
     name: str
     content: pathlib.Path
-    tmpfile: typing.IO | None = None
+    tmpfile: typing.Optional[typing.IO] = None
     flags: list[str] = dataclasses.field(default_factory=lambda: ['data', 'readonly'])
-    offset: int | None = None
+    offset: typing.Optional[int] = None
     measure: bool = False
 
     @classmethod
     def create(cls, name, contents, **kwargs):
-        if isinstance(contents, str | bytes):
+        if isinstance(contents, (str, bytes)):
             mode = 'wt' if isinstance(contents, str) else 'wb'
             tmp = tempfile.NamedTemporaryFile(mode=mode, prefix=f'tmp{name}')
             tmp.write(contents)
@@ -261,9 +261,9 @@ class Section:
 
 @dataclasses.dataclass
 class UKI:
-    executable: list[pathlib.Path|str]
+    executable: list[typing.Union[pathlib.Path, str]]
     sections: list[Section] = dataclasses.field(default_factory=list, init=False)
-    offset: int | None = dataclasses.field(default=None, init=False)
+    offset: typing.Optional[int] = dataclasses.field(default=None, init=False)
 
     def __post_init__(self):
         self.offset = round_up(pe_next_section_offset(self.executable))
@@ -426,21 +426,18 @@ def call_systemd_measure(uki, linux, opts):
 
 
 def join_initrds(initrds):
-    match initrds:
-        case []:
-            return None
-        case [initrd]:
-            return initrd
-        case multiple:
-            seq = []
-            for file in multiple:
-                initrd = file.read_bytes()
-                padding = b'\0' * round_up(len(initrd), 4)  # pad to 32 bit alignment
-                seq += [initrd, padding]
-
-            return b''.join(seq)
-
-    assert False
+    if len(initrds) == 0:
+        return None
+    elif len(initrds) == 1:
+        return initrds[0]
+
+    seq = []
+    for file in initrds:
+        initrd = file.read_bytes()
+        padding = b'\0' * round_up(len(initrd), 4)  # pad to 32 bit alignment
+        seq += [initrd, padding]
+
+    return b''.join(seq)
 
 
 def pe_validate(filename):
index 2136800ff54107e0fa6ef4a13780f1207ed6de76..49cf6845969c49717925074f0304e78a23f2b6d2 100644 (file)
@@ -171,7 +171,7 @@ Manually running a part of the Ubuntu CI test suite
 
 In some situations one may want/need to run one of the tests run by Ubuntu CI
 locally for debugging purposes. For this, you need a machine (or a VM) with
-the same Ubuntu release as is used by Ubuntu CI (Focal ATTOW).
+the same Ubuntu release as is used by Ubuntu CI (Jammy ATTOW).
 
 First of all, clone the Debian systemd repository and sync it with the code of
 the PR (set by the $UPSTREAM_PULL_REQUEST env variable) you'd like to debug:
@@ -186,18 +186,16 @@ Now install necessary build & test dependencies:
 ## PPA with some newer Ubuntu packages required by upstream systemd
 # add-apt-repository -y ppa:upstream-systemd-ci/systemd-ci
 # apt build-dep -y systemd
-# apt install -y autopkgtest debhelper genisoimage git qemu-system-x86 \
-                 libzstd-dev libfdisk-dev libtss2-dev libfido2-dev libssl-dev \
-                 python3-jinja2 zstd
+# apt install -y autopkgtest debhelper genisoimage git qemu-system-x86
 
 Build systemd deb packages with debug info:
 
-# DEB_BUILD_OPTIONS="nocheck nostrip" dpkg-buildpackage -us -uc
+# TEST_UPSTREAM=1 DEB_BUILD_OPTIONS="nocheck nostrip" dpkg-buildpackage -us -uc
 # cd ..
 
 Prepare a testbed image for autopkgtest (tweak the release as necessary):
 
-# autopkgtest-buildvm-ubuntu-cloud -v -a amd64 -r focal
+# autopkgtest-buildvm-ubuntu-cloud --ram-size 1024 -v -a amd64 -r jammy
 
 And finally run the autopkgtest itself:
 
@@ -205,7 +203,7 @@ And finally run the autopkgtest itself:
               --timeout-factor=3 \
               --test-name=boot-and-services \
               --shell-fail \
-              -- autopkgtest-virt-qemu autopkgtest-focal-amd64.img
+              -- autopkgtest-virt-qemu --ram-size 2048 autopkgtest-jammy-amd64.img
 
 where --test-name= is the name of the test you want to run/debug. The
 --shell-fail option will pause the execution in case the test fails and shows
index 7c19821ad218a8881781e09c7ad271404f4fd932..f448a4a5f1008615100e76befddf7bcc3a88ab04 100755 (executable)
@@ -20,6 +20,7 @@ test_append_files() {
         install_dmevent
         generate_module_dependencies
         inst_binary tpm2_pcrextend
+        inst_binary tpm2_pcrread
         inst_binary openssl
 }
 
index 0a5ba11f89e08e6a4439306a518236d370102288..9e7233d39bb16888f8a5e07d73ead2ef300e5f7a 100755 (executable)
@@ -992,6 +992,9 @@ DNS=127.0.0.1
         self.assertIn('nameserver 192.168.42.1\n', contents)
         self.assertIn('nameserver 127.0.0.1\n', contents)
 
+        out = subprocess.check_output(['networkctl', 'status', 'dummy0'])
+        self.assertIn(b'test.network.d/dns.conf', out)
+
     def test_dhcp_timezone(self):
         '''networkd sets time zone from DHCP'''
 
index 7f26e0a5f87e732c5ae224487327159efa3c6c43..e6c9295fc7540462c432efee1861a25e4b5b60cc 100644 (file)
@@ -1932,6 +1932,18 @@ install_dbus() {
     </policy>
 </busconfig>
 EOF
+
+    # If we run without KVM, bump the service start timeout
+    if ! get_bool "$QEMU_KVM"; then
+        cat >"$initdir/etc/dbus-1/system.d/service.timeout.conf" <<EOF
+<?xml version="1.0"?>
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+        "https://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <limit name="service_start_timeout">60000</limit>
+</busconfig>
+EOF
+    fi
 }
 
 install_user_dbus() {
index bd8843b177847727ea67775e9286e4f94c54587e..80d6bdc1d428673f339914a5fe309011cbc5ac7b 100644 (file)
@@ -5,7 +5,7 @@ Name=veth99
 [Network]
 DHCP=ipv4
 IPv6AcceptRA=no
-Address=192.168.5.250
+Address=192.168.5.250/24
 
 [DHCPv4]
 UseDomains=yes
diff --git a/test/test-network/conf/25-dhcp-client-with-static-address.network b/test/test-network/conf/25-dhcp-client-with-static-address.network
deleted file mode 100644 (file)
index 7c9d949..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Match]
-Name=veth99
-
-[Network]
-DHCP=ipv4
-IPv6AcceptRA=no
-Address=192.168.5.250
index 12c7a1391ed7e3d28118727326c199ac43df1db0..4b62a30c9c43f02c21832216fc9fd60f2901dd15 100755 (executable)
@@ -1070,6 +1070,10 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         self.assertRegex(output, r'Link File: /run/systemd/network/25-default.link')
         self.assertRegex(output, r'Network File: /run/systemd/network/11-dummy.network')
 
+        # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
+        # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
+        # Let's reprocess the interface and drop the property.
+        check_output(*udevadm_cmd, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
         output = check_output(*networkctl_cmd, '-n', '0', 'status', 'lo', env=env)
         print(output)
         self.assertRegex(output, r'Link File: n/a')
index ca84db6a032feb083d71039e5c59f7eab00d52e6..a493485fc17b9b38184e1aea50d0f80eaa1edfe3 100755 (executable)
@@ -212,12 +212,13 @@ EOF
                         {
                                 devpath         => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
                                 exp_links       => ["boot_disk1", "boot_diskXY1"],
-                                not_exp_links   => ["boot_diskXX1"],
+                                not_exp_links   => ["boot_diskXX1", "hoge"],
                         }],
                 rules           => <<EOF
 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", ATTRS{queue_depth}=="32", SYMLINK+="boot_diskXX%n"
 SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", ATTRS{queue_depth}=="1", SYMLINK+="boot_diskXY%n"
-SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", SYMLINK+="boot_disk%n"
+SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", SYMLINK+="boot_disk%n", SYMLINK+="hoge"
+SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", SYMLINK-="hoge"
 EOF
         },
         {
index ed3b39d074a3ffa4d4beae049f5fd605f01932aa..82f9fd1f629ed643505c4f0b28cc04d2a8263218 100755 (executable)
@@ -61,9 +61,7 @@ EOF
 
 udevadm control --log-priority=debug --reload --timeout=30
 
-# FIXME(?): the 'add' uevent is broadcast as for 'foobar', instead of 'hoge'. Hence, we cannot use --settle here.
-# See issue #25115.
-udevadm trigger --action=add /sys/devices/virtual/net/hoge
+udevadm trigger --action=add --settle /sys/devices/virtual/net/hoge
 udevadm wait --timeout=30 --settle /sys/devices/virtual/net/foobar
 assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/foobar)"
 timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
@@ -71,7 +69,7 @@ timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /s
 timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "active" ]]; do sleep .5; done'
 timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "active" ]]; do sleep .5; done'
 
-udevadm trigger --action=add /sys/devices/virtual/net/foobar
+udevadm trigger --action=add --settle /sys/devices/virtual/net/foobar
 udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
 assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
 timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
index 2e0350aabe394aa8fd152fcec481a139d32311d3..ee84447d90f795fec9e812aa64ce94e7e3f3c565 100755 (executable)
@@ -99,6 +99,9 @@ systemctl list-jobs --after
 systemctl list-jobs --before
 systemctl list-jobs --after --before
 systemctl list-jobs "*"
+systemctl list-dependencies sysinit.target --type=socket,mount
+systemctl list-dependencies multi-user.target --state=active
+systemctl list-dependencies sysinit.target --state=mounted --all
 
 # is-* verbs
 # Should return 4 for a missing unit file
@@ -242,6 +245,7 @@ systemctl status "systemd-*.timer"
 systemctl status "systemd-journald*.socket"
 systemctl status "sys-devices-*-ttyS0.device"
 systemctl status -- -.mount
+systemctl status 1
 
 # --marked
 systemctl restart "$UNIT_NAME"
@@ -317,9 +321,12 @@ systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO
 
 # test for sysv-generator (issue #24990)
 if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
+    # This is configurable via -Dsysvinit-path=, but we can't get the value
+    # at runtime, so let's just support the two most common paths for now.
+    [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d"
 
     # invalid dependency
-    cat >/etc/init.d/issue-24990 <<\EOF
+    cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF
 #!/bin/bash
 
 ### BEGIN INIT INFO
@@ -345,21 +352,21 @@ case "$1" in
 esac
 EOF
 
-    chmod +x /etc/init.d/issue-24990
+    chmod +x "$SYSVINIT_PATH/issue-24990"
     systemctl daemon-reload
     [[ -L /run/systemd/generator.late/test1.service ]]
     [[ -L /run/systemd/generator.late/test2.service ]]
     assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
     assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
     output=$(systemctl cat issue-24990)
-    assert_in "SourcePath=/etc/init.d/issue-24990" "$output"
+    assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
     assert_in "Description=LSB: Test" "$output"
     assert_in "After=test1.service" "$output"
     assert_in "After=remote-fs.target" "$output"
     assert_in "After=network-online.target" "$output"
     assert_in "Wants=network-online.target" "$output"
-    assert_in "ExecStart=/etc/init.d/issue-24990 start" "$output"
-    assert_in "ExecStop=/etc/init.d/issue-24990 stop" "$output"
+    assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
+    assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
     systemctl status issue-24990 || :
     systemctl show issue-24990
     assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
@@ -373,7 +380,7 @@ EOF
     systemctl stop issue-24990
 
     # valid dependency
-    cat >/etc/init.d/issue-24990 <<\EOF
+    cat >"$SYSVINIT_PATH/issue-24990" <<\EOF
 #!/bin/bash
 
 ### BEGIN INIT INFO
@@ -399,18 +406,18 @@ case "$1" in
 esac
 EOF
 
-    chmod +x /etc/init.d/issue-24990
+    chmod +x "$SYSVINIT_PATH/issue-24990"
     systemctl daemon-reload
     [[ -L /run/systemd/generator.late/test1.service ]]
     [[ -L /run/systemd/generator.late/test2.service ]]
     assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
     assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
     output=$(systemctl cat issue-24990)
-    assert_in "SourcePath=/etc/init.d/issue-24990" "$output"
+    assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
     assert_in "Description=LSB: Test" "$output"
     assert_in "After=remote-fs.target" "$output"
-    assert_in "ExecStart=/etc/init.d/issue-24990 start" "$output"
-    assert_in "ExecStop=/etc/init.d/issue-24990 stop" "$output"
+    assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
+    assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
     systemctl status issue-24990 || :
     systemctl show issue-24990
     assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
index ebe1f57b52f181e2005fc336af7b4b2f59f74056..4093c5a2a7870604f67b577f791b57ee59d2a9e8 100755 (executable)
@@ -18,7 +18,16 @@ systemd-analyze || :
 systemd-analyze time || :
 systemd-analyze blame || :
 systemd-analyze critical-chain || :
+# plot
 systemd-analyze plot >/dev/null || :
+systemd-analyze plot --json=pretty >/dev/null || :
+systemd-analyze plot --json=short >/dev/null || :
+systemd-analyze plot --json=off >/dev/null || :
+systemd-analyze plot --json=pretty --no-legend >/dev/null || :
+systemd-analyze plot --json=short --no-legend >/dev/null || :
+systemd-analyze plot --json=off --no-legend >/dev/null || :
+systemd-analyze plot --table >/dev/null || :
+systemd-analyze plot --table --no-legend >/dev/null || :
 # legacy/deprecated options (moved to systemctl, but still usable from analyze)
 systemd-analyze log-level
 systemd-analyze log-level "$(systemctl log-level)"
index d64674461f59cff8588a93051253e6f0dbf8c22c..2ab8855ddcfee02abd979e873169a18a01e7ea81 100755 (executable)
@@ -172,6 +172,36 @@ else
     echo "/usr/lib/systemd/systemd-measure or PCR sysfs files not found, skipping signed PCR policy test case"
 fi
 
+if [ -e /usr/lib/systemd/systemd-pcrphase ] && \
+       [ -f /sys/class/tpm/tpm0/pcr-sha256/11 ]; then
+
+    # Let's measure the machine ID
+    tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
+    mv /etc/machine-id /etc/machine-id.save
+    echo 994013bf23864ee7992eab39a96dd3bb >/etc/machine-id
+    SYSTEMD_FORCE_MEASURE=1 /usr/lib/systemd/systemd-pcrphase --machine-id
+    mv /etc/machine-id.save /etc/machine-id
+    tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
+
+    # And check it matches expectations
+    ( cat /tmp/oldpcr15 ;
+      echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256 ) | openssl dgst -binary -sha256 | cmp - /tmp/newpcr15
+
+    rm /tmp/oldpcr15 /tmp/newpcr15
+
+    # And similar for the boot phase measurement into PCR 11
+    tpm2_pcrread sha256:11 -Q -o /tmp/oldpcr11
+    SYSTEMD_FORCE_MEASURE=1 /usr/lib/systemd/systemd-pcrphase foobar
+    tpm2_pcrread sha256:11 -Q -o /tmp/newpcr11
+
+    ( cat /tmp/oldpcr11 ;
+      echo -n "foobar" | openssl dgst -binary -sha256 ) | openssl dgst -binary -sha256 | cmp - /tmp/newpcr11
+
+    rm /tmp/oldpcr11 /tmp/newpcr11
+else
+    echo "/usr/lib/systemd/systemd-pcrphase or PCR sysfs files not found, skipping PCR extension test case"
+fi
+
 echo OK >/testok
 
 exit 0
index 2382cccb3a03fa3e53dfdfc718d9d86672d44030..221068f284f7adbbe99fe1e83c46ccb6273143df 100755 (executable)
@@ -89,10 +89,51 @@ test_chassis() {
     fi
 }
 
+restore_sysfs_dmi() {
+    umount /sys/class/dmi/id
+    rm -rf /run/systemd/system/systemd-hostnamed.service.d
+    systemctl daemon-reload
+    systemctl stop systemd-hostnamed
+}
+
+test_firmware_date() {
+    # No DMI on s390x or ppc
+    if [[ ! -d /sys/class/dmi/id ]]; then
+        echo "/sys/class/dmi/id not found, skipping firmware date tests."
+        return 0
+    fi
+
+    trap restore_sysfs_dmi RETURN
+
+    # Ignore /sys being mounted as tmpfs
+    mkdir -p /run/systemd/system/systemd-hostnamed.service.d/
+    cat >/run/systemd/system/systemd-hostnamed.service.d/override.conf <<EOF
+[Service]
+Environment="SYSTEMD_DEVICE_VERIFY_SYSFS=0"
+EOF
+    systemctl daemon-reload
+
+    mount -t tmpfs none /sys/class/dmi/id
+    echo '1' > /sys/class/dmi/id/uevent
+
+    echo '01/01/2000' > /sys/class/dmi/id/bios_date
+    systemctl stop systemd-hostnamed
+    assert_in '2000-01-01' "$(hostnamectl)"
+
+    echo '2022' > /sys/class/dmi/id/bios_date
+    systemctl stop systemd-hostnamed
+    assert_not_in 'Firmware Date' "$(hostnamectl)"
+
+    echo 'garbage' > /sys/class/dmi/id/bios_date
+    systemctl stop systemd-hostnamed
+    assert_not_in 'Firmware Date' "$(hostnamectl)"
+}
+
 : >/failed
 
 test_hostname
 test_chassis
+test_firmware_date
 
 touch /testok
 rm /failed
index a4fda36da06d0e03015037ab7f20d00cfab81e73..4eae0f646677df1074117caf063aa797d6939759 100755 (executable)
@@ -131,9 +131,7 @@ LC_CTYPE=$i"
 
         assert_rc 0 localectl set-locale "$i"
         if [[ -f /etc/default/locale ]]; then
-            # Debian/Ubuntu patch is buggy, and LC_CTYPE= still exists.
-            assert_eq "$(cat /etc/default/locale)" "LANG=$i
-LC_CTYPE=$i"
+            assert_eq "$(cat /etc/default/locale)" "LANG=$i"
         else
             assert_eq "$(cat /etc/locale.conf)" "LANG=$i"
         fi
diff --git a/test/units/testsuite-77-netcat.service b/test/units/testsuite-77-netcat.service
deleted file mode 100644 (file)
index 8ae399a..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-77-OPENFILE
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=simple
diff --git a/test/units/testsuite-77-netcat.sh b/test/units/testsuite-77-netcat.sh
deleted file mode 100755 (executable)
index 73b4c87..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-echo "Socket" | nc -lkU /tmp/test.sock
diff --git a/test/units/testsuite-77-server.socket b/test/units/testsuite-77-server.socket
new file mode 100644 (file)
index 0000000..4305077
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=TEST-77-OPENFILE server socket
+
+[Socket]
+ListenStream=/tmp/test.sock
+Accept=yes
diff --git a/test/units/testsuite-77-server@.service b/test/units/testsuite-77-server@.service
new file mode 100644 (file)
index 0000000..8e99ac8
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-77-OPENFILE server
+
+[Service]
+ExecStart=echo "Socket"
+StandardInput=socket
+StandardOutput=socket
diff --git a/test/units/testsuite-77-socket.service b/test/units/testsuite-77-socket.service
deleted file mode 100644 (file)
index 9b6cfc6..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-77-OPENFILE
-
-[Service]
-OpenFile=/tmp/test.sock:socket:read-only
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
index 2675f054dc9176a11cf1bbc890edc810dc7ebff8..13db238ba9ce04e407fe0b6f956fbd1802d5c7cd 100755 (executable)
@@ -16,8 +16,11 @@ read -r -u 4 text
 assert_eq "$text" "File"
 
 # Test for socket
-systemctl start testsuite-77-netcat.service
-systemctl start testsuite-77-socket.service
+systemctl start testsuite-77-server.socket
+systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
+            --wait \
+            --pipe \
+            /usr/lib/systemd/tests/testdata/units/testsuite-77-client.sh
 
 # Tests for D-Bus
 diff <(systemctl show -p OpenFile testsuite-77) - <<EOF
index 79e2935a50684ff9c3f51bb7fb6b05437f5c3392..c7939a10f8147efb3384ee2f0ac4bbaeb2e482ea 100644 (file)
@@ -123,8 +123,7 @@ units = [
          'sysinit.target.wants/'],
         ['systemd-journal-gatewayd.socket',     'ENABLE_REMOTE HAVE_MICROHTTPD'],
         ['systemd-journal-remote.socket',       'ENABLE_REMOTE HAVE_MICROHTTPD'],
-        ['systemd-journald-audit.socket',       '',
-         'sockets.target.wants/'],
+        ['systemd-journald-audit.socket',       ''],
         ['systemd-journald-dev-log.socket',     '',
          'sockets.target.wants/'],
         ['systemd-journald.socket',             '',
@@ -266,6 +265,12 @@ in_units = [
          'sysinit.target.wants/'],
         ['systemd-pcrphase.service',             'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2',
          'sysinit.target.wants/'],
+        ['systemd-pcrmachine.service',           'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2',
+         'sysinit.target.wants/'],
+        ['systemd-pcrfs-root.service',           'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2'],
+        ['systemd-pcrfs@.service',               'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2'],
+        ['systemd-growfs-root.service',          ''],
+        ['systemd-growfs@.service',              ''],
 ]
 
 add_wants = []
diff --git a/units/systemd-growfs-root.service.in b/units/systemd-growfs-root.service.in
new file mode 100644 (file)
index 0000000..295bafd
--- /dev/null
@@ -0,0 +1,22 @@
+#  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=Grow Root File System
+Documentation=man:systemd-growfs-root.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=systemd-repart.service systemd-remount-fs.service
+Before=shutdown.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{ROOTLIBEXECDIR}}/systemd-growfs /
+TimeoutSec=0
diff --git a/units/systemd-growfs@.service.in b/units/systemd-growfs@.service.in
new file mode 100644 (file)
index 0000000..7154e4c
--- /dev/null
@@ -0,0 +1,23 @@
+#  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=Grow File System on %f
+Documentation=man:systemd-growfs@.service(8)
+DefaultDependencies=no
+BindsTo=%i.mount
+Conflicts=shutdown.target
+After=systemd-repart.service %i.mount
+Before=shutdown.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{ROOTLIBEXECDIR}}/systemd-growfs %f
+TimeoutSec=0
index f0c0aebc8659d70804927902a989b389b0057901..cf9b6e8b84d680e50e67b2332ae286fe4c996c03 100644 (file)
@@ -20,3 +20,7 @@ Service=systemd-journald.service
 ReceiveBuffer=128M
 ListenNetlink=audit 1
 PassCredentials=yes
+
+[Install]
+WantedBy=sockets.target
+WantedBy=systemd-journald.service
index 38ba3e28563ad80c30be46a9f8bd8b839436871b..ece872c77097f178ac3a383f9f8e165fd817dc90 100644 (file)
@@ -38,7 +38,10 @@ RestrictRealtime=yes
 RestrictSUIDSGID=yes
 RuntimeDirectory=systemd/journal
 RuntimeDirectoryPreserve=yes
-Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket
+# Audit socket is not listed here because this unit can be turned off. However
+# the link between the socket and the service units is still created thanks to
+# the 'Service=' setting specified in the socket unit.
+Sockets=systemd-journald.socket systemd-journald-dev-log.socket
 StandardOutput=null
 SystemCallArchitectures=native
 SystemCallErrorNumber=EPERM
diff --git a/units/systemd-pcrfs-root.service.in b/units/systemd-pcrfs-root.service.in
new file mode 100644 (file)
index 0000000..b0da413
--- /dev/null
@@ -0,0 +1,24 @@
+#  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=TPM2 PCR Root File System Measurement
+Documentation=man:systemd-pcrfs-root.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=systemd-pcrmachine.service
+Before=shutdown.target
+AssertPathExists=!/etc/initrd-release
+ConditionSecurity=tpm2
+ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase --file-system=/
diff --git a/units/systemd-pcrfs@.service.in b/units/systemd-pcrfs@.service.in
new file mode 100644 (file)
index 0000000..ec1ff11
--- /dev/null
@@ -0,0 +1,25 @@
+#  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=TPM2 PCR File System Measurement of %f
+Documentation=man:systemd-pcrfs@.service(8)
+DefaultDependencies=no
+BindsTo=%i.mount
+Conflicts=shutdown.target
+After=%i.mount systemd-pcrfs-root.service
+Before=shutdown.target
+AssertPathExists=!/etc/initrd-release
+ConditionSecurity=tpm2
+ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase --file-system=%f
diff --git a/units/systemd-pcrmachine.service.in b/units/systemd-pcrmachine.service.in
new file mode 100644 (file)
index 0000000..e154a7e
--- /dev/null
@@ -0,0 +1,23 @@
+#  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=TPM2 PCR Machine ID Measurement
+Documentation=man:systemd-pcrmachine.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Before=sysinit.target shutdown.target
+AssertPathExists=!/etc/initrd-release
+ConditionSecurity=tpm2
+ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase --machine-id