]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #25602 from fbuihuu/fix-TEST-73-LOCALE
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 15 Dec 2022 08:47:05 +0000 (17:47 +0900)
committerGitHub <noreply@github.com>
Thu, 15 Dec 2022 08:47:05 +0000 (17:47 +0900)
localed: reload PID1 configuration after modifying /etc/locale.conf

272 files changed:
.github/workflows/build_test.sh
.github/workflows/labeler.yml
.github/workflows/unit_tests.sh
.packit.yml
NEWS
README
TODO
coccinelle/macros.h
docs/BLOCK_DEVICE_LOCKING.md
docs/CONTRIBUTING.md
docs/ENVIRONMENT.md
docs/INITRD_INTERFACE.md
docs/PORTABLE_SERVICES.md
docs/USER_RECORD.md
hwdb.d/60-evdev.hwdb
hwdb.d/60-keyboard.hwdb
hwdb.d/60-sensor.hwdb
hwdb.d/70-pointingstick.hwdb
man/homectl.xml
man/kernel-command-line.xml
man/org.freedesktop.hostname1.xml
man/org.freedesktop.systemd1.xml
man/poweroff.xml [moved from man/halt.xml with 72% similarity]
man/repart.d.xml
man/rules/meson.build
man/sd_id128_get_machine.xml
man/systemctl.xml
man/systemd-dissect.xml
man/systemd-poweroff.service.xml [moved from man/systemd-halt.service.xml with 78% similarity]
man/systemd-repart.xml
man/systemd-sysext.xml
man/systemd-system.conf.xml
man/systemd.network.xml
man/systemd.scope.xml
man/systemd.service.xml
man/ukify.xml [new file with mode: 0644]
meson.build
meson_options.txt
mkosi.build
mkosi.conf.d/10-systemd.conf
mkosi.conf.d/fedora/10-mkosi.fedora
mkosi.kernel.config [new file with mode: 0644]
po/pt.po
rules.d/60-evdev.rules
shell-completion/bash/homectl
src/basic/alloc-util.c
src/basic/alloc-util.h
src/basic/chase-symlinks.c
src/basic/constants.h
src/basic/escape.c
src/basic/fd-util.c
src/basic/fs-util.c
src/basic/glyph-util.c
src/basic/glyph-util.h
src/basic/hashmap.c
src/basic/hexdecoct.c
src/basic/hexdecoct.h
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/basic/mountpoint-util.c
src/basic/mountpoint-util.h
src/basic/process-util.c
src/basic/process-util.h
src/basic/ratelimit.c
src/basic/socket-util.h
src/basic/stat-util.c
src/basic/umask-util.h
src/basic/unaligned.h
src/binfmt/binfmt.c
src/boot/bootctl.c
src/boot/efi/boot.c
src/boot/efi/console.c
src/boot/efi/cpio.c
src/boot/efi/missing_efi.h
src/boot/efi/part-discovery.c
src/boot/efi/secure-boot.c
src/boot/efi/util.c
src/boot/efi/util.h
src/boot/efi/vmm.c
src/busctl/busctl-introspect.c
src/core/cgroup.c
src/core/dbus-manager.c
src/core/dbus-unit.c
src/core/dbus-unit.h
src/core/dbus.c
src/core/execute.c
src/core/load-fragment-gperf.gperf.in
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/mount.c
src/core/namespace.c
src/core/selinux-access.c
src/core/selinux-setup.c
src/core/service.c
src/core/slice.c
src/core/swap.c
src/core/system.conf.in
src/core/unit.c
src/core/unit.h
src/core/user.conf.in
src/coredump/coredumpctl.c
src/cryptsetup/cryptsetup-fido2.c
src/cryptsetup/cryptsetup-fido2.h
src/cryptsetup/cryptsetup.c
src/dissect/dissect.c
src/fstab-generator/fstab-generator.c
src/fundamental/macro-fundamental.h
src/fundamental/sha256.c
src/fundamental/sha256.h
src/fundamental/string-util-fundamental.h
src/fundamental/unaligned-fundamental.h [new file with mode: 0644]
src/gpt-auto-generator/gpt-auto-generator.c
src/home/homectl.c
src/home/homework-directory.c
src/home/homework-luks.c
src/hostname/hostnamed.c
src/import/importd.c
src/journal/journald-audit.c
src/journal/journald-context.c
src/journal/journald-kmsg.c
src/journal/journald-native.c
src/journal/journald-server.c
src/journal/journald-server.h
src/journal/journald-stream.c
src/journal/journald-syslog.c
src/journal/test-journal-interleaving.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd/sd-bus/bus-common-errors.h
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-device/test-sd-device.c
src/libsystemd/sd-id128/id128-util.c
src/libsystemd/sd-id128/id128-util.h
src/libsystemd/sd-id128/sd-id128.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/journal-internal.h
src/libsystemd/sd-journal/journal-vacuum.c
src/locale/localed-util.c
src/login/systemd-user.in
src/machine-id-setup/machine-id-setup-main.c
src/machine/machine.c
src/machine/machine.h
src/machine/machined-dbus.c
src/machine/machined.c
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-dhcp6.c
src/network/networkd-setlink.c
src/network/networkd-setlink.h
src/network/networkd-util.h
src/network/test-network.c
src/nspawn/nspawn-cgroup.c
src/nspawn/nspawn-mount.c
src/nspawn/nspawn-mount.h
src/nspawn/nspawn-seccomp.c
src/nspawn/nspawn.c
src/oom/oomd-util.c
src/partition/repart.c
src/pstore/pstore.c
src/resolve/fuzz-etc-hosts.c
src/resolve/fuzz-resource-record.c [new file with mode: 0644]
src/resolve/meson.build
src/resolve/resolved-dns-rr.c
src/resolve/resolved-etc-hosts.c
src/resolve/resolved-etc-hosts.h
src/resolve/test-resolved-etc-hosts.c
src/rpm/systemd-update-helper.in
src/shared/acl-util.c
src/shared/acl-util.h
src/shared/ask-password-api.c
src/shared/base-filesystem.c
src/shared/binfmt-util.c
src/shared/binfmt-util.h
src/shared/bootspec.c
src/shared/btrfs-util.c
src/shared/copy.c
src/shared/data-fd-util.c
src/shared/data-fd-util.h
src/shared/discover-image.c
src/shared/discover-image.h
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/dns-domain.c
src/shared/dns-domain.h
src/shared/elf-util.c
src/shared/gpt.c
src/shared/gpt.h
src/shared/install.c
src/shared/json.c
src/shared/json.h
src/shared/libfido2-util.c
src/shared/loop-util.c
src/shared/loop-util.h
src/shared/machine-id-setup.c
src/shared/machine-pool.c
src/shared/machine-pool.h
src/shared/mkfs-util.c
src/shared/mount-setup.c
src/shared/mount-util.c
src/shared/mount-util.h
src/shared/reboot-util.c
src/shared/selinux-util.c
src/shared/socket-label.c
src/shared/specifier.c
src/shared/tpm2-util.c
src/shared/user-record-show.c
src/shared/user-record.c
src/shared/user-record.h
src/shared/varlink.c
src/sleep/sleep.c
src/systemctl/fuzz-systemctl-parse-argv.c
src/systemctl/systemctl-enable.c
src/systemctl/systemctl-is-active.c
src/systemctl/systemctl-is-enabled.c
src/systemctl/systemctl-sysv-compat.c
src/systemctl/systemctl-sysv-compat.h
src/systemctl/systemctl.c
src/systemctl/systemctl.h
src/test/test-condition.c
src/test/test-escape.c
src/test/test-fs-util.c
src/test/test-gpt.c
src/test/test-hexdecoct.c
src/test/test-id128.c
src/test/test-install-file.c
src/test/test-json.c
src/test/test-load-fragment.c
src/test/test-locale-util.c
src/test/test-macro.c
src/test/test-mount-util.c
src/test/test-umask-util.c
src/test/test-unit-name.c
src/timesync/timesyncd-manager.c
src/tmpfiles/tmpfiles.c
src/udev/udev-builtin-keyboard.c
src/udev/udevadm-hwdb.c
src/ukify/test/example.signing.crt.base64 [new file with mode: 0644]
src/ukify/test/example.signing.key.base64 [new file with mode: 0644]
src/ukify/test/example.tpm2-pcr-private.pem.base64 [new file with mode: 0644]
src/ukify/test/example.tpm2-pcr-private2.pem.base64 [new file with mode: 0644]
src/ukify/test/example.tpm2-pcr-public.pem.base64 [new file with mode: 0644]
src/ukify/test/example.tpm2-pcr-public2.pem.base64 [new file with mode: 0644]
src/ukify/test/meson.build [new file with mode: 0644]
src/ukify/test/setup.cfg [new file with mode: 0644]
src/ukify/test/test_ukify.py [new file with mode: 0755]
src/ukify/ukify.py [new file with mode: 0755]
src/userdb/userdbd-manager.c
src/volatile-root/volatile-root.c
test/TEST-58-REPART/test.sh
test/fuzz/fuzz-bootspec/oss-fuzz-53578 [new file with mode: 0644]
test/fuzz/fuzz-etc-hosts/oss-fuzz-47708 [new file with mode: 0644]
test/fuzz/fuzz-resource-record/crash-type-bitmap-to-json [new file with mode: 0644]
test/fuzz/fuzz-resource-record/oss-fuzz-54059 [new file with mode: 0644]
test/fuzz/fuzz-resource-record/oss-fuzz-54065 [new file with mode: 0644]
test/fuzz/fuzz-resource-record/oss-fuzz-54080 [new file with mode: 0644]
test/fuzz/fuzz-resource-record/oss-fuzz-54090 [new file with mode: 0644]
test/fuzz/fuzz-resource-record/ub-zero-length-rdata [new file with mode: 0644]
test/meson.build
test/test-bootctl-json.sh [new file with mode: 0755]
test/test-functions
test/test-network/systemd-networkd-tests.py
test/udev-test.pl
test/units/testsuite-26.sh
test/units/testsuite-50.sh
test/units/testsuite-58.sh
test/units/testsuite-59.sh
test/units/testsuite-64.sh
test/units/testsuite-65.sh
test/units/testsuite-70.sh
test/units/testsuite-75.sh
units/systemd-binfmt.service.in

index 1eed81600cc79c091bd3822c4b17ed2b8d11c992..0d18c6c18be3710f660020941d43e67140de65a7 100755 (executable)
@@ -53,6 +53,7 @@ PACKAGES=(
     python3-evdev
     python3-jinja2
     python3-lxml
+    python3-pefile
     python3-pip
     python3-pyparsing
     python3-setuptools
index 35766df59151c45b084aec4081db6031339ced55..c3b4040add3eafef667faaa20651da256126a088 100644 (file)
@@ -5,20 +5,79 @@
 name: "Pull Request Labeler"
 
 on:
-- pull_request_target
+  pull_request_target:
+    types: [opened, synchronize, reopened, ready_for_review]
+  issue_comment:
+    types: [created]
 
 permissions:
   contents: read
 
 jobs:
   triage:
-    if: github.event.repository.name != 'systemd-security'
+    if: github.repository == 'systemd/systemd'
     runs-on: ubuntu-latest
     permissions:
       pull-requests: write
     steps:
     - uses: actions/labeler@e54e5b338fbd6e6cdb5d60f51c22335fc57c401e
+      if: github.event_name == 'pull_request_target'
       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
+      with:
+        script: |
+          response = await github.rest.issues.listLabelsOnIssue({
+            issue_number: context.issue.number,
+            owner: context.repo.owner,
+            repo: context.repo.repo,
+          });
+
+          good_to_merge = [
+            "good-to-merge/waiting-for-ci 👍",
+            "good-to-merge/after-next-release",
+            "good-to-merge/with-minor-suggestions",
+            "good-to-merge/waiting-for-reporter-feedback 👍",
+          ];
+
+          if (response.data.every(l => !good_to_merge.includes(l.name))) {
+            await github.rest.issues.addLabels({
+              issue_number: context.issue.number,
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              labels: ["please-review"]
+            });
+          }
+
+          for (const label of ["reviewed/needs-rework 🔨",
+                               "ci-fails/needs-rework 🔥",
+                               "ci-failure-appears-unrelated",
+                               "needs-rebase"]) {
+            try {
+              await github.rest.issues.removeLabel({
+                issue_number: context.issue.number,
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                name: label,
+              });
+            } catch (err) {
+              if (err.status != 404) {
+                throw err;
+              }
+            }
+          }
+
+    - uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
+      if: github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/please-review')
+      with:
+        script: |
+          await github.rest.issues.addLabels({
+            issue_number: context.issue.number,
+            owner: context.repo.owner,
+            repo: context.repo.repo,
+            labels: ["please-review"]
+          })
index 9a9fbb36ab5572466189ef7b0f8cb90e99b0e96d..70ba090eb8e60424bc66b72052356c1b4df4bb64 100755 (executable)
@@ -21,6 +21,7 @@ ADDITIONAL_DEPS=(
     libzstd-dev
     perl
     python3-libevdev
+    python3-pefile
     python3-pyparsing
     rpm
     zstd
index a7502b25b40f90f5bf9168d5288e23dba00302e4..61b82d6341cefb5b63a6d528d4b5f5d2d4d3b52c 100644 (file)
@@ -32,6 +32,8 @@ actions:
     # [0] https://github.com/mesonbuild/meson/issues/7360
     # [1] https://github.com/systemd/systemd/pull/18908#issuecomment-792250110
     - 'sed -i "/^CONFIGURE_OPTS=(/a--werror" .packit_rpm/systemd.spec'
+    # Ignore unpackages standalone binaries
+    - "sed -i 's/assert False,.*/pass/' .packit_rpm/split-files.py"
 
 jobs:
 - job: copr_build
diff --git a/NEWS b/NEWS
index a295a8ebe2accff4c83cd39a093b131e6017a198..7e1e53606050dcc76d816f5ca643f760b81ee943 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,62 @@ systemd System and Service Manager
 
 CHANGES WITH 253 in spe:
 
+        Deprecations and incompatible changes
+
+        * systemctl will now warn when invoked without /proc mounted (e.g. when
+          invoked after chroot into an image without the API mount points like
+          /proc being set up.)  Operation in such an environment is not fully
+          supported.
+
+        * 'udevadm hwdb' subcommand is deprecated and will emit a warning.
+          systemd-hwdb (added in 2014) should be used instead.
+
+        * 'bootctl --json' now outputs well-formed JSON, instead of a stream
+          of newline-separated JSON objects.
+
+        * Udev rules in 60-evdev.rules have been changed to load hwdb properties
+          for all modalias patterns. Previously only the first matching pattern
+          was used. This could change what properties are assigned if the user
+          has more and less specific patterns that could match the same device,
+          but it is expected that the change will have no effect for most users.
+
+        New components:
+
+        * A tool to build, measure, and sign Unified Kernel Images (UKIs) has
+          been added. This replaces functionality provided by 'dracut --uefi'
+          and extends it with automatic calculation of offsets, insertion of
+          signed PCR policies generated by systemd-measure, support for initrd
+          concatenation, signing of the embedded Linux image and the combined
+          image with sbsign, and heuristics to autodetect the kernel uname and
+          verify the splash image.
+
+        Changes in systemd:
+
+        * Initrd environments which are not on a temporary file system (for
+          example an overlayfs combination) are now supported. Systemd will only
+          skip removal of the files in the initrd if it doesn't detect a
+          temporary file system.
+
+        * New MemoryZSwapMax= option has been added to configure
+          memory.zswap.max cgroup properties (the maximum amount of zswap used).
+
+        * Scope units now support OOMPolicy=. Login session scopes default to
+          OOMPolicy=continue, allowing login scopes to survive the oom killer
+          terminating some processes in the scope.
+
+        * systemd-fstab-generator now supports x-systemd.makefs option for
+          /sysroot (in the initrd).
+
+        Changes in udev:
+
+        * The new net naming scheme "v253" has been introduced. In the new
+          scheme, ID_NET_NAME_PATH is also set for USB devices not connected via
+          a PCI bus. This extends the converage of predictable interface names
+          in some embedded systems.
+
+          The "amba" bus path is now included in ID_NET_NAME_PATH, resulting in
+          a more informative path on some embedded systems.
+
         Changes in sd-boot, bootctl, and the Boot Loader Specification:
 
        * systemd-boot now passes its random seed directly to the kernel's RNG
@@ -14,7 +70,7 @@ CHANGES WITH 253 in spe:
          protocol or a prior seed in LINUX_EFI_RANDOM_SEED_TABLE_GUID from a
          preceding bootloader.
 
-       * The random seed stored in ESP is now refreshed whenever
+       * The random seed stored in the ESP is now refreshed whenever
          systemd-random-seed.service is run.
 
        * systemd-boot handles various seed inputs using a domain- and
@@ -32,10 +88,104 @@ CHANGES WITH 253 in spe:
          virtualized ones, and is activated in the case that the system token
          is missing from either sd-boot and sd-stub booted systems.
 
+        * systemd-boot now supports being loaded not from the ESP, for example
+          for direct kernel boot under QEMU or when embedded into the firmware.
+
+        Changes in kernel-install:
+
+        * A new "installation layout" can be configured as layout=uki. With this
+          setting, a Boot Loader Specification Type#1 entry will not be created.
+          Instead, a new kernel-install plugin 90-uki-copy.install will copy any
+          .efi files from the staging area into the boot partition. A plugin to
+          generate the UKI .efi file must be provided separately.
+
         Changes in systemctl:
 
-    * systemctl reboot has dropped support for accepting a positional argument
-      as the argument to reboot(2) syscall. Please use --reboot-argument instead.
+        * 'systemctl reboot' has dropped support for accepting a positional
+          argument as the argument to the reboot(2) syscall. Please use the
+          --reboot-argument option instead.
+
+        * 'systemctl disable' will now warn when called on units without install
+          information. A new --no-warn option has been added that silences this
+          warning.
+
+        * 'systemctl kexec' now supports XEN.
+
+        Changes in systemd-networkd and related tools:
+
+        * 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.
+
+        Changes in systemd-dissect:
+
+        * systemd-dissect gained a new option --list, to print the paths fo the
+          files and directories in the image.
+
+        * systemd-dissect gained a new option --mtree, to generate output
+          compatible with BSD mtree(5).
+
+        * systemd-dissect gained a new option --with, to execute a command in
+          the image temporarily mounted.
+
+        * systemd-dissect gained a new option --discover, to search for
+          Discoverable Disk Images (DDIs) in well-known directories. This will
+          list machine, portable service and system extension disk images.
+
+        * systemd-dissect now understands 2nd stage initrd images stored as a
+          Discoverable Disk Image (DDI).
+
+        Changes in systemd-repart:
+
+        * systemd-repart gained new options --include-partitions and
+          --exclude-partitions to filter operation on partitions by type UUID.
+          This allows systemd-repart to be used to build images in which the
+          type of one partition is set based on the contents of another
+          partition (for example when the boot partition shall include a verity
+          hash of the root partition).
+
+        * systemd-repart now supports erofs (a read-only file system similar to
+          squashfs).
+
+        Changes in systemd-homed:
+
+        * systemd-homed gained support for luksPbkdfForceIterations (the
+          intended number of iterations for the PBKDF operation on LUKS).
+
+        Changes in systemd-homenamed:
+
+        * systemd-homed now exports the contents of
+          /sys/class/dmi/id/bios_vendor and /sys/class/dmi/id/bios_date via two
+          new D-Bus properties: FirmwareVendor and FirmwareDate. This allows
+          unprivileged code to access those values.
+
+        Changes in libsystemd and shared code:
+
+        * sd-bus gained new convenience functions sd_bus_emit_signal_to(),
+          sd_bus_emit_signal_tov(), and sd_bus_message_new_signal_to().
+
+        * Detection of chroot environments now works if /proc/ is not mounted.
+          This affects systemd-detect-virt --chroot, but also means that systemd
+          tools will silently skip various operations in such an environment.
+
+        * "Lockheed Matrin Hardened Security for Intel Processors" (HS SRE)
+          virtualization is now detected.
+
+        Changes in the build system:
+
+        * Standalone variant of systemd-repart is built (if -Dstandalone=true).
+
+        * systemd-ac-power has been moved to /usr/bin/, to, for example, allow
+          scripts to conditionalize execution on AC power supply.
+
+        Changes in the documentation:
+
+        * Specifications that are not closely tied to systemd have moved to
+          https://uapi-group.org/specifications/: the Boot Loader Specification
+          and the Discoverable Partitions Specification.
+
 
 CHANGES WITH 252 🎃:
 
diff --git a/README b/README
index d8c279f9fa287d6a133b4df1aa23d808d24f9c79..b12ad3f236094338129673169fbf90105be7cdc7 100644 (file)
--- a/README
+++ b/README
@@ -207,6 +207,7 @@ REQUIREMENTS:
         docbook-xsl (optional, required for documentation)
         xsltproc    (optional, required for documentation)
         python-jinja2
+        python-pefile
         python-lxml (optional, required to build the indices)
         python >= 3.5
         meson >= 0.53.2
diff --git a/TODO b/TODO
index 4af0ffcc1fbafce2c47cd5fd87b6491330bed590..7d2c88bc45a853c2fab9b7933b05847633636adb 100644 (file)
--- a/TODO
+++ b/TODO
@@ -100,6 +100,9 @@ Deprecations and removals:
   4.3 new baseline). Then drop support for "!!" modifier for ExecStart= which
   is only supported for such old kernels.
 
+* drop support for kernels lacking memfd_create() (i.e. make 3.17 new
+  baseline), then drop all pipe() based fallbacks.
+
 * drop support for getrandom()-less kernels. (GRND_INSECURE means once kernel
   5.6 becomes our baseline). See
   https://github.com/systemd/systemd/pull/24101#issuecomment-1193966468 for
@@ -125,6 +128,35 @@ Deprecations and removals:
 
 Features:
 
+* in journald, write out a recognizable log record whenever the system clock is
+  changed ("stepped"), and in timesyncd whenever we acquire an NTP fix
+  ("slewing"). Then, in journalctl for each boot time we come across, find
+  these records, and use the structured info they include to display
+  "corrected" wallclock time, as calculted from the monotonic timestamp in the
+  log record, adjusted by the delta declared in the structured log record.
+
+* in journald: whenever we start a new journal file because the boot ID
+  changed, let's generate a recognizable log record containing info about old
+  and new new ID. Then, when displaying log stream in journalctl look for these
+  records, to be able to order them.
+
+* timesyncd: when saving/restoring clock try to take boot time into account.
+  Specifically, along with the saved clock, store the current boot ID. When
+  starting, check if the boot id matches. If so, don't do anything (we are on
+  the same boot and clock just kept running anyway). If not, then read
+  CLOCK_BOOTTIME (which started at boot), and add it to the saved clock
+  timestamp, to compensate for the time we spent booting. If EFI timestamps are
+  available, also include that in the calculation. With this we'll then only
+  miss the time spent during shutdown after timesync stopped and before the
+  system actually reset.
+
+* systemd-stub: maybe store a "boot counter" in the ESP, and pass it down to
+  userspace to allow ordering boots (for example in journalctl). The counter
+  would be monotonically increased on every boot.
+
+* systemd-sysext: for sysext DDIs picked up via EFI stub, set much stricter
+  image policy by default
+
 * systemd-dissect: maybe add "--attach" and "--detach" verbs which
   synchronously attach a DDI to a loopback device but not actually mount them.
 
@@ -963,6 +995,10 @@ Features:
   records would be stripped of all meta info, except the basic UID/name
   info. Then use this in portabled environments that do not use PrivateUsers=1.
 
+* portabled: when extracting unit files and copying to system.attached, if a
+  .p7s is available in the image, use it to protect the system.attached copy
+  with fs-verity, so that it cannot be tampered with
+
 * logind introduce two types of sessions: "heavy" and "light". The former would
   be our current sessions. But the latter would be a new type of session that
   is mostly the same but does not pull in user@.service or wait for it. Then,
index f44b3f2d26f48746e0533d2e7e6902d77510a509..adfea5fbec47bedbf0e0ff4210931ad37e9e2398 100644 (file)
@@ -43,7 +43,7 @@
 
 // src/basic/umask-util.h
 #define _cleanup_umask_
-#define RUN_WITH_UMASK(mask)                                            \
+#define WITH_UMASK(mask)                                            \
         for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \
              FLAGS_SET(_saved_umask_, S_IFMT);                          \
              _saved_umask_ &= 0777)
index 61beff0ff28192d0c637a941566c72d2af5abbad..862d6f0369d81fccc8808179a0271392c9912d9b 100644 (file)
@@ -98,3 +98,146 @@ Summarizing: it is recommended to take `LOCK_EX` BSD file locks when
 manipulating block devices in all tools that change file system block devices
 (`mkfs`, `fsck`, …) or partition tables (`fdisk`, `parted`, …), right after
 opening the node.
+
+# Example of Locking The Whole Disk
+
+The following is an example to leverage `libsystemd` infrastructure to get the whole disk of a block device and take a BSD lock on it.
+
+## Compile and Execute
+**Note that this example requires `libsystemd` version 251 or newer.**
+
+Place the code in a source file, e.g. `take_BSD_lock.c` and run the following commands:
+```
+$ gcc -o take_BSD_lock -lsystemd take_BSD_lock.c
+
+$ ./take_BSD_lock /dev/sda1
+Successfully took a BSD lock: /dev/sda
+
+$ flock -x /dev/sda ./take_BSD_lock /dev/sda1
+Failed to take a BSD lock on /dev/sda: Resource temporarily unavailable
+```
+
+## Code
+```c
+/* SPDX-License-Identifier: MIT-0 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <systemd/sd-device.h>
+#include <unistd.h>
+
+static inline void closep(int *fd) {
+    if (*fd >= 0)
+        close(*fd);
+}
+
+/**
+ * lock_whole_disk_from_devname
+ * @devname: devname of a block device, e.g., /dev/sda or /dev/sda1
+ * @open_flags: the flags to open the device, e.g., O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY
+ * @flock_operation: the operation to call flock, e.g., LOCK_EX|LOCK_NB
+ *
+ * given the devname of a block device, take a BSD lock of the whole disk
+ *
+ * Returns: negative errno value on error, or non-negative fd if the lock was taken successfully.
+ **/
+int lock_whole_disk_from_devname(const char *devname, int open_flags, int flock_operation) {
+    __attribute__((cleanup(sd_device_unrefp))) sd_device *dev = NULL;
+    sd_device *whole_dev;
+    const char *whole_disk_devname, *subsystem, *devtype;
+    int r;
+
+    // create a sd_device instance from devname
+    r = sd_device_new_from_devname(&dev, devname);
+    if (r < 0) {
+        errno = -r;
+        fprintf(stderr, "Failed to create sd_device: %m\n");
+        return r;
+    }
+
+    // if the subsystem of dev is block, but its devtype is not disk, find its parent
+    r = sd_device_get_subsystem(dev, &subsystem);
+    if (r < 0) {
+        errno = -r;
+        fprintf(stderr, "Failed to get the subsystem: %m\n");
+        return r;
+    }
+    if (strcmp(subsystem, "block") != 0) {
+        fprintf(stderr, "%s is not a block device, refusing.\n", devname);
+        return -EINVAL;
+    }
+
+    r = sd_device_get_devtype(dev, &devtype);
+    if (r < 0) {
+        errno = -r;
+        fprintf(stderr, "Failed to get the devtype: %m\n");
+        return r;
+    }
+    if (strcmp(devtype, "disk") == 0)
+        whole_dev = dev;
+    else {
+        r = sd_device_get_parent_with_subsystem_devtype(dev, "block", "disk", &whole_dev);
+        if (r < 0) {
+            errno = -r;
+            fprintf(stderr, "Failed to get the parent device: %m\n");
+            return r;
+        }
+    }
+
+    // open the whole disk device node
+    __attribute__((cleanup(closep))) int fd = sd_device_open(whole_dev, open_flags);
+    if (fd < 0) {
+        errno = -fd;
+        fprintf(stderr, "Failed to open the device: %m\n");
+        return fd;
+    }
+
+    // get the whole disk devname
+    r = sd_device_get_devname(whole_dev, &whole_disk_devname);
+    if (r < 0) {
+        errno = -r;
+        fprintf(stderr, "Failed to get the whole disk name: %m\n");
+        return r;
+    }
+
+    // take a BSD lock of the whole disk device node
+    if (flock(fd, flock_operation) < 0) {
+        r = -errno;
+        fprintf(stderr, "Failed to take a BSD lock on %s: %m\n", whole_disk_devname);
+        return r;
+    }
+
+    printf("Successfully took a BSD lock: %s\n", whole_disk_devname);
+
+    // take the fd to avoid automatic cleanup
+    int ret_fd = fd;
+    fd = -1;
+    return ret_fd;
+}
+
+int main(int argc, char **argv) {
+    if (argc != 2) {
+        fprintf(stderr, "Invalid number of parameters.\n");
+        return EXIT_FAILURE;
+    }
+
+    // try to take an exclusive and nonblocking BSD lock
+    __attribute__((cleanup(closep))) int fd =
+        lock_whole_disk_from_devname(
+            argv[1],
+            O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY,
+            LOCK_EX|LOCK_NB);
+
+    if (fd < 0)
+        return EXIT_FAILURE;
+
+    /**
+     * The device is now locked until the return below.
+     * Now you can safely manipulate the block device.
+     **/
+
+    return EXIT_SUCCESS;
+}
+```
index c106c4354446919c8b224b4c6d21da66714d528b..d980f7764e78720bb0b0a1440ebe2790db97e9fd 100644 (file)
@@ -20,9 +20,11 @@ We welcome contributions from everyone. However, please follow the following gui
 Following these guidelines makes it easier for us to process your issue, and ensures we won't close your issue right-away for being misfiled.
 
 ### Older downstream versions
+
 For older versions that are still supported by your distribution please use respective downstream tracker:
+
 * **Fedora** - [bugzilla](https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&component=systemd)
-* **RHEL/CentOS** - [bugzilla](https://bugzilla.redhat.com/) or [systemd-rhel github](https://github.com/systemd-rhel/)
+* **RHEL/CentOS stream** - [bugzilla](https://bugzilla.redhat.com/) or [systemd-rhel GitHub](https://github.com/redhat-plumbers)
 * **Debian** - [bugs.debian.org](https://bugs.debian.org/cgi-bin/pkgreport.cgi?pkg=systemd)
 
 ## Security vulnerability reports
@@ -39,18 +41,21 @@ See [reporting of security vulnerabilities](SECURITY.md).
 * After you have pushed a new version, add a comment explaining the latest changes. If you are a member of the systemd project on GitHub, remove the `reviewed/needs-rework`/`ci-fails/needs-rework`/`needs-rebase` labels.
 * If you are copying existing code from another source (eg: a compat header), please make sure the license is compatible with `LGPL-2.1-or-later`. If the license is not `LGPL-2.1-or-later`, please add a note to [`LICENSES/README.md`](https://github.com/systemd/systemd/blob/main/LICENSES/README.md).
 * If the pull request stalls without review, post a ping in a comment after some time has passed. We are always short on reviewer time, and pull requests which haven't seen any recent activity can be easily forgotten.
+* Github will automatically add the please-review label when a pull request is opened or updated. If you need
+more information after a review, you can comment `/please-review` on the pull request to have Github add the
+please-review to the pull request.
 
 ## Reviewing Pull Requests
 
 * See [filtered list of pull requests](https://github.com/systemd/systemd/pulls?q=is%3Aopen+is%3Apr+-label%3A%22reviewed%2Fneeds-rework+%F0%9F%94%A8%22+-label%3Aneeds-rebase+-label%3Agood-to-merge%2Fwith-minor-suggestions+-label%3A%22good-to-merge%2Fwaiting-for-ci+%F0%9F%91%8D%22+-label%3Apostponed+-label%3A%22needs-reporter-feedback+%E2%9D%93%22+-label%3A%22dont-merge+%F0%9F%92%A3%22+-label%3A%22ci-fails%2Fneeds-rework+%F0%9F%94%A5%22+sort%3Aupdated-desc) for requests that are ready for review.
 * After performing a review, set
 
-    * `reviewed/needs-rework` if the pull request needs significant changes
-    * `ci-fails/needs-rework` if the automatic tests fail and the failure is relevant to the pull request
-    * `ci-failure-appears-unrelated` if the test failures seem irrelevant
-    * `needs-rebase` if the pull request needs a rebase because of conflicts
-    * `good-to-merge/waiting-for-ci` if the pull request should be merged without further review
-    * `good-to-merge/with-minor-suggestions` if the pull request should be merged after an update without going through another round of reviews
+  * `reviewed/needs-rework` if the pull request needs significant changes
+  * `ci-fails/needs-rework` if the automatic tests fail and the failure is relevant to the pull request
+  * `ci-failure-appears-unrelated` if the test failures seem irrelevant
+  * `needs-rebase` if the pull request needs a rebase because of conflicts
+  * `good-to-merge/waiting-for-ci` if the pull request should be merged without further review
+  * `good-to-merge/with-minor-suggestions` if the pull request should be merged after an update without going through another round of reviews
 
 Unfortunately only members of the `systemd` organization on github can change labels.
 If your pull request is mislabeled, make a comment in the pull request and somebody will fix it.
index 01ee0655833061b58ac18767f0399992734ea9aa..604d1a6232ab2d576ba7da7fcd5523d6a32edcd2 100644 (file)
@@ -283,6 +283,13 @@ All tools:
   type as unsupported may not prevent loading some units of that type if they
   are referenced by other units of another supported type.
 
+* `$SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST` — can be set to override the mount
+  units burst rate limit for parsing `/proc/self/mountinfo`. On a system with
+  few resources but many mounts the rate limit may be hit, which will cause the
+  processing of mount units to stall. The burst limit may be adjusted when the
+  default is not appropriate for a given system. Defaults to `5`, accepts
+  positive integers.
+
 `systemd-remount-fs`:
 
 * `$SYSTEMD_REMOUNT_ROOT_RW=1` — if set and no entry for the root directory
index 6a54f9f7a6f353f30cad884c2c790d6da8ad80a9..de91ba73958fa1959238c1b8c5dbd34f56d8171d 100644 (file)
@@ -21,7 +21,7 @@ ArchLinux initrds.
 
 * The initrd should mount `/run/` as a tmpfs and pass it pre-mounted when
   jumping into the main system when executing systemd. The mount options should
-  be `mode=755,nodev,nosuid,strictatime`.
+  be `mode=0755,nodev,nosuid,strictatime`.
 
 * It's highly recommended that the initrd also mounts `/usr/` (if split off) as
   appropriate and passes it pre-mounted to the main system, to avoid the
index 7a9c7f512df24655497cd22bf5b3864f878ee773..93c55ac5eea68c234661a0c07a26fc35e521b83e 100644 (file)
@@ -223,8 +223,8 @@ images minimal.
 
 If the image is writable, and some of the files or directories that are
 overmounted from the host do not exist yet they will be automatically created.
-On read-only, immutable images (e.g. squashfs images) all files and directories
-to over-mount must exist already.
+On read-only, immutable images (e.g. `erofs` or `squashfs` images) all files
+and directories to over-mount must exist already.
 
 Note that as no new image format or metadata is defined, it's very
 straightforward to define images than can be made use of in a number of
index f330e8b13a77d88ec1d7664823b0b9e4a762e4c8..a00bec2353e9ac4e974489cd96fa53744989662b 100644 (file)
@@ -490,9 +490,12 @@ the PBKDF operation for the LUKS storage mechanism.
 
 `luksPbkdfType` → A string, indicating the PBKDF type to use for the LUKS storage mechanism.
 
+`luksPbkdfForceIterations` → An unsigned 64bit integer, indicating the intended
+number of iterations for the PBKDF operation, when LUKS storage is used.
+
 `luksPbkdfTimeCostUSec` → An unsigned 64bit integer, indicating the intended
 time cost for the PBKDF operation, when the LUKS storage mechanism is used, in
-µs.
+µs. Ignored when `luksPbkdfForceIterations` is set.
 
 `luksPbkdfMemoryCost` → An unsigned 64bit integer, indicating the intended
 memory cost for the PBKDF operation, when LUKS storage is used, in bytes.
@@ -737,7 +740,7 @@ that may be used in this section are identical to the equally named ones in the
 `gid`, `memberOf`, `fileSystemType`, `partitionUuid`, `luksUuid`,
 `fileSystemUuid`, `luksDiscard`, `luksOfflineDiscard`, `luksCipher`,
 `luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`,
-`luksPbkdfType`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
+`luksPbkdfType`, `luksPbkdfForceIterations`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
 `luksPbkdfParallelThreads`, `luksSectorSize`, `autoResizeMode`, `rebalanceWeight`,
 `rateLimitIntervalUSec`, `rateLimitBurst`, `enforcePasswordPolicy`,
 `autoLogin`, `stopDelayUSec`, `killProcesses`, `passwordChangeMinUSec`,
index 8902b8b163cf564aca6c24f0424a1fd129d6bfbe..3fbe7ca8e728a198e43976482effb811fc6cf443 100644 (file)
@@ -1,11 +1,34 @@
 # This file is part of systemd.
 #
+# ########################### MATCHING #######################################
+#
 # The lookup keys are composed in:
 #   60-evdev.rules
 #
-# Match string formats:
-# evdev:<modalias>
-# evdev:name:<device name>:dmi:<dmi string>
+# Supported hardware matches are:
+#  - Generic input devices match:
+#      evdev:input:bZZZZvYYYYpXXXXeWWWW-VVVV
+#    This matches on the kernel modalias of the input-device, mainly:
+#    ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and
+#    WWWW are the 4-digit hex uppercase vendor, product and version ID and VVVV
+#    is a variable-length input-modalias describing the device capabilities.
+#    The vendor, product and version ID for a device node "eventX" is listed
+#    in /sys/class/input/eventX/device/id.
+#
+#  - Input driver device name and DMI data match:
+#      evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
+#    <input device name> is the name device specified by the
+#    driver, <vendor> is the firmware-provided string exported
+#    by the kernel DMI modalias, see /sys/class/dmi/id/modalias.
+#
+#  - Extended input driver device name, properties and DMI data match:
+#      evdev:name:<input device name>:phys:<phys>:ev:<ev>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
+#    <input device name> is the name device specified by the
+#    driver, <phys> is the physical-device-path, "cat
+#    /sys/class/input/input?/phys", <ev> is the event bitmask, "cat
+#    /sys/class/input/input?/capabilities/ev" and <vendor> is the
+#    firmware-provided string exported by the kernel DMI modalias,
+#    see /sys/class/dmi/id/modalias.
 #
 # To add local entries, create a new file
 #   /etc/udev/hwdb.d/61-evdev-local.hwdb
@@ -711,6 +734,17 @@ evdev:input:b0003v6161p4D15*
  EVDEV_ABS_00=::152
  EVDEV_ABS_01=::244
 
+#########################################
+# Packard Bell
+#########################################
+
+# EASYNOTE_TS11HR-200GE
+evdev:name:ETPS/2 Elantech Touchpad:dmi:bvnPackardBell:bvr*:br*:svnPackardBell:pnEasyNoteTS11HR:*
+ EVDEV_ABS_00=0:2472:31
+ EVDEV_ABS_01=-524:528:31
+ EVDEV_ABS_35=0:2472:31
+ EVDEV_ABS_36=-524:528:31
+
 ###########################################################
 # Pine64
 ###########################################################
index 9707132beecaea6ec8b8d8272efe97d2a8b0b2bd..9cfc2fb4ecfb0bf0cb98d4121dda7022f00efa5d 100644 (file)
 #    This matches on the kernel modalias of the input-device, mainly:
 #    ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and
 #    WWWW are the 4-digit hex uppercase vendor, product and version ID and VVVV
-#    is an arbitrary length input-modalias describing the device capabilities.
+#    is a variable-length input-modalias describing the device capabilities.
 #    The vendor, product and version ID for a device node "eventX" is listed
 #    in /sys/class/input/eventX/device/id.
 #
 #  - AT keyboard DMI data matches:
 #      evdev:atkbd:dmi:bvn*:bvr*:bd*:svn<vendor>:pn<product>:pvr*
 #    <vendor> and <product> are the firmware-provided strings
-#    exported by the kernel DMI modalias, see /sys/class/dmi/id/modalias
+#    exported by the kernel DMI modalias, see /sys/class/dmi/id/modalias.
 #
 #  - Input driver device name and DMI data match:
 #      evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
 #    /sys/class/input/input?/phys", <ev> is the event bitmask, "cat
 #    /sys/class/input/input?/capabilities/ev" and <vendor> is the
 #    firmware-provided string exported by the kernel DMI modalias,
-#    see /sys/class/dmi/id/modalias
+#    see /sys/class/dmi/id/modalias.
+#
+# To add local entries, create a new file
+#   /etc/udev/hwdb.d/61-keyboard-local.hwdb
+# and add your rules there. To load the new rules execute (as root):
+#   systemd-hwdb update
+#   udevadm trigger /dev/input/eventXX
+# where /dev/input/eventXX is the keyboard in question. If in doubt, simply use
+# /dev/input/event* to reload all input rules.
+#
+# If your changes are generally applicable, preferably send them as a pull
+# request to
+#   https://github.com/systemd/systemd
+# or create a bug report on https://github.com/systemd/systemd/issues and
+# include your new rules, a description of the device, and the output of
+#   udevadm info /dev/input/eventXX.
 
 # ######################### KEY MAPPING ######################################
 #
 # Examples of such devices: Chromebooks where the top row is used for both
 # media and F1-F10 keys.
 
-# To update this file, create a new file
-#   /etc/udev/hwdb.d/70-keyboard.hwdb
-# and add your rules there. To load the new rules execute (as root):
-#   systemd-hwdb update
-#   udevadm trigger /dev/input/eventXX
-# where /dev/input/eventXX is the keyboard in question. If in
-# doubt, simply reload all input rules
-#   udevadm trigger --verbose --sysname-match="event*"
-#
-# If your changes are generally applicable, preferably send them as a pull
-# request to
-#   https://github.com/systemd/systemd
-# or create a bug report on https://github.com/systemd/systemd/issues and
-# include your new rules, a description of the device, and the output of
-#   udevadm info /dev/input/eventXX.
-
 ##########################################
 # Acer
 ##########################################
@@ -2011,6 +2010,8 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnViewSonic:pnVPAD10:*
 ###########################################################
 # Positivo-Vaio
 ###########################################################
+# Vaio Pro (VJPW11F11X)
+evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:bvr*:bd*:svnPositivoBahia-VAIO:pnVJPW11F11X*:pvr*:*
 # Vaio FE14 (VJFE41F11X, VJE42F11X, VJFE44F11X, VJFE54F11X)
 evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:bvr*:bd*:svnPositivoBahia-VAIO:pnVJFE*:pvr*:*
  KEYBOARD_KEY_76=f21                                   # Fn+F1 toggle touchpad
@@ -2019,7 +2020,7 @@ evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:bvr*:bd*:svnPositivoBahia-VAIO:
 # Positivo
 ###########################################################
 # Positivo DUO (k116)
-evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:svnPositivoTecnologiaSA:pn*:pvr*:rvnPositivoTecnologiaSA:rnK116* 
+evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:svnPositivoTecnologiaSA:pn*:pvr*:rvnPositivoTecnologiaSA:rnK116*
  KEYBOARD_KEY_76=f21                                    # Fn+F1 toggle touchpad
 
 # Positivo Motion (N14DP6, N14DP7, N14DP7-V2, N14DP9, N14JP6, N14KP6)
index ea6b3f045282c164a8f3ae0444200884b27f5a24..07b1c1625e70be5d0a85e72b68326abcdbf4bcb5 100644 (file)
@@ -283,6 +283,13 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo*:pnC3W6_AP108_4GB:*
 sensor:modalias:acpi:KIOX000A*:dmi:*:svnConnect:pnTablet9:*
  ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
 
+#########################################
+# CSL Computer
+#########################################
+# CSL Panther Tab HD
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnCSL*Computer*:pnCSL*Panther*Tab*HD:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
 #########################################
 # Cube
 #########################################
@@ -339,6 +346,8 @@ sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0B09:*
 sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0B0B:*
 sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0B0D:*
 sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0B11:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0C00:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0C02:*
  ACCEL_LOCATION=base
 
 # Dell Venue 8 Pro 3845
@@ -978,6 +987,9 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnUMAX:pnVisionBook10WiPro:*
 sensor:modalias:acpi:SMO8500*:dmi:*:svnUMAX:pnVisionBook10WiPlus:*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
+sensor:modalias:acpi:MXC6655*:dmi:*:svnUMAX:pnVisionbook12WrTab:*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+
 #########################################
 # Voyo
 #########################################
index 00f28c60340871c30d24ac2bacc5f68e340bd7c2..a78d08817339f8e92874709b3b14a1e2db2d0ca2 100644 (file)
@@ -1,8 +1,10 @@
 # This file is part of systemd.
 #
 # Pointingstick const-accel configuration, to make different brand / model
-# laptop pointingsticks have the same speed / feel, and per model adjustment
-# of the IBM TrackPoint driver's sensitivity setting
+# laptop pointingsticks have the same speed / feel, and per model adjustment of
+# the IBM TrackPoint and Dell DualPoint Stick driver's sensitivity setting.
+#
+# ########################### MATCHING #######################################
 #
 # The lookup keys are composed in:
 #   60-evdev.rules
@@ -13,7 +15,7 @@
 #    This matches on the kernel modalias of the input-device, mainly:
 #    ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and
 #    WWW are the 4-digit hex uppercase vendor, product and version ID and VVVV
-#    is an arbitrary length input-modalias describing the device capabilities.
+#    is a variable-length input-modalias describing the device capabilities.
 #    The vendor, product and version ID for a device node "eventX" is listed
 #    in /sys/class/input/eventX/device/id.
 #
 #      evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*:*
 #    <input device name> is the name device specified by the driver,
 #    <vendor> is the firmware-provided string from the kernel DMI modalias,
-#    see /sys/class/dmi/id/modalias
+#    see /sys/class/dmi/id/modalias.
+#
+#  - Extended input driver device name, properties and DMI data match:
+#      evdev:name:<input device name>:phys:<phys>:ev:<ev>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
+#    <input device name> is the name device specified by the
+#    driver, <phys> is the physical-device-path, "cat
+#    /sys/class/input/input?/phys", <ev> is the event bitmask, "cat
+#    /sys/class/input/input?/capabilities/ev" and <vendor> is the
+#    firmware-provided string exported by the kernel DMI modalias,
+#    see /sys/class/dmi/id/modalias.
 #
 # To add local entries, create a new file
 #   /etc/udev/hwdb.d/71-pointingstick-local.hwdb
 # and add your rules there. To load the new rules execute (as root):
 #   systemd-hwdb update
 #   udevadm trigger /dev/input/eventXX
-# where /dev/input/eventXX is the pointingstick in question. If in
-# doubt, simply use /dev/input/event* to reload all input rules.
+# where /dev/input/eventXX is the pointingstick in question. If in doubt, simply
+# use /dev/input/event* to reload all input rules.
 #
 # If your changes are generally applicable, preferably send them as a pull
 # request to
@@ -79,6 +90,7 @@
 # Generic
 ##########################################
 evdev:name:*[tT]rack[pP]oint*:*
+evdev:name:*[dD]ual[pP]oint [sS]tick*:*
  ID_INPUT_POINTINGSTICK=1
 
 #########################################
index 1064f056504c5153764000c34bd3e32c9e437847..634b95a081c95e529d189a2419de71e3a923b182 100644 (file)
         <term><option>--luks-volume-key-size=</option><replaceable>BYTES</replaceable></term>
         <term><option>--luks-pbkdf-type=</option><replaceable>TYPE</replaceable></term>
         <term><option>--luks-pbkdf-hash-algorithm=</option><replaceable>ALGORITHM</replaceable></term>
+        <term><option>--luks-pbkdf-force-iterations=</option><replaceable>ITERATIONS</replaceable></term>
         <term><option>--luks-pbkdf-time-cost=</option><replaceable>SECONDS</replaceable></term>
         <term><option>--luks-pbkdf-memory-cost=</option><replaceable>BYTES</replaceable></term>
         <term><option>--luks-pbkdf-parallel-threads=</option><replaceable>THREADS</replaceable></term>
index 368783d6feef9ae13d298626c71b6576033cbc3e..fcab0a90f40a6d3ec5c155eaf05200733467bac0 100644 (file)
@@ -72,6 +72,8 @@
         <term><varname>systemd.machine_id=</varname></term>
         <term><varname>systemd.set_credential=</varname></term>
         <term><varname>systemd.import_credentials=</varname></term>
+        <term><varname>systemd.reload_limit_interval_sec=</varname></term>
+        <term><varname>systemd.reload_limit_burst=</varname></term>
         <listitem>
           <para>Parameters understood by the system and service
           manager to control system behavior. For details, see
index 8e63b7188ae2367b00df01a0f7f9813ede495f32..02d7063e582e0d3f3bd680fdfd9fef5c5d7aa1e4 100644 (file)
@@ -89,6 +89,10 @@ node /org/freedesktop/hostname1 {
       readonly s HardwareModel = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s FirmwareVersion = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s FirmwareVendor = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s FirmwareDate = '...';
   };
   interface org.freedesktop.DBus.Peer { ... };
   interface org.freedesktop.DBus.Introspectable { ... };
@@ -104,6 +108,10 @@ node /org/freedesktop/hostname1 {
 
     <!--property FirmwareVersion is not documented!-->
 
+    <!--property FirmwareVendor is not documented!-->
+
+    <!--property FirmwareDate is not documented!-->
+
     <!--Autogenerated cross-references for systemd.directives, do not edit-->
 
     <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.hostname1"/>
@@ -166,6 +174,10 @@ node /org/freedesktop/hostname1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="FirmwareVersion"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="FirmwareVendor"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FirmwareDate"/>
+
     <!--End of Autogenerated section-->
 
     <para>Whenever the hostname or other metadata is changed via the daemon,
index 5ebb0930824697c0ed3c3f67fdd0664e1b526532..4f8936e86671ba7b19a427b66e6ea440a2399248 100644 (file)
@@ -209,6 +209,10 @@ node /org/freedesktop/systemd1 {
       DisableUnitFilesWithFlags(in  as files,
                                 in  t flags,
                                 out a(sss) changes);
+      DisableUnitFilesWithFlagsAndInstallInfo(in  as files,
+                                              in  t flags,
+                                              out b carries_install_info,
+                                              out a(sss) changes);
       ReenableUnitFiles(in  as files,
                         in  b runtime,
                         in  b force,
@@ -916,6 +920,8 @@ node /org/freedesktop/systemd1 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="DisableUnitFilesWithFlags()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="DisableUnitFilesWithFlagsAndInstallInfo()"/>
+
     <variablelist class="dbus-method" generated="True" extra-ref="ReenableUnitFiles()"/>
 
     <variablelist class="dbus-method" generated="True" extra-ref="LinkUnitFiles()"/>
@@ -1417,7 +1423,7 @@ node /org/freedesktop/systemd1 {
       enabled for runtime only (true, <filename>/run/</filename>), or persistently (false,
       <filename>/etc/</filename>). The second one controls whether symlinks pointing to other units shall be
       replaced if necessary. This method returns one boolean and an array of the changes made. The boolean
-      signals whether the unit files contained any enablement information (i.e. an [Install]) section. The
+      signals whether the unit files contained any enablement information (i.e. an [Install] section). The
       changes array consists of structures with three strings: the type of the change (one of
       <literal>symlink</literal> or <literal>unlink</literal>), the file name of the symlink and the
       destination of the symlink. Note that most of the following calls return a changes list in the same
@@ -1440,6 +1446,11 @@ node /org/freedesktop/systemd1 {
       replaced if necessary. <varname>SD_SYSTEMD_UNIT_PORTABLE</varname> will add or remove the symlinks in
       <filename>/etc/systemd/system.attached</filename> and <filename>/run/systemd/system.attached</filename>.</para>
 
+      <para><function>DisableUnitFilesWithFlagsAndInstallInfo()</function> is similar to
+      <function>DisableUnitFilesWithFlags()</function> and takes the same arguments, but returns
+      a boolean to indicate whether the unit files contain any enablement information, like
+      <function>EnableUnitFiles()</function>. The changes made are still returned in an array.</para>
+
       <para>Similarly, <function>ReenableUnitFiles()</function> applies the changes to one or more units that
       would result from disabling and enabling the unit quickly one after the other in an atomic
       fashion. This is useful to apply updated [Install] information contained in unit files.</para>
similarity index 72%
rename from man/halt.xml
rename to man/poweroff.xml
index c13ee5c1448507334894411225093b3596814b3c..2841dc77690d7dd105e63ea1b7e1644bd2d7fcdb 100644 (file)
@@ -3,37 +3,37 @@
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
 
-<refentry id="halt"
+<refentry id="poweroff"
     xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
-    <title>halt</title>
+    <title>poweroff</title>
     <productname>systemd</productname>
   </refentryinfo>
 
   <refmeta>
-    <refentrytitle>halt</refentrytitle>
+    <refentrytitle>poweroff</refentrytitle>
     <manvolnum>8</manvolnum>
   </refmeta>
 
   <refnamediv>
-    <refname>halt</refname>
     <refname>poweroff</refname>
     <refname>reboot</refname>
-    <refpurpose>Halt, power-off or reboot the machine</refpurpose>
+    <refname>halt</refname>
+    <refpurpose>Power off, reboot, or halt the machine</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <cmdsynopsis>
-      <command>halt</command>
+      <command>poweroff</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
     </cmdsynopsis>
     <cmdsynopsis>
-      <command>poweroff</command>
+      <command>reboot</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
     </cmdsynopsis>
     <cmdsynopsis>
-      <command>reboot</command>
+      <command>halt</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -41,8 +41,8 @@
   <refsect1>
     <title>Description</title>
 
-    <para><command>halt</command>, <command>poweroff</command>, <command>reboot</command> may be used to
-    halt, power-off, or reboot the machine. All three commands take the same options.</para>
+    <para><command>poweroff</command>, <command>reboot</command>, and <command>halt</command> may be used to
+    power off, reboot, or halt the machine. All three commands take the same options.</para>
 
   </refsect1>
 
@@ -69,7 +69,7 @@
         <term><option>-p</option></term>
         <term><option>--poweroff</option></term>
 
-        <listitem><para>Power-off the machine, when either <command>halt</command>
+        <listitem><para>Power off the machine, when either <command>halt</command>
         or <command>poweroff</command> is invoked. This option is ignored when
         <command>reboot</command> is invoked.</para></listitem>
       </varlistentry>
         <term><option>--force</option></term>
 
         <listitem>
-          <para>Force immediate halt, power-off, reboot. If specified, the command does not contact the init
-          system. In most cases, filesystems are not properly unmounted before shutdown. For example, the
-          command <command>reboot -f</command> is mostly equivalent to <command>systemctl reboot -ff</command>,
-          instead of <command>systemctl reboot -f</command>.</para>
+          <para>Force immediate power-off, halt, or reboot. If specified, the command does not contact the
+          init system. In most cases, filesystems are not properly unmounted before shutdown. For example,
+          the command <command>reboot -f</command> is mostly equivalent to
+          <command>systemctl reboot -ff</command>, instead of <command>systemctl reboot -f</command>.
+          </para>
         </listitem>
       </varlistentry>
 
         <term><option>-w</option></term>
         <term><option>--wtmp-only</option></term>
 
-        <listitem><para>Only write wtmp shutdown entry, do not
-        actually halt, power-off, reboot.</para></listitem>
+        <listitem><para>Only write wtmp shutdown entry, do not actually power off, reboot, or halt.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>-d</option></term>
         <term><option>--no-wtmp</option></term>
 
-        <listitem><para>Do not write wtmp shutdown
-        entry.</para></listitem>
+        <listitem><para>Do not write wtmp shutdown entry.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>-n</option></term>
         <term><option>--no-sync</option></term>
 
-        <listitem><para>Don't sync hard disks/storage media before
-        halt, power-off, reboot.</para></listitem>
+        <listitem><para>Don't sync hard disks/storage media before power-off, reboot, or halt.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--no-wall</option></term>
 
-        <listitem><para>Do not send wall message before halt,
-        power-off, reboot.</para></listitem>
+        <listitem><para>Do not send wall message before power-off, reboot, or halt.</para></listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
   <refsect1>
     <title>Exit status</title>
 
-    <para>On success, 0 is returned, a non-zero failure code
-    otherwise.</para>
+    <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
   </refsect1>
 
   <refsect1>
     <title>Notes</title>
 
     <para>These commands are implemented in a way that preserves basic compatibility with the original SysV
-    commands.  <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    verbs <command>halt</command>, <command>poweroff</command>, <command>reboot</command> provide the same
+    commands. <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    verbs <command>poweroff</command>, <command>reboot</command>, <command>halt</command> provide the same
     functionality with some additional features.</para>
 
     <para>Note that on many SysV systems <command>halt</command> used to be synonymous to
index fe3de8d58a4678bd3be9a5816c735c45b840c497..1da38a4e91b7d52648462037894ac6e1f749b9ac 100644 (file)
         <term><varname>Format=</varname></term>
 
         <listitem><para>Takes a file system name, such as <literal>ext4</literal>, <literal>btrfs</literal>,
-        <literal>xfs</literal>, <literal>vfat</literal>, <literal>squashfs</literal>, or the special value
-        <literal>swap</literal>. If specified and the partition is newly created it is formatted with the
-        specified file system (or as swap device). The file system UUID and label are automatically derived
-        from the partition UUID and label. If this option is used, the size allocation algorithm is slightly
-        altered: the partition is created as least as big as required for the minimal file system of the
-        specified type (or 4KiB if the minimal size is not known).</para>
+        <literal>xfs</literal>, <literal>vfat</literal>, <literal>erofs</literal>,
+        <literal>squashfs</literal> or the special value <literal>swap</literal>. If specified and the partition
+        is newly created it is formatted with the specified file system (or as swap device). The file system
+        UUID and label are automatically derived from the partition UUID and label. If this option is used,
+        the size allocation algorithm is slightly altered: the partition is created as least as big as
+        required for the minimal file system of the specified type (or 4KiB if the minimal size is not
+        known).</para>
 
         <para>This option has no effect if the partition already exists.</para>
 
       <varlistentry>
         <term><varname>Minimize=</varname></term>
 
-        <listitem><para>Takes a boolean. Disabled by default. If enabled, the partition is created at least
-        as big as required for the minimal file system of the type specified by <varname>Format=</varname>,
-        taking into account the sources configured with  <varname>CopyFiles=</varname>. Note that unless the
-        filesystem is a read-only filesystem, <command>systemd-repart</command> will have to populate the
-        filesystem twice, so enabling this option might slow down repart when populating large partitions.
+        <listitem><para>Takes one of <literal>off</literal>, <literal>best</literal>, and
+        <literal>guess</literal> (alternatively, also accepts a boolean value, which is mapped to
+        <literal>off</literal> when false, and <literal>best</literal> when true). Defaults to
+        <literal>off</literal>. If set to <literal>best</literal>, the partition will have the minimal size
+        required to store the sources configured with <varname>CopyFiles=</varname>. <literal>best</literal>
+        is currently only supported for read-only filesystems. If set to <literal>guess</literal>, the
+        partition is created at least as big as required to store the sources configured with
+        <varname>CopyFiles=</varname>. Note that unless the filesystem is a read-only filesystem,
+        <command>systemd-repart</command> will have to populate the filesystem twice to guess the minimal
+        required size, so enabling this option might slow down repart when populating large partitions.
         </para></listitem>
       </varlistentry>
     </variablelist>
index ac4196e548e48f12f139a7e5caf613284c231d49..b1a20d38dd270065e226b145240c1afa4c6aaf1c 100644 (file)
@@ -18,7 +18,6 @@ manpages = [
   'ENABLE_RESOLVE'],
  ['environment.d', '5', [], 'ENABLE_ENVIRONMENT_D'],
  ['file-hierarchy', '7', [], ''],
- ['halt', '8', ['poweroff', 'reboot'], ''],
  ['homectl', '1', [], 'ENABLE_HOMED'],
  ['homed.conf', '5', ['homed.conf.d'], 'ENABLE_HOMED'],
  ['hostname', '5', [], ''],
@@ -67,6 +66,7 @@ manpages = [
  ['pam_systemd', '8', [], 'HAVE_PAM'],
  ['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'],
  ['portablectl', '1', [], 'ENABLE_PORTABLED'],
+ ['poweroff', '8', ['halt', 'reboot'], ''],
  ['pstore.conf', '5', ['pstore.conf.d'], 'ENABLE_PSTORE'],
  ['repart.d', '5', [], 'ENABLE_REPART'],
  ['resolvectl', '1', ['resolvconf'], 'ENABLE_RESOLVE'],
@@ -897,13 +897,6 @@ manpages = [
  ['systemd-fstab-generator', '8', [], ''],
  ['systemd-getty-generator', '8', [], ''],
  ['systemd-gpt-auto-generator', '8', [], 'HAVE_BLKID'],
- ['systemd-halt.service',
-  '8',
-  ['systemd-kexec.service',
-   'systemd-poweroff.service',
-   'systemd-reboot.service',
-   'systemd-shutdown'],
-  ''],
  ['systemd-hibernate-resume-generator', '8', [], 'ENABLE_HIBERNATE'],
  ['systemd-hibernate-resume@.service',
   '8',
@@ -978,6 +971,13 @@ manpages = [
    'systemd-pcrphase-sysinit.service'],
   'HAVE_GNU_EFI'],
  ['systemd-portabled.service', '8', ['systemd-portabled'], 'ENABLE_PORTABLED'],
+ ['systemd-poweroff.service',
+  '8',
+  ['systemd-halt.service',
+   'systemd-kexec.service',
+   'systemd-reboot.service',
+   'systemd-shutdown'],
+  ''],
  ['systemd-pstore.service', '8', ['systemd-pstore'], 'ENABLE_PSTORE'],
  ['systemd-quotacheck.service',
   '8',
@@ -1191,6 +1191,7 @@ manpages = [
   ''],
  ['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
  ['udevadm', '8', [], ''],
+ ['ukify', '1', [], 'HAVE_GNU_EFI'],
  ['user@.service',
   '5',
   ['systemd-user-runtime-dir', 'user-runtime-dir@.service'],
index a778f8a2b06eede99caf911f61d28567a3270596..cdab87ab63e6b25244126a5d20e751c3fde239a1 100644 (file)
     has properties similar to the machine ID during that time.</para>
 
     <para><function>sd_id128_get_invocation()</function> returns the invocation ID of the currently executed
-    service. In its current implementation, this reads and parses the <varname>$INVOCATION_ID</varname> environment
-    variable that the service manager sets when activating a service, see
-    <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The
-    ID is cached internally. In future a different mechanism to determine the invocation ID may be added.</para>
+    service. In its current implementation, this tries to read and parse the following:
+    <itemizedlist>
+      <listitem>
+        <para>The <varname>$INVOCATION_ID</varname> environment variable that the service manager sets when
+        activating a service.</para>
+      </listitem>
+      <listitem>
+        <para>An entry in the kernel keyring that the system service manager sets when activating a service.
+        </para>
+      </listitem>
+    </itemizedlist>
+    See <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for details. The ID is cached internally. In future a different mechanism to determine the invocation ID
+    may be added.</para>
 
     <para>Note that <function>sd_id128_get_machine_app_specific()</function>,
     <function>sd_id128_get_boot()</function>, <function>sd_id128_get_boot_app_specific()</function>, and
         <varlistentry>
           <term><constant>-ENOENT</constant></term>
 
-          <listitem><para>Returned by <function>sd_id128_get_machine()</function>,
-          <function>sd_id128_get_machine_app_specific()</function>, and
-          <function>sd_id128_get_boot_app_specific()</function> when <filename>/etc/machine-id</filename> is
-          missing.</para></listitem>
+          <listitem><para>Returned by <function>sd_id128_get_machine()</function> and
+          <function>sd_id128_get_machine_app_specific()</function> when <filename>/etc/machine-id</filename>
+          is missing.</para></listitem>
         </varlistentry>
 
         <varlistentry>
           <term><constant>-ENOMEDIUM</constant></term>
 
-          <listitem><para>Returned by <function>sd_id128_get_machine()</function>,
-          <function>sd_id128_get_machine_app_specific()</function>, and
-          <function>sd_id128_get_boot_app_specific()</function> when <filename>/etc/machine-id</filename> is
-          empty or all zeros.</para></listitem>
+          <listitem><para>Returned by <function>sd_id128_get_machine()</function> and
+          <function>sd_id128_get_machine_app_specific()</function> when <filename>/etc/machine-id</filename>
+          is empty or all zeros. Also returned by <function>sd_id128_get_invocation()</function> when the
+          invocation ID is all zeros.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>Returned by <function>sd_id128_get_machine()</function> and
+          <function>sd_id128_get_machine_app_specific()</function> when the content of
+          <filename>/etc/machine-id</filename> is <literal>uninitialized</literal>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOSYS</constant></term>
+
+          <listitem><para>Returned by <function>sd_id128_get_boot()</function> and
+          <function>sd_id128_get_boot_app_specific()</function> when <filename>/proc/</filename> is not
+          mounted.</para></listitem>
         </varlistentry>
 
         <varlistentry>
         </varlistentry>
 
         <varlistentry>
-          <term><constant>-EIO</constant></term>
+          <term><constant>-EUCLEAN</constant></term>
 
           <listitem><para>Returned by any of the functions described here when the configured value has
           invalid format.</para></listitem>
index 1d91c8a726298c4965394502f2e06f21cc47902c..193f6b9800476f4c4b9e488c21cca00c28be291d 100644 (file)
@@ -771,6 +771,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             account.
             </para>
 
+            <para>When using this operation on units without install information, a warning about it is shown.
+            <option>--no-warn</option> can be used to suppress the warning.</para>
+
             <para>Enabling units should not be confused with starting (activating) units, as done by the
             <command>start</command> command. Enabling and starting units is orthogonal: units may be enabled without
             being started and started without being enabled. Enabling simply hooks the unit into various suggested
@@ -814,8 +817,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             executed. This output may be suppressed by passing <option>--quiet</option>.
             </para>
 
-            <para>This command honors <option>--system</option>, <option>--user</option>, <option>--runtime</option>
-            and <option>--global</option> in a similar way as <command>enable</command>.</para>
+            <para>This command honors <option>--system</option>, <option>--user</option>, <option>--runtime</option>,
+            <option>--global</option> and <option>--no-warn</option> in a similar way as <command>enable</command>.</para>
           </listitem>
         </varlistentry>
 
@@ -951,6 +954,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
                     <entry>The unit file is invalid or another error occurred. Note that <command>is-enabled</command> will not actually return this state, but print an error message instead. However the unit file listing printed by <command>list-unit-files</command> might show it.</entry>
                     <entry>&gt; 0</entry>
                   </row>
+                  <row>
+                    <entry><literal>not-found</literal></entry>
+                    <entry>The unit file doesn't exist.</entry>
+                    <entry>4</entry>
+                  </row>
                 </tbody>
               </tgroup>
             </table>
@@ -1997,6 +2005,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <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>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--no-block</option></term>
 
index 2eb8972feed4ec248e468549036874bbe0ecfc19..3b014b47b8831a6bf8aa57f9f9f13f83f6ff80ab 100644 (file)
         operation begins.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--discover</option></term>
+
+        <listitem><para>Show a list of DDIs in well-known directories. This will show machine, portable
+        service and system extension disk images in the usual directories
+        <filename>/usr/lib/machines/</filename>, <filename>/usr/lib/portables/</filename>,
+        <filename>/usr/lib/extensions/</filename>, <filename>/var/lib/machines/</filename>,
+        <filename>/var/lib/portables/</filename>, <filename>/var/lib/extensions/</filename> and so
+        on.</para></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
         systems. If <literal>all</literal> discarding is unconditionally enabled.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--in-memory</option></term>
+
+        <listitem><para>If specified an in-memory copy of the specified disk image is used. This may be used
+        to operate with write-access on a (possibly read-only) image, without actually modifying the original
+        file. This may also be used in order to operate on a disk image without keeping the originating file
+        system busy, in order to allow it to be unmounted.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--root-hash=</option></term>
         <term><option>--root-hash-sig=</option></term>
similarity index 78%
rename from man/systemd-halt.service.xml
rename to man/systemd-poweroff.service.xml
index 96072ae7fbac167f58a547d9bd7a34a202fd0a9d..9adfcc5af063b580b777e1b3fe79e366fc73a5ec 100644 (file)
@@ -3,21 +3,21 @@
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
 
-<refentry id="systemd-halt.service">
+<refentry id="systemd-poweroff.service">
 
   <refentryinfo>
-    <title>systemd-halt.service</title>
+    <title>systemd-poweroff.service</title>
     <productname>systemd</productname>
   </refentryinfo>
 
   <refmeta>
-    <refentrytitle>systemd-halt.service</refentrytitle>
+    <refentrytitle>systemd-poweroff.service</refentrytitle>
     <manvolnum>8</manvolnum>
   </refmeta>
 
   <refnamediv>
-    <refname>systemd-halt.service</refname>
     <refname>systemd-poweroff.service</refname>
+    <refname>systemd-halt.service</refname>
     <refname>systemd-reboot.service</refname>
     <refname>systemd-kexec.service</refname>
     <refname>systemd-shutdown</refname>
@@ -25,8 +25,8 @@
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>systemd-halt.service</filename></para>
     <para><filename>systemd-poweroff.service</filename></para>
+    <para><filename>systemd-halt.service</filename></para>
     <para><filename>systemd-reboot.service</filename></para>
     <para><filename>systemd-kexec.service</filename></para>
     <para><filename>/usr/lib/systemd/systemd-shutdown</filename></para>
   <refsect1>
     <title>Description</title>
 
-    <para><filename>systemd-halt.service</filename> is a system
-    service that is pulled in by <filename>halt.target</filename> and
-    is responsible for the actual system halt. Similarly,
-    <filename>systemd-poweroff.service</filename> is pulled in by
-    <filename>poweroff.target</filename>,
+    <para><filename>systemd-poweroff.service</filename> is a system
+    service that is pulled in by <filename>poweroff.target</filename> and
+    is responsible for the actual system power-off operation. Similarly,
+    <filename>systemd-halt.service</filename> is pulled in by
+    <filename>halt.target</filename>,
     <filename>systemd-reboot.service</filename> by
     <filename>reboot.target</filename> and
     <filename>systemd-kexec.service</filename> by
     cannot be re-mounted read-only.</para>
 
     <para>Immediately before executing the actual system
-    halt/poweroff/reboot/kexec <filename>systemd-shutdown</filename>
+    power-off/halt/reboot/kexec <filename>systemd-shutdown</filename>
     will run all executables in
     <filename>/usr/lib/systemd/system-shutdown/</filename> and pass
-    one arguments to them: either <literal>halt</literal>,
-    <literal>poweroff</literal>, <literal>reboot</literal> or
+    one arguments to them: either <literal>poweroff</literal>,
+    <literal>halt</literal>, <literal>reboot</literal>, or
     <literal>kexec</literal>, depending on the chosen action. All
     executables in this directory are executed in parallel, and
     execution of the action is not continued before all executables
     finished.</para>
 
-    <para>Note that <filename>systemd-halt.service</filename> (and the
-    related units) should never be executed directly. Instead, trigger
-    system shutdown with a command such as <literal>systemctl
-    halt</literal> or suchlike.</para>
+    <para>Note that <filename>systemd-poweroff.service</filename> (and the related units) should never be
+    executed directly. Instead, trigger system shutdown with a command such as <literal>systemctl
+    poweroff</literal>.</para>
   </refsect1>
 
   <refsect1>
index 2c74afbe0fa347dffe7103419156c14e61eb1cc2..657caad43238e7447346bccdcb659f2e39e73796 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--skip-partitions=</option><arg rep="repeat">PARTITION</arg></term>
+        <term><option>--defer-partitions=</option><arg rep="repeat">PARTITION</arg></term>
 
         <listitem><para>This option specifies which partition types <command>systemd-repart</command> should
         skip. All partitions that are skipped using this option are still taken into account when calculating
index 1de1627850aa22b6bcf3128fdaa89d7dc2ef7d36..99436ced59d067a3d3fb721a1b86bd2e5d7eed36 100644 (file)
@@ -73,7 +73,8 @@
       <listitem><para>Plain directories or btrfs subvolumes containing the OS tree</para></listitem>
       <listitem><para>Disk images with a GPT disk label, following the <ulink
       url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions Specification</ulink></para></listitem>
-      <listitem><para>Disk images lacking a partition table, with a naked Linux file system (e.g. squashfs or ext4)</para></listitem>
+      <listitem><para>Disk images lacking a partition table, with a naked Linux file system (e.g. erofs,
+      squashfs or ext4)</para></listitem>
     </orderedlist>
 
     <para>These image formats are the same ones that
index ac21c31d9aa3b3bf0b65d3871c9ac05bfcd498f6..6406df13ae64578da5bbd1894db2251c2780fc25 100644 (file)
         <para>If the value is <literal>/</literal>, only labels specified with <varname>SmackProcessLabel=</varname>
         are assigned and the compile-time default is ignored.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>ReloadLimitIntervalSec=</varname></term>
+        <term><varname>ReloadLimitBurst=</varname></term>
+
+        <listitem><para>Rate limiting for daemon-reload requests. Default to unset, and any number of daemon-reload
+        operations can be requested at any time. <varname>ReloadLimitIntervalSec=</varname> takes a value in seconds
+        to configure the rate limit window, and <varname>ReloadLimitBurst=</varname> takes a positive integer to
+        configure the maximum allowed number of reloads within the configured time window.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 7b30bd2e78c97566a0740a5b6a28f12b77021bcb..d83141de1103f8e16f4e98a7b0c2a206ae570f66 100644 (file)
       <varlistentry>
         <term><varname>DefaultRouteOnDevice=</varname></term>
         <listitem>
-          <para>Takes a boolean. If set to true, sets up the default route bound to the interface.
+          <para>Takes a boolean. If set to true, sets up the IPv4 default route bound to the interface.
           Defaults to false. This is useful when creating routes on point-to-point interfaces. This is
           equivalent to e.g. the following,
           <programlisting>ip route add default dev veth99</programlisting>
@@ -433,6 +433,11 @@ Gateway=0.0.0.0</programlisting></para>
           following instead:
           <programlisting>[Route]
 Gateway=0.0.0.0
+Table=1234</programlisting></para>
+          <para>If you'd like to create an IPv6 default route bound to the interface, please use the
+          following:
+          <programlisting>[Route]
+Gateway=::
 Table=1234</programlisting></para>
         </listitem>
       </varlistentry>
index 17d2700069fe278a4c5968acd60af07798c57a26..95969bf097d1df3bd0c2c1d78a3de799a08ea883 100644 (file)
     of scope units are the following:</para>
 
     <variablelist class='unit-directives'>
+      <xi:include href="systemd.service.xml" xpointer="oom-policy" />
+
       <varlistentry>
         <term><varname>RuntimeMaxSec=</varname></term>
 
index 8d8dd77689010ac1720978111e8956c1362e537a..1c9e59f7229d2931cc96e0b517a6ffa9480dfa03 100644 (file)
         above.</para></listitem>
       </varlistentry>
 
-      <varlistentry>
+      <varlistentry id='oom-policy'>
         <term><varname>OOMPolicy=</varname></term>
 
-        <listitem><para>Configure the out-of-memory (OOM) kernel killer policy. Note that the userspace OOM
+        <listitem><para>Configure the out-of-memory (OOM) killing policy for the kernel and the userspace OOM
         killer
-        <citerefentry><refentrytitle>systemd-oomd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-        is a more flexible solution that aims to prevent out-of-memory situations for the userspace, not just
-        the kernel.</para>
-
-        <para>On Linux, when memory becomes scarce to the point that the kernel has trouble allocating memory
-        for itself, it might decide to kill a running process in order to free up memory and reduce memory
-        pressure. This setting takes one of <constant>continue</constant>, <constant>stop</constant> or
-        <constant>kill</constant>. If set to <constant>continue</constant> and a process of the service is
-        killed by the kernel's OOM killer this is logged but the service continues running. If set to
-        <constant>stop</constant> the event is logged but the service is terminated cleanly by the service
-        manager. If set to <constant>kill</constant> and one of the service's processes is killed by the OOM
-        killer the kernel is instructed to kill all remaining processes of the service too, by setting the
+        <citerefentry><refentrytitle>systemd-oomd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+        On Linux, when memory becomes scarce to the point that the kernel has trouble allocating memory for
+        itself, it might decide to kill a running process in order to free up memory and reduce memory
+        pressure. Note that <filename>systemd-oomd.service</filename> is a more flexible solution that aims
+        to prevent out-of-memory situations for the userspace too, not just the kernel, by attempting to
+        terminate services earlier, before the kernel would have to act.</para>
+
+        <para>This setting takes one of <constant>continue</constant>, <constant>stop</constant> or
+        <constant>kill</constant>. If set to <constant>continue</constant> and a process in the unit is
+        killed by the OOM killer, this is logged but the unit continues running. If set to
+        <constant>stop</constant> the event is logged but the unit is terminated cleanly by the service
+        manager. If set to <constant>kill</constant> and one of the unit's processes is killed by the OOM
+        killer the kernel is instructed to kill all remaining processes of the unit too, by setting the
         <filename>memory.oom.group</filename> attribute to <constant>1</constant>; also see <ulink
-        url="https://docs.kernel.org/admin-guide/cgroup-v2.html">kernel documentation</ulink>.
-        </para>
+        url="https://docs.kernel.org/admin-guide/cgroup-v2.html">kernel documentation</ulink>.</para>
 
         <para>Defaults to the setting <varname>DefaultOOMPolicy=</varname> in
         <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        is set to, except for services where <varname>Delegate=</varname> is turned on, where it defaults to
+        is set to, except for units where <varname>Delegate=</varname> is turned on, where it defaults to
         <constant>continue</constant>.</para>
 
         <para>Use the <varname>OOMScoreAdjust=</varname> setting to configure whether processes of the unit
         details.</para>
 
         <para>This setting also applies to <command>systemd-oomd</command>. Similarly to the kernel OOM
-        kills, this setting determines the state of the service after <command>systemd-oomd</command> kills a
-        cgroup associated with the service.</para></listitem>
+        kills, this setting determines the state of the unit after <command>systemd-oomd</command> kills a
+        cgroup associated with it.</para></listitem>
       </varlistentry>
-
     </variablelist>
 
     <para id='shared-unit-options'>Check
diff --git a/man/ukify.xml b/man/ukify.xml
new file mode 100644 (file)
index 0000000..17546d5
--- /dev/null
@@ -0,0 +1,312 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+<refentry id="ukify" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='HAVE_GNU_EFI'>
+
+  <refentryinfo>
+    <title>ukify</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>ukify</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>ukify</refname>
+    <refpurpose>Combine kernel and initrd into a signed Unified Kernel Image</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>/usr/lib/systemd/ukify</command>
+      <arg choice="plain"><replaceable>LINUX</replaceable></arg>
+      <arg choice="plain" rep="repeat"><replaceable>INITRD</replaceable></arg>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>Note: this command is experimental for now. While it is intended to become a regular component of
+    systemd, it might still change in behaviour and interface.</para>
+
+    <para><command>ukify</command> is a tool that combines a kernel and an initrd with
+    a UEFI boot stub to create a
+    <ulink url="https://uapi-group.org/specifications/specs/unified_kernel_image/">Unified Kernel Image (UKI)</ulink>
+    — a PE binary that can be executed by the firmware to start the embedded linux kernel.
+    See <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    for details about the stub.</para>
+
+    <para>Additional sections will be inserted into the UKI, either automatically or only if a specific
+    option is provided. See the discussions of
+    <option>--cmdline=</option>,
+    <option>--os-release=</option>,
+    <option>--devicetree=</option>,
+    <option>--splash=</option>,
+    <option>--pcrpkey=</option>,
+    <option>--uname=</option>,
+    and <option>--section=</option>
+    below.</para>
+
+    <para>If PCR signing keys are provided via the <option>--pcr-public-key=</option> and
+    <option>--pcr-private-key=</option> options, PCR values that will be seen after booting with the given
+    kernel, initrd, and other sections, will be calculated, signed, and embedded in the UKI.
+    <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry> is
+    used to perform this calculation and signing.</para>
+
+    <para>The calculation of PCR values is done for specific boot phase paths. Those can be specified with
+    <option>--phases=</option> option. If not specified, the default provided by
+    <command>systemd-measure</command> is used. It is also possible to specify the
+    <option>--pcr-private-key=</option>, <option>--pcr-public-key=</option>, and <option>--phases=</option>
+    arguments more than once. Signatures will be then performed with each of the specified keys. When both
+    <option>--phases=</option> and <option>--pcr-private-key=</option> are used, they must be specified the
+    same number of times, and then the n-th boot phase path set will be signed by the n-th key. This can be
+    used to build different trust policies for different phases of the boot.</para>
+
+    <para>If a SecureBoot signing key is provided via the <option>--secureboot-private-key=</option> option,
+    the resulting PE binary will be signed as a whole, allowing the resulting UKI to be trusted by
+    SecureBoot. Also see the discussion of automatic enrollment in
+    <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>Note that the <replaceable>LINUX</replaceable> positional argument is mandatory. The
+    <replaceable>INITRD</replaceable> positional arguments are optional. If more than one is specified, they
+    will all be combined into a single PE section. This is useful to for example prepend microcode before the
+    actual initrd.</para>
+
+    <para>The following options are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>--cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
+
+        <listitem><para>Specify the kernel command line (the <literal>.cmdline</literal> section). The
+        argument may be a literal string, or <literal>@</literal> followed by a path name. If not specified,
+        no command line will be embedded.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--os-release=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
+
+        <listitem><para>Specify the os-release description (the <literal>.osrel</literal> section). The
+        argument may be a literal string, or <literal>@</literal> followed by a path name. If not specified,
+        the <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        file will be picked up from the host system.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--devicetree=<replaceable>PATH</replaceable></option></term>
+
+        <listitem><para>Specify the devicetree description (the <literal>.dtb</literal> section). The
+        argument is a path to a compiled binary DeviceTree file. If not specified, the section will not be
+        present.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--splash=<replaceable>PATH</replaceable></option></term>
+
+        <listitem><para>Specify a picture to display during boot (the <literal>.splash</literal> section).
+        The argument is a path to a BMP file. If not specified, the section will not be present.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--pcrpkey=<replaceable>PATH</replaceable></option></term>
+
+        <listitem><para>Specify a path to a public key to embed in the <literal>.pcrpkey</literal> section.
+        If not specified, and there's exactly one <option>--pcr-public-key=</option> argument, that key will
+        be used. Otherwise, the section will not be present.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--uname=<replaceable>VERSION</replaceable></option></term>
+
+        <listitem><para>Specify the kernel version (as in <command>uname -r</command>, the
+        <literal>.uname</literal> section). If not specified, an attempt will be made to extract the version
+        string from the kernel image. It is recommended to pass this explicitly if known, because the
+        extraction is based on heuristics and not very reliable. If not specified and extraction fails, the
+        section will not be present.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--section=<replaceable>NAME</replaceable>:<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
+
+        <listitem><para>Specify an arbitrary additional section
+        <literal><replaceable>NAME</replaceable></literal>. Note that the name is used as-is, and if the
+        section name should start with a dot, it must be included in <replaceable>NAME</replaceable>. The
+        argument may be a literal string, or <literal>@</literal> followed by a path name. This option may be
+        specified more than once. Any sections specified in this fashion will be inserted (in order) before
+        the <literal>.linux</literal> section which is always last.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--pcr-private-key=<replaceable>PATH</replaceable></option></term>
+
+        <listitem><para>Specify a private key to use for signing PCR policies. This option may be specified
+        more than once, in which case multiple signatures will be made.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--pcr-public-key=<replaceable>PATH</replaceable></option></term>
+
+        <listitem><para>Specify a public key to use for signing PCR policies. This option may be specified
+        more than once, similarly to the <option>--pcr-private-key=</option> option. If not present, the
+        public keys will be extracted from the private keys. If present, the this option must be specified
+        the same number of times as the <option>--pcr-private-key=</option> option.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--phases=<replaceable>LIST</replaceable></option></term>
+
+        <listitem><para>A comma or space-separated list of colon-separated phase paths to sign a policy for.
+        If not present, the default of
+        <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        will be used. When this argument is present, it must appear the same number of times as the
+        <option>--pcr-private-key=</option> option. Each set of boot phase paths will be signed with the
+        corresponding private key.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--pcr-banks=<replaceable>PATH</replaceable></option></term>
+
+        <listitem><para>A comma or space-separated list of PCR banks to sign a policy for. If not present,
+        all known banks will be used (<literal>sha1</literal>, <literal>sha256</literal>,
+        <literal>sha384</literal>, <literal>sha512</literal>), which will fail if not supported by the
+        system.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--secureboot-private-key=<replaceable>SB_KEY</replaceable></option></term>
+
+        <listitem><para>A path to a private key to use for signing of the resulting binary. If the
+        <option>--signing-engine=</option> option is used, this may also be an engine-specific
+        designation.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--secureboot-certificate=<replaceable>SB_CERT</replaceable></option></term>
+
+        <listitem><para>A path to a certificate to use for signing of the resulting binary. If the
+        <option>--signing-engine=</option> option is used, this may also be an engine-specific
+        designation.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--signing-engine=<replaceable>ENGINE</replaceable></option></term>
+
+        <listitem><para>An "engine" to for signing of the resulting binary. This option is currently passed
+        verbatim to the <option>--engine=</option> option of
+        <citerefentry><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--sign-kernel</option></term>
+        <term><option>--no-sign-kernel</option></term>
+
+        <listitem><para>Override the detection of whether to sign the Linux binary itself before it is
+        embedded in the combined image. If not specified, it will be signed if a SecureBoot signing key is
+        provided via the <option>--secureboot-private-key=</option> option and the binary has not already
+        been signed. If <option>--sign-kernel</option> is specified, and the binary has already been signed,
+        the signature will be appended anyway.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--tools=<replaceable>DIR</replaceable></option></term>
+
+        <listitem><para>Specify a directory with helper tools. <command>ukify</command> will look for helper
+        tools in that directory first, and if not found, try to load them from <varname>$PATH</varname> in
+        the usual fashion.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--measure</option></term>
+        <term><option>--no-measure</option></term>
+
+        <listitem><para>Enable or disable a call to <command>systmed-measure</command> to print
+        pre-calculated PCR values. Defaults to false.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--output=<replaceable>FILENAME</replaceable></option></term>
+
+        <listitem><para>The output filename. If not specified, the name of the
+        <replaceable>LINUX</replaceable> argument, with the suffix <literal>.unsigned.efi</literal> or
+        <literal>.signed.efi</literal> will be used, depending on whether signing for SecureBoot was
+        performed.</para></listitem>
+      </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Minimal invocation</title>
+
+      <programlisting>ukify \
+      /lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
+      /some/path/initramfs-6.0.9-300.fc37.x86_64.img \
+      --cmdline='quiet rw'
+      </programlisting>
+
+      <para>This creates an unsigned UKI <filename>./vmlinuz.unsigned.efi</filename>.</para>
+    </example>
+
+    <example>
+      <title>All the bells and whistles</title>
+
+      <programlisting>/usr/lib/systemd/ukify \
+      /lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
+      early_cpio \
+      /some/path/initramfs-6.0.9-300.fc37.x86_64.img \
+      --pcr-private-key=pcr-private-initrd-key.pem \
+      --pcr-public-key=pcr-public-initrd-key.pem \
+      --phases='enter-initrd' \
+      --pcr-private-key=pcr-private-system-key.pem \
+      --pcr-public-key=pcr-public-system-key.pem \
+      --phases='enter-initrd:leave-initrd enter-initrd:leave-initrd:sysinit \
+                enter-initrd:leave-initrd:sysinit:ready' \
+      --pcr-banks=sha384,sha512 \
+      --secureboot-private-key=sb.key \
+      --secureboot-certificate=sb.cert \
+      --sign-kernel \
+      --cmdline='quiet rw rhgb'
+      </programlisting>
+
+      <para>This creates a signed UKI <filename index='false'>./vmlinuz.signed.efi</filename>.
+      The initrd section contains two concatenated parts, <filename index='false'>early_cpio</filename>
+      and <filename index='false'>initramfs-6.0.9-300.fc37.x86_64.img</filename>.
+      The policy embedded in the <literal>.pcrsig</literal> section will be signed for the initrd (the
+      <constant>enter-initrd</constant> phase) with the key
+      <filename index='false'>pcr-private-initrd-key.pem</filename>, and for the main system (phases
+      <constant>leave-initrd</constant>, <constant>sysinit</constant>, <constant>ready</constant>) with the
+      key <filename index='false'>pcr-private-system-key.pem</filename>. The Linux binary and the resulting
+      combined image will be signed with the SecureBoot key <filename index='false'>sb.key</filename>.</para>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+     </para>
+  </refsect1>
+
+</refentry>
index 5706c776ffec96a9b4b7767e5e6e8037936c64c3..1ddb42993a0525ec00b2dccd832cdcccfe761aa8 100644 (file)
@@ -510,6 +510,10 @@ conf.set('SIZEOF_TIME_T', cc.sizeof('time_t', prefix : '#include <sys/time.h>'))
 conf.set('SIZEOF_RLIM_T', cc.sizeof('rlim_t', prefix : '#include <sys/resource.h>'))
 conf.set('SIZEOF_TIMEX_MEMBER', cc.sizeof('typeof(((struct timex *)0)->freq)', prefix : '#include <sys/timex.h>'))
 
+long_max = cc.compute_int('LONG_MAX', prefix : '#include <limits.h>')
+assert(long_max > 100000)
+conf.set_quoted('LONG_MAX_STR', '@0@'.format(long_max))
+
 decl_headers = '''
 #include <uchar.h>
 #include <sys/mount.h>
@@ -712,6 +716,17 @@ 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')
@@ -2187,6 +2202,7 @@ subdir('src/test')
 subdir('src/fuzz')
 subdir('rules.d')
 subdir('test')
+subdir('src/ukify/test')  # needs to be last for test_env variable
 
 ############################################################
 
@@ -2583,7 +2599,7 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
                 boot_link_with = [libsystemd_static, libshared_static]
         endif
 
-        public_programs += executable(
+        exe = executable(
                 'bootctl',
                 'src/boot/bootctl.c',
                 include_directories : includes,
@@ -2592,6 +2608,14 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
                                 versiondep],
                 install_rpath : rootpkglibdir,
                 install : true)
+        public_programs += exe
+
+        if want_tests != 'false'
+                test('test-bootctl-json',
+                     test_bootctl_json_sh,
+                     args : exe.full_path(),
+                     depends : exe)
+        endif
 
         public_programs += executable(
                 'systemd-bless-boot',
@@ -3840,6 +3864,7 @@ if conf.get('ENABLE_REPART') == 1
                         install_rpath : rootpkglibdir,
                         install : true,
                         install_dir : rootbindir)
+                public_programs += exe
         endif
 endif
 
@@ -3854,6 +3879,22 @@ executable(
         install : true,
         install_dir : rootlibexecdir)
 
+if have_standalone_binaries
+        executable(
+                'systemd-shutdown.standalone',
+                systemd_shutdown_sources,
+                include_directories : includes,
+                c_args : '-DSTANDALONE',
+                link_with : [libshared_static,
+                             libbasic,
+                             libsystemd_static],
+                dependencies : [libmount,
+                                versiondep],
+                install_rpath : rootpkglibdir,
+                install : true,
+                install_dir : rootlibexecdir)
+endif
+
 executable(
         'systemd-update-done',
         'src/update-done/update-done.c',
@@ -3987,6 +4028,18 @@ if want_tests != 'false' and want_kernel_install
              args : [exe.full_path(), loaderentry_install])
 endif
 
+if want_ukify
+   exe = custom_target(
+        'ukify',
+        input : 'src/ukify/ukify.py',
+        output : 'ukify',
+        command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
+        install : true,
+        install_mode : 'rwxr-xr-x',
+        install_dir : rootlibexecdir)
+   public_programs += exe
+endif
+
 ############################################################
 
 runtest_env = custom_target(
index 19e9c2d990949feffd9a5c954a278c6195f85a51..6a95955dd9254c00446e6be81d0995cbf7729c8d 100644 (file)
@@ -501,6 +501,8 @@ option('llvm-fuzz', type : 'boolean', value : false,
        description : 'build against LLVM libFuzzer')
 option('kernel-install', type: 'boolean', value: true,
        description : 'install kernel-install and associated files')
+option('ukify', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'install ukify')
 option('analyze', type: 'boolean', value: true,
        description : 'install systemd-analyze')
 
index 5f6208dd887473f0be2234adcd9107594dfb0646..c1435fd0fc06dd66992e0e69e04411343a6ccf3a 100755 (executable)
@@ -284,38 +284,10 @@ if [ -d mkosi.kernel/ ]; then
         export KBUILD_BUILD_TIMESTAMP="Fri Jun  5 15:58:00 CEST 2015"
         export KBUILD_BUILD_HOST="mkosi"
 
-        make O="$BUILDDIR/mkosi.kernel" defconfig
-
-        scripts/config \
-                --file "$BUILDDIR/mkosi.kernel/.config" \
-                --enable BPF_SYSCALL \
-                --enable BPF_JIT \
-                --enable BPF_JIT_ALWAYS_ON \
-                --enable BPF_JIT_DEFAULT_ON \
-                --enable BPF_UNPRIV_DEFAULT_OFF \
-                --enable USERMODE_DRIVER \
-                --enable BPF_PRELOAD \
-                --enable BPF_PRELOAD_UMD \
-                --enable BPF_LSM \
-                --enable DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT \
-                --enable DEBUG_INFO_BTF \
-                --enable BTRFS_FS \
-                --enable BTRFS_FS_POSIX_ACL \
-                --enable PSI \
-                --enable CGROUPS \
-                --enable CGROUP_BPF \
-                --enable MEMCG \
-                --enable MEMCG_SWAP \
-                --enable MEMCG_KMEM \
-                --enable IMA_ARCH_POLICY \
-                --enable DM_VERITY_VERIFY_ROOTHASH_SIG \
-                --enable DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING \
-                --enable INTEGRITY_MACHINE_KEYRING \
-                --enable NETFILTER_ADVANCED \
-                --enable NF_CONNTRACK_MARK
-
-        # Make sure all unset options are set to their default value.
-        make O="$BUILDDIR/mkosi.kernel" olddefconfig
+        scripts/kconfig/merge_config.sh -O "$BUILDDIR/mkosi.kernel" \
+                ../mkosi.kernel.config \
+                tools/testing/selftests/bpf/config.x86_64 \
+                tools/testing/selftests/bpf/config
 
         make O="$BUILDDIR/mkosi.kernel" -j "$(nproc)"
 
@@ -326,4 +298,6 @@ if [ -d mkosi.kernel/ ]; then
         make O="$BUILDDIR/mkosi.kernel" INSTALL_PATH="$DESTDIR/usr/lib/modules/$KERNEL_RELEASE" install
         mkdir -p "$DESTDIR/usr/lib/kernel/selftests"
         make -C tools/testing/selftests -j "$(nproc)" O="$BUILDDIR/mkosi.kernel" KSFT_INSTALL_PATH="$DESTDIR/usr/lib/kernel/selftests" SKIP_TARGETS="" install
+
+        ln -sf /usr/lib/kernel/selftests/bpf/bpftool "$DESTDIR/usr/bin/bpftool"
 fi
index f9e4d086160ed3a3c2bef1b3b24b7d93b7df720c..a91378da971b7255b70e734a73462f65f5f4a971 100644 (file)
@@ -3,9 +3,7 @@
 # This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
 
 [Output]
-Format=gpt_btrfs
 Bootable=yes
-HostonlyInitrd=yes
 # Prevent ASAN warnings when building the image
 Environment=ASAN_OPTIONS=verify_asan_link_order=false
 OutputDirectory=mkosi.output
index 5f92aab95cdb705364e3cddba61f04e6705eb606..8ce05f5331b6c3b97a2e83546728fd8fcd803541 100644 (file)
@@ -18,7 +18,6 @@ Packages=
         fuse
         glib2
         glibc-minimal-langpack
-        glibc.i686
         gnutls
         iproute
         iproute-tc
@@ -46,9 +45,7 @@ BuildPackages=
         bpftool
         docbook-xsl
         dwarves
-        glibc-devel.i686
         glibc-static
-        glibc-static.i686
         gnu-efi-devel
         libcap-static
         pam-devel
diff --git a/mkosi.kernel.config b/mkosi.kernel.config
new file mode 100644 (file)
index 0000000..d12074d
--- /dev/null
@@ -0,0 +1,201 @@
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_ATA=y
+CONFIG_AUTOFS4_FS=y
+CONFIG_BINFMT_MISC=y
+CONFIG_BLK_CGROUP_IOCOST=y
+CONFIG_BLK_CGROUP_IOLATENCY=y
+CONFIG_BLK_CGROUP_IOPRIO=y
+CONFIG_BLK_CGROUP=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BPF_EVENTS=y
+CONFIG_BPF_JIT=y
+CONFIG_BPF_LSM=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_BTRFS_FS=y
+CONFIG_CFG80211=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_BPF=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_HUGETLB=y
+CONFIG_CGROUP_MISC=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_RDMA=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CGROUPS=y
+CONFIG_CONNECTOR=y
+CONFIG_CPUSETS=y
+CONFIG_CRASH_DUMP=y
+CONFIG_DEBUG_INFO_BTF=y
+CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEVTMPFS=y
+CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING=y
+CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
+CONFIG_DM_VERITY=y
+CONFIG_EFI_MIXED=y
+CONFIG_EFI_STUB=y
+CONFIG_EFI_ZBOOT=y
+CONFIG_EFI=y
+CONFIG_EXPERT=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_HIBERNATION=y
+CONFIG_HIDRAW=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HPET=y
+CONFIG_HUGETLBFS=y
+CONFIG_HW_RANDOM=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_IMA_APPRAISE=y
+CONFIG_IMA_ARCH_POLICY=y
+CONFIG_IMA=y
+CONFIG_INET=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_MISC=y
+CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y
+CONFIG_INTEGRITY_MACHINE_KEYRING=y
+CONFIG_INTEGRITY_PLATFORM_KEYRING=y
+CONFIG_INTEGRITY_SIGNATURE=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_ISO9660_FS=y
+CONFIG_KEXEC=y
+CONFIG_KPROBES=y
+CONFIG_LOAD_UEFI_KEYS=y
+CONFIG_MAC80211=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MD=y
+CONFIG_MEMCG_KMEM=y
+CONFIG_MEMCG=y
+CONFIG_MICROCODE_AMD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULES=y
+CONFIG_MSDOS_FS=y
+CONFIG_NAMESPACES=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_NET_9P=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_CLS_CGROUP=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_SCHED=y
+CONFIG_NET=y
+CONFIG_NETCONSOLE=y
+CONFIG_NETDEVICES=y
+CONFIG_NETFILTER_ADVANCED=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER=y
+CONFIG_NETLABEL=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NF_NAT=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_NO_HZ_FULL=y
+CONFIG_NUMA=y
+CONFIG_NVRAM=y
+CONFIG_PACKET=y
+CONFIG_PARAVIRT=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_PM_DEBUG=y
+CONFIG_PM_TRACE_RTC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROFILING=y
+CONFIG_PSI=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QUOTA=y
+CONFIG_RFKILL=y
+CONFIG_RTC_CLASS=y
+CONFIG_SATA_AHCI=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_VIRTIO=y
+CONFIG_SCSI=y
+CONFIG_SECONDARY_TRUSTED_KEYRING=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RSA=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_NONSTANDARD=y
+CONFIG_SMP=y
+CONFIG_SWAP=y
+CONFIG_SYSTEM_BLACKLIST_KEYRING=y
+CONFIG_SYSVIPC=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_TMPFS_XATTR=y
+CONFIG_TMPFS=y
+CONFIG_UNIX=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_MON=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UHCI_HCD=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB=y
+CONFIG_USER_NS=y
+CONFIG_VFAT_FS=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_VIRTIO_INPUT=y
+CONFIG_VIRTIO_NET=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_WATCHDOG=y
+CONFIG_X86_ACPI_CPUFREQ=y
+CONFIG_X86_CPUID=y
+CONFIG_X86_MSR=y
+CONFIG_XFRM_USER=y
+CONFIG_XFS_FS=y
+CONFIG_XFS_POSIX_ACL=y
index c9a027e10e76e93226fb73b5e3c48588955dfd94..7f2f0adf7f05f20176881d2603d5ce09245191f7 100644 (file)
--- a/po/pt.po
+++ b/po/pt.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2022-10-20 10:35+0200\n"
-"PO-Revision-Date: 2022-01-13 11:16+0000\n"
+"PO-Revision-Date: 2022-12-10 12:19+0000\n"
 "Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n"
 "Language-Team: Portuguese <https://translate.fedoraproject.org/projects/"
 "systemd/master/pt/>\n"
@@ -15,7 +15,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.10.1\n"
+"X-Generator: Weblate 4.14.2\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -162,22 +162,19 @@ msgstr "É necessária autenticação para obter a UUID."
 
 #: src/hostname/org.freedesktop.hostname1.policy:61
 msgid "Get hardware serial number"
-msgstr ""
+msgstr "Obter o número de série do hardware"
 
 #: src/hostname/org.freedesktop.hostname1.policy:62
-#, fuzzy
 msgid "Authentication is required to get hardware serial number."
-msgstr "É necessária autenticação para definir o horário do sistema."
+msgstr "É necessária autenticação para obter o número de série do hardware."
 
 #: src/hostname/org.freedesktop.hostname1.policy:71
-#, fuzzy
 msgid "Get system description"
-msgstr "Definir fuso horário do sistema"
+msgstr "Obter descrição do sistema"
 
 #: src/hostname/org.freedesktop.hostname1.policy:72
-#, fuzzy
 msgid "Authentication is required to get system description."
-msgstr "É necessária autenticação para definir o fuso horário do sistema."
+msgstr "É necessária autenticação para obter a descrição do sistema."
 
 #: src/import/org.freedesktop.import1.policy:22
 msgid "Import a VM or container image"
index 055a62a12d75c094ba1e9c71b58e89a9d259ec18..15483101e7515c740893dd9f51eabe88659ed93f 100644 (file)
@@ -3,23 +3,28 @@
 ACTION=="remove", GOTO="evdev_end"
 KERNEL!="event*", GOTO="evdev_end"
 
-# skip later rules when we find something for this input device
-IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \
-  IMPORT{builtin}="keyboard", GOTO="evdev_end"
+# Execute the match patterns below, from least-to-most specific.
+
+# Device matching the modalias string (bustype, vendor, product, version, other properties)
+IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:",
+  ENV{.HAVE_HWDB_PROPERTIES}="1"
 
 # AT keyboard matching by the machine's DMI data
 DRIVERS=="atkbd", \
   IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \
-  IMPORT{builtin}="keyboard", GOTO="evdev_end"
+  ENV{.HAVE_HWDB_PROPERTIES}="1"
 
-# device matching the input device name + properties + the machine's DMI data
+# Device matching the input device name and the machine's DMI data
 KERNELS=="input*", \
-  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
-  IMPORT{builtin}="keyboard", GOTO="evdev_end"
+  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
+  ENV{.HAVE_HWDB_PROPERTIES}="1"
 
-# device matching the input device name and the machine's DMI data
+# Device matching the input device name + properties + the machine's DMI data
 KERNELS=="input*", \
-  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
-  IMPORT{builtin}="keyboard", GOTO="evdev_end"
+  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
+  ENV{.HAVE_HWDB_PROPERTIES}="1"
+
+ENV{.HAVE_HWDB_PROPERTIES}=="1", \
+  IMPORT{builtin}="keyboard"
 
 LABEL="evdev_end"
index 166a50828efc1f93fadc547e1f2afcdd16103b90..0a7bd0d13c88685de0c886de7fad7dbca54eab8f 100644 (file)
@@ -93,6 +93,7 @@ _homectl() {
                         --luks-volume-key-size
                         --luks-pbkdf-type
                         --luks-pbkdf-hash-algorithm
+                        --luks-pbkdf-force-iterations
                         --luks-pbkdf-time-cost
                         --luks-pbkdf-memory-cost
                         --luks-pbkdf-parallel-threads
index b030f454b2f6e0b173ab0bac4a718940efc9d276..6063943c88ab55ba355919f3f1b9d1f902d243d2 100644 (file)
@@ -102,3 +102,7 @@ void* greedy_realloc0(
 
         return q;
 }
+
+void *expand_to_usable(void *ptr, size_t newsize _unused_) {
+        return ptr;
+}
index b38db7d4737d1fb7aa93bbcd3747501218bfa884..eb53aae6f3e1469f64b1dff234e0892651a5811f 100644 (file)
@@ -2,6 +2,7 @@
 #pragma once
 
 #include <alloca.h>
+#include <malloc.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
@@ -184,17 +185,34 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
 #  define msan_unpoison(r, s)
 #endif
 
-/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), in a way that
- * is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the
- * object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of
- * malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by
- * both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the
- * size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory,
- * too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and
- * __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner
- * case. */
+/* Dummy allocator to tell the compiler that the new size of p is newsize. The implementation returns the
+ * pointer as is; the only reason for its existence is as a conduit for the _alloc_ attribute. This cannot be
+ * a static inline because gcc then loses the attributes on the function.
+ * See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96503 */
+void *expand_to_usable(void *p, size_t newsize) _alloc_(2) _returns_nonnull_;
+
+static inline size_t malloc_sizeof_safe(void **xp) {
+        if (_unlikely_(!xp || !*xp))
+                return 0;
+
+        size_t sz = malloc_usable_size(*xp);
+        *xp = expand_to_usable(*xp, sz);
+        /* GCC doesn't see the _returns_nonnull_ when built with ubsan, so yet another hint to make it doubly
+         * clear that expand_to_usable won't return NULL.
+         * See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79265 */
+        if (!*xp)
+                assert_not_reached();
+        return sz;
+}
+
+/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), which may
+ * return a value larger than the size that was actually allocated. Access to that additional memory is
+ * discouraged because it violates the C standard; a compiler cannot see that this as valid. To help the
+ * compiler out, the MALLOC_SIZEOF_SAFE macro 'allocates' the usable size using a dummy allocator function
+ * expand_to_usable. There is a possibility of malloc_usable_size() returning different values during the
+ * lifetime of an object, which may cause problems, but the glibc allocator does not do that at the moment. */
 #define MALLOC_SIZEOF_SAFE(x) \
-        MIN(malloc_usable_size(x), __builtin_object_size(x, 0))
+        malloc_sizeof_safe((void**) &__builtin_choose_expr(__builtin_constant_p(x), (void*) { NULL }, (x)))
 
 /* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items
  * that fit into the specified memory block */
index 0bb07000bad8922b10e83a4793cb6bd059997df0..385d0aed6984b8b9f619311b2ace96ffb2810059 100644 (file)
@@ -466,8 +466,10 @@ int chase_symlinks(
                         return -errno;
 
                 flags |= CHASE_AT_RESOLVE_IN_ROOT;
-        } else
+        } else {
+                path = absolute;
                 fd = AT_FDCWD;
+        }
 
         r = chase_symlinks_at(fd, path, flags & ~CHASE_PREFIX_ROOT, ret_path ? &p : NULL, ret_fd ? &pfd : NULL);
         if (r < 0)
@@ -557,7 +559,7 @@ int chase_symlinks_and_opendir(
                 return r;
         assert(path_fd >= 0);
 
-        d = opendir(FORMAT_PROC_FD_PATH(path_fd));
+        d = xopendirat(path_fd, ".", O_NOFOLLOW);
         if (!d)
                 return -errno;
 
index 54021911ab31a35b0858490fcafc5e5caf9f9923..4a24ba9c8cba162afe60937513f81d4df4a1916d 100644 (file)
@@ -43,7 +43,7 @@
 #define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)
 
 /* The default value for the net.unix.max_dgram_qlen sysctl */
-#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512UL
+#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512
 
 #define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
 #define SIGNALS_IGNORE SIGPIPE
index 1cb7ced545dd9f2e925688303ea7f5c2a0859691..e04b435d5b9fb51c04385fc25cea403117c46819 100644 (file)
@@ -445,31 +445,30 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
 }
 
 char* octescape(const char *s, size_t len) {
-        char *r, *t;
-        const char *f;
+        char *buf, *t;
 
-        /* Escapes all chars in bad, in addition to \ and " chars,
-         * in \nnn style escaping. */
+        /* Escapes all chars in bad, in addition to \ and " chars, in \nnn style escaping. */
 
-        r = new(char, len * 4 + 1);
-        if (!r)
+        assert(s || len == 0);
+
+        t = buf = new(char, len * 4 + 1);
+        if (!buf)
                 return NULL;
 
-        for (f = s, t = r; f < s + len; f++) {
+        for (size_t i = 0; i < len; i++) {
+                uint8_t u = (uint8_t) s[i];
 
-                if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) {
+                if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"')) {
                         *(t++) = '\\';
-                        *(t++) = '0' + (*f >> 6);
-                        *(t++) = '0' + ((*f >> 3) & 8);
-                        *(t++) = '0' + (*f & 8);
+                        *(t++) = '0' + (u >> 6);
+                        *(t++) = '0' + ((u >> 3) & 7);
+                        *(t++) = '0' + (u & 7);
                 } else
-                        *(t++) = *f;
+                        *(t++) = u;
         }
 
         *t = 0;
-
-        return r;
-
+        return buf;
 }
 
 static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
index 01a45e4384daacc7b8efd5b05a0f0d21cb1c1a66..b97039284cdab820ce393ee529eac8ec73f7e5e9 100644 (file)
@@ -501,7 +501,8 @@ void cmsg_close_all(struct msghdr *mh) {
 
         CMSG_FOREACH(cmsg, mh)
                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
-                        close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+                        close_many(CMSG_TYPED_DATA(cmsg, int),
+                                   (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
 }
 
 bool fdname_is_valid(const char *s) {
index 33b4d1f07bfa51b86e52e62c641b77c11020e5d0..fbc7492b15264f0826b824403a67fe16a112e136 100644 (file)
@@ -204,9 +204,17 @@ int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid
                 fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
                 if (fd < 0)
                         return -errno;
+                dir_fd = fd;
+
+        } else if (dir_fd == AT_FDCWD) {
+                /* Let's acquire an O_PATH fd of the current directory */
+                fd = openat(dir_fd, ".", O_PATH|O_CLOEXEC|O_NOFOLLOW|O_DIRECTORY);
+                if (fd < 0)
+                        return -errno;
+                dir_fd = fd;
         }
 
-        return fchmod_and_chown(path ? fd : dir_fd, mode, uid, gid);
+        return fchmod_and_chown(dir_fd, mode, uid, gid);
 }
 
 int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
index 67f2270daf48f19d6e0159d007a131fd6c0129fc..2833125ed99f07efa5a244a231ca8e6f153a6546 100644 (file)
@@ -9,15 +9,15 @@ bool emoji_enabled(void) {
         static int cached_emoji_enabled = -1;
 
         if (cached_emoji_enabled < 0) {
-                int val;
-
-                val = getenv_bool("SYSTEMD_EMOJI");
-                if (val < 0)
-                        cached_emoji_enabled =
-                                is_locale_utf8() &&
-                                !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux");
-                else
-                        cached_emoji_enabled = val;
+                int val = getenv_bool("SYSTEMD_EMOJI");
+                if (val >= 0)
+                        return (cached_emoji_enabled = val);
+
+                const char *term = getenv("TERM");
+                if (!term || STR_IN_SET(term, "dumb", "linux"))
+                        return (cached_emoji_enabled = false);
+
+                cached_emoji_enabled = is_locale_utf8();
         }
 
         return cached_emoji_enabled;
@@ -71,6 +71,7 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_RECYCLING]               = "~",
                         [SPECIAL_GLYPH_DOWNLOAD]                = "\\",
                         [SPECIAL_GLYPH_SPARKLES]                = "*",
+                        [SPECIAL_GLYPH_WARNING_SIGN]            = "!",
                 },
 
                 /* UTF-8 */
@@ -124,10 +125,11 @@ const char *special_glyph(SpecialGlyph code) {
                         /* This emoji is a single character cell glyph in Unicode, and two in ASCII */
                         [SPECIAL_GLYPH_TOUCH]                   = u8"👆",       /* actually called: BACKHAND INDEX POINTING UP */
 
-                        /* These three emojis are single character cell glyphs in Unicode and also in ASCII. */
+                        /* These four emojis are single character cell glyphs in Unicode and also in ASCII. */
                         [SPECIAL_GLYPH_RECYCLING]               = u8"♻️",        /* actually called: UNIVERSAL RECYCLNG SYMBOL */
                         [SPECIAL_GLYPH_DOWNLOAD]                = u8"⤵️",        /* actually called: RIGHT ARROW CURVING DOWN */
                         [SPECIAL_GLYPH_SPARKLES]                = u8"✨",
+                        [SPECIAL_GLYPH_WARNING_SIGN]            = u8"⚠️",
                 },
         };
 
index 621d7a85b7d6fba3cf3e755f4ec7e345572c58f1..b64639622eee6c8d361ca6a31979dc0349df867b 100644 (file)
@@ -44,6 +44,7 @@ typedef enum SpecialGlyph {
         SPECIAL_GLYPH_RECYCLING,
         SPECIAL_GLYPH_DOWNLOAD,
         SPECIAL_GLYPH_SPARKLES,
+        SPECIAL_GLYPH_WARNING_SIGN,
         _SPECIAL_GLYPH_MAX,
         _SPECIAL_GLYPH_INVALID = -EINVAL,
 } SpecialGlyph;
index d19627595f07aedc8319282b62998b43ceb5ed41..de7d16c3970ad99cf55537a3b5df5445bf16f689 100644 (file)
@@ -373,8 +373,9 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
 }
 
 static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) {
-        return (struct hashmap_base_entry*)
-                ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
+        return CAST_ALIGN_PTR(
+                        struct hashmap_base_entry,
+                        (uint8_t *) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
 }
 
 static struct plain_hashmap_entry* plain_bucket_at(Hashmap *h, unsigned idx) {
index 190fca8ee4d8ddffc28bbaa4e3afe38ad44f90ac..0ff8eb3256eefac039c09fd82d8a4f5d7e17787b 100644 (file)
@@ -59,11 +59,13 @@ char *hexmem(const void *p, size_t l) {
         const uint8_t *x;
         char *r, *z;
 
+        assert(p || l == 0);
+
         z = r = new(char, l * 2 + 1);
         if (!r)
                 return NULL;
 
-        for (x = p; x < (const uint8_t*) p + l; x++) {
+        for (x = p; x && x < (const uint8_t*) p + l; x++) {
                 *(z++) = hexchar(*x >> 4);
                 *(z++) = hexchar(*x & 15);
         }
@@ -606,7 +608,7 @@ ssize_t base64mem_full(
         if (!r)
                 return -ENOMEM;
 
-        for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
+        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);
                 *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
@@ -649,58 +651,74 @@ ssize_t base64mem_full(
         return z - r;
 }
 
-static int base64_append_width(
-                char **prefix, int plen,
-                char sep, int indent,
-                const void *p, size_t l,
-                int width) {
+static ssize_t base64_append_width(
+                char **prefix,
+                size_t plen,
+                char sep,
+                size_t indent,
+                const void *p,
+                size_t l,
+                size_t width) {
 
         _cleanup_free_ char *x = NULL;
         char *t, *s;
-        ssize_t len, avail, line, lines;
+        size_t lines;
+        ssize_t len;
+
+        assert(prefix);
+        assert(*prefix || plen == 0);
+        assert(p || l == 0);
 
         len = base64mem(p, l, &x);
-        if (len <= 0)
+        if (len < 0)
                 return len;
+        if (len == 0)
+                return plen;
 
         lines = DIV_ROUND_UP(len, width);
 
-        if ((size_t) plen >= SSIZE_MAX - 1 - 1 ||
+        if (plen >= SSIZE_MAX - 1 - 1 ||
             lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1))
                 return -ENOMEM;
 
-        t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines);
+        t = realloc(*prefix, plen + 1 + 1 + (indent + width + 1) * lines);
         if (!t)
                 return -ENOMEM;
 
-        t[plen] = sep;
+        s = t + plen;
+        for (size_t line = 0; line < lines; line++) {
+                size_t act = MIN(width, (size_t) len);
 
-        for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) {
-                int act = MIN(width, avail);
+                if (line > 0)
+                        sep = '\n';
 
-                if (line > 0 || sep == '\n') {
-                        memset(s, ' ', indent);
-                        s += indent;
+                if (s > t) {
+                        *s++ = sep;
+                        if (sep == '\n')
+                                s = mempset(s, ' ', indent);
                 }
 
                 s = mempcpy(s, x + width * line, act);
-                *(s++) = line < lines - 1 ? '\n' : '\0';
-                avail -= act;
+                len -= act;
         }
-        assert(avail == 0);
+        assert(len == 0);
 
+        *s = '\0';
         *prefix = t;
-        return 0;
+        return s - t;
 }
 
-int base64_append(
-                char **prefix, int plen,
-                const void *p, size_t l,
-                int indent, int width) {
+ssize_t base64_append(
+                char **prefix,
+                size_t plen,
+                const void *p,
+                size_t l,
+                size_t indent,
+                size_t width) {
 
         if (plen > width / 2 || plen + indent > width)
                 /* leave indent on the left, keep last column free */
-                return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1);
+                return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent);
         else
                 /* leave plen on the left, keep last column free */
                 return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1);
index 5218f78665a06b17bd6dbed5fadec233843ee5ea..319b21a17c658d704ac36da111895fa2d01503d5 100644 (file)
@@ -38,9 +38,13 @@ static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
         return base64mem_full(p, l, SIZE_MAX, ret);
 }
 
-int base64_append(char **prefix, int plen,
-                  const void *p, size_t l,
-                  int margin, int width);
+ssize_t base64_append(
+                char **prefix,
+                size_t plen,
+                const void *p,
+                size_t l,
+                size_t margin,
+                size_t width);
 int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
 static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
         return unbase64mem_full(p, l, false, mem, len);
index 8ee5c0cc209330ecbaed426c273f28b0aa04e560..07ac87c6c4020d18043ca2ff0c163d5a1405e693 100644 (file)
@@ -921,7 +921,7 @@ int in_addr_prefix_from_string_auto_internal(
 
 }
 
-static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
+void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
         assert(a);
         assert(state);
 
@@ -929,7 +929,7 @@ static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash
         siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
 }
 
-static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
+int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
         int r;
 
         assert(x);
@@ -942,7 +942,18 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
         return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
 }
 
-DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
+DEFINE_HASH_OPS(
+        in_addr_data_hash_ops,
+        struct in_addr_data,
+        in_addr_data_hash_func,
+        in_addr_data_compare_func);
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+        in_addr_data_hash_ops_free,
+        struct in_addr_data,
+        in_addr_data_hash_func,
+        in_addr_data_compare_func,
+        free);
 
 void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
         assert(addr);
@@ -958,7 +969,12 @@ int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
         return memcmp(a, b, sizeof(*a));
 }
 
-DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);
+DEFINE_HASH_OPS(
+        in6_addr_hash_ops,
+        struct in6_addr,
+        in6_addr_hash_func,
+        in6_addr_compare_func);
+
 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
         in6_addr_hash_ops_free,
         struct in6_addr,
index 8d6efaa006253eb2beaa4b5b8d050eb803de652d..19354ad404f560966ff1310f8cfeaf7e8a9a67af 100644 (file)
@@ -177,10 +177,13 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
  * See also oss-fuzz#11344. */
 #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
 
+void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state);
+int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y);
 void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
 int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
 
 extern const struct hash_ops in_addr_data_hash_ops;
+extern const struct hash_ops in_addr_data_hash_ops_free;
 extern const struct hash_ops in6_addr_hash_ops;
 extern const struct hash_ops in6_addr_hash_ops_free;
 
index dc682688a7c358be4c0c0b3d18c9b6922b722341..09fbdcfebec31503c5b8dff4fd341012adc5ce91 100644 (file)
@@ -37,7 +37,6 @@ int name_to_handle_at_loop(
                 int *ret_mnt_id,
                 int flags) {
 
-        _cleanup_free_ struct file_handle *h = NULL;
         size_t n = ORIGINAL_MAX_HANDLE_SZ;
 
         assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
@@ -50,6 +49,7 @@ int name_to_handle_at_loop(
          * as NULL if there's no interest in either. */
 
         for (;;) {
+                _cleanup_free_ struct file_handle *h = NULL;
                 int mnt_id = -1;
 
                 h = malloc0(offsetof(struct file_handle, f_handle) + n);
@@ -91,8 +91,6 @@ int name_to_handle_at_loop(
                 n = h->handle_bytes;
                 if (offsetof(struct file_handle, f_handle) + n < n) /* check for addition overflow */
                         return -EOVERFLOW;
-
-                h = mfree(h);
         }
 }
 
@@ -524,6 +522,52 @@ int dev_is_devtmpfs(void) {
         return false;
 }
 
+int mount_fd(const char *source,
+             int target_fd,
+             const char *filesystemtype,
+             unsigned long mountflags,
+             const void *data) {
+
+        if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                /* ENOENT can mean two things: either that the source is missing, or that /proc/ isn't
+                 * mounted. Check for the latter to generate better error messages. */
+                if (proc_mounted() == 0)
+                        return -ENOSYS;
+
+                return -ENOENT;
+        }
+
+        return 0;
+}
+
+int mount_nofollow(
+                const char *source,
+                const char *target,
+                const char *filesystemtype,
+                unsigned long mountflags,
+                const void *data) {
+
+        _cleanup_close_ int fd = -1;
+
+        /* In almost all cases we want to manipulate the mount table without following symlinks, hence
+         * mount_nofollow() is usually the way to go. The only exceptions are environments where /proc/ is
+         * not available yet, since we need /proc/self/fd/ for this logic to work. i.e. during the early
+         * initialization of namespacing/container stuff where /proc is not yet mounted (and maybe even the
+         * fs to mount) we can only use traditional mount() directly.
+         *
+         * Note that this disables following only for the final component of the target, i.e symlinks within
+         * the path of the target are honoured, as are symlinks in the source path everywhere. */
+
+        fd = open(target, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+        if (fd < 0)
+                return -errno;
+
+        return mount_fd(source, fd, filesystemtype, mountflags, data);
+}
+
 const char *mount_propagation_flags_to_string(unsigned long flags) {
 
         switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
index e2b493aa20f08ca91ea618a0546b58a3900c4e57..da5d852ff3ef1e1fb7f7e1e2dadef13a27b06e68 100644 (file)
@@ -5,6 +5,36 @@
 #include <stdbool.h>
 #include <sys/types.h>
 
+/* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't
+ * consume any space and udev isn't supposed to create regular file either. There's no limit on the
+ * max number of inodes since such limit is hard to guess especially on large storage array
+ * systems. */
+#define TMPFS_LIMITS_DEV             ",size=4m"
+
+/* The limit used for /dev in private namespaces. 4MB for contents of regular files. The number of
+ * inodes should be relatively low in private namespaces but for now use a 64k limit. */
+#define TMPFS_LIMITS_PRIVATE_DEV     ",size=4m,nr_inodes=64k"
+
+/* Very little, if any use expected */
+#define TMPFS_LIMITS_EMPTY_OR_ALMOST ",size=4m,nr_inodes=1k"
+#define TMPFS_LIMITS_SYS             TMPFS_LIMITS_EMPTY_OR_ALMOST
+#define TMPFS_LIMITS_SYS_FS_CGROUP   TMPFS_LIMITS_EMPTY_OR_ALMOST
+
+/* On an extremely small device with only 256MB of RAM, 20% of RAM should be enough for the re-execution of
+ * PID1 because 16MB of free space is required. */
+#define TMPFS_LIMITS_RUN             ",size=20%,nr_inodes=800k"
+
+/* The limit used for various nested tmpfs mounts, in particular for guests started by systemd-nspawn.
+ * 10% of RAM (using 16GB of RAM as a baseline) translates to 400k inodes (assuming 4k each) and 25%
+ * translates to 1M inodes.
+ * (On the host, /tmp is configured through a .mount unit file.) */
+#define NESTED_TMPFS_LIMITS          ",size=10%,nr_inodes=400k"
+
+/* More space for volatile root and /var */
+#define TMPFS_LIMITS_VAR             ",size=25%,nr_inodes=1m"
+#define TMPFS_LIMITS_ROOTFS          TMPFS_LIMITS_VAR
+#define TMPFS_LIMITS_VOLATILE_STATE  TMPFS_LIMITS_VAR
+
 int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
 
 int path_get_mnt_id(const char *path, int *ret);
@@ -22,5 +52,8 @@ bool fstype_can_uid_gid(const char *fstype);
 
 int dev_is_devtmpfs(void);
 
+int mount_fd(const char *source, int target_fd, const char *filesystemtype, unsigned long mountflags, const void *data);
+int mount_nofollow(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data);
+
 const char *mount_propagation_flags_to_string(unsigned long flags);
 int mount_propagation_flags_from_string(const char *name, unsigned long *ret);
index 183ff8b84eb4f7b408047f82028b3bc909ffccf5..009f3f531765fd5371487fca62a413b5ef04b6bf 100644 (file)
@@ -36,6 +36,7 @@
 #include "memory-util.h"
 #include "missing_sched.h"
 #include "missing_syscall.h"
+#include "mountpoint-util.h"
 #include "namespace-util.h"
 #include "nulstr-util.h"
 #include "parse-util.h"
@@ -1252,15 +1253,26 @@ int safe_fork_full(
         }
 
         if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) {
-
                 /* Optionally, make sure we never propagate mounts to the host. */
-
                 if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
                         log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m");
                         _exit(EXIT_FAILURE);
                 }
         }
 
+        if (FLAGS_SET(flags, FORK_PRIVATE_TMP)) {
+                assert(FLAGS_SET(flags, FORK_NEW_MOUNTNS));
+
+                /* Optionally, overmount new tmpfs instance on /tmp/. */
+                r = mount_nofollow("tmpfs", "/tmp", "tmpfs",
+                                   MS_NOSUID|MS_NODEV,
+                                   "mode=01777" TMPFS_LIMITS_RUN);
+                if (r < 0) {
+                        log_full_errno(prio, r, "Failed to overmount /tmp/: %m");
+                        _exit(EXIT_FAILURE);
+                }
+        }
+
         if (flags & FORK_CLOSE_ALL_FDS) {
                 /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */
                 log_close();
index 83663670ac485214adbac653865a9f09c4ed9831..8f265e703d44e54f0fa6ae430cdf45110978a46e 100644 (file)
@@ -147,11 +147,12 @@ typedef enum ForkFlags {
         FORK_WAIT               = 1 <<  7, /* Wait until child exited */
         FORK_NEW_MOUNTNS        = 1 <<  8, /* Run child in its own mount namespace */
         FORK_MOUNTNS_SLAVE      = 1 <<  9, /* Make child's mount namespace MS_SLAVE */
-        FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
-        FORK_STDOUT_TO_STDERR   = 1 << 11, /* Make stdout a copy of stderr */
-        FORK_FLUSH_STDIO        = 1 << 12, /* fflush() stdout (and stderr) before forking */
-        FORK_NEW_USERNS         = 1 << 13, /* Run child in its own user namespace */
-        FORK_CLOEXEC_OFF        = 1 << 14, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
+        FORK_PRIVATE_TMP        = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
+        FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
+        FORK_STDOUT_TO_STDERR   = 1 << 12, /* Make stdout a copy of stderr */
+        FORK_FLUSH_STDIO        = 1 << 13, /* fflush() stdout (and stderr) before forking */
+        FORK_NEW_USERNS         = 1 << 14, /* Run child in its own user namespace */
+        FORK_CLOEXEC_OFF        = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
 } ForkFlags;
 
 int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
index c16c8f7103ae0bb76eea1f56fe4ba7636303aaf3..f90a63b1a90882c09b0dd49db08408352d4c251d 100644 (file)
@@ -10,6 +10,7 @@
 
 bool ratelimit_below(RateLimit *r) {
         usec_t ts;
+        bool good = false;
 
         assert(r);
 
@@ -24,18 +25,12 @@ bool ratelimit_below(RateLimit *r) {
 
                 /* Reset counter */
                 r->num = 0;
-                goto good;
-        }
+                good = true;
+        } else if (r->num < r->burst)
+                good = true;
 
-        if (r->num < r->burst)
-                goto good;
-
-        r->num++;
-        return false;
-
-good:
         r->num++;
-        return true;
+        return good;
 }
 
 unsigned ratelimit_num_dropped(RateLimit *r) {
index 2e36e1a56b6af9cec567fd73fab7b707af3c196e..0b8d53e8954257d03a3b523c2fe41b8a7170220f 100644 (file)
@@ -175,15 +175,17 @@ int flush_accept(int fd);
 #define CMSG_FOREACH(cmsg, mh)                                          \
         for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
 
+#define CMSG_TYPED_DATA(cmsg, type)                                     \
+        ({                                                              \
+                struct cmsghdr *_cmsg = cmsg;                           \
+                _cmsg ? CAST_ALIGN_PTR(type, CMSG_DATA(_cmsg)) : (type*) NULL; \
+        })
+
 struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
 
 /* Type-safe, dereferencing version of cmsg_find() */
-#define CMSG_FIND_DATA(mh, level, type, ctype) \
-        ({                                                            \
-                struct cmsghdr *_found;                               \
-                _found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \
-                (ctype*) (_found ? CMSG_DATA(_found) : NULL);         \
-        })
+#define CMSG_FIND_DATA(mh, level, type, ctype)                          \
+        CMSG_TYPED_DATA(cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))), ctype)
 
 /* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type
  * itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr"
index 97dbcaac66a3348bd7a1b87985f0a532c7c683c2..0974f6178ab7845fef5cdb5fd726ae0fcffce81e 100644 (file)
@@ -163,26 +163,37 @@ int null_or_empty_fd(int fd) {
         return null_or_empty(&st);
 }
 
-int path_is_read_only_fs(const char *path) {
+static int fd_is_read_only_fs(int fd) {
         struct statvfs st;
 
-        assert(path);
+        assert(fd >= 0);
 
-        if (statvfs(path, &st) < 0)
+        if (fstatvfs(fd, &st) < 0)
                 return -errno;
 
         if (st.f_flag & ST_RDONLY)
                 return true;
 
-        /* On NFS, statvfs() might not reflect whether we can actually
-         * write to the remote share. Let's try again with
-         * access(W_OK) which is more reliable, at least sometimes. */
-        if (access(path, W_OK) < 0 && errno == EROFS)
+        /* On NFS, fstatvfs() might not reflect whether we can actually write to the remote share. Let's try
+         * again with access(W_OK) which is more reliable, at least sometimes. */
+        if (access_fd(fd, W_OK) == -EROFS)
                 return true;
 
         return false;
 }
 
+int path_is_read_only_fs(const char *path) {
+        _cleanup_close_ int fd = -EBADFD;
+
+        assert(path);
+
+        fd = open(path, O_CLOEXEC | O_PATH);
+        if (fd < 0)
+                return -errno;
+
+        return fd_is_read_only_fs(fd);
+}
+
 int files_same(const char *filea, const char *fileb, int flags) {
         struct stat a, b;
 
index 90d18f70ba441d5085aa17b31f34a1f562a55400..6f0e1cc2b24a88328da71b89a9d2f74b11e4801e 100644 (file)
@@ -15,12 +15,12 @@ static inline void umaskp(mode_t *u) {
 
 /* We make use of the fact here that the umask() concept is using only the lower 9 bits of mode_t, although
  * mode_t has space for the file type in the bits further up. We simply OR in the file type mask S_IFMT to
- * distinguish the first and the second iteration of the RUN_WITH_UMASK() loop, so that we can run the first
- * one, and exit on the second. */
+ * distinguish the first and the second iteration of the WITH_UMASK() loop, so that we can run the first one,
+ * and exit on the second. */
 
 assert_cc((S_IFMT & 0777) == 0);
 
-#define RUN_WITH_UMASK(mask)                                            \
+#define WITH_UMASK(mask)                                            \
         for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \
              FLAGS_SET(_saved_umask_, S_IFMT);                          \
              _saved_umask_ &= 0777)
index 4100be08031fb1f3a566b42c44c002af1e1d9a70..04580cfb636b5b7d1130dc936862170fc38100f2 100644 (file)
@@ -4,6 +4,8 @@
 #include <endian.h>
 #include <stdint.h>
 
+#include "unaligned-fundamental.h"
+
 /* BE */
 
 static inline uint16_t unaligned_read_be16(const void *_u) {
@@ -79,21 +81,3 @@ static inline void unaligned_write_le64(void *_u, uint64_t a) {
 
         u->x = le64toh(a);
 }
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-#define unaligned_read_ne16 unaligned_read_be16
-#define unaligned_read_ne32 unaligned_read_be32
-#define unaligned_read_ne64 unaligned_read_be64
-
-#define unaligned_write_ne16 unaligned_write_be16
-#define unaligned_write_ne32 unaligned_write_be32
-#define unaligned_write_ne64 unaligned_write_be64
-#else
-#define unaligned_read_ne16 unaligned_read_le16
-#define unaligned_read_ne32 unaligned_read_le32
-#define unaligned_read_ne64 unaligned_read_le64
-
-#define unaligned_write_ne16 unaligned_write_le16
-#define unaligned_write_ne32 unaligned_write_le32
-#define unaligned_write_ne64 unaligned_write_le64
-#endif
index ba209a8d470a43e3903805edc99a375da73cae8a..624c63ec98b33f8db92b64138b0b6e1428f13de7 100644 (file)
@@ -190,6 +190,18 @@ static int parse_argv(int argc, char *argv[]) {
         return 1;
 }
 
+static int binfmt_mounted_warn(void) {
+        int r;
+
+        r = binfmt_mounted();
+        if (r < 0)
+                return log_error_errno(r, "Failed to check if /proc/sys/fs/binfmt_misc is mounted: %m");
+        if (r == 0)
+                log_debug("/proc/sys/fs/binfmt_misc is not mounted in read-write mode, skipping.");
+
+        return r;
+}
+
 static int run(int argc, char *argv[]) {
         int r, k;
 
@@ -206,13 +218,17 @@ static int run(int argc, char *argv[]) {
         if (arg_unregister)
                 return disable_binfmt();
 
-        if (argc > optind)
+        if (argc > optind) {
+                r = binfmt_mounted_warn();
+                if (r <= 0)
+                        return r;
+
                 for (int i = optind; i < argc; i++) {
                         k = apply_file(argv[i], false);
                         if (k < 0 && r >= 0)
                                 r = k;
                 }
-        else {
+        else {
                 _cleanup_strv_free_ char **files = NULL;
 
                 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("binfmt.d"));
@@ -225,6 +241,10 @@ static int run(int argc, char *argv[]) {
                         return cat_files(NULL, files, 0);
                 }
 
+                r = binfmt_mounted_warn();
+                if (r <= 0)
+                        return r;
+
                 /* Flush out all rules */
                 r = write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", WRITE_STRING_FILE_DISABLE_BUFFER);
                 if (r < 0)
index 0df456827c1977763ddafbf366a947386c0ff06e..7721c3d2e38ed16c2baf96a263ecc9e2409be9e1 100644 (file)
@@ -179,7 +179,7 @@ static int load_etc_machine_id(void) {
         int r;
 
         r = sd_id128_get_machine(&arg_machine_id);
-        if (IN_SET(r, -ENOENT, -ENOMEDIUM)) /* Not set or empty */
+        if (IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG)) /* Not set or empty */
                 return 0;
         if (r < 0)
                 return log_error_errno(r, "Failed to get machine-id: %m");
@@ -828,7 +828,7 @@ static int copy_file_with_version_check(const char *from, const char *to, bool f
         if (r < 0)
                 return log_oom();
 
-        RUN_WITH_UMASK(0000) {
+        WITH_UMASK(0000) {
                 fd_to = open(t, O_WRONLY|O_CREAT|O_CLOEXEC|O_EXCL|O_NOFOLLOW, 0644);
                 if (fd_to < 0)
                         return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", t);
@@ -2073,7 +2073,7 @@ static int install_random_seed(const char *esp) {
         /* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
          * and possibly get identification information or too much insight into the kernel's entropy pool
          * state. */
-        RUN_WITH_UMASK(0077) {
+        WITH_UMASK(0077) {
                 r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
                 if (r < 0) {
                         if (!arg_graceful)
@@ -2147,7 +2147,7 @@ static int verb_install(int argc, char *argv[], void *userdata) {
 
         const char *arch = arg_arch_all ? "" : get_efi_arch();
 
-        RUN_WITH_UMASK(0002) {
+        WITH_UMASK(0002) {
                 if (install) {
                         /* Don't create any of these directories when we are just updating. When we update
                          * we'll drop-in our files (unless there are newer ones already), but we won't create
index 9123c9a84c96cc65f9f847968a4b6603f3062a6e..2e657a8bf91932177eaf615da1a4b3adf692aa6f 100644 (file)
@@ -1550,7 +1550,7 @@ static EFI_STATUS efivar_get_timeout(const char16_t *var, uint32_t *ret_value) {
 
 static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
         _cleanup_free_ char *content = NULL;
-        UINTN value;
+        UINTN value = 0;  /* avoid false maybe-uninitialized warning */
         EFI_STATUS err;
 
         assert(root_dir);
@@ -2233,7 +2233,7 @@ static void config_load_xbootldr(
                 EFI_HANDLE *device) {
 
         _cleanup_(file_closep) EFI_FILE *root_dir = NULL;
-        EFI_HANDLE new_device;
+        EFI_HANDLE new_device = NULL;  /* avoid false maybe-uninitialized warning */
         EFI_STATUS err;
 
         assert(config);
@@ -2638,12 +2638,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         /* Uncomment the next line if you need to wait for debugger. */
         // debug_break();
 
-        /* The firmware may skip initializing some devices for the sake of a faster boot. This is especially
-         * true for fastboot enabled firmwares. But this means that things we use like input devices or the
-         * xbootldr partition may not be available yet. Reconnect all drivers should hopefully make the
-         * firmware initialize everything we need. */
-        (void) reconnect_all_drivers();
-
         err = BS->OpenProtocol(image,
                         &LoadedImageProtocol,
                         (void **)&loaded_image,
index 14c0008afb47bf751a5d8644c196160a67cf5dd8..cd980fd535f8d417e84659778146909c06b002a5 100644 (file)
 #define VERTICAL_MAX_OK 1080
 #define VIEWPORT_RATIO 10
 
+static EFI_STATUS console_connect(void) {
+        EFI_BOOT_MANAGER_POLICY_PROTOCOL *boot_policy;
+        EFI_STATUS err;
+
+        /* This should make console devices appear/fully initialize on fastboot firmware. */
+
+        err = BS->LocateProtocol(
+                        &(EFI_GUID) EFI_BOOT_MANAGER_POLICY_PROTOCOL_GUID, NULL, (void **) &boot_policy);
+        if (err != EFI_SUCCESS)
+                return err;
+
+        return boot_policy->ConnectDeviceClass(boot_policy, &(EFI_GUID) EFI_BOOT_MANAGER_POLICY_CONSOLE_GUID);
+}
+
 static inline void event_closep(EFI_EVENT *event) {
         if (!*event)
                 return;
@@ -47,6 +61,8 @@ EFI_STATUS console_key_read(uint64_t *key, uint64_t timeout_usec) {
         assert(key);
 
         if (!checked) {
+                console_connect();
+
                 /* Get the *first* TextInputEx device.*/
                 err = BS->LocateProtocol(&SimpleTextInputExProtocol, NULL, (void **) &extraInEx);
                 if (err != EFI_SUCCESS || BS->CheckEvent(extraInEx->WaitForKeyEx) == EFI_INVALID_PARAMETER)
index 76e2cd7f4e0a48c8b386086636a71844cea5f51c..79b5d4327ba4b28ec586b885cd0e01ddba53187a 100644 (file)
@@ -468,7 +468,7 @@ EFI_STATUS pack_cpio(
 
         for (UINTN i = 0; i < n_items; i++) {
                 _cleanup_free_ char *content = NULL;
-                UINTN contentsize;
+                UINTN contentsize = 0;  /* avoid false maybe-uninitialized warning */
 
                 err = file_read(extra_dir, items[i], 0, 0, &content, &contentsize);
                 if (err != EFI_SUCCESS) {
index 250c84c2486551b5115ca111ad66be0bb31cf77e..b446e0399f053426334a6e1a48088966072a70fe 100644 (file)
@@ -398,3 +398,22 @@ typedef struct {
         void *StdErr;
 } EFI_SHELL_PARAMETERS_PROTOCOL;
 #endif
+
+#ifndef EFI_BOOT_MANAGER_POLICY_PROTOCOL_GUID
+#define EFI_BOOT_MANAGER_POLICY_PROTOCOL_GUID \
+        { 0xFEDF8E0C, 0xE147, 0x11E3, { 0x99, 0x03, 0xB8, 0xE8, 0x56, 0x2C, 0xBA, 0xFA } }
+#define EFI_BOOT_MANAGER_POLICY_CONSOLE_GUID \
+        { 0xCAB0E94C, 0xE15F, 0x11E3, { 0x91, 0x8D, 0xB8, 0xE8, 0x56, 0x2C, 0xBA, 0xFA } }
+
+typedef struct EFI_BOOT_MANAGER_POLICY_PROTOCOL EFI_BOOT_MANAGER_POLICY_PROTOCOL;
+struct EFI_BOOT_MANAGER_POLICY_PROTOCOL {
+        UINT64 Revision;
+        EFI_STATUS (EFIAPI *ConnectDevicePath)(
+                EFI_BOOT_MANAGER_POLICY_PROTOCOL *This,
+                EFI_DEVICE_PATH *DevicePath,
+                BOOLEAN Recursive);
+        EFI_STATUS (EFIAPI *ConnectDeviceClass)(
+                EFI_BOOT_MANAGER_POLICY_PROTOCOL *This,
+                EFI_GUID *Class);
+};
+#endif
index de6d6112a13280cc1d24c7db64f5e071c9ee6cb8..14479c06eaee937ec2d48755da910cf938c36784 100644 (file)
@@ -202,6 +202,10 @@ static EFI_STATUS find_device(const EFI_GUID *type, EFI_HANDLE *device, EFI_DEVI
         if (err != EFI_SUCCESS)
                 return err;
 
+        /* The drivers for other partitions on this drive may not be initialized on fastboot firmware, so we
+         * have to ask the firmware to do just that. */
+        (void) BS->ConnectController(disk_handle, NULL, NULL, true);
+
         err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io);
         if (err != EFI_SUCCESS)
                 return err;
index 65457bf4232efc3b65bf2fcf285dd48749f053ce..62128681341a1cec8714a236017e1f2f2c57b73b 100644 (file)
@@ -6,7 +6,7 @@
 #include "util.h"
 
 bool secure_boot_enabled(void) {
-        bool secure;
+        bool secure = false;  /* avoid false maybe-uninitialized warning */
         EFI_STATUS err;
 
         err = efivar_get_boolean_u8(EFI_GLOBAL_GUID, L"SecureBoot", &secure);
index f9aeeb483310b54d98499888ac946cb79c44c65c..1f07fbc38c44cd64dc62bb88b531954674acc88a 100644 (file)
@@ -91,7 +91,7 @@ EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const char16_t *name, ui
         return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
 }
 
-EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **value) {
+EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **ret) {
         _cleanup_free_ char16_t *buf = NULL;
         EFI_STATUS err;
         char16_t *val;
@@ -108,12 +108,12 @@ EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **v
         if ((size % sizeof(char16_t)) != 0)
                 return EFI_INVALID_PARAMETER;
 
-        if (!value)
+        if (!ret)
                 return EFI_SUCCESS;
 
         /* Return buffer directly if it happens to be NUL terminated already */
         if (size >= sizeof(char16_t) && buf[size / sizeof(char16_t) - 1] == 0) {
-                *value = TAKE_PTR(buf);
+                *ret = TAKE_PTR(buf);
                 return EFI_SUCCESS;
         }
 
@@ -123,18 +123,17 @@ EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **v
         memcpy(val, buf, size);
         val[size / sizeof(char16_t) - 1] = 0; /* NUL terminate */
 
-        *value = val;
+        *ret = val;
         return EFI_SUCCESS;
 }
 
-EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, UINTN *i) {
+EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, UINTN *ret) {
         _cleanup_free_ char16_t *val = NULL;
         EFI_STATUS err;
         uint64_t u;
 
         assert(vendor);
         assert(name);
-        assert(i);
 
         err = efivar_get(vendor, name, &val);
         if (err != EFI_SUCCESS)
@@ -143,7 +142,8 @@ EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name,
         if (!parse_number16(val, &u, NULL) || u > UINTN_MAX)
                 return EFI_INVALID_PARAMETER;
 
-        *i = u;
+        if (ret)
+                *ret = u;
         return EFI_SUCCESS;
 }
 
@@ -156,15 +156,17 @@ EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, ui
         assert(name);
 
         err = efivar_get_raw(vendor, name, &buf, &size);
-        if (err == EFI_SUCCESS && ret) {
-                if (size != sizeof(uint32_t))
-                        return EFI_BUFFER_TOO_SMALL;
+        if (err != EFI_SUCCESS)
+                return err;
 
+        if (size != sizeof(uint32_t))
+                return EFI_BUFFER_TOO_SMALL;
+
+        if (ret)
                 *ret = (uint32_t) buf[0] << 0U | (uint32_t) buf[1] << 8U | (uint32_t) buf[2] << 16U |
                         (uint32_t) buf[3] << 24U;
-        }
 
-        return err;
+        return EFI_SUCCESS;
 }
 
 EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret) {
@@ -176,19 +178,21 @@ EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, ui
         assert(name);
 
         err = efivar_get_raw(vendor, name, &buf, &size);
-        if (err == EFI_SUCCESS && ret) {
-                if (size != sizeof(uint64_t))
-                        return EFI_BUFFER_TOO_SMALL;
+        if (err != EFI_SUCCESS)
+                return err;
+
+        if (size != sizeof(uint64_t))
+                return EFI_BUFFER_TOO_SMALL;
 
+        if (ret)
                 *ret = (uint64_t) buf[0] << 0U | (uint64_t) buf[1] << 8U | (uint64_t) buf[2] << 16U |
                         (uint64_t) buf[3] << 24U | (uint64_t) buf[4] << 32U | (uint64_t) buf[5] << 40U |
                         (uint64_t) buf[6] << 48U | (uint64_t) buf[7] << 56U;
-        }
 
-        return err;
+        return EFI_SUCCESS;
 }
 
-EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **buffer, UINTN *size) {
+EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **ret, UINTN *ret_size) {
         _cleanup_free_ char *buf = NULL;
         UINTN l;
         EFI_STATUS err;
@@ -200,16 +204,15 @@ EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **b
         buf = xmalloc(l);
 
         err = RT->GetVariable((char16_t *) name, (EFI_GUID *) vendor, NULL, &l, buf);
-        if (err == EFI_SUCCESS) {
-
-                if (buffer)
-                        *buffer = TAKE_PTR(buf);
+        if (err != EFI_SUCCESS)
+                return err;
 
-                if (size)
-                        *size = l;
-        }
+        if (ret)
+                *ret = TAKE_PTR(buf);
+        if (ret_size)
+                *ret_size = l;
 
-        return err;
+        return EFI_SUCCESS;
 }
 
 EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret) {
@@ -219,13 +222,15 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, b
 
         assert(vendor);
         assert(name);
-        assert(ret);
 
         err = efivar_get_raw(vendor, name, &b, &size);
-        if (err == EFI_SUCCESS)
+        if (err != EFI_SUCCESS)
+                return err;
+
+        if (ret)
                 *ret = *b > 0;
 
-        return err;
+        return EFI_SUCCESS;
 }
 
 void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec) {
index 08f732f484e163a78b1cbe6b88d93d9d30e8a4f5..fb525ba63630341c55f29a73903a6c5e8df1c46c 100644 (file)
@@ -130,9 +130,9 @@ EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const char16_t *NAME, ui
 EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t value, uint32_t flags);
 void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec);
 
-EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **value);
-EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **buffer, UINTN *size);
-EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, UINTN *i);
+EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **ret);
+EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **ret, UINTN *ret_size);
+EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, UINTN *ret);
 EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t *ret);
 EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret);
 EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret);
index b1bfd778fc490ed6733d5fd8607779843742785a..2260b217b79b318cdf09cfb3d2f7f453ee1b73fa 100644 (file)
@@ -83,6 +83,10 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
         assert(ret_vmm_dev);
         assert(ret_vmm_dir);
 
+        /* Make sure all file systems have been initialized. Only do this in VMs as this is slow
+         * on some real firmwares. */
+        (void) reconnect_all_drivers();
+
         /* find all file system handles */
         err = BS->LocateHandleBuffer(ByProtocol, &FileSystemProtocol, NULL, &n_handles, &handles);
         if (err != EFI_SUCCESS)
index a248205f3f30cceb559dd7a17aecd20821073710..3da4a1390b989a83888aa7f6e77c28085e79bae0 100644 (file)
@@ -676,7 +676,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
 }
 
 int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) {
-        Context context = {
+        _cleanup_(context_reset_interface) Context context = {
                 .ops = ops,
                 .userdata = userdata,
                 .current = xml,
@@ -692,36 +692,24 @@ int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospec
                 _cleanup_free_ char *name = NULL;
 
                 r = xml_tokenize(&context.current, &name, &context.xml_state, NULL);
-                if (r < 0) {
-                        log_error("XML parse error");
-                        goto finish;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "XML parse error");
 
-                if (r == XML_END) {
-                        r = 0;
+                if (r == XML_END)
                         break;
-                }
 
                 if (r == XML_TAG_OPEN) {
 
                         if (streq(name, "node")) {
                                 r = parse_xml_node(&context, prefix, 0);
                                 if (r < 0)
-                                        goto finish;
-                        } else {
-                                log_error("Unexpected tag '%s' in introspection data.", name);
-                                r = -EBADMSG;
-                                goto finish;
-                        }
-                } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) {
-                        log_error("Unexpected token.");
-                        r = -EBADMSG;
-                        goto finish;
-                }
+                                        return r;
+                        } else
+                                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                                       "Unexpected tag '%s' in introspection data.", name);
+                } else if (r != XML_TEXT || !in_charset(name, WHITESPACE))
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected token.");
         }
 
-finish:
-        context_reset_interface(&context);
-
-        return r;
+        return 0;
 }
index 1e9cb758de93a12340336419f5672ba26fb9efa7..ecc3cb32effa4b97481aaea4a46dd7b8d220fbb7 100644 (file)
@@ -4167,7 +4167,7 @@ int compare_job_priority(const void *a, const void *b) {
 int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
         _cleanup_free_ char *path = NULL;
         FreezerState target, kernel = _FREEZER_STATE_INVALID;
-        int r;
+        int r, ret;
 
         assert(u);
         assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
@@ -4175,9 +4175,23 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
         if (!cg_freezer_supported())
                 return 0;
 
+        /* Ignore all requests to thaw init.scope or -.slice and reject all requests to freeze them */
+        if (unit_has_name(u, SPECIAL_ROOT_SLICE) || unit_has_name(u, SPECIAL_INIT_SCOPE))
+                return action == FREEZER_FREEZE ? -EPERM : 0;
+
         if (!u->cgroup_realized)
                 return -EBUSY;
 
+        if (action == FREEZER_THAW) {
+                Unit *slice = UNIT_GET_SLICE(u);
+
+                if (slice) {
+                        r = unit_cgroup_freezer_action(slice, FREEZER_THAW);
+                        if (r < 0)
+                                return log_unit_error_errno(u, r, "Failed to thaw slice %s of unit: %m", slice->id);
+                }
+        }
+
         target = action == FREEZER_FREEZE ? FREEZER_FROZEN : FREEZER_RUNNING;
 
         r = unit_freezer_state_kernel(u, &kernel);
@@ -4186,8 +4200,11 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
 
         if (target == kernel) {
                 u->freezer_state = target;
-                return 0;
-        }
+                if (action == FREEZER_FREEZE)
+                        return 0;
+                ret = 0;
+        } else
+                ret = 1;
 
         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.freeze", &path);
         if (r < 0)
@@ -4195,16 +4212,18 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
 
         log_unit_debug(u, "%s unit.", action == FREEZER_FREEZE ? "Freezing" : "Thawing");
 
-        if (action == FREEZER_FREEZE)
-                u->freezer_state = FREEZER_FREEZING;
-        else
-                u->freezer_state = FREEZER_THAWING;
+        if (target != kernel) {
+                if (action == FREEZER_FREEZE)
+                        u->freezer_state = FREEZER_FREEZING;
+                else
+                        u->freezer_state = FREEZER_THAWING;
+        }
 
         r = write_string_file(path, one_zero(action == FREEZER_FREEZE), WRITE_STRING_FILE_DISABLE_BUFFER);
         if (r < 0)
                 return r;
 
-        return 1;
+        return ret;
 }
 
 int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) {
index ab2617153a6ccec674e4ec3b9c8531263f77815c..79e96f948c0282177d9eaf088deebbc8d14557de 100644 (file)
@@ -1451,6 +1451,29 @@ int verify_run_space_and_log(const char *message) {
         return 0;
 }
 
+static void log_reload_caller(sd_bus_message *message, Manager *manager) {
+        _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+        const char *comm = NULL;
+        Unit *caller;
+        pid_t pid;
+
+        assert(message);
+        assert(manager);
+
+        if (sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID|SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_COMM, &creds) < 0)
+                return;
+
+        /* We need at least the PID, otherwise there's nothing to log, the rest is optional */
+        if (sd_bus_creds_get_pid(creds, &pid) < 0)
+                return;
+
+        (void) sd_bus_creds_get_comm(creds, &comm);
+        caller = manager_get_unit_by_pid(manager, pid);
+
+        log_info("Reloading requested from client PID " PID_FMT " ('%s') (from unit '%s')...",
+                 pid, strna(comm), strna(caller ? caller->id : NULL));
+}
+
 static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Manager *m = ASSERT_PTR(userdata);
         int r;
@@ -1471,6 +1494,17 @@ static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *
         if (r == 0)
                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
 
+        /* Write a log message noting the unit or process who requested the Reload() */
+        log_reload_caller(message, m);
+
+        /* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */
+        if (!ratelimit_below(&m->reload_ratelimit)) {
+                log_warning("Reloading request rejected due to rate limit.");
+                return sd_bus_error_setf(error,
+                                         SD_BUS_ERROR_LIMITS_EXCEEDED,
+                                         "Reload() request rejected due to rate limit.");
+        }
+
         /* Instead of sending the reply back right away, we just
          * remember that we need to and then send it after the reload
          * is finished. That way the caller knows when the reload
@@ -2425,6 +2459,7 @@ static int method_disable_unit_files_generic(
                 sd_bus_message *message,
                 Manager *m,
                 int (*call)(LookupScope scope, UnitFileFlags flags, const char *root_dir, char *files[], InstallChange **changes, size_t *n_changes),
+                bool carries_install_info,
                 sd_bus_error *error) {
 
         _cleanup_strv_free_ char **l = NULL;
@@ -2440,7 +2475,8 @@ static int method_disable_unit_files_generic(
         if (r < 0)
                 return r;
 
-        if (sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlags")) {
+        if (sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlags") ||
+            sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlagsAndInstallInfo")) {
                 uint64_t raw_flags;
 
                 r = sd_bus_message_read(message, "t", &raw_flags);
@@ -2469,19 +2505,23 @@ static int method_disable_unit_files_generic(
         if (r < 0)
                 return install_error(error, r, changes, n_changes);
 
-        return reply_install_changes_and_free(m, message, -1, changes, n_changes, error);
+        return reply_install_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes, error);
 }
 
 static int method_disable_unit_files_with_flags(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        return method_disable_unit_files_generic(message, userdata, unit_file_disable, error);
+        return method_disable_unit_files_generic(message, userdata, unit_file_disable, /* carries_install_info = */ false, error);
+}
+
+static int method_disable_unit_files_with_flags_and_install_info(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return method_disable_unit_files_generic(message, userdata, unit_file_disable, /* carries_install_info = */ true, error);
 }
 
 static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        return method_disable_unit_files_generic(message, userdata, unit_file_disable, error);
+        return method_disable_unit_files_generic(message, userdata, unit_file_disable, /* carries_install_info = */ false, error);
 }
 
 static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        return method_disable_unit_files_generic(message, userdata, unit_file_unmask, error);
+        return method_disable_unit_files_generic(message, userdata, unit_file_unmask, /* carries_install_info = */ false, error);
 }
 
 static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -2623,11 +2663,10 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
 
 static int method_get_unit_file_links(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);
         InstallChange *changes = NULL;
         size_t n_changes = 0, i;
-        UnitFileFlags flags;
         const char *name;
-        char **p;
         int runtime, r;
 
         r = sd_bus_message_read(message, "sb", &name, &runtime);
@@ -2642,11 +2681,9 @@ static int method_get_unit_file_links(sd_bus_message *message, void *userdata, s
         if (r < 0)
                 return r;
 
-        p = STRV_MAKE(name);
-        flags = UNIT_FILE_DRY_RUN |
-                (runtime ? UNIT_FILE_RUNTIME : 0);
-
-        r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, flags, NULL, p, &changes, &n_changes);
+        r = unit_file_disable(m->unit_file_scope,
+                              UNIT_FILE_DRY_RUN | (runtime ? UNIT_FILE_RUNTIME : 0),
+                              NULL, STRV_MAKE(name), &changes, &n_changes);
         if (r < 0) {
                 log_error_errno(r, "Failed to get file links for %s: %m", name);
                 goto finish;
@@ -3194,6 +3231,11 @@ const sd_bus_vtable bus_manager_vtable[] = {
                                 SD_BUS_RESULT("a(sss)", changes),
                                 method_disable_unit_files_with_flags,
                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("DisableUnitFilesWithFlagsAndInstallInfo",
+                                SD_BUS_ARGS("as", files, "t", flags),
+                                SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes),
+                                method_disable_unit_files_with_flags_and_install_info,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD_WITH_ARGS("ReenableUnitFiles",
                                 SD_BUS_ARGS("as", files, "b", runtime, "b", force),
                                 SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes),
index 19a71b6cb388f2ee447b0b380eff3d86f8bb452c..1ef98da6fd9f265fb0942d12534650b1ba95f952 100644 (file)
@@ -782,14 +782,15 @@ static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userda
         if (r == 0)
                 reply_no_delay = true;
 
-        assert(!u->pending_freezer_message);
+        if (u->pending_freezer_invocation) {
+                bus_unit_send_pending_freezer_message(u, true);
+                assert(!u->pending_freezer_invocation);
+        }
 
-        r = sd_bus_message_new_method_return(message, &u->pending_freezer_message);
-        if (r < 0)
-                return r;
+        u->pending_freezer_invocation = sd_bus_message_ref(message);
 
         if (reply_no_delay) {
-                r = bus_unit_send_pending_freezer_message(u);
+                r = bus_unit_send_pending_freezer_message(u, false);
                 if (r < 0)
                         return r;
         }
@@ -1661,19 +1662,31 @@ void bus_unit_send_pending_change_signal(Unit *u, bool including_new) {
         bus_unit_send_change_signal(u);
 }
 
-int bus_unit_send_pending_freezer_message(Unit *u) {
+int bus_unit_send_pending_freezer_message(Unit *u, bool cancelled) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         int r;
 
         assert(u);
 
-        if (!u->pending_freezer_message)
+        if (!u->pending_freezer_invocation)
                 return 0;
 
-        r = sd_bus_send(NULL, u->pending_freezer_message, NULL);
+        if (cancelled)
+                r = sd_bus_message_new_method_error(
+                                u->pending_freezer_invocation,
+                                &reply,
+                                &SD_BUS_ERROR_MAKE_CONST(
+                                                BUS_ERROR_FREEZE_CANCELLED, "Freeze operation aborted"));
+        else
+                r = sd_bus_message_new_method_return(u->pending_freezer_invocation, &reply);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_send(NULL, reply, NULL);
         if (r < 0)
                 log_warning_errno(r, "Failed to send queued message, ignoring: %m");
 
-        u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
+        u->pending_freezer_invocation = sd_bus_message_unref(u->pending_freezer_invocation);
 
         return 0;
 }
index 643edcd87e972a44a9efac907708e11a2111fc57..6b7828e4bae50e46518eef7521db7daf9496bed8 100644 (file)
@@ -10,7 +10,7 @@ extern const sd_bus_vtable bus_unit_cgroup_vtable[];
 
 void bus_unit_send_change_signal(Unit *u);
 void bus_unit_send_pending_change_signal(Unit *u, bool including_new);
-int bus_unit_send_pending_freezer_message(Unit *u);
+int bus_unit_send_pending_freezer_message(Unit *u, bool cancelled);
 void bus_unit_send_removed_signal(Unit *u);
 
 int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error);
index 141c3ffe12ae45249f59707b9a277b197ffc1f59..c6024a061ebcf2b731b5f5e9cce32ccb1a341219 100644 (file)
@@ -941,7 +941,7 @@ int bus_init_private(Manager *m) {
         if (fd < 0)
                 return log_error_errno(errno, "Failed to allocate private socket: %m");
 
-        RUN_WITH_UMASK(0077)
+        WITH_UMASK(0077)
                 r = bind(fd, &sa.sa, sa_len);
         if (r < 0)
                 return log_error_errno(errno, "Failed to bind private socket: %m");
@@ -998,8 +998,8 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
                         u->bus_track = sd_bus_track_unref(u->bus_track);
 
                 /* Get rid of pending freezer messages on this bus */
-                if (u->pending_freezer_message && sd_bus_message_get_bus(u->pending_freezer_message) == *bus)
-                        u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
+                if (u->pending_freezer_invocation && sd_bus_message_get_bus(u->pending_freezer_invocation) == *bus)
+                        u->pending_freezer_invocation = sd_bus_message_unref(u->pending_freezer_invocation);
         }
 
         /* Get rid of queued message on this bus */
index 8e70ecab8219947a0d81b738e10fab44e4c90f1c..7963582ea2f24095ee545249fa39965ef528fe3e 100644 (file)
@@ -2663,6 +2663,7 @@ static int load_credential(
         assert(id);
         assert(path);
         assert(unit);
+        assert(read_dfd >= 0 || read_dfd == AT_FDCWD);
         assert(write_dfd >= 0);
         assert(left);
 
@@ -2889,7 +2890,7 @@ static int acquire_credentials(
                                         lc->path,
                                         lc->encrypted,
                                         unit,
-                                        -1,
+                                        AT_FDCWD,
                                         dfd,
                                         uid,
                                         ownership_ok,
index bba6666a5281d45a55d3b451ae9079285c5bd1e6..b315dd0afa50c3c4cdf5c9786558a47c12e82a89 100644 (file)
@@ -556,6 +556,7 @@ Path.TriggerLimitBurst,                  config_parse_unsigned,
 Scope.RuntimeMaxSec,                     config_parse_sec,                            0,                                  offsetof(Scope, runtime_max_usec)
 Scope.RuntimeRandomizedExtraSec,         config_parse_sec,                            0,                                  offsetof(Scope, runtime_rand_extra_usec)
 Scope.TimeoutStopSec,                    config_parse_sec,                            0,                                  offsetof(Scope, timeout_stop_usec)
+Scope.OOMPolicy,                         config_parse_oom_policy,                     0,                                  offsetof(Scope, oom_policy)
 {# The [Install] section is ignored here #}
 Install.Alias,                           NULL,                                        0,                                  0
 Install.WantedBy,                        NULL,                                        0,                                  0
index 119c518664ff78877bebd87a4a055ae11e2d7971..4adb4165acb315799dc98fe3f55a129a285a73e0 100644 (file)
@@ -173,6 +173,8 @@ static size_t arg_random_seed_size;
 static int arg_default_oom_score_adjust;
 static bool arg_default_oom_score_adjust_set;
 static char *arg_default_smack_process_label;
+static usec_t arg_reload_limit_interval_sec;
+static unsigned arg_reload_limit_burst;
 
 /* A copy of the original environment block */
 static char **saved_env = NULL;
@@ -483,6 +485,28 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 arg_random_seed = sz > 0 ? p : mfree(p);
                 arg_random_seed_size = sz;
 
+        } else if (proc_cmdline_key_streq(key, "systemd.reload_limit_interval_sec")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = parse_sec(value, &arg_reload_limit_interval_sec);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to parse systemd.reload_limit_interval_sec= argument '%s', ignoring: %m", value);
+                        return 0;
+                }
+
+        } else if (proc_cmdline_key_streq(key, "systemd.reload_limit_burst")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = safe_atou(value, &arg_reload_limit_burst);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to parse systemd.reload_limit_burst= argument '%s', ignoring: %m", value);
+                        return 0;
+                }
+
         } else if (streq(key, "quiet") && !value) {
 
                 if (arg_show_status == _SHOW_STATUS_INVALID)
@@ -662,6 +686,8 @@ static int parse_config_file(void) {
                 { "Manager", "CtrlAltDelBurstAction",        config_parse_emergency_action,      0,                        &arg_cad_burst_action             },
                 { "Manager", "DefaultOOMPolicy",             config_parse_oom_policy,            0,                        &arg_default_oom_policy           },
                 { "Manager", "DefaultOOMScoreAdjust",        config_parse_oom_score_adjust,      0,                        NULL                              },
+                { "Manager", "ReloadLimitIntervalSec",       config_parse_sec,                   0,                        &arg_reload_limit_interval_sec    },
+                { "Manager", "ReloadLimitBurst",             config_parse_unsigned,              0,                        &arg_reload_limit_burst           },
 #if ENABLE_SMACK
                 { "Manager", "DefaultSmackProcessLabel",     config_parse_string,                0,                        &arg_default_smack_process_label  },
 #else
@@ -762,6 +788,10 @@ static void set_manager_settings(Manager *m) {
         m->confirm_spawn = arg_confirm_spawn;
         m->service_watchdogs = arg_service_watchdogs;
         m->cad_burst_action = arg_cad_burst_action;
+        /* Note that we don't do structured initialization here, otherwise it will reset the rate limit
+         * counter on every daemon-reload. */
+        m->reload_ratelimit.interval = arg_reload_limit_interval_sec;
+        m->reload_ratelimit.burst = arg_reload_limit_burst;
 
         manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog);
         manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog);
@@ -1184,9 +1214,10 @@ static void bump_file_max_and_nr_open(void) {
 #if BUMP_PROC_SYS_FS_FILE_MAX
         /* The maximum the kernel allows for this since 5.2 is LONG_MAX, use that. (Previously things were
          * different, but the operation would fail silently.) */
-        r = sysctl_writef("fs/file-max", "%li\n", LONG_MAX);
+        r = sysctl_write("fs/file-max", LONG_MAX_STR);
         if (r < 0)
-                log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to bump fs.file-max, ignoring: %m");
+                log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING,
+                               r, "Failed to bump fs.file-max, ignoring: %m");
 #endif
 
 #if BUMP_PROC_SYS_FS_NR_OPEN
@@ -1218,7 +1249,7 @@ static void bump_file_max_and_nr_open(void) {
                         break;
                 }
 
-                r = sysctl_writef("fs/nr_open", "%i\n", v);
+                r = sysctl_writef("fs/nr_open", "%i", v);
                 if (r == -EINVAL) {
                         log_debug("Couldn't write fs.nr_open as %i, halving it.", v);
                         v /= 2;
@@ -1375,7 +1406,7 @@ static int write_container_id(void) {
         if (isempty(c))
                 return 0;
 
-        RUN_WITH_UMASK(0022)
+        WITH_UMASK(0022)
                 r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
         if (r < 0)
                 return log_warning_errno(r, "Failed to write /run/systemd/container, ignoring: %m");
@@ -1404,8 +1435,7 @@ static int bump_unix_max_dgram_qlen(void) {
         if (v >= DEFAULT_UNIX_MAX_DGRAM_QLEN)
                 return 0;
 
-        r = write_string_filef("/proc/sys/net/unix/max_dgram_qlen", WRITE_STRING_FILE_DISABLE_BUFFER,
-                               "%lu", DEFAULT_UNIX_MAX_DGRAM_QLEN);
+        r = sysctl_write("net/unix/max_dgram_qlen", STRINGIFY(DEFAULT_UNIX_MAX_DGRAM_QLEN));
         if (r < 0)
                 return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
                                       "Failed to bump AF_UNIX datagram queue length, ignoring: %m");
@@ -2451,6 +2481,9 @@ static void reset_arguments(void) {
 
         arg_default_oom_score_adjust_set = false;
         arg_default_smack_process_label = mfree(arg_default_smack_process_label);
+
+        arg_reload_limit_interval_sec = 0;
+        arg_reload_limit_burst = 0;
 }
 
 static void determine_default_oom_score_adjust(void) {
index 598604d69453d3e3cecb2077f1ccf7cefff12e7d..292e82fd87a83b1fb00c76207957e376591029bc 100644 (file)
@@ -6,6 +6,7 @@
 #include <sys/epoll.h>
 #include <sys/inotify.h>
 #include <sys/ioctl.h>
+#include <sys/mount.h>
 #include <sys/reboot.h>
 #include <sys/timerfd.h>
 #include <sys/utsname.h>
@@ -62,6 +63,7 @@
 #include "manager-serialize.h"
 #include "memory-util.h"
 #include "mkdir-label.h"
+#include "mount-util.h"
 #include "os-util.h"
 #include "parse-util.h"
 #include "path-lookup.h"
@@ -1103,7 +1105,7 @@ static int manager_setup_cgroups_agent(Manager *m) {
                 (void) sockaddr_un_unlink(&sa.un);
 
                 /* Only allow root to connect to this socket */
-                RUN_WITH_UMASK(0077)
+                WITH_UMASK(0077)
                         r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
                 if (r < 0)
                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
@@ -3674,7 +3676,7 @@ static int manager_run_environment_generators(Manager *m) {
         if (!generator_path_any((const char* const*) paths))
                 return 0;
 
-        RUN_WITH_UMASK(0022)
+        WITH_UMASK(0022)
                 r = execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, gather_environment,
                                         args, NULL, m->transient_environment,
                                         EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS | EXEC_DIR_SET_SYSTEMD_EXEC_PID);
@@ -3740,8 +3742,45 @@ static int build_generator_environment(Manager *m, char ***ret) {
         return 0;
 }
 
+static int manager_execute_generators(Manager *m, char **paths, bool remount_ro) {
+        _cleanup_strv_free_ char **ge = NULL;
+        const char *argv[] = {
+                NULL, /* Leave this empty, execute_directory() will fill something in */
+                m->lookup_paths.generator,
+                m->lookup_paths.generator_early,
+                m->lookup_paths.generator_late,
+                NULL,
+        };
+        int r;
+
+        r = build_generator_environment(m, &ge);
+        if (r < 0)
+                return log_error_errno(r, "Failed to build generator environment: %m");
+
+        if (remount_ro) {
+                /* Remount most of the filesystem tree read-only. We leave /sys/ as-is, because our code
+                 * checks whether it is read-only to detect containerized execution environments. We leave
+                 * /run/ as-is too, because that's where our output goes. We also leave /proc/ and /dev/shm/
+                 * because they're API, and /tmp/ that safe_fork() mounted for us.
+                 */
+                r = bind_remount_recursive("/", MS_RDONLY, MS_RDONLY,
+                                           STRV_MAKE("/sys", "/run", "/proc", "/dev/shm", "/tmp"));
+                if (r < 0)
+                        log_warning_errno(r, "Read-only bind remount failed, ignoring: %m");
+        }
+
+        BLOCK_WITH_UMASK(0022);
+        return execute_directories(
+                        (const char* const*) paths,
+                        DEFAULT_TIMEOUT_USEC,
+                        /* callbacks= */ NULL, /* callback_args= */ NULL,
+                        (char**) argv,
+                        ge,
+                        EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS | EXEC_DIR_SET_SYSTEMD_EXEC_PID);
+}
+
 static int manager_run_generators(Manager *m) {
-        _cleanup_strv_free_ char **paths = NULL, **ge = NULL;
+        _cleanup_strv_free_ char **paths = NULL;
         int r;
 
         assert(m);
@@ -3762,30 +3801,22 @@ static int manager_run_generators(Manager *m) {
                 goto finish;
         }
 
-        const char *argv[] = {
-                NULL, /* Leave this empty, execute_directory() will fill something in */
-                m->lookup_paths.generator,
-                m->lookup_paths.generator_early,
-                m->lookup_paths.generator_late,
-                NULL,
-        };
-
-        r = build_generator_environment(m, &ge);
-        if (r < 0) {
-                log_error_errno(r, "Failed to build generator environment: %m");
+        /* If we are the system manager, we fork and invoke the generators in a sanitized mount namespace. If
+         * we are the user manager, let's just execute the generators directly. We might not have the
+         * necessary privileges, and the system manager has already mounted /tmp/ and everything else for us.
+         */
+        if (MANAGER_IS_USER(m)) {
+                r = manager_execute_generators(m, paths, /* remount_ro= */ false);
                 goto finish;
         }
 
-        RUN_WITH_UMASK(0022)
-                (void) execute_directories(
-                                (const char* const*) paths,
-                                DEFAULT_TIMEOUT_USEC,
-                                /* callbacks= */ NULL, /* callback_args= */ NULL,
-                                (char**) argv,
-                                ge,
-                                EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS | EXEC_DIR_SET_SYSTEMD_EXEC_PID);
-
-        r = 0;
+        r = safe_fork("(sd-gens)",
+                      FORK_RESET_SIGNALS | FORK_LOG | FORK_WAIT | FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE | FORK_PRIVATE_TMP,
+                      NULL);
+        if (r == 0) {
+                r = manager_execute_generators(m, paths, /* remount_ro= */ true);
+                _exit(r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+        }
 
 finish:
         lookup_paths_trim_generator(&m->lookup_paths);
index 75c16d6e263111724d67aa330e0196de053bf823..4d4b56c3cc0d4999283068146562eae2c39012da 100644 (file)
@@ -461,6 +461,9 @@ struct Manager {
         struct restrict_fs_bpf *restrict_fs;
 
         char *default_smack_process_label;
+
+        /* Allow users to configure a rate limit for Reload() operations */
+        RateLimit reload_ratelimit;
 };
 
 static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
index be46e566896e8c2923e543a05bb23a7cb2f7587c..f16e5c487bcbbc6a816ea66e952761734068ee41 100644 (file)
@@ -1909,6 +1909,7 @@ static void mount_enumerate(Manager *m) {
         mnt_init_debug(0);
 
         if (!m->mount_monitor) {
+                unsigned mount_rate_limit_burst = 5;
                 int fd;
 
                 m->mount_monitor = mnt_new_monitor();
@@ -1948,7 +1949,15 @@ static void mount_enumerate(Manager *m) {
                         goto fail;
                 }
 
-                r = sd_event_source_set_ratelimit(m->mount_event_source, 1 * USEC_PER_SEC, 5);
+                /* Let users override the default (5 in 1s), as it stalls the boot sequence on busy systems. */
+                const char *e = secure_getenv("SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST");
+                if (e) {
+                        r = safe_atou(e, &mount_rate_limit_burst);
+                        if (r < 0)
+                                log_debug("Invalid value in $SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST, ignoring: %s", e);
+                }
+
+                r = sd_event_source_set_ratelimit(m->mount_event_source, 1 * USEC_PER_SEC, mount_rate_limit_burst);
                 if (r < 0) {
                         log_error_errno(r, "Failed to enable rate limit for mount events: %m");
                         goto fail;
index 4e9292f2de4993b3124ad4b8df892160a2c53017..af30f40503ddecaae6f1de12668a0fcbf6170073 100644 (file)
@@ -104,7 +104,7 @@ static const MountEntry apivfs_table[] = {
         { "/proc",               PROCFS,       false },
         { "/dev",                BIND_DEV,     false },
         { "/sys",                SYSFS,        false },
-        { "/run",                RUN,          false, .options_const = "mode=755" TMPFS_LIMITS_RUN, .flags = MS_NOSUID|MS_NODEV|MS_STRICTATIME },
+        { "/run",                RUN,          false, .options_const = "mode=0755" TMPFS_LIMITS_RUN, .flags = MS_NOSUID|MS_NODEV|MS_STRICTATIME },
 };
 
 /* ProtectKernelTunables= option and the related filesystem APIs */
@@ -366,7 +366,7 @@ static int append_empty_dir_mounts(MountEntry **p, char **strv) {
                         .mode = EMPTY_DIR,
                         .ignore = false,
                         .read_only = true,
-                        .options_const = "mode=755" TMPFS_LIMITS_EMPTY_OR_ALMOST,
+                        .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST,
                         .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
                 };
         }
@@ -927,7 +927,7 @@ static int mount_private_dev(MountEntry *m) {
 
         dev = strjoina(temporary_mount, "/dev");
         (void) mkdir(dev, 0755);
-        r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755" TMPFS_LIMITS_PRIVATE_DEV);
+        r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=0755" TMPFS_LIMITS_PRIVATE_DEV);
         if (r < 0)
                 goto fail;
 
@@ -1113,8 +1113,8 @@ static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) {
                  * added in the same commit: if it's supported it is thus also per-instance. */
 
                 const char *hpv = ns_info->protect_proc == PROTECT_PROC_DEFAULT ?
-                                "off" :
-                                protect_proc_to_string(ns_info->protect_proc);
+                                  "off" :
+                                  protect_proc_to_string(ns_info->protect_proc);
 
                 /* hidepid= support was added in 5.8, so we can use fsconfig()/fsopen() (which were added in
                  * 5.2) to check if hidepid= is supported. This avoids a noisy dmesg log by the kernel when
@@ -1256,8 +1256,8 @@ static int mount_image(const MountEntry *m, const char *root_directory) {
         }
 
         r = verity_dissect_and_mount(
-                                /* src_fd= */ -1, mount_entry_source(m), mount_entry_path(m), m->image_options,
-                                host_os_release_id, host_os_release_version_id, host_os_release_sysext_level, NULL);
+                        /* src_fd= */ -1, mount_entry_source(m), mount_entry_path(m), m->image_options,
+                        host_os_release_id, host_os_release_version_id, host_os_release_sysext_level, NULL);
         if (r == -ENOENT && m->ignore)
                 return 0;
         if (r == -ESTALE && host_os_release_id)
@@ -1704,7 +1704,7 @@ static size_t namespace_calculate_mounts(
                 n_bind_mounts +
                 n_mount_images +
                 (n_extension_images > 0 || n_extension_directories > 0 ? /* Mount each image and directory plus an overlay per hierarchy */
-                        n_hierarchies + n_extension_images + n_extension_directories: 0) +
+                 n_hierarchies + n_extension_images + n_extension_directories: 0) +
                 n_temporary_filesystems +
                 ns_info->private_dev +
                 (ns_info->protect_kernel_tunables ?
@@ -2488,7 +2488,7 @@ int setup_namespace(
                 goto finish;
 
         /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
-        r = mount_pivot_root(root);
+        r = mount_switch_root(root, MOUNT_ATTR_PROPAGATION_INHERIT);
         if (r == -EINVAL && root_directory) {
                 /* If we are using root_directory and we don't have privileges (ie: user manager in a user
                  * namespace) and the root_directory is already a mount point in the parent namespace,
@@ -2498,7 +2498,7 @@ int setup_namespace(
                 r = mount_nofollow_verbose(LOG_DEBUG, root, root, NULL, MS_BIND|MS_REC, NULL);
                 if (r < 0)
                         goto finish;
-                r = mount_pivot_root(root);
+                r = mount_switch_root(root, MOUNT_ATTR_PROPAGATION_INHERIT);
         }
         if (r < 0) {
                 log_debug_errno(r, "Failed to mount root with MS_MOVE: %m");
@@ -2708,7 +2708,7 @@ static int make_tmp_prefix(const char *prefix) {
         if (errno != ENOENT)
                 return -errno;
 
-        RUN_WITH_UMASK(000)
+        WITH_UMASK(000)
                 r = mkdir_parents(prefix, 0755);
         if (r < 0)
                 return r;
@@ -2765,7 +2765,7 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
         if (r < 0)
                 return r;
 
-        RUN_WITH_UMASK(0077)
+        WITH_UMASK(0077)
                 if (!mkdtemp(x)) {
                         if (errno == EROFS || ERRNO_IS_DISK_SPACE(errno))
                                 rw = false;
@@ -2778,9 +2778,9 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
                 if (!y)
                         return -ENOMEM;
 
-                RUN_WITH_UMASK(0000)
+                WITH_UMASK(0000)
                         if (mkdir(y, 0777 | S_ISVTX) < 0)
-                                    return -errno;
+                                return -errno;
 
                 r = label_fix_full(AT_FDCWD, y, prefix, 0);
                 if (r < 0)
@@ -2792,7 +2792,7 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
                 /* Trouble: we failed to create the directory. Instead of failing, let's simulate /tmp being
                  * read-only. This way the service will get the EROFS result as if it was writing to the real
                  * file system. */
-                RUN_WITH_UMASK(0000)
+                WITH_UMASK(0000)
                         r = mkdir_p(RUN_SYSTEMD_EMPTY, 0500);
                 if (r < 0)
                         return r;
index e05ebdc631d533536160b3ac6af0bb8fd57cb1dd..62181a6309b936929ed92b68fed07dbec62183c8 100644 (file)
@@ -229,9 +229,7 @@ int mac_selinux_access_check_internal(
         } else {
                 /* If no unit context is known, use our own */
                 if (getcon_raw(&fcon) < 0) {
-                        r = -errno;
-
-                        log_warning_errno(r, "SELinux getcon_raw() failed%s (perm=%s): %m",
+                        log_warning_errno(errno, "SELinux getcon_raw() failed%s (perm=%s): %m",
                                           enforce ? "" : ", ignoring",
                                           permission);
                         if (!enforce)
@@ -239,6 +237,12 @@ int mac_selinux_access_check_internal(
 
                         return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context: %m");
                 }
+                if (!fcon) {
+                        if (!enforce)
+                                return 0;
+
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "We appear not to have any SELinux context: %m");
+                }
 
                 acon = fcon;
                 tclass = "system";
index 153322442c7eaf00a289849aa9c8ec3d2601d94c..17fa2c0142945ee73a5d2a52aa0744f9416a868c 100644 (file)
@@ -30,30 +30,28 @@ int mac_selinux_setup(bool *loaded_policy) {
         usec_t before_load, after_load;
         char *con;
         int r;
-        bool initialized = false;
+        bool initialized;
 
         assert(loaded_policy);
 
         /* Turn off all of SELinux' own logging, we want to do that */
-        selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) { .func_log = null_log });
+        selinux_set_callback(SELINUX_CB_LOG, (const union selinux_callback) { .func_log = null_log });
 
-        /* Don't load policy in the initrd if we don't appear to have
-         * it.  For the real root, we check below if we've already
-         * loaded policy, and return gracefully.
-         */
+        /* Don't load policy in the initrd if we don't appear to have it.  For the real root, we check below
+         * if we've already loaded policy, and return gracefully. */
         if (in_initrd() && access(selinux_path(), F_OK) < 0)
                 return 0;
 
         /* Already initialized by somebody else? */
         r = getcon_raw(&con);
-        /* getcon_raw can return 0, and still give us a NULL pointer if
-         * /proc/self/attr/current is empty. SELinux guarantees this won't
-         * happen, but that file isn't specific to SELinux, and may be provided
-         * by some other arbitrary LSM with different semantics. */
+        /* getcon_raw can return 0, and still give us a NULL pointer if /proc/self/attr/current is
+         * empty. SELinux guarantees this won't happen, but that file isn't specific to SELinux, and may be
+         * provided by some other arbitrary LSM with different semantics. */
         if (r == 0 && con) {
                 initialized = !streq(con, "kernel");
                 freecon(con);
-        }
+        } else
+                initialized = false;
 
         /* Make sure we have no fds open while loading the policy and
          * transitioning */
index 2c734eb0966e217bf3f8f73dfd7b1167d4e66e11..bb190b1e8aa2159ae0ce2bd062525815bb56ab33 100644 (file)
@@ -2421,7 +2421,7 @@ static void service_enter_reload(Service *s) {
                 r = service_spawn(s,
                                   s->control_command,
                                   s->timeout_start_usec,
-                                  EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP|EXEC_WRITE_CREDENTIALS,
+                                  EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP,
                                   &s->control_pid);
                 if (r < 0)
                         goto fail;
index c453aa033e7115bbeadea61c05e793226785abfe..4824a300d0718c4c45e4aa82d0467ff8cb5407aa 100644 (file)
@@ -381,6 +381,9 @@ static int slice_freezer_action(Unit *s, FreezerAction action) {
         }
 
         UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
+                if (!member->cgroup_realized)
+                        continue;
+
                 if (action == FREEZER_FREEZE)
                         r = UNIT_VTABLE(member)->freeze(member);
                 else
index 2196793ad10a469b348fbf9ae4e7eb8e5e0a9eea..5c83c4780f58b745b6a9e8210bcf768ab03f9de0 100644 (file)
@@ -827,7 +827,7 @@ static void swap_enter_activating(Swap *s) {
                 }
         }
 
-        r = exec_command_set(s->control_command, "/sbin/swapon", NULL);
+        r = exec_command_set(s->control_command, "/sbin/swapon", "--fixpgsz", NULL);
         if (r < 0)
                 goto fail;
 
index 71a5869ec0a24d80e179eca560aedf9e5fbde9fe..531a7694d98b368e887a2fadc2d9d91b3dfa10c3 100644 (file)
@@ -75,3 +75,5 @@
 #DefaultLimitRTTIME=
 #DefaultOOMPolicy=stop
 #DefaultSmackProcessLabel=
+#ReloadLimitIntervalSec=
+#ReloadLimitBurst=
index 29b07a6e7a84dc43b657ac81fc1ee6da3794b2bb..b7cca3e2871a8ac6d2e4b49773ff2764af7c0380 100644 (file)
@@ -687,7 +687,7 @@ Unit* unit_free(Unit *u) {
         u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
         u->bus_track = sd_bus_track_unref(u->bus_track);
         u->deserialized_refs = strv_free(u->deserialized_refs);
-        u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
+        u->pending_freezer_invocation = sd_bus_message_unref(u->pending_freezer_invocation);
 
         unit_free_requires_mounts_for(u);
 
@@ -4469,7 +4469,7 @@ int unit_make_transient(Unit *u) {
         /* Let's open the file we'll write the transient settings into. This file is kept open as long as we are
          * creating the transient, and is closed in unit_load(), as soon as we start loading the file. */
 
-        RUN_WITH_UMASK(0022) {
+        WITH_UMASK(0022) {
                 f = fopen(path, "we");
                 if (!f)
                         return -errno;
@@ -5829,7 +5829,7 @@ void unit_frozen(Unit *u) {
 
         u->freezer_state = FREEZER_FROZEN;
 
-        bus_unit_send_pending_freezer_message(u);
+        bus_unit_send_pending_freezer_message(u, false);
 }
 
 void unit_thawed(Unit *u) {
@@ -5837,7 +5837,7 @@ void unit_thawed(Unit *u) {
 
         u->freezer_state = FREEZER_RUNNING;
 
-        bus_unit_send_pending_freezer_message(u);
+        bus_unit_send_pending_freezer_message(u, false);
 }
 
 static int unit_freezer_action(Unit *u, FreezerAction action) {
@@ -5862,7 +5862,8 @@ static int unit_freezer_action(Unit *u, FreezerAction action) {
         if (s != UNIT_ACTIVE)
                 return -EHOSTDOWN;
 
-        if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING))
+        if ((IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING) && action == FREEZER_FREEZE) ||
+            (u->freezer_state == FREEZER_THAWING && action == FREEZER_THAW))
                 return -EALREADY;
 
         r = method(u);
index 3bc7de3d1c39f4c1ca2671fb0d4bc12e6f1a0cec..58417ebd0ec080b8dbc43fee78d2cf878c905366 100644 (file)
@@ -239,7 +239,7 @@ typedef struct Unit {
         FILE *transient_file;
 
         /* Freezer state */
-        sd_bus_message *pending_freezer_message;
+        sd_bus_message *pending_freezer_invocation;
         FreezerState freezer_state;
 
         /* Job timeout and action to take */
index b69974978eeff73ef50d9826bb09b4ab25e40b02..b4938942d146ea59b384bf8c407a0fa7041fd152 100644 (file)
@@ -48,3 +48,5 @@
 #DefaultLimitRTPRIO=
 #DefaultLimitRTTIME=
 #DefaultSmackProcessLabel=
+#ReloadLimitIntervalSec=
+#ReloadLimitBurst
index a6abcc6496de3564b73f3f4a3d4e9b47f07842bf..6a6a968149a63e1483edaca04cf4ee902f5d71b4 100644 (file)
@@ -19,6 +19,7 @@
 #include "compress.h"
 #include "constants.h"
 #include "dissect-image.h"
+#include "escape.h"
 #include "fd-util.h"
 #include "format-table.h"
 #include "fs-util.h"
@@ -790,9 +791,10 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
 
                 r = json_parse(pkgmeta_json, 0, &v, NULL, NULL);
-                if (r < 0)
-                        log_warning_errno(r, "json_parse on %s failed, ignoring: %m", pkgmeta_json);
-                else {
+                if (r < 0) {
+                        _cleanup_free_ char *esc = cescape(pkgmeta_json);
+                        log_warning_errno(r, "json_parse on \"%s\" failed, ignoring: %m", strnull(esc));
+                } else {
                         const char *module_name;
                         JsonVariant *module_json;
 
index 74053b8ce3478ed7c562d24833b4daa99967e7b6..a3bdedba1d8359d0d303325d79e5eb968a069f95 100644 (file)
@@ -38,6 +38,10 @@ int acquire_fido2_key(
         size_t salt_size;
         int r;
 
+        if ((required & (FIDO2ENROLL_PIN | FIDO2ENROLL_UP | FIDO2ENROLL_UV)) && headless)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOPKG),
+                                        "Local verification is required to unlock this volume, but the 'headless' parameter was set.");
+
         ask_password_flags |= ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
 
         assert(cid);
@@ -76,28 +80,6 @@ int acquire_fido2_key(
         }
 
         for (;;) {
-                if (!FLAGS_SET(required, FIDO2ENROLL_PIN) || pins) {
-                        r = fido2_use_hmac_hash(
-                                        device,
-                                        rp_id ?: "io.systemd.cryptsetup",
-                                        salt, salt_size,
-                                        cid, cid_size,
-                                        pins,
-                                        required,
-                                        ret_decrypted_key,
-                                        ret_decrypted_key_size);
-                        if (!IN_SET(r,
-                                    -ENOANO,   /* needs pin */
-                                    -ENOLCK))  /* pin incorrect */
-                                return r;
-
-                        device_exists = true; /* that a PIN is needed/wasn't correct means that we managed to
-                                               * talk to a device */
-                }
-
-                if (headless)
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
-
                 if (!device_exists) {
                         /* Before we inquire for the PIN we'll need, if we never talked to the device, check
                          * if the device actually is plugged in. Otherwise we'll ask for the PIN already when
@@ -112,6 +94,30 @@ int acquire_fido2_key(
                         device_exists = true;  /* now we know for sure, a device exists, no need to ask again */
                 }
 
+                /* Always make an attempt before asking for PIN.
+                 * fido2_use_hmac_hash() will perform a pre-flight check for whether the credential for
+                 * can be found on one of the connected devices. This way, we can avoid prompting the user
+                 * for a PIN when we are sure that no device can be used. */
+                r = fido2_use_hmac_hash(
+                                device,
+                                rp_id ?: "io.systemd.cryptsetup",
+                                salt, salt_size,
+                                cid, cid_size,
+                                pins,
+                                required,
+                                ret_decrypted_key,
+                                ret_decrypted_key_size);
+                if (!IN_SET(r,
+                            -ENOANO,   /* needs pin */
+                            -ENOLCK))  /* pin incorrect */
+                        return r;
+
+                device_exists = true; /* that a PIN is needed/wasn't correct means that we managed to
+                                       * talk to a device */
+
+                if (headless)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
+
                 pins = strv_free_erase(pins);
                 r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, ask_password_flags, &pins);
                 if (r < 0)
@@ -121,35 +127,38 @@ int acquire_fido2_key(
         }
 }
 
-int find_fido2_auto_data(
+int acquire_fido2_key_auto(
                 struct crypt_device *cd,
-                char **ret_rp_id,
-                void **ret_salt,
-                size_t *ret_salt_size,
-                void **ret_cid,
-                size_t *ret_cid_size,
-                int *ret_keyslot,
-                Fido2EnrollFlags *ret_required) {
-
-        _cleanup_free_ void *cid = NULL, *salt = NULL;
-        size_t cid_size = 0, salt_size = 0;
-        _cleanup_free_ char *rp = NULL;
-        int r, keyslot = -1;
+                const char *name,
+                const char *friendly_name,
+                const char *fido2_device,
+                const char *key_file,
+                size_t key_file_size,
+                uint64_t key_file_offset,
+                usec_t until,
+                bool headless,
+                void **ret_decrypted_key,
+                size_t *ret_decrypted_key_size,
+                AskPasswordFlags ask_password_flags) {
+
+        _cleanup_free_ void *cid = NULL;
+        size_t cid_size = 0;
+        int r, ret = -ENOENT;
         Fido2EnrollFlags required = 0;
 
         assert(cd);
-        assert(ret_salt);
-        assert(ret_salt_size);
-        assert(ret_cid);
-        assert(ret_cid_size);
-        assert(ret_keyslot);
-        assert(ret_required);
+        assert(name);
+        assert(ret_decrypted_key);
+        assert(ret_decrypted_key_size);
 
         /* Loads FIDO2 metadata from LUKS2 JSON token headers. */
 
         for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token ++) {
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
                 JsonVariant *w;
+                _cleanup_free_ void *salt = NULL;
+                _cleanup_free_ char *rp = NULL;
+                size_t salt_size = 0;
                 int ks;
 
                 r = cryptsetup_get_token_as_json(cd, token, "systemd-fido2", &v);
@@ -166,13 +175,6 @@ int find_fido2_auto_data(
                         continue;
                 }
 
-                if (cid)
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
-                                               "Multiple FIDO2 tokens enrolled, cannot automatically determine token.");
-
-                assert(keyslot < 0);
-                keyslot = ks;
-
                 w = json_variant_by_key(v, "fido2-credential");
                 if (!w || !json_variant_is_string(w))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -243,20 +245,33 @@ int find_fido2_auto_data(
                         SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w));
                 } else
                         required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */
+
+                ret = acquire_fido2_key(
+                                name,
+                                friendly_name,
+                                fido2_device,
+                                rp,
+                                cid, cid_size,
+                                key_file, key_file_size, key_file_offset,
+                                salt, salt_size,
+                                until,
+                                headless,
+                                required,
+                                ret_decrypted_key, ret_decrypted_key_size,
+                                ask_password_flags);
+                if (ret == 0)
+                        break;
         }
 
         if (!cid)
                 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
                                        "No valid FIDO2 token data found.");
 
-        log_info("Automatically discovered security FIDO2 token unlocks volume.");
+        if (ret == -EAGAIN) /* fido2 device does not exist, or UV is blocked; caller will prompt for retry */
+                return log_debug_errno(ret, "FIDO2 token does not exist, or UV is blocked.");
+        if (ret < 0)
+                return log_error_errno(ret, "Failed to unlock LUKS volume with FIDO2 token: %m");
 
-        *ret_rp_id = TAKE_PTR(rp);
-        *ret_cid = TAKE_PTR(cid);
-        *ret_cid_size = cid_size;
-        *ret_salt = TAKE_PTR(salt);
-        *ret_salt_size = salt_size;
-        *ret_keyslot = keyslot;
-        *ret_required = required;
-        return 0;
+        log_info("Unlocked volume via automatically discovered security FIDO2 token.");
+        return ret;
 }
index 204f1e06c6849b9b286c561e4665ecf88e947c73..371bf21f0e687e1decd773adb41d00e674d0d452 100644 (file)
@@ -29,15 +29,19 @@ int acquire_fido2_key(
                 size_t *ret_decrypted_key_size,
                 AskPasswordFlags ask_password_flags);
 
-int find_fido2_auto_data(
+int acquire_fido2_key_auto(
                 struct crypt_device *cd,
-                char **ret_rp_id,
-                void **ret_salt,
-                size_t *ret_salt_size,
-                void **ret_cid,
-                size_t *ret_cid_size,
-                int *ret_keyslot,
-                Fido2EnrollFlags *ret_required);
+                const char *name,
+                const char *friendly_name,
+                const char *fido2_device,
+                const char *key_file,
+                size_t key_file_size,
+                uint64_t key_file_offset,
+                usec_t until,
+                bool headless,
+                void **ret_decrypted_key,
+                size_t *ret_decrypted_key_size,
+                AskPasswordFlags ask_password_flags);
 
 #else
 
@@ -64,15 +68,19 @@ static inline int acquire_fido2_key(
                                "FIDO2 token support not available.");
 }
 
-static inline int find_fido2_auto_data(
+static inline int acquire_fido2_key_auto(
                 struct crypt_device *cd,
-                char **ret_rp_id,
-                void **ret_salt,
-                size_t *ret_salt_size,
-                void **ret_cid,
-                size_t *ret_cid_size,
-                int *ret_keyslot,
-                Fido2EnrollFlags *ret_required) {
+                const char *name,
+                const char *friendly_name,
+                const char *fido2_device,
+                const char *key_file,
+                size_t key_file_size,
+                uint64_t key_file_offset,
+                usec_t until,
+                bool headless,
+                void **ret_decrypted_key,
+                size_t *ret_decrypted_key_size,
+                AskPasswordFlags ask_password_flags) {
 
         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                "FIDO2 token support not available.");
index 90b5ab21a991d5df63f691eca77a85aa189c56fc..389bf4fdcf1f0b0e6b606554aa5ad4a09beedc9b 100644 (file)
@@ -1061,9 +1061,8 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
         _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
         _cleanup_(erase_and_freep) void *decrypted_key = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
-        _cleanup_free_ void *discovered_salt = NULL, *discovered_cid = NULL;
-        size_t discovered_salt_size, discovered_cid_size, decrypted_key_size, cid_size = 0;
-        _cleanup_free_ char *friendly = NULL, *discovered_rp_id = NULL;
+        size_t decrypted_key_size, cid_size = 0;
+        _cleanup_free_ char *friendly = NULL;
         int keyslot = arg_key_slot, r;
         const char *rp_id = NULL;
         const void *cid = NULL;
@@ -1088,32 +1087,6 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
                  * use PIN + UP when needed, and do not configure UV at all. Eventually, we should make this
                  * explicitly configurable. */
                 required = FIDO2ENROLL_PIN_IF_NEEDED | FIDO2ENROLL_UP_IF_NEEDED | FIDO2ENROLL_UV_OMIT;
-        } else if (!use_libcryptsetup_plugin) {
-                r = find_fido2_auto_data(
-                                cd,
-                                &discovered_rp_id,
-                                &discovered_salt,
-                                &discovered_salt_size,
-                                &discovered_cid,
-                                &discovered_cid_size,
-                                &keyslot,
-                                &required);
-
-                if (IN_SET(r, -ENOTUNIQ, -ENXIO))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                               "Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
-                if (r < 0)
-                        return r;
-
-                if ((required & (FIDO2ENROLL_PIN | FIDO2ENROLL_UP | FIDO2ENROLL_UV)) && arg_headless)
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOPKG),
-                                               "Local verification is required to unlock this volume, but the 'headless' parameter was set.");
-
-                rp_id = discovered_rp_id;
-                key_data = discovered_salt;
-                key_data_size = discovered_salt_size;
-                cid = discovered_cid;
-                cid_size = discovered_cid_size;
         }
 
         friendly = friendly_disk_name(crypt_get_device_name(cd), name);
@@ -1128,19 +1101,31 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
                                                        "Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
 
                 } else {
-                        r = acquire_fido2_key(
-                                        name,
-                                        friendly,
-                                        arg_fido2_device,
-                                        rp_id,
-                                        cid, cid_size,
-                                        key_file, arg_keyfile_size, arg_keyfile_offset,
-                                        key_data, key_data_size,
-                                        until,
-                                        arg_headless,
-                                        required,
-                                        &decrypted_key, &decrypted_key_size,
-                                        arg_ask_password_flags);
+                        if (cid)
+                                r = acquire_fido2_key(
+                                                name,
+                                                friendly,
+                                                arg_fido2_device,
+                                                rp_id,
+                                                cid, cid_size,
+                                                key_file, arg_keyfile_size, arg_keyfile_offset,
+                                                key_data, key_data_size,
+                                                until,
+                                                arg_headless,
+                                                required,
+                                                &decrypted_key, &decrypted_key_size,
+                                                arg_ask_password_flags);
+                        else
+                                r = acquire_fido2_key_auto(
+                                                cd,
+                                                name,
+                                                friendly,
+                                                arg_fido2_device,
+                                                key_file, arg_keyfile_size, arg_keyfile_offset,
+                                                until,
+                                                arg_headless,
+                                                &decrypted_key, &decrypted_key_size,
+                                                arg_ask_password_flags);
                         if (r >= 0)
                                 break;
                 }
index 3cf250e8ec3aa504e5d629cb6d13b10eea57d2e1..b7bcf731f7590c25ef4168d2e212a147f4ed323f 100644 (file)
@@ -17,6 +17,7 @@
 #include "copy.h"
 #include "device-util.h"
 #include "devnum-util.h"
+#include "discover-image.h"
 #include "dissect-image.h"
 #include "env-util.h"
 #include "escape.h"
@@ -56,6 +57,7 @@ static enum {
         ACTION_WITH,
         ACTION_COPY_FROM,
         ACTION_COPY_TO,
+        ACTION_DISCOVER,
 } arg_action = ACTION_DISSECT;
 static const char *arg_image = NULL;
 static const char *arg_path = NULL;
@@ -75,6 +77,7 @@ static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
 static PagerFlags arg_pager_flags = 0;
 static bool arg_legend = true;
 static bool arg_rmdir = false;
+static bool arg_in_memory = false;
 static char **arg_argv = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
@@ -106,6 +109,7 @@ static int help(void) {
                "     --mkdir              Make mount directory before mounting, if missing\n"
                "     --rmdir              Remove mount directory after unmounting\n"
                "     --discard=MODE       Choose 'discard' mode (disabled, loop, all, crypto)\n"
+               "     --in-memory          Copy image into memory\n"
                "     --root-hash=HASH     Specify root hash for verity\n"
                "     --root-hash-sig=SIG  Specify pkcs7 signature of root hash for verity\n"
                "                          as a DER encoded PKCS7, either as a path to a file\n"
@@ -128,6 +132,7 @@ static int help(void) {
                "     --with               Mount, run command, unmount\n"
                "  -x --copy-from          Copy files from image to host\n"
                "  -a --copy-to            Copy files from host to image\n"
+               "     --discover           Discover DDIs in well known directories\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
@@ -197,8 +202,10 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VERITY_DATA,
                 ARG_MKDIR,
                 ARG_RMDIR,
+                ARG_IN_MEMORY,
                 ARG_JSON,
                 ARG_MTREE,
+                ARG_DISCOVER,
         };
 
         static const struct option options[] = {
@@ -218,11 +225,13 @@ static int parse_argv(int argc, char *argv[]) {
                 { "verity-data",   required_argument, NULL, ARG_VERITY_DATA   },
                 { "mkdir",         no_argument,       NULL, ARG_MKDIR         },
                 { "rmdir",         no_argument,       NULL, ARG_RMDIR         },
+                { "in-memory",     no_argument,       NULL, ARG_IN_MEMORY     },
                 { "list",          no_argument,       NULL, 'l'               },
                 { "mtree",         no_argument,       NULL, ARG_MTREE         },
                 { "copy-from",     no_argument,       NULL, 'x'               },
                 { "copy-to",       no_argument,       NULL, 'a'               },
                 { "json",          required_argument, NULL, ARG_JSON          },
+                { "discover",      no_argument,       NULL, ARG_DISCOVER      },
                 {}
         };
 
@@ -335,6 +344,10 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_IN_MEMORY:
+                        arg_in_memory = true;
+                        break;
+
                 case ARG_ROOT_HASH: {
                         _cleanup_free_ void *p = NULL;
                         size_t l;
@@ -400,6 +413,10 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_DISCOVER:
+                        arg_action = ACTION_DISCOVER;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -491,6 +508,13 @@ static int parse_argv(int argc, char *argv[]) {
 
                 break;
 
+        case ACTION_DISCOVER:
+                if (optind != argc)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Expected no argument.");
+
+                break;
+
         default:
                 assert_not_reached();
         }
@@ -1176,9 +1200,9 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
 
 static int action_umount(const char *path) {
         _cleanup_close_ int fd = -1;
-        _cleanup_free_ char *canonical = NULL, *devname = NULL;
+        _cleanup_free_ char *canonical = NULL;
         _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
-        dev_t devno;
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
         int r;
 
         fd = chase_symlinks_and_open(path, NULL, 0, O_DIRECTORY, &canonical);
@@ -1193,18 +1217,26 @@ static int action_umount(const char *path) {
         if (r < 0)
                 return log_error_errno(r, "Failed to determine whether '%s' is a mount point: %m", canonical);
 
-        r = fd_get_whole_disk(fd, /*backing=*/ true, &devno);
-        if (r < 0)
-                return log_error_errno(r, "Failed to find backing block device for '%s': %m", canonical);
+        r = block_device_new_from_fd(fd, BLOCK_DEVICE_LOOKUP_WHOLE_DISK | BLOCK_DEVICE_LOOKUP_BACKING, &dev);
+        if (r < 0) {
+                _cleanup_close_ int usr_fd = -1;
+
+                /* The command `systemd-dissect --mount` expects that the image at least has the root or /usr
+                 * partition. If it does not have the root partition, then we mount the /usr partition on a
+                 * tmpfs. Hence, let's try to find the backing block device through the /usr partition. */
 
-        r = devname_from_devnum(S_IFBLK, devno, &devname);
+                usr_fd = openat(fd, "usr", O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
+                if (usr_fd < 0)
+                        return log_error_errno(errno, "Failed to open '%s/usr': %m", canonical);
+
+                r = block_device_new_from_fd(usr_fd, BLOCK_DEVICE_LOOKUP_WHOLE_DISK | BLOCK_DEVICE_LOOKUP_BACKING, &dev);
+        }
         if (r < 0)
-                return log_error_errno(r, "Failed to get devname of block device " DEVNUM_FORMAT_STR ": %m",
-                                       DEVNUM_FORMAT_VAL(devno));
+                return log_error_errno(r, "Failed to find backing block device for '%s': %m", canonical);
 
-        r = loop_device_open_from_path(devname, 0, LOCK_EX, &d);
+        r = loop_device_open(dev, 0, LOCK_EX, &d);
         if (r < 0)
-                return log_error_errno(r, "Failed to open loop device '%s': %m", devname);
+                return log_device_error_errno(dev, r, "Failed to open loopback block device: %m");
 
         /* We've locked the loop device, now we're ready to unmount. To allow the unmount to succeed, we have
          * to close the O_PATH fd we opened earlier. */
@@ -1317,13 +1349,61 @@ static int action_with(DissectedImage *m, LoopDevice *d) {
         return rcode;
 }
 
+static int action_discover(void) {
+        _cleanup_(hashmap_freep) Hashmap *images = NULL;
+        _cleanup_(table_unrefp) Table *t = NULL;
+        Image *img;
+        int r;
+
+        images = hashmap_new(&image_hash_ops);
+        if (!images)
+                return log_oom();
+
+        for (ImageClass cl = 0; cl < _IMAGE_CLASS_MAX; cl++) {
+                r = image_discover(cl, NULL, images);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to discover images: %m");
+        }
+
+        if ((arg_json_format_flags & JSON_FORMAT_OFF) && hashmap_isempty(images)) {
+                log_info("No images found.");
+                return 0;
+        }
+
+        t = table_new("name", "type", "class", "ro", "path", "time", "usage");
+        if (!t)
+                return log_oom();
+
+        HASHMAP_FOREACH(img, images) {
+
+                if (!IN_SET(img->type, IMAGE_RAW, IMAGE_BLOCK))
+                        continue;
+
+                r = table_add_many(
+                                t,
+                                TABLE_STRING, img->name,
+                                TABLE_STRING, image_type_to_string(img->type),
+                                TABLE_STRING, image_class_to_string(img->class),
+                                TABLE_BOOLEAN, img->read_only,
+                                TABLE_PATH, img->path,
+                                TABLE_TIMESTAMP, img->mtime != 0 ? img->mtime : img->crtime,
+                                TABLE_SIZE, img->usage);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
+        (void) table_set_sort(t, (size_t) 0);
+
+        return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
+}
+
 static int run(int argc, char *argv[]) {
         _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
         _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
-        int r;
+        uint32_t loop_flags;
+        int open_flags, r;
 
-        log_parse_environment();
-        log_open();
+        log_setup();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -1331,6 +1411,8 @@ static int run(int argc, char *argv[]) {
 
         if (arg_action == ACTION_UMOUNT)
                 return action_umount(arg_path);
+        if (arg_action == ACTION_DISCOVER)
+                return action_discover();
 
         r = verity_settings_load(
                         &arg_verity_settings,
@@ -1344,12 +1426,13 @@ static int run(int argc, char *argv[]) {
                                                                 * available we turn off partition table
                                                                 * support */
 
-        r = loop_device_make_by_path(
-                        arg_image,
-                        FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
-                        FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
-                        LOCK_SH,
-                        &d);
+        open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR;
+        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);
+        else
+                r = loop_device_make_by_path(arg_image, open_flags, 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 872e01f58f7102976b3a184c06b99534e758515d..b0ea536eb29778ca7f1f4a770210cb7572e1e612 100644 (file)
@@ -671,12 +671,12 @@ static int parse_fstab(bool initrd) {
 
                 if (path_is_read_only_fs("/sys") > 0) {
                         if (streq(what, "sysfs")) {
-                                log_info("Running in a container, ignoring fstab entry for %s.", what);
+                                log_info("/sys/ is read-only (running in a container?), ignoring fstab entry for %s.", me->mnt_dir);
                                 continue;
                         }
 
                         if (is_device_path(what)) {
-                                log_info("Running in a container, ignoring fstab device entry for %s.", what);
+                                log_info("/sys/ is read-only (running in a container?), ignoring fstab device entry for %s.", what);
                                 continue;
                         }
                 }
@@ -824,7 +824,8 @@ static int sysroot_is_nfsroot(void) {
 static int add_sysroot_mount(void) {
         _cleanup_free_ char *what = NULL;
         const char *opts, *fstype;
-        bool default_rw;
+        bool default_rw, makefs;
+        MountPointFlags flags;
         int r;
 
         if (isempty(arg_root_what)) {
@@ -899,6 +900,9 @@ static int add_sysroot_mount(void) {
                         return r;
         }
 
+        makefs = fstab_test_option(opts, "x-systemd.makefs\0");
+        flags = makefs * MOUNT_MAKEFS;
+
         return add_mount("/proc/cmdline",
                          arg_dest,
                          what,
@@ -907,13 +911,15 @@ static int add_sysroot_mount(void) {
                          fstype,
                          opts,
                          is_device_path(what) ? 1 : 0, /* passno */
-                         0,                            /* makefs off, growfs off, noauto off, nofail off, automount off */
+                         flags,                        /* makefs, growfs off, noauto off, nofail off, automount off */
                          SPECIAL_INITRD_ROOT_FS_TARGET);
 }
 
 static int add_sysroot_usr_mount(void) {
         _cleanup_free_ char *what = NULL;
         const char *opts;
+        bool makefs;
+        MountPointFlags flags;
         int r;
 
         /* Returns 0 if we didn't do anything, > 0 if we either generated a unit for the /usr/ mount, or we
@@ -979,6 +985,9 @@ static int add_sysroot_usr_mount(void) {
 
         log_debug("Found entry what=%s where=/sysusr/usr type=%s opts=%s", what, strna(arg_usr_fstype), strempty(opts));
 
+        makefs = fstab_test_option(opts, "x-systemd.makefs\0");
+        flags = makefs * MOUNT_MAKEFS;
+
         r = add_mount("/proc/cmdline",
                       arg_dest,
                       what,
@@ -987,7 +996,7 @@ static int add_sysroot_usr_mount(void) {
                       arg_usr_fstype,
                       opts,
                       is_device_path(what) ? 1 : 0, /* passno */
-                      0,
+                      flags,
                       SPECIAL_INITRD_USR_FS_TARGET);
         if (r < 0)
                 return r;
index c11a5b15f46b790095ea7306e13dbfb022888c4f..db778c7609975150d8deb9ea800d9b92bc1b47c4 100644 (file)
@@ -327,14 +327,23 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
         return ((l + ali - 1) & ~(ali - 1));
 }
 
+#define ALIGN2(l) ALIGN_TO(l, 2)
 #define ALIGN4(l) ALIGN_TO(l, 4)
 #define ALIGN8(l) ALIGN_TO(l, 8)
+#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
 #ifndef SD_BOOT
 /* libefi also provides ALIGN, and we do not use them in sd-boot explicitly. */
 #define ALIGN(l)  ALIGN_TO(l, sizeof(void*))
 #define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
 #endif
 
+/* Checks if the specified pointer is aligned as appropriate for the specific type */
+#define IS_ALIGNED16(p) (((uintptr_t) p) % __alignof__(uint16_t) == 0)
+#define IS_ALIGNED32(p) (((uintptr_t) p) % __alignof__(uint32_t) == 0)
+#define IS_ALIGNED64(p) (((uintptr_t) p) % __alignof__(uint64_t) == 0)
+
 /* Same as ALIGN_TO but callable in constant contexts. */
 #define CONST_ALIGN_TO(l, ali)                                         \
         __builtin_choose_expr(                                         \
@@ -345,6 +354,16 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
                 ((l) + (ali) - 1) & ~((ali) - 1),                      \
                 VOID_0)
 
+/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable
+ * alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a
+ * warning or if you want to assert that the cast gives a pointer of suitable alignment. */
+#define CAST_ALIGN_PTR(t, p)                                    \
+        ({                                                      \
+                const void *_p = (p);                           \
+                assert(((uintptr_t) _p) % __alignof__(t) == 0); \
+                (t *) _p;                                       \
+        })
+
 #define UPDATE_FLAG(orig, flag, b)                      \
         ((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
 #define SET_FLAG(v, flag, b) \
index 9b717645b377e5ddd3c0e29221243c44aed2c392..307f672507c1faf1d44f6a0f03dd467a0413470e 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "macro-fundamental.h"
 #include "sha256.h"
+#include "unaligned-fundamental.h"
 
 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 # define SWAP(n)                                                        \
 # define SWAP64(n) (n)
 #endif
 
-/* The condition below is from glibc's string/string-inline.c.
- * See definition of _STRING_INLINE_unaligned. */
-#if !defined(__mc68020__) && !defined(__s390__) && !defined(__i386__)
-#  define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__(uint32_t) != 0)
-#else
-#  define UNALIGNED_P(p) false
-#endif
-
 /* This array contains the bytes used to pad the buffer to the next
    64-byte boundary.  (FIPS 180-2:5.1.1)  */
 static const uint8_t fillbuf[64] = {
@@ -128,11 +121,7 @@ uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_
 
         /* Put result from CTX in first 32 bytes following RESBUF.  */
         for (size_t i = 0; i < 8; ++i)
-                if (UNALIGNED_P(resbuf))
-                        memcpy(resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t));
-                else
-                        ((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]);
-
+                unaligned_write_ne32(resbuf + i * sizeof(uint32_t), SWAP(ctx->H[i]));
         return resbuf;
 }
 
@@ -165,18 +154,17 @@ void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx
 
         /* Process available complete blocks.  */
         if (len >= 64) {
-                if (UNALIGNED_P(buffer))
+                if (IS_ALIGNED32(buffer)) {
+                        sha256_process_block(buffer, len & ~63, ctx);
+                        buffer = (const char *) buffer + (len & ~63);
+                        len &= 63;
+                } else
                         while (len > 64) {
                                 memcpy(ctx->buffer, buffer, 64);
                                 sha256_process_block(ctx->buffer, 64, ctx);
                                 buffer = (const char *) buffer + 64;
                                 len -= 64;
                         }
-                else {
-                        sha256_process_block(buffer, len & ~63, ctx);
-                        buffer = (const char *) buffer + (len & ~63);
-                        len &= 63;
-                }
         }
 
         /* Move remaining bytes into internal buffer.  */
index 31790c2ebd1737a0ab95fc37ba48b9d49fc76355..7c29c785cb46b0b0c3ca6f21d97c92065f869c93 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include "stdint.h"
+#include <stdint.h>
 
 #define SHA256_DIGEST_SIZE 32
 
index ecf32e519fde1884af583e812d759c634d3835c4..d8231478811b6be8b6a8cd99d5ef6dce577b3791 100644 (file)
@@ -110,6 +110,10 @@ static inline bool ascii_isdigit(sd_char a) {
         return a >= '0' && a <= '9';
 }
 
+static inline bool ascii_ishex(sd_char a) {
+        return ascii_isdigit(a) || (a >= 'a' && a <= 'f') || (a >= 'A' && a <= 'F');
+}
+
 static inline bool ascii_isalpha(sd_char a) {
         /* A pure ASCII, locale independent version of isalpha() */
         return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
diff --git a/src/fundamental/unaligned-fundamental.h b/src/fundamental/unaligned-fundamental.h
new file mode 100644 (file)
index 0000000..a4c810a
--- /dev/null
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdint.h>
+
+static inline uint16_t unaligned_read_ne16(const void *_u) {
+        const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
+
+        return u->x;
+}
+
+static inline uint32_t unaligned_read_ne32(const void *_u) {
+        const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
+
+        return u->x;
+}
+
+static inline uint64_t unaligned_read_ne64(const void *_u) {
+        const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
+
+        return u->x;
+}
+
+static inline void unaligned_write_ne16(void *_u, uint16_t a) {
+        struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
+
+        u->x = a;
+}
+
+static inline void unaligned_write_ne32(void *_u, uint32_t a) {
+        struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
+
+        u->x = a;
+}
+
+static inline void unaligned_write_ne64(void *_u, uint64_t a) {
+        struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
+
+        u->x = a;
+}
index 23a215231ca7063bc86d04322f939b76962888c4..f2f6cc1a53d901c9e2fc147acc7b75795dea165c 100644 (file)
@@ -358,23 +358,19 @@ static int add_automount(
 
         _cleanup_free_ char *unit = NULL, *p = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        const char *opt = "noauto";
         int r;
 
         assert(id);
         assert(where);
         assert(description);
 
-        if (options)
-                opt = strjoina(options, ",", opt);
-
         r = add_mount(id,
                       what,
                       where,
                       fstype,
                       rw,
                       growfs,
-                      opt,
+                      options,
                       description,
                       NULL);
         if (r < 0)
index 3e846e370ab588b1c6d94855ae6a748bb3665a83..a6d25c84fc1b5aa53d73e06bdbb57c75c8d0e805 100644 (file)
@@ -2392,6 +2392,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_IO_WEIGHT,
                 ARG_LUKS_PBKDF_TYPE,
                 ARG_LUKS_PBKDF_HASH_ALGORITHM,
+                ARG_LUKS_PBKDF_FORCE_ITERATIONS,
                 ARG_LUKS_PBKDF_TIME_COST,
                 ARG_LUKS_PBKDF_MEMORY_COST,
                 ARG_LUKS_PBKDF_PARALLEL_THREADS,
@@ -2473,6 +2474,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "luks-volume-key-size",        required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE        },
                 { "luks-pbkdf-type",             required_argument, NULL, ARG_LUKS_PBKDF_TYPE             },
                 { "luks-pbkdf-hash-algorithm",   required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM   },
+                { "luks-pbkdf-force-iterations", required_argument, NULL, ARG_LUKS_PBKDF_FORCE_ITERATIONS },
                 { "luks-pbkdf-time-cost",        required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST        },
                 { "luks-pbkdf-memory-cost",      required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST      },
                 { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
@@ -3093,10 +3095,12 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_LUKS_VOLUME_KEY_SIZE:
+                case ARG_LUKS_PBKDF_FORCE_ITERATIONS:
                 case ARG_LUKS_PBKDF_PARALLEL_THREADS:
                 case ARG_RATE_LIMIT_BURST: {
                         const char *field =
                                        c == ARG_LUKS_VOLUME_KEY_SIZE ? "luksVolumeKeySize" :
+                                c == ARG_LUKS_PBKDF_FORCE_ITERATIONS ? "luksPbkdfForceIterations" :
                                 c == ARG_LUKS_PBKDF_PARALLEL_THREADS ? "luksPbkdfParallelThreads" :
                                            c == ARG_RATE_LIMIT_BURST ? "rateLimitBurst" : NULL;
                         unsigned n;
index 6dc3df55a85acf980f385990a214a72e9f76c1fc..d3bc54742981d417e0ac4ceaa65c7b51ae25af9d 100644 (file)
@@ -130,7 +130,7 @@ int home_create_directory_or_subvolume(UserRecord *h, HomeSetup *setup, UserReco
         switch (user_record_storage(h)) {
 
         case USER_SUBVOLUME:
-                RUN_WITH_UMASK(0077)
+                WITH_UMASK(0077)
                         r = btrfs_subvol_make(d);
 
                 if (r >= 0) {
index 39ad56808d12217d6a950a14dbafe2b4d398420b..4d043597400d1d2290716ea3ef3f53221234c273 100644 (file)
@@ -1670,12 +1670,16 @@ static struct crypt_pbkdf_type* build_good_pbkdf(struct crypt_pbkdf_type *buffer
         assert(buffer);
         assert(hr);
 
+        bool benchmark = user_record_luks_pbkdf_force_iterations(hr) == UINT64_MAX;
+
         *buffer = (struct crypt_pbkdf_type) {
                 .hash = user_record_luks_pbkdf_hash_algorithm(hr),
                 .type = user_record_luks_pbkdf_type(hr),
-                .time_ms = user_record_luks_pbkdf_time_cost_usec(hr) / USEC_PER_MSEC,
+                .time_ms = benchmark ? user_record_luks_pbkdf_time_cost_usec(hr) / USEC_PER_MSEC : 0,
+                .iterations = benchmark ? 0 : user_record_luks_pbkdf_force_iterations(hr),
                 .max_memory_kb = user_record_luks_pbkdf_memory_cost(hr) / 1024,
                 .parallel_threads = user_record_luks_pbkdf_parallel_threads(hr),
+                .flags = benchmark ? 0 : CRYPT_PBKDF_NO_BENCHMARK,
         };
 
         return buffer;
index f3d3131828afc57c8e20c84c685fa76c16ff3cb5..ea19dfbb0412b397e8158ac133b87424b7ce19ba 100644 (file)
@@ -246,6 +246,14 @@ static int get_firmware_version(char **ret) {
          return get_hardware_firmware_data("bios_version", ret);
 }
 
+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 const char* valid_chassis(const char *chassis) {
         assert(chassis);
 
@@ -628,6 +636,37 @@ static int property_get_firmware_version(
         return sd_bus_message_append(reply, "s", firmware_version);
 }
 
+static int property_get_firmware_vendor(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *firmware_vendor = NULL;
+
+        (void) get_firmware_vendor(&firmware_vendor);
+
+        return sd_bus_message_append(reply, "s", firmware_vendor);
+}
+
+static int property_get_firmware_date(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *firmware_date = NULL;
+
+        (void) get_firmware_date(&firmware_date);
+
+        return sd_bus_message_append(reply, "s", firmware_date);
+}
 static int property_get_hostname(
                 sd_bus *bus,
                 const char *path,
@@ -1149,7 +1188,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;
+                *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL,
+                *firmware_vendor = NULL, *firmware_date = NULL;
         _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;
@@ -1213,6 +1253,8 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
                 (void) get_hardware_serial(&serial);
         }
         (void) get_firmware_version(&firmware_version);
+        (void) get_firmware_vendor(&firmware_vendor);
+        (void) get_firmware_date(&firmware_date);
 
         r = json_build(&v, JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)),
@@ -1234,6 +1276,8 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
                                        JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model ?: c->data[PROP_HARDWARE_MODEL])),
                                        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_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)));
 
@@ -1275,6 +1319,8 @@ static const sd_bus_vtable hostname_vtable[] = {
         SD_BUS_PROPERTY("HardwareVendor", "s", property_get_hardware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         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_METHOD_WITH_ARGS("SetHostname",
                                 SD_BUS_ARGS("s", hostname, "b", interactive),
index 9a3ea131c122ad4cb65d6bc929cdd2bde925de91..4f6be5f20c914f143e8e79da193a36dab3470b7c 100644 (file)
@@ -94,6 +94,9 @@ struct Manager {
         int notify_fd;
 
         sd_event_source *notify_event_source;
+
+        bool use_btrfs_subvol;
+        bool use_btrfs_quota;
 };
 
 #define TRANSFERS_MAX 64
@@ -628,10 +631,15 @@ static int manager_new(Manager **ret) {
 
         assert(ret);
 
-        m = new0(Manager, 1);
+        m = new(Manager, 1);
         if (!m)
                 return -ENOMEM;
 
+        *m = (Manager) {
+                .use_btrfs_subvol = true,
+                .use_btrfs_quota = true,
+        };
+
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
@@ -719,7 +727,7 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
                                          "Local name %s is invalid", local);
 
-        r = setup_machine_directory(error);
+        r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
         if (r < 0)
                 return r;
 
@@ -788,7 +796,7 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
                                          "Local name %s is invalid", local);
 
-        r = setup_machine_directory(error);
+        r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
         if (r < 0)
                 return r;
 
@@ -939,7 +947,7 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
                                          "Unknown verification mode %s", verify);
 
-        r = setup_machine_directory(error);
+        r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
         if (r < 0)
                 return r;
 
@@ -1351,6 +1359,28 @@ static int manager_run(Manager *m) {
                         m);
 }
 
+static void manager_parse_env(Manager *m) {
+        int r;
+
+        assert(m);
+
+        /* Same as src/import/{import,pull}.c:
+         * Let's make these relatively low-level settings also controllable via env vars. User can then set
+         * them for systemd-importd.service if they like to tweak behaviour */
+
+        r = getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
+        if (r >= 0)
+                m->use_btrfs_subvol = r;
+        else if (r != -ENXIO)
+                log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
+
+        r = getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
+        if (r >= 0)
+                m->use_btrfs_quota = r;
+        else if (r != -ENXIO)
+                log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
+}
+
 static int run(int argc, char *argv[]) {
         _cleanup_(manager_unrefp) Manager *m = NULL;
         int r;
@@ -1373,6 +1403,8 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate manager object: %m");
 
+        manager_parse_env(m);
+
         r = manager_add_bus_objects(m);
         if (r < 0)
                 return r;
index d301d2896621d981231bcb1973cb630be302fc20..a2ebf97c9cb18b281392090e313dca75630c1957 100644 (file)
@@ -8,6 +8,7 @@
 #include "fd-util.h"
 #include "hexdecoct.h"
 #include "io-util.h"
+#include "journal-internal.h"
 #include "journald-audit.h"
 #include "missing_audit.h"
 #include "string-util.h"
@@ -441,7 +442,7 @@ void server_process_audit_message(
         }
 
         if (!NLMSG_OK(nl, buffer_size)) {
-                log_ratelimit_error(JOURNALD_LOG_RATELIMIT, "Audit netlink message truncated.");
+                log_ratelimit_error(JOURNAL_LOG_RATELIMIT, "Audit netlink message truncated.");
                 return;
         }
 
index 6d58422dddac0debc0f010dc6185a7248f36e287..222855ae60f3c58eebe03f675cf7c50dd0e94cc2 100644 (file)
@@ -12,6 +12,7 @@
 #include "fileio.h"
 #include "fs-util.h"
 #include "io-util.h"
+#include "journal-internal.h"
 #include "journal-util.h"
 #include "journald-context.h"
 #include "parse-util.h"
@@ -258,7 +259,7 @@ static int client_context_read_label(
 
                 /* If we got no SELinux label passed in, let's try to acquire one */
 
-                if (getpidcon(c->pid, &con) >= 0) {
+                if (getpidcon(c->pid, &con) >= 0 && con) {
                         free_and_replace(c->label, con);
                         c->label_size = strlen(c->label);
                 }
@@ -771,7 +772,7 @@ void client_context_acquire_default(Server *s) {
 
                 r = client_context_acquire(s, ucred.pid, &ucred, NULL, 0, NULL, &s->my_context);
                 if (r < 0)
-                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                     "Failed to acquire our own context, ignoring: %m");
         }
 
@@ -781,7 +782,7 @@ void client_context_acquire_default(Server *s) {
 
                 r = client_context_acquire(s, 1, NULL, NULL, 0, NULL, &s->pid1_context);
                 if (r < 0)
-                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                     "Failed to acquire PID1's context, ignoring: %m");
 
         }
index 10faf2dd06e475f6c6f4cb72f1c206fb975af21a..99eace084876bb72b01a1faf078b5dca6800158a 100644 (file)
@@ -16,6 +16,7 @@
 #include "format-util.h"
 #include "fs-util.h"
 #include "io-util.h"
+#include "journal-internal.h"
 #include "journald-kmsg.h"
 #include "journald-server.h"
 #include "journald-syslog.h"
@@ -329,7 +330,7 @@ static int server_read_dev_kmsg(Server *s) {
                 if (ERRNO_IS_TRANSIENT(errno) || errno == EPIPE)
                         return 0;
 
-                return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT, "Failed to read from /dev/kmsg: %m");
+                return log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT, "Failed to read from /dev/kmsg: %m");
         }
 
         dev_kmsg_record(s, buffer, l);
@@ -368,7 +369,7 @@ static int dispatch_dev_kmsg(sd_event_source *es, int fd, uint32_t revents, void
         assert(fd == s->dev_kmsg_fd);
 
         if (revents & EPOLLERR)
-                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
                                       "/dev/kmsg buffer overrun, some messages lost.");
 
         if (!(revents & EPOLLIN))
index 21e20db2d44f033690dcff7c170a40d12ba96150..847f69c1ffebe545b37ac864e9c7958dd53d0cd8 100644 (file)
@@ -11,6 +11,7 @@
 #include "fs-util.h"
 #include "io-util.h"
 #include "journal-importer.h"
+#include "journal-internal.h"
 #include "journal-util.h"
 #include "journald-console.h"
 #include "journald-kmsg.h"
@@ -309,7 +310,7 @@ void server_process_native_message(
         if (ucred && pid_is_valid(ucred->pid)) {
                 r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
                 if (r < 0)
-                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                     "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m",
                                                     ucred->pid);
         }
@@ -350,33 +351,33 @@ void server_process_native_file(
 
                 r = fd_get_path(fd, &k);
                 if (r < 0) {
-                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
                                                   "readlink(/proc/self/fd/%i) failed: %m", fd);
                         return;
                 }
 
                 e = PATH_STARTSWITH_SET(k, "/dev/shm/", "/tmp/", "/var/tmp/");
                 if (!e) {
-                        log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
                                             "Received file outside of allowed directories. Refusing.");
                         return;
                 }
 
                 if (!filename_is_valid(e)) {
-                        log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
                                             "Received file in subdirectory of allowed directories. Refusing.");
                         return;
                 }
         }
 
         if (fstat(fd, &st) < 0) {
-                log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
                                           "Failed to stat passed file, ignoring: %m");
                 return;
         }
 
         if (!S_ISREG(st.st_mode)) {
-                log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
                                     "File passed is not regular. Ignoring.");
                 return;
         }
@@ -387,7 +388,7 @@ void server_process_native_file(
         /* When !sealed, set a lower memory limit. We have to read the file,
          * effectively doubling memory use. */
         if (st.st_size > ENTRY_SIZE_MAX / (sealed ? 1 : 2)) {
-                log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
                                     "File passed too large (%"PRIu64" bytes). Ignoring.",
                                     (uint64_t) st.st_size);
                 return;
@@ -402,7 +403,7 @@ void server_process_native_file(
                 ps = PAGE_ALIGN(st.st_size);
                 p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0);
                 if (p == MAP_FAILED) {
-                        log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
                                                   "Failed to map memfd, ignoring: %m");
                         return;
                 }
@@ -415,7 +416,7 @@ void server_process_native_file(
                 ssize_t n;
 
                 if (fstatvfs(fd, &vfs) < 0) {
-                        log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
                                                   "Failed to stat file system of passed file, not processing it: %m");
                         return;
                 }
@@ -426,7 +427,7 @@ void server_process_native_file(
                  * https://github.com/systemd/systemd/issues/1822
                  */
                 if (vfs.f_flag & ST_MANDLOCK) {
-                        log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
                                             "Received file descriptor from file system with mandatory locking enabled, not processing it.");
                         return;
                 }
@@ -440,7 +441,7 @@ void server_process_native_file(
                  * and so is SMB. */
                 r = fd_nonblock(fd, true);
                 if (r < 0) {
-                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
                                                   "Failed to make fd non-blocking, not processing it: %m");
                         return;
                 }
@@ -457,7 +458,7 @@ void server_process_native_file(
 
                 n = pread(fd, p, st.st_size, 0);
                 if (n < 0)
-                        log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
                                                   "Failed to read file, ignoring: %m");
                 else if (n > 0)
                         server_process_native_message(s, p, n, ucred, tv, label, label_len);
index cb94a037d572e744fb826c9278f26f37e8614b96..b268db0220ea4387283e660fddc691977e83c901 100644 (file)
@@ -102,10 +102,10 @@ static int determine_path_usage(
         d = opendir(path);
         if (!d)
                 return log_ratelimit_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
-                                                errno, JOURNALD_LOG_RATELIMIT, "Failed to open %s: %m", path);
+                                                errno, JOURNAL_LOG_RATELIMIT, "Failed to open %s: %m", path);
 
         if (fstatvfs(dirfd(d), &ss) < 0)
-                return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                return log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
                                                 "Failed to fstatvfs(%s): %m", path);
 
         *ret_free = ss.f_bsize * ss.f_bavail;
@@ -256,7 +256,7 @@ static void server_add_acls(ManagedJournalFile *f, uid_t uid) {
 
         r = fd_add_uid_acl_permission(f->file->fd, uid, ACL_READ);
         if (r < 0)
-                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                             "Failed to set ACL on %s, ignoring: %m", f->file->path);
 #endif
 }
@@ -357,7 +357,7 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
                         patch_min_use(&s->system_storage);
                 } else {
                         if (!IN_SET(r, -ENOENT, -EROFS))
-                                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                             "Failed to open system journal: %m");
 
                         r = 0;
@@ -387,7 +387,7 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
                         r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_storage.metrics, &s->runtime_journal);
                         if (r < 0) {
                                 if (r != -ENOENT)
-                                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                                     "Failed to open runtime journal: %m");
 
                                 r = 0;
@@ -402,7 +402,7 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
 
                         r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_storage.metrics, &s->runtime_journal);
                         if (r < 0)
-                                return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                                    "Failed to open runtime journal: %m");
                 }
 
@@ -500,10 +500,10 @@ static int do_rotate(
         r = managed_journal_file_rotate(f, s->mmap, file_flags, s->compress.threshold_bytes, s->deferred_closes);
         if (r < 0) {
                 if (*f)
-                        return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                        return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
                                                          "Failed to rotate %s: %m", (*f)->file->path);
                 else
-                        return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                        return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
                                                          "Failed to create new %s journal: %m", name);
         }
 
@@ -554,7 +554,7 @@ static int vacuum_offline_user_journals(Server *s) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+                return log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
                                                  "Failed to open %s: %m", s->system_storage.path);
         }
 
@@ -570,7 +570,7 @@ static int vacuum_offline_user_journals(Server *s) {
                 de = readdir_no_dot(d);
                 if (!de) {
                         if (errno != 0)
-                                log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT,
+                                log_ratelimit_warning_errno(errno, JOURNAL_LOG_RATELIMIT,
                                                             "Failed to enumerate %s, ignoring: %m",
                                                             s->system_storage.path);
 
@@ -605,7 +605,7 @@ static int vacuum_offline_user_journals(Server *s) {
                 fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
                 if (fd < 0) {
                         log_ratelimit_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING,
-                                                 errno, JOURNALD_LOG_RATELIMIT,
+                                                 errno, JOURNAL_LOG_RATELIMIT,
                                                  "Failed to open journal file '%s' for rotation: %m", full);
                         continue;
                 }
@@ -628,13 +628,13 @@ static int vacuum_offline_user_journals(Server *s) {
                                 NULL,
                                 &f);
                 if (r < 0) {
-                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                     "Failed to read journal file %s for rotation, trying to move it out of the way: %m",
                                                     full);
 
                         r = journal_file_dispose(dirfd(d), de->d_name);
                         if (r < 0)
-                                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                                log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                             "Failed to move %s out of the way, ignoring: %m",
                                                             full);
                         else
@@ -692,21 +692,21 @@ void server_sync(Server *s) {
         if (s->system_journal) {
                 r = managed_journal_file_set_offline(s->system_journal, false);
                 if (r < 0)
-                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                     "Failed to sync system journal, ignoring: %m");
         }
 
         ORDERED_HASHMAP_FOREACH(f, s->user_journals) {
                 r = managed_journal_file_set_offline(f, false);
                 if (r < 0)
-                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                     "Failed to sync user journal, ignoring: %m");
         }
 
         if (s->sync_event_source) {
                 r = sd_event_source_set_enabled(s->sync_event_source, SD_EVENT_OFF);
                 if (r < 0)
-                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
                                                   "Failed to disable sync timer source: %m");
         }
 
@@ -729,7 +729,7 @@ static void do_vacuum(Server *s, JournalStorage *storage, bool verbose) {
                                      storage->metrics.n_max_files, s->max_retention_usec,
                                      &s->oldest_file_usec, verbose);
         if (r < 0 && r != -ENOENT)
-                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                             "Failed to vacuum %s, ignoring: %m", storage->path);
 
         cache_space_invalidate(&storage->space);
@@ -801,38 +801,46 @@ static bool shall_try_append_again(JournalFile *f, int r) {
                 log_debug("%s: Allocation limit reached, rotating.", f->path);
                 return true;
 
+        case -EROFS: /* Read-only file system */
+                /* When appending an entry fails if shall_try_append_again returns true, the journal is
+                 * rotated. If the FS is read-only, rotation will fail and s->system_journal will be set to
+                 * NULL. After that, when find_journal will try to open the journal since s->system_journal
+                 * will be NULL, it will open the runtime journal. */
+                log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "%s: Read-only file system, rotating.", f->path);
+                return true;
+
         case -EIO:             /* I/O error of some kind (mmap) */
-                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: IO error, rotating.", f->path);
+                log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "%s: IO error, rotating.", f->path);
                 return true;
 
         case -EHOSTDOWN:       /* Other machine                 */
-                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "%s: Journal file from other machine, rotating.", f->path);
+                log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "%s: Journal file from other machine, rotating.", f->path);
                 return true;
 
         case -EBUSY:           /* Unclean shutdown              */
-                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "%s: Unclean shutdown, rotating.", f->path);
+                log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "%s: Unclean shutdown, rotating.", f->path);
                 return true;
 
         case -EPROTONOSUPPORT: /* Unsupported feature           */
-                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "%s: Unsupported feature, rotating.", f->path);
+                log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "%s: Unsupported feature, rotating.", f->path);
                 return true;
 
         case -EBADMSG:         /* Corrupted                     */
         case -ENODATA:         /* Truncated                     */
         case -ESHUTDOWN:       /* Already archived              */
-                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Journal file corrupted, rotating.", f->path);
+                log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "%s: Journal file corrupted, rotating.", f->path);
                 return true;
 
         case -EIDRM:           /* Journal file has been deleted */
-                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Journal file has been deleted, rotating.", f->path);
+                log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "%s: Journal file has been deleted, rotating.", f->path);
                 return true;
 
         case -ETXTBSY:         /* Journal file is from the future */
-                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Journal file is from the future, rotating.", f->path);
+                log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "%s: Journal file is from the future, rotating.", f->path);
                 return true;
 
         case -EAFNOSUPPORT:
-                log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
                                       "%s: underlying file system does not support memory mapping or another required file system feature.",
                                       f->path);
                 return false;
@@ -864,7 +872,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, size_t n
                  * to ensure that the entries in the journal files are strictly ordered by time, in order to ensure
                  * bisection works correctly. */
 
-                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "Time jumped backwards, rotating.");
+                log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "Time jumped backwards, rotating.");
                 rotate = true;
         } else {
 
@@ -873,7 +881,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, size_t n
                         return;
 
                 if (journal_file_rotate_suggested(f->file, s->max_file_usec, LOG_INFO)) {
-                        log_ratelimit_info(JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_info(JOURNAL_LOG_RATELIMIT,
                                            "%s: Journal header limits reached or header out-of-date, rotating.",
                                            f->file->path);
                         rotate = true;
@@ -1212,7 +1220,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
 
         r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY);
         if (r < 0)
-                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+                return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
                                                  "Failed to read runtime journal: %m");
 
         sd_journal_set_data_threshold(j, 0);
@@ -1228,7 +1236,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
 
                 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
                 if (r < 0) {
-                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Can't read entry: %m");
+                        log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Can't read entry: %m");
                         goto finish;
                 }
 
@@ -1237,17 +1245,17 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
                         continue;
 
                 if (!shall_try_append_again(s->system_journal->file, r)) {
-                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Can't write entry: %m");
+                        log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Can't write entry: %m");
                         goto finish;
                 }
 
-                log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "Rotating system journal.");
+                log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "Rotating system journal.");
 
                 server_rotate(s);
                 server_vacuum(s, false);
 
                 if (!s->system_journal) {
-                        log_ratelimit_notice(JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_notice(JOURNAL_LOG_RATELIMIT,
                                              "Didn't flush runtime journal since rotation of system journal wasn't successful.");
                         r = -EIO;
                         goto finish;
@@ -1256,7 +1264,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
                 log_debug("Retrying write.");
                 r = journal_file_copy_entry(f, s->system_journal->file, o, f->current_offset);
                 if (r < 0) {
-                        log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Can't write entry: %m");
+                        log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Can't write entry: %m");
                         goto finish;
                 }
         }
@@ -1284,7 +1292,7 @@ finish:
         fn = strjoina(s->runtime_directory, "/flushed");
         k = touch(fn);
         if (k < 0)
-                log_ratelimit_warning_errno(k, JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_warning_errno(k, JOURNAL_LOG_RATELIMIT,
                                             "Failed to touch %s, ignoring: %m", fn);
 
         server_refresh_idle_timer(s);
@@ -1314,7 +1322,7 @@ static int server_relinquish_var(Server *s) {
 
         fn = strjoina(s->runtime_directory, "/flushed");
         if (unlink(fn) < 0 && errno != ENOENT)
-                log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_warning_errno(errno, JOURNAL_LOG_RATELIMIT,
                                             "Failed to unlink %s, ignoring: %m", fn);
 
         server_refresh_idle_timer(s);
@@ -1387,11 +1395,11 @@ int server_process_datagram(
                 if (ERRNO_IS_TRANSIENT(n))
                         return 0;
                 if (n == -EXFULL) {
-                        log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
                                               "Got message with truncated control data (too many fds sent?), ignoring.");
                         return 0;
                 }
-                return log_ratelimit_error_errno(n, JOURNALD_LOG_RATELIMIT, "recvmsg() failed: %m");
+                return log_ratelimit_error_errno(n, JOURNAL_LOG_RATELIMIT, "recvmsg() failed: %m");
         }
 
         CMSG_FOREACH(cmsg, &msghdr)
@@ -1424,7 +1432,7 @@ int server_process_datagram(
                 if (n > 0 && n_fds == 0)
                         server_process_syslog_message(s, s->buffer, n, ucred, tv, label, label_len);
                 else if (n_fds > 0)
-                        log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
                                               "Got file descriptors via syslog socket. Ignoring.");
 
         } else if (fd == s->native_fd) {
@@ -1433,7 +1441,7 @@ int server_process_datagram(
                 else if (n == 0 && n_fds == 1)
                         server_process_native_file(s, fds[0], ucred, tv, label, label_len);
                 else if (n_fds > 0)
-                        log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
                                               "Got too many file descriptors via native socket. Ignoring.");
 
         } else {
@@ -1442,7 +1450,7 @@ int server_process_datagram(
                 if (n > 0 && n_fds == 0)
                         server_process_audit_message(s, s->buffer, n, ucred, &sa, msghdr.msg_namelen);
                 else if (n_fds > 0)
-                        log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
                                               "Got file descriptors via audit socket. Ignoring.");
         }
 
@@ -1496,7 +1504,7 @@ static void server_full_rotate(Server *s) {
         fn = strjoina(s->runtime_directory, "/rotated");
         r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
         if (r < 0)
-                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                             "Failed to write %s, ignoring: %m", fn);
 }
 
@@ -1600,7 +1608,7 @@ static void server_full_sync(Server *s) {
         fn = strjoina(s->runtime_directory, "/synced");
         r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
         if (r < 0)
-                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                             "Failed to write %s, ignoring: %m", fn);
 
         return;
index fb512dcfeb7664906f3c19464053145848a014b7..ee8f37419088c2e7e9c99e29ba078ec28b4923d5 100644 (file)
@@ -20,8 +20,6 @@ typedef struct Server Server;
 #include "time-util.h"
 #include "varlink.h"
 
-#define JOURNALD_LOG_RATELIMIT ((const RateLimit) { .interval = 60 * USEC_PER_SEC, .burst = 3 })
-
 typedef enum Storage {
         STORAGE_AUTO,
         STORAGE_VOLATILE,
index abfd04683713f42c91ca08665119e74e0acedadf..49f28972ea205edf140b4fde8f7da0744871ea1c 100644 (file)
@@ -19,6 +19,7 @@
 #include "fileio.h"
 #include "fs-util.h"
 #include "io-util.h"
+#include "journal-internal.h"
 #include "journald-console.h"
 #include "journald-context.h"
 #include "journald-kmsg.h"
@@ -160,7 +161,7 @@ static int stdout_stream_save(StdoutStream *s) {
 
                 r = fstat(s->fd, &st);
                 if (r < 0)
-                        return log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT,
+                        return log_ratelimit_warning_errno(errno, JOURNAL_LOG_RATELIMIT,
                                                            "Failed to stat connected stream: %m");
 
                 /* We use device and inode numbers as identifier for the stream */
@@ -232,7 +233,7 @@ static int stdout_stream_save(StdoutStream *s) {
                 if (s->server->notify_event_source) {
                         r = sd_event_source_set_enabled(s->server->notify_event_source, SD_EVENT_ON);
                         if (r < 0)
-                                log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to enable notify event source: %m");
+                                log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to enable notify event source: %m");
                 }
         }
 
@@ -240,7 +241,7 @@ static int stdout_stream_save(StdoutStream *s) {
 
 fail:
         (void) unlink(s->state_file);
-        return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+        return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
                                          "Failed to save stream data %s: %m", s->state_file);
 }
 
@@ -268,7 +269,7 @@ static int stdout_stream_log(
         else if (pid_is_valid(s->ucred.pid)) {
                 r = client_context_acquire(s->server, s->ucred.pid, &s->ucred, s->label, strlen_ptr(s->label), s->unit_id, &s->context);
                 if (r < 0)
-                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                     "Failed to acquire client context, ignoring: %m");
         }
 
@@ -366,7 +367,7 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
 
         /* line breaks by NUL, line max length or EOF are not permissible during the negotiation part of the protocol */
         if (line_break != LINE_BREAK_NEWLINE && s->state != STDOUT_STREAM_RUNNING)
-                return log_ratelimit_warning_errno(SYNTHETIC_ERRNO(EINVAL), JOURNALD_LOG_RATELIMIT,
+                return log_ratelimit_warning_errno(SYNTHETIC_ERRNO(EINVAL), JOURNAL_LOG_RATELIMIT,
                                                    "Control protocol line not properly terminated.");
 
         switch (s->state) {
@@ -398,7 +399,7 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
 
                 priority = syslog_parse_priority_and_facility(p);
                 if (priority < 0)
-                        return log_ratelimit_warning_errno(priority, JOURNALD_LOG_RATELIMIT,
+                        return log_ratelimit_warning_errno(priority, JOURNAL_LOG_RATELIMIT,
                                                            "Failed to parse log priority line: %m");
 
                 s->priority = priority;
@@ -409,7 +410,7 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         case STDOUT_STREAM_LEVEL_PREFIX:
                 r = parse_boolean(p);
                 if (r < 0)
-                        return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                            "Failed to parse level prefix line: %m");
 
                 s->level_prefix = r;
@@ -419,7 +420,7 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         case STDOUT_STREAM_FORWARD_TO_SYSLOG:
                 r = parse_boolean(p);
                 if (r < 0)
-                        return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                            "Failed to parse forward to syslog line: %m");
 
                 s->forward_to_syslog = r;
@@ -429,7 +430,7 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         case STDOUT_STREAM_FORWARD_TO_KMSG:
                 r = parse_boolean(p);
                 if (r < 0)
-                        return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                            "Failed to parse copy to kmsg line: %m");
 
                 s->forward_to_kmsg = r;
@@ -439,7 +440,7 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         case STDOUT_STREAM_FORWARD_TO_CONSOLE:
                 r = parse_boolean(p);
                 if (r < 0)
-                        return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                            "Failed to parse copy to console line.");
 
                 s->forward_to_console = r;
@@ -597,7 +598,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
                 if (ERRNO_IS_TRANSIENT(errno))
                         return 0;
 
-                log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT, "Failed to read from stream: %m");
+                log_ratelimit_warning_errno(errno, JOURNAL_LOG_RATELIMIT, "Failed to read from stream: %m");
                 goto terminate;
         }
         cmsg_close_all(&msghdr);
@@ -656,7 +657,7 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
 
         r = sd_id128_randomize(&id);
         if (r < 0)
-                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to generate stream ID: %m");
+                return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to generate stream ID: %m");
 
         stream = new(StdoutStream, 1);
         if (!stream)
@@ -672,7 +673,7 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
 
         r = getpeercred(fd, &stream->ucred);
         if (r < 0)
-                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to determine peer credentials: %m");
+                return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to determine peer credentials: %m");
 
         r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
         if (r < 0)
@@ -681,18 +682,18 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
         if (mac_selinux_use()) {
                 r = getpeersec(fd, &stream->label);
                 if (r < 0 && r != -EOPNOTSUPP)
-                        (void) log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to determine peer security context: %m");
+                        (void) log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to determine peer security context: %m");
         }
 
         (void) shutdown(fd, SHUT_WR);
 
         r = sd_event_add_io(s->event, &stream->event_source, fd, EPOLLIN, stdout_stream_process, stream);
         if (r < 0)
-                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to add stream to event loop: %m");
+                return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to add stream to event loop: %m");
 
         r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5);
         if (r < 0)
-                return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to adjust stdout event source priority: %m");
+                return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to adjust stdout event source priority: %m");
 
         stream->fd = fd;
 
@@ -724,7 +725,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
                 if (ERRNO_IS_ACCEPT_AGAIN(errno))
                         return 0;
 
-                return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT, "Failed to accept stdout connection: %m");
+                return log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT, "Failed to accept stdout connection: %m");
         }
 
         if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
index 6394adfdfdf2291d30b214c92b99f4770b82ff5e..d8708b07755ce98676ecbbfc79d349b11c43ea79 100644 (file)
@@ -10,6 +10,7 @@
 #include "fd-util.h"
 #include "format-util.h"
 #include "io-util.h"
+#include "journal-internal.h"
 #include "journald-console.h"
 #include "journald-kmsg.h"
 #include "journald-server.h"
@@ -334,7 +335,7 @@ void server_process_syslog_message(
         if (ucred && pid_is_valid(ucred->pid)) {
                 r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
                 if (r < 0)
-                        log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
                                                     "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m",
                                                     ucred->pid);
         }
index 7939574c372d82350226b6a64e248a34b5097d04..379a0c8caf99e3415e11bdd503e815d7ad5495e9 100644 (file)
@@ -3,6 +3,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 
+#include "sd-id128.h"
 #include "sd-journal.h"
 
 #include "alloc-util.h"
@@ -262,22 +263,26 @@ static void test_sequence_numbers_one(void) {
 
         test_close(one);
 
-        /* restart server */
-        seqnum = 0;
+        /* If the machine-id is not initialized, the header file verification
+         * (which happens when re-opening a journal file) will fail. */
+        if (sd_id128_get_machine(NULL) >= 0) {
+                /* restart server */
+                seqnum = 0;
 
-        assert_se(managed_journal_file_open(-1, "two.journal", O_RDWR, JOURNAL_COMPRESS, 0,
-                                            UINT64_MAX, NULL, m, NULL, NULL, &two) == 0);
+                assert_se(managed_journal_file_open(-1, "two.journal", O_RDWR, JOURNAL_COMPRESS, 0,
+                                                    UINT64_MAX, NULL, m, NULL, NULL, &two) == 0);
 
-        assert_se(sd_id128_equal(two->file->header->seqnum_id, seqnum_id));
+                assert_se(sd_id128_equal(two->file->header->seqnum_id, seqnum_id));
 
-        append_number(two, 7, &seqnum);
-        printf("seqnum=%"PRIu64"\n", seqnum);
-        assert_se(seqnum == 5);
+                append_number(two, 7, &seqnum);
+                printf("seqnum=%"PRIu64"\n", seqnum);
+                assert_se(seqnum == 5);
 
-        /* So..., here we have the same seqnum in two files with the
-         * same seqnum_id. */
+                /* So..., here we have the same seqnum in two files with the
+                 * same seqnum_id. */
 
-        test_close(two);
+                test_close(two);
+        }
 
         log_info("Done...");
 
index ee48160046005f992e319b4a2e5f7cdc0656307f..3f34b46280894f7a5833e3eb78121ddc3fb9f8a8 100644 (file)
@@ -1293,7 +1293,7 @@ static int client_receive_message(
                 if (cmsg->cmsg_level == SOL_SOCKET &&
                     cmsg->cmsg_type == SO_TIMESTAMP &&
                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
-                        triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
+                        triple_timestamp_from_realtime(&t, timeval_load(CMSG_TYPED_DATA(cmsg, struct timeval)));
         }
 
         if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
index 57c23965eda3ab3b73b0bfdf61856213fa879756..3dcccf1729e6186157a68d49acd0d87e430e16a7 100644 (file)
@@ -619,7 +619,7 @@ static int dhcp6_lease_parse_message(
                                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
                                                               "Received information refresh time option with an invalid length (%zu).", optlen);
 
-                        irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
+                        irt = unaligned_read_be32(optval) * USEC_PER_SEC;
                         break;
                 }
         }
index d4a1fb689ebf2faba9129d2e9449dadeb6672684..40d6abc41319671c371754613befe687108ac2a2 100644 (file)
@@ -31,6 +31,7 @@
 #define BUS_ERROR_NOTHING_TO_CLEAN             "org.freedesktop.systemd1.NothingToClean"
 #define BUS_ERROR_UNIT_BUSY                    "org.freedesktop.systemd1.UnitBusy"
 #define BUS_ERROR_UNIT_INACTIVE                "org.freedesktop.systemd1.UnitInactive"
+#define BUS_ERROR_FREEZE_CANCELLED             "org.freedesktop.systemd1.FreezeCancelled"
 
 #define BUS_ERROR_NO_SUCH_MACHINE              "org.freedesktop.machine1.NoSuchMachine"
 #define BUS_ERROR_NO_SUCH_IMAGE                "org.freedesktop.machine1.NoSuchImage"
index 55a0ee730e89f21a4c9d41f287fd790782681bab..9c4743f05508aa3d2d84aac98a172a04549068c4 100644 (file)
@@ -2332,9 +2332,14 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
                                        sysattr, value, ret_value ? "" : ", ignoring");
                 if (ret_value)
                         return r;
-        } else if (ret_value)
-                *ret_value = TAKE_PTR(value);
 
+                return 0;
+        }
+
+        if (ret_value)
+                *ret_value = value;
+
+        TAKE_PTR(value);
         return 0;
 }
 
index 32ee6707013f4c691ebd01015b7fc320f2871efa..2bb9c287889a1907cbeaf63ea5365b7d00c19ca6 100644 (file)
@@ -180,15 +180,16 @@ static void test_sd_device_one(sd_device *d) {
         } else
                 assert_se(r == -ENOENT);
 
-        r = sd_device_get_sysattr_value(d, "name_assign_type", &val);
-        assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || IN_SET(r, -ENOENT, -EINVAL));
-
-        if (r > 0) {
+        r = sd_device_get_sysattr_value(d, "nsid", NULL);
+        if (r >= 0) {
                 unsigned x;
 
-                assert_se(device_get_sysattr_unsigned(d, "name_assign_type", NULL) >= 0);
-                assert_se(device_get_sysattr_unsigned(d, "name_assign_type", &x) >= 0);
-        }
+                assert_se(device_get_sysattr_unsigned(d, "nsid", NULL) >= 0);
+                r = device_get_sysattr_unsigned(d, "nsid", &x);
+                assert_se(r >= 0);
+                assert_se((x > 0) == (r > 0));
+        } else
+                assert_se(ERRNO_IS_PRIVILEGE(r) || IN_SET(r, -ENOENT, -EINVAL));
 }
 
 TEST(sd_device_enumerator_devices) {
index 4f52c14f64b4f675bc38bd45425cb680a98c722c..eae2562410cca074b18d751fcc086a96142556b1 100644 (file)
 #include "sync-util.h"
 
 bool id128_is_valid(const char *s) {
-        size_t i, l;
+        size_t l;
 
         assert(s);
 
         l = strlen(s);
-        if (l == 32) {
 
+        if (l == SD_ID128_STRING_MAX - 1)
                 /* Plain formatted 128bit hex string */
+                return in_charset(s, HEXDIGITS);
 
-                for (i = 0; i < l; i++) {
-                        char c = s[i];
-
-                        if (!ascii_isdigit(c) &&
-                            !(c >= 'a' && c <= 'f') &&
-                            !(c >= 'A' && c <= 'F'))
-                                return false;
-                }
-
-        } else if (l == 36) {
-
+        if (l == SD_ID128_UUID_STRING_MAX - 1) {
                 /* Formatted UUID */
-
-                for (i = 0; i < l; i++) {
+                for (size_t i = 0; i < l; i++) {
                         char c = s[i];
 
                         if (IN_SET(i, 8, 13, 18, 23)) {
                                 if (c != '-')
                                         return false;
-                        } else {
-                                if (!ascii_isdigit(c) &&
-                                    !(c >= 'a' && c <= 'f') &&
-                                    !(c >= 'A' && c <= 'F'))
-                                        return false;
-                        }
+                        } else if (!ascii_ishex(c))
+                                return false;
                 }
+                return true;
+        }
 
-        } else
-                return false;
-
-        return true;
+        return false;
 }
 
-int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
-        char buffer[36 + 2];
+int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret) {
+        char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
         ssize_t l;
+        int r;
 
         assert(fd >= 0);
-        assert(f < _ID128_FORMAT_MAX);
 
         /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
          * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
          * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
-         * accept". */
+         * accept".
+         *
+         * This returns the following:
+         *     -ENOMEDIUM: an empty string,
+         *     -ENOPKG:    "uninitialized" or "uninitialized\n",
+         *     -EUCLEAN:   other invalid strings. */
 
         l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
         if (l < 0)
@@ -75,43 +65,43 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
 
         switch (l) {
 
-        case 13:
-        case 14:
-                /* Treat an "uninitialized" id file like an empty one */
-                return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL;
+        case STRLEN("uninitialized"):
+        case STRLEN("uninitialized\n"):
+                return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL;
 
-        case 33: /* plain UUID with trailing newline */
-                if (buffer[32] != '\n')
-                        return -EINVAL;
+        case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */
+                if (buffer[SD_ID128_STRING_MAX-1] != '\n')
+                        return -EUCLEAN;
 
                 _fallthrough_;
-        case 32: /* plain UUID without trailing newline */
-                if (f == ID128_UUID)
-                        return -EINVAL;
+        case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */
+                if (!FLAGS_SET(f, ID128_FORMAT_PLAIN))
+                        return -EUCLEAN;
 
-                buffer[32] = 0;
+                buffer[SD_ID128_STRING_MAX-1] = 0;
                 break;
 
-        case 37: /* RFC UUID with trailing newline */
-                if (buffer[36] != '\n')
-                        return -EINVAL;
+        case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */
+                if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n')
+                        return -EUCLEAN;
 
                 _fallthrough_;
-        case 36: /* RFC UUID without trailing newline */
-                if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT))
-                        return -EINVAL;
+        case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */
+                if (!FLAGS_SET(f, ID128_FORMAT_UUID))
+                        return -EUCLEAN;
 
-                buffer[36] = 0;
+                buffer[SD_ID128_UUID_STRING_MAX-1] = 0;
                 break;
 
         default:
-                return -EINVAL;
+                return -EUCLEAN;
         }
 
-        return sd_id128_from_string(buffer, ret);
+        r = sd_id128_from_string(buffer, ret);
+        return r == -EINVAL ? -EUCLEAN : r;
 }
 
-int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
+int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret) {
         _cleanup_close_ int fd = -1;
 
         fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
@@ -121,29 +111,28 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
         return id128_read_fd(fd, f, ret);
 }
 
-int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
-        char buffer[36 + 2];
+int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id) {
+        char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
         size_t sz;
         int r;
 
         assert(fd >= 0);
-        assert(f < _ID128_FORMAT_MAX);
+        assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID));
 
-        if (f != ID128_UUID) {
+        if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) {
                 assert_se(sd_id128_to_string(id, buffer));
-                buffer[SD_ID128_STRING_MAX - 1] = '\n';
                 sz = SD_ID128_STRING_MAX;
         } else {
                 assert_se(sd_id128_to_uuid_string(id, buffer));
-                buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n';
                 sz = SD_ID128_UUID_STRING_MAX;
         }
 
+        buffer[sz - 1] = '\n';
         r = loop_write(fd, buffer, sz, false);
         if (r < 0)
                 return r;
 
-        if (do_sync) {
+        if (FLAGS_SET(f, ID128_SYNC_ON_WRITE)) {
                 r = fsync_full(fd);
                 if (r < 0)
                         return r;
@@ -152,14 +141,14 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
         return 0;
 }
 
-int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
+int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id) {
         _cleanup_close_ int fd = -1;
 
         fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444);
         if (fd < 0)
                 return -errno;
 
-        return id128_write_fd(fd, f, id, do_sync);
+        return id128_write_fd(fd, f, id);
 }
 
 void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
@@ -194,9 +183,9 @@ int id128_get_product(sd_id128_t *ret) {
         /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
          * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
 
-        r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
+        r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
         if (r == -ENOENT)
-                r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid);
+                r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
         if (r < 0)
                 return r;
 
index 17b180c10c14860c35dc477c51eea15a54b209ee..4b91a16bd163f21c58a160b2a72d2df96b72e5a1 100644 (file)
 
 bool id128_is_valid(const char *s) _pure_;
 
-typedef enum Id128Format {
-        ID128_ANY,
-        ID128_PLAIN,  /* formatted as 32 hex chars as-is */
-        ID128_PLAIN_OR_UNINIT,  /* formatted as 32 hex chars as-is; allow special "uninitialized"
-                                 * value when reading from file (id128_read() and id128_read_fd()).
-                                 *
-                                 * This format should be used when reading a machine-id file. */
-        ID128_UUID,   /* formatted as 36 character uuid string */
-        _ID128_FORMAT_MAX,
-} Id128Format;
-
-int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret);
-int id128_read(const char *p, Id128Format f, sd_id128_t *ret);
-
-int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync);
-int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
+typedef enum Id128FormatFlag {
+        ID128_FORMAT_PLAIN = 1 << 0,  /* formatted as 32 hex chars as-is */
+        ID128_FORMAT_UUID  = 1 << 1,  /* formatted as 36 character uuid string */
+        ID128_FORMAT_ANY   = ID128_FORMAT_PLAIN | ID128_FORMAT_UUID,
+
+        ID128_SYNC_ON_WRITE = 1 << 2, /* Sync the file after write. Used only when writing an ID. */
+} Id128FormatFlag;
+
+int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret);
+int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret);
+
+int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id);
+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_;
index c8f051196df40b59507dab7a40853a108c510de6..ec3a496dba5def1927ceeb06a70e99e1b013afe4 100644 (file)
 #include "macro.h"
 #include "missing_syscall.h"
 #include "random-util.h"
+#include "stat-util.h"
 #include "user-util.h"
 
 _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
+        size_t k = 0;
+
         assert_return(s, NULL);
 
-        for (size_t n = 0; n < 16; n++) {
-                s[n*2] = hexchar(id.bytes[n] >> 4);
-                s[n*2+1] = hexchar(id.bytes[n] & 0xF);
+        for (size_t n = 0; n < sizeof(sd_id128_t); n++) {
+                s[k++] = hexchar(id.bytes[n] >> 4);
+                s[k++] = hexchar(id.bytes[n] & 0xF);
         }
 
-        s[SD_ID128_STRING_MAX-1] = 0;
+        assert(k == SD_ID128_STRING_MAX - 1);
+        s[k] = 0;
 
         return s;
 }
@@ -37,7 +41,7 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD
 
         /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
 
-        for (size_t n = 0; n < 16; n++) {
+        for (size_t n = 0; n < sizeof(sd_id128_t); n++) {
 
                 if (IN_SET(n, 4, 6, 8, 10))
                         s[k++] = '-';
@@ -52,14 +56,14 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD
         return s;
 }
 
-_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
-        unsigned n, i;
+_public_ int sd_id128_from_string(const char *s, sd_id128_t *ret) {
+        size_t n, i;
         sd_id128_t t;
         bool is_guid = false;
 
         assert_return(s, -EINVAL);
 
-        for (n = 0, i = 0; n < 16;) {
+        for (n = 0, i = 0; n < sizeof(sd_id128_t);) {
                 int a, b;
 
                 if (s[i] == '-') {
@@ -89,7 +93,7 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
                 t.bytes[n++] = (a << 4) | b;
         }
 
-        if (i != (is_guid ? 36 : 32))
+        if (i != (is_guid ? SD_ID128_UUID_STRING_MAX : SD_ID128_STRING_MAX) - 1)
                 return -EINVAL;
 
         if (s[i] != 0)
@@ -120,10 +124,8 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
         static thread_local sd_id128_t saved_machine_id = {};
         int r;
 
-        assert_return(ret, -EINVAL);
-
         if (sd_id128_is_null(saved_machine_id)) {
-                r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id);
+                r = id128_read("/etc/machine-id", ID128_FORMAT_PLAIN, &saved_machine_id);
                 if (r < 0)
                         return r;
 
@@ -131,7 +133,8 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
                         return -ENOMEDIUM;
         }
 
-        *ret = saved_machine_id;
+        if (ret)
+                *ret = saved_machine_id;
         return 0;
 }
 
@@ -139,15 +142,19 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
         static thread_local sd_id128_t saved_boot_id = {};
         int r;
 
-        assert_return(ret, -EINVAL);
-
         if (sd_id128_is_null(saved_boot_id)) {
-                r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id);
+                r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID, &saved_boot_id);
+                if (r == -ENOENT && proc_mounted() == 0)
+                        return -ENOSYS;
                 if (r < 0)
                         return r;
+
+                if (sd_id128_is_null(saved_boot_id))
+                        return -ENOMEDIUM;
         }
 
-        *ret = saved_boot_id;
+        if (ret)
+                *ret = saved_boot_id;
         return 0;
 }
 
@@ -197,22 +204,22 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
         /* Chop off the final description string */
         d = strrchr(description, ';');
         if (!d)
-                return -EIO;
+                return -EUCLEAN;
         *d = 0;
 
         /* Look for the permissions */
         p = strrchr(description, ';');
         if (!p)
-                return -EIO;
+                return -EUCLEAN;
 
         errno = 0;
         perms = strtoul(p + 1, &e, 16);
         if (errno > 0)
                 return -errno;
         if (e == p + 1) /* Read at least one character */
-                return -EIO;
+                return -EUCLEAN;
         if (e != d) /* Must reached the end */
-                return -EIO;
+                return -EUCLEAN;
 
         if ((perms & ~MAX_PERMS) != 0)
                 return -EPERM;
@@ -222,7 +229,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
         /* Look for the group ID */
         g = strrchr(description, ';');
         if (!g)
-                return -EIO;
+                return -EUCLEAN;
         r = parse_gid(g + 1, &gid);
         if (r < 0)
                 return r;
@@ -233,7 +240,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
         /* Look for the user ID */
         u = strrchr(description, ';');
         if (!u)
-                return -EIO;
+                return -EUCLEAN;
         r = parse_uid(u + 1, &uid);
         if (r < 0)
                 return r;
@@ -244,13 +251,14 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
         if (c < 0)
                 return -errno;
         if (c != sizeof(sd_id128_t))
-                return -EIO;
+                return -EUCLEAN;
 
         return 0;
 }
 
 static int get_invocation_from_environment(sd_id128_t *ret) {
         const char *e;
+        int r;
 
         assert(ret);
 
@@ -258,33 +266,31 @@ static int get_invocation_from_environment(sd_id128_t *ret) {
         if (!e)
                 return -ENXIO;
 
-        return sd_id128_from_string(e, ret);
+        r = sd_id128_from_string(e, ret);
+        return r == -EINVAL ? -EUCLEAN : r;
 }
 
 _public_ int sd_id128_get_invocation(sd_id128_t *ret) {
         static thread_local sd_id128_t saved_invocation_id = {};
         int r;
 
-        assert_return(ret, -EINVAL);
-
         if (sd_id128_is_null(saved_invocation_id)) {
                 /* We first check the environment. The environment variable is primarily relevant for user
                  * services, and sufficiently safe as long as no privilege boundary is involved. */
                 r = get_invocation_from_environment(&saved_invocation_id);
-                if (r >= 0) {
-                        *ret = saved_invocation_id;
-                        return 0;
-                } else if (r != -ENXIO)
-                        return r;
-
-                /* The kernel keyring is relevant for system services (as for user services we don't store
-                 * the invocation ID in the keyring, as there'd be no trust benefit in that). */
-                r = get_invocation_from_keyring(&saved_invocation_id);
+                if (r == -ENXIO)
+                        /* The kernel keyring is relevant for system services (as for user services we don't
+                         * store the invocation ID in the keyring, as there'd be no trust benefit in that). */
+                        r = get_invocation_from_keyring(&saved_invocation_id);
                 if (r < 0)
                         return r;
+
+                if (sd_id128_is_null(saved_invocation_id))
+                        return -ENOMEDIUM;
         }
 
-        *ret = saved_invocation_id;
+        if (ret)
+                *ret = saved_invocation_id;
         return 0;
 }
 
index 9084da41e3648171ad25049d847908b16f7bb380..ab518fea80938c381247f348781ebf5e3131db98 100644 (file)
@@ -23,6 +23,7 @@
 #include "journal-authenticate.h"
 #include "journal-def.h"
 #include "journal-file.h"
+#include "journal-internal.h"
 #include "lookup3.h"
 #include "memory-util.h"
 #include "path-util.h"
@@ -383,7 +384,7 @@ static int journal_file_refresh_header(JournalFile *f) {
         assert(f->header);
 
         r = sd_id128_get_machine(&f->header->machine_id);
-        if (IN_SET(r, -ENOENT, -ENOMEDIUM))
+        if (IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG))
                 /* We don't have a machine-id, let's continue without */
                 zero(f->header->machine_id);
         else if (r < 0)
@@ -3582,22 +3583,24 @@ static int journal_file_warn_btrfs(JournalFile *f) {
 
         r = fd_is_fs_type(f->fd, BTRFS_SUPER_MAGIC);
         if (r < 0)
-                return log_warning_errno(r, "Failed to determine if journal is on btrfs: %m");
+                return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to determine if journal is on btrfs: %m");
         if (!r)
                 return 0;
 
         r = read_attr_fd(f->fd, &attrs);
         if (r < 0)
-                return log_warning_errno(r, "Failed to read file attributes: %m");
+                return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to read file attributes: %m");
 
         if (attrs & FS_NOCOW_FL) {
                 log_debug("Detected btrfs file system with copy-on-write disabled, all is good.");
                 return 0;
         }
 
-        log_notice("Creating journal file %s on a btrfs file system, and copy-on-write is enabled. "
-                   "This is likely to slow down journal access substantially, please consider turning "
-                   "off the copy-on-write file attribute on the journal directory, using chattr +C.", f->path);
+        log_ratelimit_notice(JOURNAL_LOG_RATELIMIT,
+                             "Creating journal file %s on a btrfs file system, and copy-on-write is enabled. "
+                             "This is likely to slow down journal access substantially, please consider turning "
+                             "off the copy-on-write file attribute on the journal directory, using chattr +C.",
+                             f->path);
 
         return 1;
 }
@@ -4161,10 +4164,6 @@ int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, u
         return 1;
 }
 
-/* Ideally this would be a function parameter but initializers for static fields have to be compile
- * time constants so we hardcode the interval instead. */
-#define LOG_RATELIMIT ((const RateLimit) { .interval = 60 * USEC_PER_SEC, .burst = 3 })
-
 bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log_level) {
         assert(f);
         assert(f->header);
@@ -4172,7 +4171,8 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
         /* If we gained new header fields we gained new features,
          * hence suggest a rotation */
         if (le64toh(f->header->header_size) < sizeof(Header)) {
-                log_full(log_level, "%s uses an outdated header, suggesting rotation.", f->path);
+                log_ratelimit_full(log_level, JOURNAL_LOG_RATELIMIT,
+                                   "%s uses an outdated header, suggesting rotation.", f->path);
                 return true;
         }
 
@@ -4183,7 +4183,7 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
         if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
                 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
                         log_ratelimit_full(
-                                log_level, LOG_RATELIMIT,
+                                log_level, JOURNAL_LOG_RATELIMIT,
                                 "Data hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.",
                                 f->path,
                                 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
@@ -4197,7 +4197,7 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
                 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
                         log_ratelimit_full(
-                                log_level, LOG_RATELIMIT,
+                                log_level, JOURNAL_LOG_RATELIMIT,
                                 "Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.",
                                 f->path,
                                 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
@@ -4211,7 +4211,7 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
         if (JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth) &&
             le64toh(f->header->data_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
                 log_ratelimit_full(
-                        log_level, LOG_RATELIMIT,
+                        log_level, JOURNAL_LOG_RATELIMIT,
                         "Data hash table of %s has deepest hash chain of length %" PRIu64 ", suggesting rotation.",
                         f->path, le64toh(f->header->data_hash_chain_depth));
                 return true;
@@ -4220,7 +4220,7 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
         if (JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth) &&
             le64toh(f->header->field_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
                 log_ratelimit_full(
-                        log_level, LOG_RATELIMIT,
+                        log_level, JOURNAL_LOG_RATELIMIT,
                         "Field hash table of %s has deepest hash chain of length at %" PRIu64 ", suggesting rotation.",
                         f->path, le64toh(f->header->field_hash_chain_depth));
                 return true;
@@ -4232,7 +4232,7 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
             le64toh(f->header->n_data) > 0 &&
             le64toh(f->header->n_fields) == 0) {
                 log_ratelimit_full(
-                        log_level, LOG_RATELIMIT,
+                        log_level, JOURNAL_LOG_RATELIMIT,
                         "Data objects of %s are not indexed by field objects, suggesting rotation.",
                         f->path);
                 return true;
@@ -4246,7 +4246,7 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log
 
                 if (h > 0 && t > h + max_file_usec) {
                         log_ratelimit_full(
-                                log_level, LOG_RATELIMIT,
+                                log_level, JOURNAL_LOG_RATELIMIT,
                                 "Oldest entry in %s is older than the configured file retention duration (%s), suggesting rotation.",
                                 f->path, FORMAT_TIMESPAN(max_file_usec, USEC_PER_SEC));
                         return true;
index 7fc6896522e27df3c73cd52251ba83032b3af1e3..ed052d1b890de82277e6802dcf509afba658be7c 100644 (file)
@@ -14,6 +14,8 @@
 #include "list.h"
 #include "set.h"
 
+#define JOURNAL_LOG_RATELIMIT ((const RateLimit) { .interval = 60 * USEC_PER_SEC, .burst = 3 })
+
 typedef struct Match Match;
 typedef struct Location Location;
 typedef struct Directory Directory;
index eac35002023a1f379798227b304d604110d5b7d9..7b5e0fa65fa0ade51eedbef4fd064f0490534433 100644 (file)
@@ -13,6 +13,7 @@
 #include "fs-util.h"
 #include "journal-def.h"
 #include "journal-file.h"
+#include "journal-internal.h"
 #include "journal-vacuum.h"
 #include "sort-util.h"
 #include "string-util.h"
@@ -251,7 +252,9 @@ int journal_directory_vacuum(
 
                                 freed += size;
                         } else if (r != -ENOENT)
-                                log_warning_errno(r, "Failed to delete empty archived journal %s/%s: %m", directory, p);
+                                log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
+                                                            "Failed to delete empty archived journal %s/%s: %m",
+                                                            directory, p);
 
                         continue;
                 }
@@ -299,7 +302,9 @@ int journal_directory_vacuum(
                                 sum = 0;
 
                 } else if (r != -ENOENT)
-                        log_warning_errno(r, "Failed to delete archived journal %s/%s: %m", directory, list[i].filename);
+                        log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
+                                                    "Failed to delete archived journal %s/%s: %m",
+                                                    directory, list[i].filename);
         }
 
         if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec))
index 880d0d209c5c35e020e45db27540aa8cb6ce517d..8fb65b96997d1ac202e8712c4ca4b76ec974fbd9 100644 (file)
@@ -254,7 +254,7 @@ int vconsole_write_data(Context *c) {
 
 int x11_write_data(Context *c) {
         _cleanup_fclose_ FILE *f = NULL;
-        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_(unlink_and_freep) char *temp_path = NULL;
         struct stat st;
         int r;
 
@@ -300,23 +300,15 @@ int x11_write_data(Context *c) {
 
         r = fflush_sync_and_check(f);
         if (r < 0)
-                goto fail;
+                return r;
 
-        if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
-                r = -errno;
-                goto fail;
-        }
+        if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
+                return -errno;
 
         if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) >= 0)
                 c->x11_mtime = timespec_load(&st.st_mtim);
 
         return 0;
-
-fail:
-        if (temp_path)
-                (void) unlink(temp_path);
-
-        return r;
 }
 
 static int read_next_mapping(const char* filename,
index 39bcbd71fe3015e10868accd3a18dfa735586b24..9a665bd95946b0b87df6c495a84d3affcc65235c 100644 (file)
@@ -6,16 +6,17 @@
 {% if ENABLE_HOMED %}
 -account sufficient pam_systemd_home.so
 {% endif %}
-account sufficient pam_unix.so no_pass_expiry
-account required pam_permit.so
+account  sufficient pam_unix.so no_pass_expiry
+account  required   pam_permit.so
 
 {% if HAVE_SELINUX %}
-session required pam_selinux.so close
-session required pam_selinux.so nottys open
+session  required   pam_selinux.so close
+session  required   pam_selinux.so nottys open
 {% endif %}
-session required pam_loginuid.so
-session optional pam_keyinit.so force revoke
+session  required   pam_loginuid.so
+session  optional   pam_keyinit.so force revoke
+session  required   pam_namespace.so
 {% if ENABLE_HOMED %}
--session optional pam_systemd_home.so
+-session optional   pam_systemd_home.so
 {% endif %}
-session optional pam_systemd.so
+session  optional   pam_systemd.so
index 6aef48650d21d4da422167917cedd4f058b5466f..9101f4e11a724bea6e563a40f04d23c21b6a5112 100644 (file)
@@ -164,7 +164,7 @@ static int run(int argc, char *argv[]) {
                         return r;
 
                 etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
-                r = id128_read(etc_machine_id, ID128_PLAIN, &id);
+                r = id128_read(etc_machine_id, ID128_FORMAT_PLAIN, &id);
                 if (r < 0)
                         return log_error_errno(r, "Failed to read machine ID back: %m");
         } else {
index de7b20ff2dbba78cc6d90feb3b7fb19890bad3c0..c08a645814d00f53cd91c0f0f4252c40cb773a26 100644 (file)
 #include "unit-name.h"
 #include "user-util.h"
 
-Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
-        Machine *m;
+DEFINE_TRIVIAL_CLEANUP_FUNC(Machine*, machine_free);
+
+int machine_new(Manager *manager, MachineClass class, const char *name, Machine **ret) {
+        _cleanup_(machine_freep) Machine *m = NULL;
+        int r;
 
         assert(manager);
         assert(class < _MACHINE_CLASS_MAX);
         assert(name);
+        assert(ret);
 
         /* Passing class == _MACHINE_CLASS_INVALID here is fine. It
          * means as much as "we don't know yet", and that we'll figure
@@ -46,31 +50,28 @@ Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
 
         m = new0(Machine, 1);
         if (!m)
-                return NULL;
+                return -ENOMEM;
 
         m->name = strdup(name);
         if (!m->name)
-                goto fail;
+                return -ENOMEM;
 
         if (class != MACHINE_HOST) {
                 m->state_file = path_join("/run/systemd/machines", m->name);
                 if (!m->state_file)
-                        goto fail;
+                        return -ENOMEM;
         }
 
         m->class = class;
 
-        if (hashmap_put(manager->machines, m->name, m) < 0)
-                goto fail;
+        r = hashmap_put(manager->machines, m->name, m);
+        if (r < 0)
+                return r;
 
         m->manager = manager;
 
-        return m;
-
-fail:
-        free(m->state_file);
-        free(m->name);
-        return mfree(m);
+        *ret = TAKE_PTR(m);
+        return 0;
 }
 
 Machine* machine_free(Machine *m) {
index 5e0e529567a45c27ddff8e5405a58cee1f675570..54ebcb3b26cf5b0d989da3f2dd410f912b4a0efe 100644 (file)
@@ -66,7 +66,7 @@ struct Machine {
         LIST_FIELDS(Machine, gc_queue);
 };
 
-Machine* machine_new(Manager *manager, MachineClass class, const char *name);
+int machine_new(Manager *manager, MachineClass class, const char *name, Machine **ret);
 Machine* machine_free(Machine *m);
 bool machine_may_gc(Machine *m, bool drop_not_started);
 void machine_add_to_gc_queue(Machine *m);
index e2d078485790770069d6124e039d15359ab5d483..3da639279d8c74c64b85972ee745a4653b876462 100644 (file)
@@ -863,7 +863,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
                 return 1; /* Will call us back */
 
         /* Set up the machine directory if necessary */
-        r = setup_machine_directory(error);
+        r = setup_machine_directory(error, /* use_btrfs_subvol= */ true, /* use_btrfs_quota= */ true);
         if (r < 0)
                 return r;
 
@@ -1492,15 +1492,16 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
 
 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
         Machine *machine;
+        int r;
 
         assert(m);
         assert(name);
 
         machine = hashmap_get(m->machines, name);
         if (!machine) {
-                machine = machine_new(m, _MACHINE_CLASS_INVALID, name);
-                if (!machine)
-                        return -ENOMEM;
+                r = machine_new(m, _MACHINE_CLASS_INVALID, name, &machine);
+                if (r < 0)
+                        return r;
         }
 
         if (_machine)
index 9ecba8720fdd81c9e26bf30a4c009ab63d921f63..b4ff97ab701189e80a8a8e93f6d733b7f7b78d24 100644 (file)
@@ -117,9 +117,9 @@ static int manager_add_host_machine(Manager *m) {
         if (!unit)
                 return log_oom();
 
-        t = machine_new(m, MACHINE_HOST, ".host");
-        if (!t)
-                return log_oom();
+        r = machine_new(m, MACHINE_HOST, ".host", &t);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create machine: %m");
 
         t->leader = 1;
         t->id = mid;
index eae374323e9352e931ef556b7d832a70d571eb06..5b3b7d128aaef8f0e556a4d59418d7f46ef92ca5 100644 (file)
@@ -309,6 +309,14 @@ DEFINE_PRIVATE_HASH_OPS(
         address_kernel_hash_func,
         address_kernel_compare_func);
 
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+        address_kernel_hash_ops_free,
+        Address,
+        address_kernel_hash_func,
+        address_kernel_compare_func,
+        address_free);
+
+/* The functions below are mainly used by managing Request. */
 static void address_hash_func(const Address *a, struct siphash *state) {
         assert(a);
 
@@ -367,12 +375,37 @@ int address_compare_func(const Address *a1, const Address *a2) {
         return 0;
 }
 
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
-        address_hash_ops_free,
-        Address,
-        address_hash_func,
-        address_compare_func,
-        address_free);
+int address_equal(const Address *a1, const Address *a2) {
+        if (a1 == a2)
+                return true;
+
+        if (!a1 || !a2)
+                return false;
+
+        return address_compare_func(a1, a2) == 0;
+}
+
+static int address_equalify(Address *address, const Address *src) {
+        int r;
+
+        assert(address);
+        assert(src);
+
+        if (address_kernel_compare_func(address, src) != 0)
+                return -EINVAL;
+
+        if (address->family == AF_INET) {
+                address->broadcast = src->broadcast;
+                r = free_and_strdup(&address->label, src->label);
+                if (r < 0)
+                        return r;
+        } else {
+                address->prefixlen = src->prefixlen;
+                address->in_addr_peer = src->in_addr_peer;
+        }
+
+        return 0;
+}
 
 int address_dup(const Address *src, Address **ret) {
         _cleanup_(address_freep) Address *dest = NULL;
@@ -451,7 +484,7 @@ static int address_add(Link *link, Address *address) {
         assert(link);
         assert(address);
 
-        r = set_ensure_put(&link->addresses, &address_hash_ops_free, address);
+        r = set_ensure_put(&link->addresses, &address_kernel_hash_ops_free, address);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -542,10 +575,10 @@ int link_get_address(Link *link, int family, const union in_addr_union *address,
          * and does not have peer address. When the prefixlen is zero, then an Address object with an
          * arbitrary prefixlen will be returned. */
 
-        if (prefixlen != 0) {
+        if (family == AF_INET6 || prefixlen != 0) {
                 _cleanup_(address_freep) Address *tmp = NULL;
 
-                /* If prefixlen is set, then we can use address_get(). */
+                /* In this case, we can use address_get(). */
 
                 r = address_new(&tmp);
                 if (r < 0)
@@ -554,20 +587,24 @@ int link_get_address(Link *link, int family, const union in_addr_union *address,
                 tmp->family = family;
                 tmp->in_addr = *address;
                 tmp->prefixlen = prefixlen;
-                address_set_broadcast(tmp, link);
 
-                if (address_get(link, tmp, &a) >= 0) {
-                        if (ret)
-                                *ret = a;
+                r = address_get(link, tmp, &a);
+                if (r < 0)
+                        return r;
 
-                        return 0;
+                if (family == AF_INET6) {
+                        /* IPv6 addresses are managed without peer address and prefix length. Hence, we need
+                         * to check them explicitly. */
+                        if (in_addr_is_set(family, &a->in_addr_peer))
+                                return -ENOENT;
+                        if (prefixlen != 0 && a->prefixlen != prefixlen)
+                                return -ENOENT;
                 }
 
-                if (family == AF_INET6)
-                        return -ENOENT;
+                if (ret)
+                        *ret = a;
 
-                /* IPv4 addresses may have label and/or non-default broadcast address.
-                 * Hence, we need to always fallback below. */
+                return 0;
         }
 
         SET_FOREACH(a, link->addresses) {
@@ -925,7 +962,11 @@ int link_drop_foreign_addresses(Link *link) {
         ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section) {
                 Address *existing;
 
-                if (address_get(link, address, &existing) >= 0)
+                /* On update, the kernel ignores the address label and broadcast address. Hence we need to
+                 * distinguish addresses with different labels or broadcast addresses. Thus, we need to check
+                 * the existing address with address_equal(). Otherwise, the label or broadcast address
+                 * change will not be applied when we reconfigure the interface. */
+                if (address_get(link, address, &existing) >= 0 && address_equal(address, existing))
                         address_unmark(existing);
         }
 
@@ -1199,6 +1240,9 @@ int link_request_address(
 
                 existing = TAKE_PTR(tmp);
         } else {
+                r = address_equalify(existing, address);
+                if (r < 0)
+                        return r;
                 existing->source = address->source;
                 existing->provider = address->provider;
                 existing->duplicate_address_detection = address->duplicate_address_detection;
@@ -1468,6 +1512,12 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
         case RTM_NEWADDR:
                 if (address) {
                         /* update flags and etc. */
+                        r = address_equalify(address, tmp);
+                        if (r < 0) {
+                                log_link_warning_errno(link, r, "Failed to update properties of address %s, ignoring: %m",
+                                                       IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
+                                return 0;
+                        }
                         address->flags = tmp->flags;
                         address->scope = tmp->scope;
                         address_set_lifetime(m, address, &cinfo);
@@ -2085,9 +2135,8 @@ int network_drop_invalid_addresses(Network *network) {
                         address_free(dup);
                 }
 
-                /* Use address_kernel_hash_ops here. The function address_kernel_compare_func() matches
-                 * how kernel compares addresses, and is more lenient than address_compare_func().
-                 * Hence, the logic of dedup here is stricter than when address_hash_ops is used. */
+                /* Use address_kernel_hash_ops, instead of address_kernel_hash_ops_free. Otherwise, the
+                 * Address objects will be freed. */
                 r = set_ensure_put(&addresses, &address_kernel_hash_ops, address);
                 if (r < 0)
                         return log_oom();
index 7a1e44632d5d207c56e77f0695a72f019717fce1..89b962179169e84d3a98f561a1349400be7d4a89 100644 (file)
@@ -118,6 +118,7 @@ int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, Ma
 int network_drop_invalid_addresses(Network *network);
 
 int address_compare_func(const Address *a1, const Address *a2);
+int address_equal(const Address *a1, const Address *a2);
 
 DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Address, address);
 
index d6ec2333512bf9b6010c8eab2b7ff236dee32611..c44c37f3aa4c9b0057266210c9c36b049ff07dd5 100644 (file)
@@ -163,8 +163,7 @@ static int verify_dhcp6_address(Link *link, const Address *address) {
 
         const char *pretty = IN6_ADDR_TO_STRING(&address->in_addr.in6);
 
-        if (address_get(link, address, &existing) < 0 &&
-            link_get_address(link, AF_INET6, &address->in_addr, 0, &existing) < 0) {
+        if (address_get(link, address, &existing) < 0) {
                 /* New address. */
                 log_level = LOG_INFO;
                 goto simple_log;
index b6aaa1e9dbc4e345da32888ba23fedd75fc1e20a..541c4b8a728feba7f4fc080d19dd33b9ac922d4d 100644 (file)
@@ -502,6 +502,14 @@ static int link_is_ready_to_set_link(Link *link, Request *req) {
                         r = link_down_now(link);
                         if (r < 0)
                                 return r;
+
+                        /* If the kind of the link is "bond", we need
+                         * set the slave link down as well. */
+                        if (streq_ptr(link->kind, "bond")) {
+                                r = link_down_slave_links(link);
+                                if (r < 0)
+                                        return r;
+                        }
                 }
                 break;
 
@@ -1226,6 +1234,21 @@ int link_down_now(Link *link) {
         return 0;
 }
 
+int link_down_slave_links(Link *link) {
+        Link *slave;
+        int r;
+
+        assert(link);
+
+        SET_FOREACH(slave, link->slaves) {
+                r = link_down_now(slave);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int link_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
index 7e5ba32ef1c4846a60594cd566071d14fa2de884..841e5eeb9c4142fd7b6e4ac3ed73ee4fe18de059 100644 (file)
@@ -25,4 +25,5 @@ int link_request_to_activate(Link *link);
 int link_request_to_bring_up_or_down(Link *link, bool up);
 
 int link_down_now(Link *link);
+int link_down_slave_links(Link *link);
 int link_remove(Link *link);
index 31ff4bc4256adf5df878df078994d25e4716f436..f75fb1f8685c37bf592a71f6a8a826718e3e4b81 100644 (file)
@@ -97,7 +97,8 @@ int network_config_state_to_string_alloc(NetworkConfigState s, char **ret);
         static inline void name##_enter_configuring(type *t) {          \
                 name##_update_state(t,                                  \
                                     NETWORK_CONFIG_STATE_REQUESTING |   \
-                                    NETWORK_CONFIG_STATE_CONFIGURING,   \
+                                    NETWORK_CONFIG_STATE_CONFIGURING |  \
+                                    NETWORK_CONFIG_STATE_REMOVING,      \
                                     NETWORK_CONFIG_STATE_CONFIGURING);  \
         }                                                               \
         static inline void name##_enter_configured(type *t) {           \
index 0145c8b7c71031420cc29c4e82478ad39b0ffdd3..250ab9eff49fe527fc089814b4f758f73af1bf89 100644 (file)
@@ -174,16 +174,6 @@ static int test_load_config(Manager *manager) {
         return 0;
 }
 
-static bool address_equal(const Address *a1, const Address *a2) {
-        if (a1 == a2)
-                return true;
-
-        if (!a1 || !a2)
-                return false;
-
-        return address_compare_func(a1, a2) == 0;
-}
-
 static void test_address_equality(void) {
         _cleanup_(address_freep) Address *a1 = NULL, *a2 = NULL;
 
index bdb8985fbede4420d907ee0f71fdaaa363cbbc20..d7ca3afc8aca9ca532474aa0c267849bec7d4df1 100644 (file)
@@ -317,7 +317,7 @@ static int mount_legacy_cgns_supported(
                  * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
                  * pass uid 0 and not uid_shift to tmpfs_patch_options().
                  */
-                r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, 0, selinux_apifs_context, &options);
+                r = tmpfs_patch_options("mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP, 0, selinux_apifs_context, &options);
                 if (r < 0)
                         return log_oom();
 
@@ -390,7 +390,8 @@ skip_controllers:
 
         if (!userns)
                 return mount_nofollow_verbose(LOG_ERR, NULL, cgroup_root, NULL,
-                                              MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
+                                              MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY,
+                                              "mode=0755");
 
         return 0;
 }
@@ -419,7 +420,10 @@ static int mount_legacy_cgns_unsupported(
         if (r == 0) {
                 _cleanup_free_ char *options = NULL;
 
-                r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
+                r = tmpfs_patch_options("mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP,
+                                        uid_shift == 0 ? UID_INVALID : uid_shift,
+                                        selinux_apifs_context,
+                                        &options);
                 if (r < 0)
                         return log_oom();
 
@@ -498,7 +502,8 @@ skip_controllers:
                 return r;
 
         return mount_nofollow_verbose(LOG_ERR, NULL, cgroup_root, NULL,
-                                      MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
+                                      MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY,
+                                      "mode=0755");
 }
 
 static int mount_unified_cgroups(const char *dest) {
index a54f1464bac04bb48586c714d400480759ea0085..9de20126f4a2c858ef5d8b61633bb7e7e187e4f0 100644 (file)
@@ -13,6 +13,7 @@
 #include "mkdir-label.h"
 #include "mount-util.h"
 #include "mountpoint-util.h"
+#include "namespace-util.h"
 #include "nspawn-mount.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -510,6 +511,9 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
                                       MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT|extra_flags, NULL);
 }
 
+#define PROC_DEFAULT_MOUNT_FLAGS (MS_NOSUID|MS_NOEXEC|MS_NODEV)
+#define SYS_DEFAULT_MOUNT_FLAGS  (MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV)
+
 int mount_all(const char *dest,
               MountSettingsMask mount_settings,
               uid_t uid_shift,
@@ -538,7 +542,7 @@ int mount_all(const char *dest,
 
         static const MountPoint mount_table[] = {
                 /* First we list inner child mounts (i.e. mounts applied *after* entering user namespacing) */
-                { "proc",            "/proc",           "proc",  NULL,        MS_NOSUID|MS_NOEXEC|MS_NODEV,
+                { "proc",            "/proc",           "proc",  NULL,        PROC_DEFAULT_MOUNT_FLAGS,
                   MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_MKDIR|MOUNT_FOLLOW_SYMLINKS }, /* we follow symlinks here since not following them requires /proc/ already being mounted, which we don't have here. */
 
                 { "/proc/sys",       "/proc/sys",       NULL,    NULL,        MS_BIND,
@@ -572,19 +576,19 @@ int mount_all(const char *dest,
                   MOUNT_IN_USERNS|MOUNT_MKDIR },
 
                 /* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing) */
-                { "tmpfs",                  "/tmp",                         "tmpfs", "mode=1777" NESTED_TMPFS_LIMITS,  MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+                { "tmpfs",                  "/tmp",                         "tmpfs", "mode=01777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_APPLY_TMPFS_TMP|MOUNT_MKDIR },
-                { "tmpfs",                  "/sys",                         "tmpfs", "mode=555" TMPFS_LIMITS_SYS,      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+                { "tmpfs",                  "/sys",                         "tmpfs", "mode=0555" TMPFS_LIMITS_SYS,     MS_NOSUID|MS_NOEXEC|MS_NODEV,
                   MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS|MOUNT_MKDIR },
-                { "sysfs",                  "/sys",                         "sysfs", NULL,                             MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV,
+                { "sysfs",                  "/sys",                         "sysfs", NULL,                             SYS_DEFAULT_MOUNT_FLAGS,
                   MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO|MOUNT_MKDIR },    /* skipped if above was mounted */
                 { "sysfs",                  "/sys",                         "sysfs", NULL,                             MS_NOSUID|MS_NOEXEC|MS_NODEV,
                   MOUNT_FATAL|MOUNT_MKDIR },                          /* skipped if above was mounted */
-                { "tmpfs",                  "/dev",                         "tmpfs", "mode=755" TMPFS_LIMITS_PRIVATE_DEV, MS_NOSUID|MS_STRICTATIME,
+                { "tmpfs",                  "/dev",                         "tmpfs", "mode=0755" TMPFS_LIMITS_PRIVATE_DEV, MS_NOSUID|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_MKDIR },
-                { "tmpfs",                  "/dev/shm",                     "tmpfs", "mode=1777" NESTED_TMPFS_LIMITS,  MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+                { "tmpfs",                  "/dev/shm",                     "tmpfs", "mode=01777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_MKDIR },
-                { "tmpfs",                  "/run",                         "tmpfs", "mode=755" TMPFS_LIMITS_RUN,      MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+                { "tmpfs",                  "/run",                         "tmpfs", "mode=0755" TMPFS_LIMITS_RUN,     MS_NOSUID|MS_NODEV|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_MKDIR },
                 { "/run/host",              "/run/host",                    NULL,    NULL,                             MS_BIND,
                   MOUNT_FATAL|MOUNT_MKDIR|MOUNT_PREFIX_ROOT }, /* Prepare this so that we can make it read-only when we are done */
@@ -1039,7 +1043,7 @@ static int setup_volatile_state(const char *directory, uid_t uid_shift, const ch
         if (r < 0 && errno != EEXIST)
                 return log_error_errno(errno, "Failed to create %s: %m", directory);
 
-        options = "mode=755" TMPFS_LIMITS_VOLATILE_STATE;
+        options = "mode=0755" TMPFS_LIMITS_VOLATILE_STATE;
         r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
         if (r < 0)
                 return log_oom();
@@ -1083,7 +1087,7 @@ static int setup_volatile_yes(const char *directory, uid_t uid_shift, const char
         if (!mkdtemp(template))
                 return log_error_errno(errno, "Failed to create temporary directory: %m");
 
-        options = "mode=755" TMPFS_LIMITS_ROOTFS;
+        options = "mode=0755" TMPFS_LIMITS_ROOTFS;
         r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
         if (r < 0)
                 goto fail;
@@ -1150,7 +1154,7 @@ static int setup_volatile_overlay(const char *directory, uid_t uid_shift, const
         if (!mkdtemp(template))
                 return log_error_errno(errno, "Failed to create temporary directory: %m");
 
-        options = "mode=755" TMPFS_LIMITS_ROOTFS;
+        options = "mode=0755" TMPFS_LIMITS_ROOTFS;
         r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
         if (r < 0)
                 goto finish;
@@ -1336,3 +1340,60 @@ done:
 
         return r;
 }
+
+#define NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS "/run/host/proc"
+#define NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS "/run/host/sys"
+
+int pin_fully_visible_fs(void) {
+        int r;
+
+        (void) mkdir_p(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, 0755);
+        (void) mkdir_p(NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS, 0755);
+
+        r = mount_follow_verbose(LOG_ERR, "proc", NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, "proc", PROC_DEFAULT_MOUNT_FLAGS, NULL);
+        if (r < 0)
+                return r;
+
+        r = mount_follow_verbose(LOG_ERR, "sysfs", NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS, "sysfs", SYS_DEFAULT_MOUNT_FLAGS, NULL);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int do_wipe_fully_visible_fs(void) {
+        if (umount2(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, MNT_DETACH) < 0)
+                return log_error_errno(errno, "Failed to unmount temporary proc: %m");
+
+        if (rmdir(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS) < 0)
+                return log_error_errno(errno, "Failed to remove temporary proc mountpoint: %m");
+
+        if (umount2(NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS, MNT_DETACH) < 0)
+                return log_error_errno(errno, "Failed to unmount temporary sys: %m");
+
+        if (rmdir(NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS) < 0)
+                return log_error_errno(errno, "Failed to remove temporary sys mountpoint: %m");
+
+        return 0;
+}
+
+int wipe_fully_visible_fs(int mntns_fd) {
+        _cleanup_close_ int orig_mntns_fd = -EBADF;
+        int r, rr;
+
+        r = namespace_open(0, NULL, &orig_mntns_fd, NULL, NULL, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to pin originating mount namespace: %m");
+
+        r = namespace_enter(-EBADF, mntns_fd, -EBADF, -EBADF, -EBADF);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enter mount namespace: %m");
+
+        rr = do_wipe_fully_visible_fs();
+
+        r = namespace_enter(-EBADF, orig_mntns_fd, -EBADF, -EBADF, -EBADF);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enter original mount namespace: %m");
+
+        return rr;
+}
index 6bedbf9b3faec6d2615cc635d681c31ef2f16a92..bf5e47dce405fbe4b7361338051b36256540f05c 100644 (file)
@@ -67,3 +67,5 @@ int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s
 int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old);
 
 int tmpfs_patch_options(const char *options,uid_t uid_shift, const char *selinux_apifs_context, char **ret);
+int pin_fully_visible_fs(void);
+int wipe_fully_visible_fs(int mntns_fd);
index 27044fadd2a877083d651725a46238ae9333dd34..3d666eeb7966ea7dc74c38c61d77d127708fcb8d 100644 (file)
@@ -33,90 +33,90 @@ static int add_syscall_filters(
                 const char* name;
         } allow_list[] = {
                 /* Let's use set names where we can */
-                { 0,                  "@aio"                   },
-                { 0,                  "@basic-io"              },
-                { 0,                  "@chown"                 },
-                { 0,                  "@default"               },
-                { 0,                  "@file-system"           },
-                { 0,                  "@io-event"              },
-                { 0,                  "@ipc"                   },
-                { 0,                  "@mount"                 },
-                { 0,                  "@network-io"            },
-                { 0,                  "@process"               },
-                { 0,                  "@resources"             },
-                { 0,                  "@setuid"                },
-                { 0,                  "@signal"                },
-                { 0,                  "@sync"                  },
-                { 0,                  "@timer"                 },
-
-                /* The following four are sets we optionally enable, in case the caps have been configured for it */
-                { CAP_SYS_TIME,       "@clock"                 },
-                { CAP_SYS_MODULE,     "@module"                },
-                { CAP_SYS_RAWIO,      "@raw-io"                },
-                { CAP_IPC_LOCK,       "@memlock"               },
+                { 0,                  "@aio"                         },
+                { 0,                  "@basic-io"                    },
+                { 0,                  "@chown"                       },
+                { 0,                  "@default"                     },
+                { 0,                  "@file-system"                 },
+                { 0,                  "@io-event"                    },
+                { 0,                  "@ipc"                         },
+                { 0,                  "@mount"                       },
+                { 0,                  "@network-io"                  },
+                { 0,                  "@process"                     },
+                { 0,                  "@resources"                   },
+                { 0,                  "@setuid"                      },
+                { 0,                  "@signal"                      },
+                { 0,                  "@sync"                        },
+                { 0,                  "@timer"                       },
+
+                /* The following four are sets we optionally enable, n case the caps have been configured for it */
+                { CAP_SYS_TIME,       "@clock"                       },
+                { CAP_SYS_MODULE,     "@module"                      },
+                { CAP_SYS_RAWIO,      "@raw-io"                      },
+                { CAP_IPC_LOCK,       "@memlock"                     },
 
                 /* Plus a good set of additional syscalls which are not part of any of the groups above */
-                { 0,                  "brk"                    },
-                { 0,                  "capget"                 },
-                { 0,                  "capset"                 },
-                { 0,                  "copy_file_range"        },
-                { 0,                  "fadvise64"              },
-                { 0,                  "fadvise64_64"           },
-                { 0,                  "flock"                  },
-                { 0,                  "get_mempolicy"          },
-                { 0,                  "getcpu"                 },
-                { 0,                  "getpriority"            },
-                { 0,                  "getrandom"              },
-                { 0,                  "ioctl"                  },
-                { 0,                  "ioprio_get"             },
-                { 0,                  "kcmp"                   },
-                { 0,                  "madvise"                },
-                { 0,                  "mincore"                },
-                { 0,                  "mprotect"               },
-                { 0,                  "mremap"                 },
-                { 0,                  "name_to_handle_at"      },
-                { 0,                  "oldolduname"            },
-                { 0,                  "olduname"               },
-                { 0,                  "personality"            },
-                { 0,                  "readahead"              },
-                { 0,                  "readdir"                },
-                { 0,                  "remap_file_pages"       },
-                { 0,                  "sched_get_priority_max" },
-                { 0,                  "sched_get_priority_min" },
-                { 0,                  "sched_getaffinity"      },
-                { 0,                  "sched_getattr"          },
-                { 0,                  "sched_getparam"         },
-                { 0,                  "sched_getscheduler"     },
-                { 0,                  "sched_rr_get_interval"  },
+                { 0,                  "brk"                          },
+                { 0,                  "capget"                       },
+                { 0,                  "capset"                       },
+                { 0,                  "copy_file_range"              },
+                { 0,                  "fadvise64"                    },
+                { 0,                  "fadvise64_64"                 },
+                { 0,                  "flock"                        },
+                { 0,                  "get_mempolicy"                },
+                { 0,                  "getcpu"                       },
+                { 0,                  "getpriority"                  },
+                { 0,                  "getrandom"                    },
+                { 0,                  "ioctl"                        },
+                { 0,                  "ioprio_get"                   },
+                { 0,                  "kcmp"                         },
+                { 0,                  "madvise"                      },
+                { 0,                  "mincore"                      },
+                { 0,                  "mprotect"                     },
+                { 0,                  "mremap"                       },
+                { 0,                  "name_to_handle_at"            },
+                { 0,                  "oldolduname"                  },
+                { 0,                  "olduname"                     },
+                { 0,                  "personality"                  },
+                { 0,                  "readahead"                    },
+                { 0,                  "readdir"                      },
+                { 0,                  "remap_file_pages"             },
+                { 0,                  "sched_get_priority_max"       },
+                { 0,                  "sched_get_priority_min"       },
+                { 0,                  "sched_getaffinity"            },
+                { 0,                  "sched_getattr"                },
+                { 0,                  "sched_getparam"               },
+                { 0,                  "sched_getscheduler"           },
+                { 0,                  "sched_rr_get_interval"        },
                 { 0,                  "sched_rr_get_interval_time64" },
-                { 0,                  "sched_yield"            },
-                { 0,                  "seccomp"                },
-                { 0,                  "sendfile"               },
-                { 0,                  "sendfile64"             },
-                { 0,                  "setdomainname"          },
-                { 0,                  "setfsgid"               },
-                { 0,                  "setfsgid32"             },
-                { 0,                  "setfsuid"               },
-                { 0,                  "setfsuid32"             },
-                { 0,                  "sethostname"            },
-                { 0,                  "setpgid"                },
-                { 0,                  "setsid"                 },
-                { 0,                  "splice"                 },
-                { 0,                  "sysinfo"                },
-                { 0,                  "tee"                    },
-                { 0,                  "umask"                  },
-                { 0,                  "uname"                  },
-                { 0,                  "userfaultfd"            },
-                { 0,                  "vmsplice"               },
+                { 0,                  "sched_yield"                  },
+                { 0,                  "seccomp"                      },
+                { 0,                  "sendfile"                     },
+                { 0,                  "sendfile64"                   },
+                { 0,                  "setdomainname"                },
+                { 0,                  "setfsgid"                     },
+                { 0,                  "setfsgid32"                   },
+                { 0,                  "setfsuid"                     },
+                { 0,                  "setfsuid32"                   },
+                { 0,                  "sethostname"                  },
+                { 0,                  "setpgid"                      },
+                { 0,                  "setsid"                       },
+                { 0,                  "splice"                       },
+                { 0,                  "sysinfo"                      },
+                { 0,                  "tee"                          },
+                { 0,                  "umask"                        },
+                { 0,                  "uname"                        },
+                { 0,                  "userfaultfd"                  },
+                { 0,                  "vmsplice"                     },
 
                 /* The following individual syscalls are added depending on specified caps */
-                { CAP_SYS_PACCT,      "acct"                   },
-                { CAP_SYS_PTRACE,     "process_vm_readv"       },
-                { CAP_SYS_PTRACE,     "process_vm_writev"      },
-                { CAP_SYS_PTRACE,     "ptrace"                 },
-                { CAP_SYS_BOOT,       "reboot"                 },
-                { CAP_SYSLOG,         "syslog"                 },
-                { CAP_SYS_TTY_CONFIG, "vhangup"                },
+                { CAP_SYS_PACCT,      "acct"                         },
+                { CAP_SYS_PTRACE,     "process_vm_readv"             },
+                { CAP_SYS_PTRACE,     "process_vm_writev"            },
+                { CAP_SYS_PTRACE,     "ptrace"                       },
+                { CAP_SYS_BOOT,       "reboot"                       },
+                { CAP_SYSLOG,         "syslog"                       },
+                { CAP_SYS_TTY_CONFIG, "vhangup"                      },
 
                 /*
                  * The following syscalls and groups are knowingly excluded:
index 1e0c8a2448d520fc40c282ed7fdfeefd274282c7..324cd0e69aaa2063d531c6a47ed92c2b7933455b 100644 (file)
 
 /* The notify socket inside the container it can use to talk to nspawn using the sd_notify(3) protocol */
 #define NSPAWN_NOTIFY_SOCKET_PATH "/run/host/notify"
+#define NSPAWN_MOUNT_TUNNEL "/run/host/incoming"
 
 #define EXIT_FORCE_RESTART 133
 
@@ -2195,7 +2196,7 @@ static int setup_boot_id(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to generate random boot id: %m");
 
-        r = id128_write(path, ID128_UUID, rnd, false);
+        r = id128_write(path, ID128_FORMAT_UUID, rnd);
         if (r < 0)
                 return log_error_errno(r, "Failed to write boot id: %m");
 
@@ -2496,13 +2497,13 @@ static int setup_credentials(const char *root) {
         return mount_nofollow_verbose(LOG_ERR, NULL, q, NULL, MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, "mode=0500");
 }
 
-static int setup_kmsg(int kmsg_socket) {
+static int setup_kmsg(int fd_inner_socket) {
         _cleanup_(unlink_and_freep) char *from = NULL;
         _cleanup_free_ char *fifo = NULL;
         _cleanup_close_ int fd = -1;
         int r;
 
-        assert(kmsg_socket >= 0);
+        assert(fd_inner_socket >= 0);
 
         BLOCK_WITH_UMASK(0000);
 
@@ -2529,7 +2530,7 @@ static int setup_kmsg(int kmsg_socket) {
                 return log_error_errno(errno, "Failed to open fifo: %m");
 
         /* Store away the fd in the socket, so that it stays open as long as we run the child */
-        r = send_one_fd(kmsg_socket, fd, 0);
+        r = send_one_fd(fd_inner_socket, fd, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to send FIFO fd: %m");
 
@@ -2776,7 +2777,7 @@ static int reset_audit_loginuid(void) {
         return 0;
 }
 
-static int setup_propagate(const char *root) {
+static int mount_tunnel_dig(const char *root) {
         const char *p, *q;
         int r;
 
@@ -2789,11 +2790,11 @@ static int setup_propagate(const char *root) {
         if (r < 0)
                 return log_error_errno(r, "Failed to create /run/host: %m");
 
-        r = userns_mkdir(root, "/run/host/incoming", 0600, 0, 0);
+        r = userns_mkdir(root, NSPAWN_MOUNT_TUNNEL, 0600, 0, 0);
         if (r < 0)
-                return log_error_errno(r, "Failed to create /run/host/incoming: %m");
+                return log_error_errno(r, "Failed to create "NSPAWN_MOUNT_TUNNEL": %m");
 
-        q = prefix_roota(root, "/run/host/incoming");
+        q = prefix_roota(root, NSPAWN_MOUNT_TUNNEL);
         r = mount_nofollow_verbose(LOG_ERR, p, q, NULL, MS_BIND, NULL);
         if (r < 0)
                 return r;
@@ -2802,8 +2803,17 @@ static int setup_propagate(const char *root) {
         if (r < 0)
                 return r;
 
-        /* machined will MS_MOVE into that directory, and that's only supported for non-shared mounts. */
-        return mount_nofollow_verbose(LOG_ERR, NULL, q, NULL, MS_SLAVE, NULL);
+        return 0;
+}
+
+static int mount_tunnel_open(void) {
+        int r;
+
+        r = mount_follow_verbose(LOG_ERR, NULL, NSPAWN_MOUNT_TUNNEL, NULL, MS_SLAVE, NULL);
+        if (r < 0)
+                return r;
+
+        return 0;
 }
 
 static int setup_machine_id(const char *directory) {
@@ -2820,9 +2830,9 @@ static int setup_machine_id(const char *directory) {
 
         etc_machine_id = prefix_roota(directory, "/etc/machine-id");
 
-        r = id128_read(etc_machine_id, ID128_PLAIN_OR_UNINIT, &id);
+        r = id128_read(etc_machine_id, ID128_FORMAT_PLAIN, &id);
         if (r < 0) {
-                if (!IN_SET(r, -ENOENT, -ENOMEDIUM)) /* If the file is missing or empty, we don't mind */
+                if (!IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG)) /* If the file is missing, empty, or uninitialized, we don't mind */
                         return log_error_errno(r, "Failed to read machine ID from container image: %m");
 
                 if (sd_id128_is_null(arg_uuid)) {
@@ -3200,9 +3210,7 @@ static int inner_child(
                 Barrier *barrier,
                 const char *directory,
                 bool secondary,
-                int kmsg_socket,
-                int rtnl_socket,
-                int master_pty_socket,
+                int fd_inner_socket,
                 FDSet *fds,
                 char **os_release_pairs) {
 
@@ -3240,7 +3248,7 @@ static int inner_child(
 
         assert(barrier);
         assert(directory);
-        assert(kmsg_socket >= 0);
+        assert(fd_inner_socket >= 0);
 
         log_debug("Inner child is initializing.");
 
@@ -3312,10 +3320,9 @@ static int inner_child(
         if (r < 0)
                 return r;
 
-        r = setup_kmsg(kmsg_socket);
+        r = setup_kmsg(fd_inner_socket);
         if (r < 0)
                 return r;
-        kmsg_socket = safe_close(kmsg_socket);
 
         r = mount_custom(
                         "/",
@@ -3335,10 +3342,9 @@ static int inner_child(
                 (void) loopback_setup();
 
         if (arg_expose_ports) {
-                r = expose_port_send_rtnl(rtnl_socket);
+                r = expose_port_send_rtnl(fd_inner_socket);
                 if (r < 0)
                         return r;
-                rtnl_socket = safe_close(rtnl_socket);
         }
 
         if (arg_console_mode != CONSOLE_PIPE) {
@@ -3354,10 +3360,9 @@ static int inner_child(
                 if (r < 0)
                         return log_error_errno(r, "Failed to set up /dev/console: %m");
 
-                r = send_one_fd(master_pty_socket, master, 0);
+                r = send_one_fd(fd_inner_socket, master, 0);
                 if (r < 0)
                         return log_error_errno(r, "Failed to send master fd: %m");
-                master_pty_socket = safe_close(master_pty_socket);
 
                 r = setup_stdio_as_dev_console();
                 if (r < 0)
@@ -3619,20 +3624,14 @@ static int outer_child(
                 const char *directory,
                 DissectedImage *dissected_image,
                 bool secondary,
-                int pid_socket,
-                int uuid_socket,
-                int notify_socket,
-                int kmsg_socket,
-                int rtnl_socket,
-                int uid_shift_socket,
-                int master_pty_socket,
-                int unified_cgroup_hierarchy_socket,
+                int fd_outer_socket,
+                int fd_inner_socket,
                 FDSet *fds,
                 int netns_fd) {
 
         _cleanup_(bind_user_context_freep) BindUserContext *bind_user_context = NULL;
         _cleanup_strv_free_ char **os_release_pairs = NULL;
-        _cleanup_close_ int fd = -1;
+        _cleanup_close_ int fd = -1, mntns_fd = -EBADF;
         bool idmap = false;
         const char *p;
         pid_t pid;
@@ -3647,11 +3646,8 @@ static int outer_child(
 
         assert(barrier);
         assert(directory);
-        assert(pid_socket >= 0);
-        assert(uuid_socket >= 0);
-        assert(notify_socket >= 0);
-        assert(master_pty_socket >= 0);
-        assert(kmsg_socket >= 0);
+        assert(fd_outer_socket >= 0);
+        assert(fd_inner_socket >= 0);
 
         log_debug("Outer child is initializing.");
 
@@ -3697,8 +3693,17 @@ static int outer_child(
                 return r;
 
         if (arg_userns_mode != USER_NAMESPACE_NO) {
+                r = namespace_open(0, NULL, &mntns_fd, NULL, NULL, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to pin outer mount namespace: %m");
+
+                l = send_one_fd(fd_outer_socket, mntns_fd, 0);
+                if (l < 0)
+                        return log_error_errno(l, "Failed to send outer mount namespace fd: %m");
+                mntns_fd = safe_close(mntns_fd);
+
                 /* Let the parent know which UID shift we read from the image */
-                l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
+                l = send(fd_outer_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
                 if (l < 0)
                         return log_error_errno(errno, "Failed to send UID shift: %m");
                 if (l != sizeof(arg_uid_shift))
@@ -3710,7 +3715,7 @@ static int outer_child(
                          * UID shift we just read from the image is available. If yes, it will send the UID
                          * shift back to us, if not it will pick a different one, and send it back to us. */
 
-                        l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0);
+                        l = recv(fd_outer_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0);
                         if (l < 0)
                                 return log_error_errno(errno, "Failed to recv UID shift: %m");
                         if (l != sizeof(arg_uid_shift))
@@ -3775,7 +3780,7 @@ static int outer_child(
                                 (uid_t) bind_user_context->data[i].host_group->gid,
                         };
 
-                        l = send(uid_shift_socket, map, sizeof(map), MSG_NOSIGNAL);
+                        l = send(fd_outer_socket, map, sizeof(map), MSG_NOSIGNAL);
                         if (l < 0)
                                 return log_error_errno(errno, "Failed to send user UID map: %m");
                         if (l != sizeof(map))
@@ -3848,29 +3853,14 @@ static int outer_child(
                 if (r < 0)
                         return r;
 
-                l = send(unified_cgroup_hierarchy_socket, &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), MSG_NOSIGNAL);
+                l = send(fd_outer_socket, &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), MSG_NOSIGNAL);
                 if (l < 0)
                         return log_error_errno(errno, "Failed to send cgroup mode: %m");
                 if (l != sizeof(arg_unified_cgroup_hierarchy))
                         return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                                "Short write while sending cgroup mode.");
-
-                unified_cgroup_hierarchy_socket = safe_close(unified_cgroup_hierarchy_socket);
         }
 
-        /* Mark everything as shared so our mounts get propagated down. This is required to make new bind
-         * mounts available in systemd services inside the container that create a new mount namespace.  See
-         * https://github.com/systemd/systemd/issues/3860 Further submounts (such as /dev) done after this
-         * will inherit the shared propagation mode.
-         *
-         * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
-         * directory mount to root later on.
-         * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
-         */
-        r = mount_nofollow_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
-        if (r < 0)
-                return r;
-
         r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
         if (r < 0)
                 return r;
@@ -3910,7 +3900,7 @@ static int outer_child(
         if (r < 0)
                 return r;
 
-        r = setup_propagate(directory);
+        r = mount_tunnel_dig(directory);
         if (r < 0)
                 return r;
 
@@ -3974,10 +3964,40 @@ static int outer_child(
                         return r;
         }
 
-        r = mount_move_root(directory);
+        /* Mark everything as shared so our mounts get propagated down. This is required to make new bind
+         * mounts available in systemd services inside the container that create a new mount namespace.  See
+         * https://github.com/systemd/systemd/issues/3860 Further submounts (such as /dev) done after this
+         * will inherit the shared propagation mode.
+         *
+         * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
+         * directory mount to root later on.
+         * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
+         */
+        r = mount_switch_root(directory, MOUNT_ATTR_PROPAGATION_SHARED);
         if (r < 0)
                 return log_error_errno(r, "Failed to move root directory: %m");
 
+        /* We finished setting up the rootfs which is a shared mount. The mount tunnel needs to be a
+         * dependent mount otherwise we can't MS_MOVE mounts that were propagated from the host into
+         * the container. */
+        r = mount_tunnel_open();
+        if (r < 0)
+                return r;
+
+        if (arg_userns_mode != USER_NAMESPACE_NO) {
+                /* In order to mount procfs and sysfs in an unprivileged container the kernel
+                 * requires that a fully visible instance is already present in the target mount
+                 * namespace. Mount one here so the inner child can mount its own instances. Later
+                 * we umount the temporary instances created here before we actually exec the
+                 * payload. Since the rootfs is shared the umount will propagate into the container.
+                 * Note, the inner child wouldn't be able to unmount the instances on its own since
+                 * it doesn't own the originating mount namespace. IOW, the outer child needs to do
+                 * this. */
+                r = pin_fully_visible_fs();
+                if (r < 0)
+                        return r;
+        }
+
         fd = setup_notify_child();
         if (fd < 0)
                 return fd;
@@ -3988,10 +4008,7 @@ static int outer_child(
         if (pid < 0)
                 return log_error_errno(errno, "Failed to fork inner child: %m");
         if (pid == 0) {
-                pid_socket = safe_close(pid_socket);
-                uuid_socket = safe_close(uuid_socket);
-                notify_socket = safe_close(notify_socket);
-                uid_shift_socket = safe_close(uid_shift_socket);
+                fd_outer_socket = safe_close(fd_outer_socket);
 
                 /* The inner child has all namespaces that are requested, so that we all are owned by the
                  * user if user namespaces are turned on. */
@@ -4002,37 +4019,33 @@ static int outer_child(
                                 return log_error_errno(r, "Failed to join network namespace: %m");
                 }
 
-                r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, master_pty_socket, fds, os_release_pairs);
+                r = inner_child(barrier, directory, secondary, fd_inner_socket, fds, os_release_pairs);
                 if (r < 0)
                         _exit(EXIT_FAILURE);
 
                 _exit(EXIT_SUCCESS);
         }
 
-        l = send(pid_socket, &pid, sizeof(pid), MSG_NOSIGNAL);
+        l = send(fd_outer_socket, &pid, sizeof(pid), MSG_NOSIGNAL);
         if (l < 0)
                 return log_error_errno(errno, "Failed to send PID: %m");
         if (l != sizeof(pid))
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                        "Short write while sending PID.");
 
-        l = send(uuid_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL);
+        l = send(fd_outer_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL);
         if (l < 0)
                 return log_error_errno(errno, "Failed to send machine ID: %m");
         if (l != sizeof(arg_uuid))
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                        "Short write while sending machine ID.");
 
-        l = send_one_fd(notify_socket, fd, 0);
+        l = send_one_fd(fd_outer_socket, fd, 0);
         if (l < 0)
                 return log_error_errno(l, "Failed to send notify fd: %m");
 
-        pid_socket = safe_close(pid_socket);
-        uuid_socket = safe_close(uuid_socket);
-        notify_socket = safe_close(notify_socket);
-        master_pty_socket = safe_close(master_pty_socket);
-        kmsg_socket = safe_close(kmsg_socket);
-        rtnl_socket = safe_close(rtnl_socket);
+        fd_outer_socket = safe_close(fd_outer_socket);
+        fd_inner_socket = safe_close(fd_inner_socket);
         netns_fd = safe_close(netns_fd);
 
         return 0;
@@ -4731,16 +4744,10 @@ static int run_container(
         _cleanup_(release_lock_file) LockFile uid_shift_lock = LOCK_FILE_INIT;
         _cleanup_close_ int etc_passwd_lock = -1;
         _cleanup_close_pair_ int
-                kmsg_socket_pair[2] = { -1, -1 },
-                rtnl_socket_pair[2] = { -1, -1 },
-                pid_socket_pair[2] = { -1, -1 },
-                uuid_socket_pair[2] = { -1, -1 },
-                notify_socket_pair[2] = { -1, -1 },
-                uid_shift_socket_pair[2] = { -1, -1 },
-                master_pty_socket_pair[2] = { -1, -1 },
-                unified_cgroup_hierarchy_socket_pair[2] = { -1, -1};
-
-        _cleanup_close_ int notify_socket = -1;
+                fd_inner_socket_pair[2] = { -EBADF, -EBADF },
+                fd_outer_socket_pair[2] = { -EBADF, -EBADF };
+
+        _cleanup_close_ int notify_socket = -1, mntns_fd = -EBADF, fd_kmsg_fifo = -EBADF;
         _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
         _cleanup_(sd_event_source_unrefp) sd_event_source *notify_event_source = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
@@ -4775,31 +4782,11 @@ static int run_container(
         if (r < 0)
                 return log_error_errno(r, "Cannot initialize IPC barrier: %m");
 
-        if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0)
-                return log_error_errno(errno, "Failed to create kmsg socket pair: %m");
-
-        if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0)
-                return log_error_errno(errno, "Failed to create rtnl socket pair: %m");
-
-        if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0)
-                return log_error_errno(errno, "Failed to create pid socket pair: %m");
+        if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, fd_inner_socket_pair) < 0)
+                return log_error_errno(errno, "Failed to create inner socket pair: %m");
 
-        if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0)
-                return log_error_errno(errno, "Failed to create id socket pair: %m");
-
-        if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0)
-                return log_error_errno(errno, "Failed to create notify socket pair: %m");
-
-        if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, master_pty_socket_pair) < 0)
-                return log_error_errno(errno, "Failed to create console socket pair: %m");
-
-        if (arg_userns_mode != USER_NAMESPACE_NO)
-                if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0)
-                        return log_error_errno(errno, "Failed to create uid shift socket pair: %m");
-
-        if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN)
-                if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, unified_cgroup_hierarchy_socket_pair) < 0)
-                        return log_error_errno(errno, "Failed to create unified cgroup socket pair: %m");
+        if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, fd_outer_socket_pair) < 0)
+                return log_error_errno(errno, "Failed to create outer socket pair: %m");
 
         /* Child can be killed before execv(), so handle SIGCHLD in order to interrupt
          * parent's blocking calls and give it a chance to call wait() and terminate. */
@@ -4836,14 +4823,8 @@ static int run_container(
                 /* The outer child only has a file system namespace. */
                 barrier_set_role(&barrier, BARRIER_CHILD);
 
-                kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
-                rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
-                pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
-                uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]);
-                notify_socket_pair[0] = safe_close(notify_socket_pair[0]);
-                master_pty_socket_pair[0] = safe_close(master_pty_socket_pair[0]);
-                uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]);
-                unified_cgroup_hierarchy_socket_pair[0] = safe_close(unified_cgroup_hierarchy_socket_pair[0]);
+                fd_inner_socket_pair[0] = safe_close(fd_inner_socket_pair[0]);
+                fd_outer_socket_pair[0] = safe_close(fd_outer_socket_pair[0]);
 
                 (void) reset_all_signal_handlers();
                 (void) reset_signal_mask();
@@ -4852,14 +4833,8 @@ static int run_container(
                                 arg_directory,
                                 dissected_image,
                                 secondary,
-                                pid_socket_pair[1],
-                                uuid_socket_pair[1],
-                                notify_socket_pair[1],
-                                kmsg_socket_pair[1],
-                                rtnl_socket_pair[1],
-                                uid_shift_socket_pair[1],
-                                master_pty_socket_pair[1],
-                                unified_cgroup_hierarchy_socket_pair[1],
+                                fd_outer_socket_pair[1],
+                                fd_inner_socket_pair[1],
                                 fds,
                                 child_netns_fd);
                 if (r < 0)
@@ -4872,18 +4847,16 @@ static int run_container(
 
         fdset_close(fds);
 
-        kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
-        rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
-        pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
-        uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
-        notify_socket_pair[1] = safe_close(notify_socket_pair[1]);
-        master_pty_socket_pair[1] = safe_close(master_pty_socket_pair[1]);
-        uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
-        unified_cgroup_hierarchy_socket_pair[1] = safe_close(unified_cgroup_hierarchy_socket_pair[1]);
+        fd_inner_socket_pair[1] = safe_close(fd_inner_socket_pair[1]);
+        fd_outer_socket_pair[1] = safe_close(fd_outer_socket_pair[1]);
 
         if (arg_userns_mode != USER_NAMESPACE_NO) {
+                mntns_fd = receive_one_fd(fd_outer_socket_pair[0], 0);
+                if (mntns_fd < 0)
+                        return log_error_errno(mntns_fd, "Failed to receive mount namespace fd from outer child: %m");
+
                 /* The child just let us know the UID shift it might have read from the image. */
-                l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0);
+                l = recv(fd_outer_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0);
                 if (l < 0)
                         return log_error_errno(errno, "Failed to read UID shift: %m");
                 if (l != sizeof arg_uid_shift)
@@ -4898,7 +4871,7 @@ static int run_container(
                         if (r < 0)
                                 return log_error_errno(r, "Failed to pick suitable UID/GID range: %m");
 
-                        l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, MSG_NOSIGNAL);
+                        l = send(fd_outer_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, MSG_NOSIGNAL);
                         if (l < 0)
                                 return log_error_errno(errno, "Failed to send UID shift: %m");
                         if (l != sizeof arg_uid_shift)
@@ -4915,7 +4888,7 @@ static int run_container(
                                 return log_oom();
 
                         for (size_t i = 0; i < n_bind_user_uid; i++) {
-                                l = recv(uid_shift_socket_pair[0], bind_user_uid + i*4, sizeof(uid_t)*4, 0);
+                                l = recv(fd_outer_socket_pair[0], bind_user_uid + i*4, sizeof(uid_t)*4, 0);
                                 if (l < 0)
                                         return log_error_errno(errno, "Failed to read user UID map pair: %m");
                                 if (l != sizeof(uid_t)*4)
@@ -4928,7 +4901,7 @@ static int run_container(
 
         if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
                 /* The child let us know the support cgroup mode it might have read from the image. */
-                l = recv(unified_cgroup_hierarchy_socket_pair[0], &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), 0);
+                l = recv(fd_outer_socket_pair[0], &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), 0);
                 if (l < 0)
                         return log_error_errno(errno, "Failed to read cgroup mode: %m");
                 if (l != sizeof(arg_unified_cgroup_hierarchy))
@@ -4944,21 +4917,21 @@ static int run_container(
                 return -EIO;
 
         /* And now retrieve the PID of the inner child. */
-        l = recv(pid_socket_pair[0], pid, sizeof *pid, 0);
+        l = recv(fd_outer_socket_pair[0], pid, sizeof *pid, 0);
         if (l < 0)
                 return log_error_errno(errno, "Failed to read inner child PID: %m");
         if (l != sizeof *pid)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading inner child PID.");
 
         /* We also retrieve container UUID in case it was generated by outer child */
-        l = recv(uuid_socket_pair[0], &arg_uuid, sizeof arg_uuid, 0);
+        l = recv(fd_outer_socket_pair[0], &arg_uuid, sizeof arg_uuid, 0);
         if (l < 0)
                 return log_error_errno(errno, "Failed to read container machine ID: %m");
         if (l != sizeof(arg_uuid))
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading container machined ID.");
 
         /* We also retrieve the socket used for notifications generated by outer child */
-        notify_socket = receive_one_fd(notify_socket_pair[0], 0);
+        notify_socket = receive_one_fd(fd_outer_socket_pair[0], 0);
         if (notify_socket < 0)
                 return log_error_errno(notify_socket,
                                        "Failed to receive notification socket from the outer child: %m");
@@ -5143,6 +5116,13 @@ static int run_container(
         if (r < 0)
                 return r;
 
+        if (arg_userns_mode != USER_NAMESPACE_NO) {
+                r = wipe_fully_visible_fs(mntns_fd);
+                if (r < 0)
+                        return r;
+                mntns_fd = safe_close(mntns_fd);
+        }
+
         /* Let the child know that we are ready and wait that the child is completely ready now. */
         if (!barrier_place_and_sync(&barrier)) /* #5 */
                 return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early.");
@@ -5173,8 +5153,13 @@ static int run_container(
         /* Exit when the child exits */
         (void) sd_event_add_signal(event, NULL, SIGCHLD, on_sigchld, PID_TO_PTR(*pid));
 
+        /* Retrieve the kmsg fifo allocated by inner child */
+        fd_kmsg_fifo = receive_one_fd(fd_inner_socket_pair[0], 0);
+        if (fd_kmsg_fifo < 0)
+                return log_error_errno(fd_kmsg_fifo, "Failed to receive kmsg fifo from inner child: %m");
+
         if (arg_expose_ports) {
-                r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, expose_args, &rtnl);
+                r = expose_port_watch_rtnl(event, fd_inner_socket_pair[0], on_address_change, expose_args, &rtnl);
                 if (r < 0)
                         return r;
 
@@ -5182,14 +5167,12 @@ static int run_container(
                 (void) expose_port_execute(rtnl, &expose_args->fw_ctx, arg_expose_ports, AF_INET6, &expose_args->address6);
         }
 
-        rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
-
         if (arg_console_mode != CONSOLE_PIPE) {
                 _cleanup_close_ int fd = -1;
                 PTYForwardFlags flags = 0;
 
                 /* Retrieve the master pty allocated by inner child */
-                fd = receive_one_fd(master_pty_socket_pair[0], 0);
+                fd = receive_one_fd(fd_inner_socket_pair[0], 0);
                 if (fd < 0)
                         return log_error_errno(fd, "Failed to receive master pty from the inner child: %m");
 
@@ -5220,6 +5203,8 @@ static int run_container(
                 *master = TAKE_FD(fd);
         }
 
+        fd_inner_socket_pair[0] = safe_close(fd_inner_socket_pair[0]);
+
         r = sd_event_loop(event);
         if (r < 0)
                 return log_error_errno(r, "Failed to run event loop: %m");
@@ -5241,6 +5226,8 @@ static int run_container(
         /* Normally redundant, but better safe than sorry */
         (void) kill(*pid, SIGKILL);
 
+        fd_kmsg_fifo = safe_close(fd_kmsg_fifo);
+
         if (arg_private_network) {
                 /* Move network interfaces back to the parent network namespace. We use `safe_fork`
                  * to avoid having to move the parent to the child network namespace. */
index 7291e044eb1fd369f604791ed9119e55f7e4b716..f97b771c5053cd681ce653fc0f8ad06fae69aa93 100644 (file)
@@ -242,7 +242,7 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) {
                 if (r < 0)
                         return r;
 
-                log_debug("oomd dry-run: Would have tried to kill %s with recurse=%s", cg_path, true_false(recurse));
+                log_info("oomd dry-run: Would have tried to kill %s with recurse=%s", cg_path, true_false(recurse));
                 return 0;
         }
 
index 46b68c730cb1bddc7fbcc52bd7f1fd1bf61e6bed..80cd7daba321db5d34394c3d7576123c826ce60b 100644 (file)
@@ -145,8 +145,8 @@ static bool arg_split = false;
 static sd_id128_t *arg_filter_partitions = NULL;
 static size_t arg_n_filter_partitions = 0;
 static FilterPartitionsType arg_filter_partitions_type = FILTER_PARTITIONS_NONE;
-static sd_id128_t *arg_skip_partitions = NULL;
-static size_t arg_n_skip_partitions = 0;
+static sd_id128_t *arg_defer_partitions = NULL;
+static size_t arg_n_defer_partitions = 0;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -158,9 +158,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
 
-typedef struct Partition Partition;
 typedef struct FreeArea FreeArea;
-typedef struct Context Context;
 
 typedef enum EncryptMode {
         ENCRYPT_OFF,
@@ -180,7 +178,15 @@ typedef enum VerityMode {
         _VERITY_MODE_INVALID = -EINVAL,
 } VerityMode;
 
-struct Partition {
+typedef enum MinimizeMode {
+        MINIMIZE_OFF,
+        MINIMIZE_BEST,
+        MINIMIZE_GUESS,
+        _MINIMIZE_MODE_MAX,
+        _MINIMIZE_MODE_INVALID = -EINVAL,
+} MinimizeMode;
+
+typedef struct Partition {
         char *definition_path;
         char **drop_in_files;
 
@@ -211,6 +217,7 @@ struct Partition {
         FreeArea *allocated_to_area;
 
         char *copy_blocks_path;
+        bool copy_blocks_path_is_our_file;
         bool copy_blocks_auto;
         const char *copy_blocks_root;
         int copy_blocks_fd;
@@ -222,7 +229,7 @@ struct Partition {
         EncryptMode encrypt;
         VerityMode verity;
         char *verity_match_key;
-        bool minimize;
+        MinimizeMode minimize;
 
         uint64_t gpt_flags;
         int no_auto;
@@ -233,12 +240,12 @@ struct Partition {
         size_t roothash_size;
 
         char *split_name_format;
-        char *split_name_resolved;
+        char *split_path;
 
-        Partition *siblings[_VERITY_MODE_MAX];
+        struct Partition *siblings[_VERITY_MODE_MAX];
 
-        LIST_FIELDS(Partition, partitions);
-};
+        LIST_FIELDS(struct Partition, partitions);
+} Partition;
 
 #define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
 #define PARTITION_EXISTS(p) (!!(p)->current_partition)
@@ -249,7 +256,7 @@ struct FreeArea {
         uint64_t allocated;
 };
 
-struct Context {
+typedef struct Context {
         LIST_HEAD(Partition, partitions);
         size_t n_partitions;
 
@@ -263,7 +270,13 @@ struct Context {
         uint64_t grain_size;
 
         sd_id128_t seed;
-};
+
+        char *node;
+        bool node_is_our_file;
+        int backing_fd;
+
+        bool from_scratch;
+} Context;
 
 static const char *encrypt_mode_table[_ENCRYPT_MODE_MAX] = {
         [ENCRYPT_OFF] = "off",
@@ -279,8 +292,15 @@ static const char *verity_mode_table[_VERITY_MODE_MAX] = {
         [VERITY_SIG]  = "signature",
 };
 
+static const char *minimize_mode_table[_MINIMIZE_MODE_MAX] = {
+        [MINIMIZE_OFF]   = "off",
+        [MINIMIZE_BEST]  = "best",
+        [MINIMIZE_GUESS] = "guess",
+};
+
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(minimize_mode, MinimizeMode, MINIMIZE_BEST);
 
 static uint64_t round_down_size(uint64_t v, uint64_t p) {
         return (v / p) * p;
@@ -340,7 +360,10 @@ static Partition* partition_free(Partition *p) {
         if (p->new_partition)
                 fdisk_unref_partition(p->new_partition);
 
-        free(p->copy_blocks_path);
+        if (p->copy_blocks_path_is_our_file)
+                unlink_and_free(p->copy_blocks_path);
+        else
+                free(p->copy_blocks_path);
         safe_close(p->copy_blocks_fd);
 
         free(p->format);
@@ -351,7 +374,7 @@ static Partition* partition_free(Partition *p) {
         free(p->roothash);
 
         free(p->split_name_format);
-        free(p->split_name_resolved);
+        unlink_and_free(p->split_path);
 
         return mfree(p);
 }
@@ -400,11 +423,11 @@ static bool partition_exclude(const Partition *p) {
         return arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE;
 }
 
-static bool partition_skip(const Partition *p) {
+static bool partition_defer(const Partition *p) {
         assert(p);
 
-        for (size_t i = 0; i < arg_n_skip_partitions; i++)
-                if (sd_id128_equal(p->type.uuid, arg_skip_partitions[i]))
+        for (size_t i = 0; i < arg_n_defer_partitions; i++)
+                if (sd_id128_equal(p->type.uuid, arg_defer_partitions[i]))
                         return true;
 
         return false;
@@ -464,6 +487,12 @@ static Context *context_free(Context *context) {
         if (context->fdisk_context)
                 fdisk_unref_context(context->fdisk_context);
 
+        safe_close(context->backing_fd);
+        if (context->node_is_our_file)
+                unlink_and_free(context->node);
+        else
+                free(context->node);
+
         return mfree(context);
 }
 
@@ -1518,6 +1547,7 @@ static int config_parse_uuid(
 }
 
 static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF, "Invalid verity mode");
+static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF, "Invalid minimize mode");
 
 static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
 
@@ -1545,7 +1575,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
                 { "Partition", "NoAuto",          config_parse_tristate,    0, &p->no_auto           },
                 { "Partition", "GrowFileSystem",  config_parse_tristate,    0, &p->growfs            },
                 { "Partition", "SplitName",       config_parse_string,      0, &p->split_name_format },
-                { "Partition", "Minimize",        config_parse_bool,        0, &p->minimize          },
+                { "Partition", "Minimize",        config_parse_minimize,    0, &p->minimize          },
                 {}
         };
         int r;
@@ -1602,10 +1632,14 @@ static int partition_read_definition(Partition *p, const char *path, const char
                         return log_oom();
         }
 
-        if (p->minimize && !p->format)
+        if (p->minimize != MINIMIZE_OFF && !p->format)
                 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
                                   "Minimize= can only be enabled if Format= is set");
 
+        if (p->minimize == MINIMIZE_BEST && !fstype_is_ro(p->format))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Minimize=best can only be used with read-only filesystems");
+
         if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && !mkfs_supports_root_option(p->format) && geteuid() != 0)
                 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EPERM),
                                   "Need to be root to populate %s filesystems with CopyFiles=/MakeDirectories=",
@@ -1920,11 +1954,7 @@ static int derive_uuid(sd_id128_t base, const char *token, sd_id128_t *ret) {
         return 0;
 }
 
-static int context_load_partition_table(
-                Context *context,
-                const char *node,
-                int *backing_fd) {
-
+static int context_load_partition_table(Context *context) {
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
         uint64_t left_boundary = UINT64_MAX, first_lba, last_lba, nsectors;
@@ -1937,8 +1967,6 @@ static int context_load_partition_table(
         int r;
 
         assert(context);
-        assert(node);
-        assert(backing_fd);
         assert(!context->fdisk_context);
         assert(!context->free_areas);
         assert(context->start == UINT64_MAX);
@@ -1947,14 +1975,14 @@ static int context_load_partition_table(
 
         /* 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 (*backing_fd < 0) {
+        if (context->backing_fd < 0) {
                 c = fdisk_new_context();
                 if (!c)
                         return log_oom();
 
-                r = fdisk_assign_device(c, node, arg_dry_run);
+                r = fdisk_assign_device(c, context->node, arg_dry_run);
         } else
-                r = fdisk_new_context_fd(*backing_fd, arg_dry_run, &c);
+                r = fdisk_new_context_fd(context->backing_fd, arg_dry_run, &c);
 
         if (r == -EINVAL && arg_size_auto) {
                 struct stat st;
@@ -1962,12 +1990,12 @@ static int context_load_partition_table(
                 /* libfdisk returns EINVAL if opening a file of size zero. Let's check for that, and accept
                  * it if automatic sizing is requested. */
 
-                if (*backing_fd < 0)
-                        r = stat(node, &st);
+                if (context->backing_fd < 0)
+                        r = stat(context->node, &st);
                 else
-                        r = fstat(*backing_fd, &st);
+                        r = fstat(context->backing_fd, &st);
                 if (r < 0)
-                        return log_error_errno(errno, "Failed to stat block device '%s': %m", node);
+                        return log_error_errno(errno, "Failed to stat block device '%s': %m", context->node);
 
                 if (S_ISREG(st.st_mode) && st.st_size == 0) {
                         /* User the fallback values if we have no better idea */
@@ -1979,16 +2007,16 @@ static int context_load_partition_table(
                 r = -EINVAL;
         }
         if (r < 0)
-                return log_error_errno(r, "Failed to open device '%s': %m", node);
+                return log_error_errno(r, "Failed to open device '%s': %m", context->node);
 
-        if (*backing_fd < 0) {
+        if (context->backing_fd < 0) {
                 /* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */
-                *backing_fd = fd_reopen(fdisk_get_devfd(c), O_RDONLY|O_CLOEXEC);
-                if (*backing_fd < 0)
-                        return log_error_errno(*backing_fd, "Failed to duplicate fdisk fd: %m");
+                context->backing_fd = fd_reopen(fdisk_get_devfd(c), O_RDONLY|O_CLOEXEC);
+                if (context->backing_fd < 0)
+                        return log_error_errno(context->backing_fd, "Failed to duplicate fdisk fd: %m");
 
                 /* Tell udev not to interfere while we are processing the device */
-                if (flock(*backing_fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
+                if (flock(context->backing_fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
                         return log_error_errno(errno, "Failed to lock block device: %m");
         }
 
@@ -2014,7 +2042,7 @@ static int context_load_partition_table(
         case EMPTY_REFUSE:
                 /* Refuse empty disks, insist on an existing GPT partition table */
                 if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
-                        return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not repartitioning.", node);
+                        return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not repartitioning.", context->node);
 
                 break;
 
@@ -2022,9 +2050,9 @@ static int context_load_partition_table(
                 /* Require an empty disk, refuse any existing partition table */
                 r = fdisk_has_label(c);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", node);
+                        return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", context->node);
                 if (r > 0)
-                        return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s already has a disk label, refusing.", node);
+                        return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s already has a disk label, refusing.", context->node);
 
                 from_scratch = true;
                 break;
@@ -2033,10 +2061,10 @@ static int context_load_partition_table(
                 /* Allow both an empty disk and an existing partition table, but only GPT */
                 r = fdisk_has_label(c);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", node);
+                        return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", context->node);
                 if (r > 0) {
                         if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
-                                return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has non-GPT disk label, not repartitioning.", node);
+                                return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has non-GPT disk label, not repartitioning.", context->node);
                 } else
                         from_scratch = true;
 
@@ -2342,30 +2370,47 @@ static const char *partition_label(const Partition *p) {
         return gpt_partition_type_uuid_to_string(p->type.uuid);
 }
 
-static int context_dump_partitions(Context *context, const char *node) {
+static int context_dump_partitions(Context *context) {
         _cleanup_(table_unrefp) Table *t = NULL;
         uint64_t sum_padding = 0, sum_size = 0;
         int r;
-        const size_t roothash_col = 13, dropin_files_col = 14;
-        bool has_roothash = false, has_dropin_files = false;
+        const size_t roothash_col = 13, dropin_files_col = 14, split_path_col = 15;
+        bool has_roothash = false, has_dropin_files = false, has_split_path = false;
 
         if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) {
                 log_info("Empty partition table.");
                 return 0;
         }
 
-        t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity", "roothash", "drop-in files");
+        t = table_new("type",
+                      "label",
+                      "uuid",
+                      "file",
+                      "node",
+                      "offset",
+                      "old size",
+                      "raw size",
+                      "size",
+                      "old padding",
+                      "raw padding",
+                      "padding",
+                      "activity",
+                      "roothash",
+                      "drop-in files",
+                      "split path");
         if (!t)
                 return log_oom();
 
         if (!DEBUG_LOGGING) {
                 if (arg_json_format_flags & JSON_FORMAT_OFF)
                         (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
-                                                    (size_t) 8, (size_t) 11, roothash_col, dropin_files_col);
+                                                    (size_t) 8, (size_t) 11, roothash_col, dropin_files_col,
+                                                    split_path_col);
                 else
                         (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
                                                     (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10,
-                                                    (size_t) 12, roothash_col, dropin_files_col);
+                                                    (size_t) 12, roothash_col, dropin_files_col,
+                                                    split_path_col);
         }
 
         (void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
@@ -2390,7 +2435,7 @@ static int context_dump_partitions(Context *context, const char *node) {
                         activity = "resize";
 
                 label = partition_label(p);
-                partname = p->partno != UINT64_MAX ? fdisk_partname(node, p->partno+1) : NULL;
+                partname = p->partno != UINT64_MAX ? fdisk_partname(context->node, p->partno+1) : NULL;
 
                 r = format_size_change(p->current_size, p->new_size, &size_change);
                 if (r < 0)
@@ -2427,12 +2472,14 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL,
                                 TABLE_STRING, activity ?: "unchanged",
                                 TABLE_STRING, rh,
-                                TABLE_STRV, p->drop_in_files);
+                                TABLE_STRV, p->drop_in_files,
+                                TABLE_STRING, empty_to_null(p->split_path) ?: "-");
                 if (r < 0)
                         return table_log_add_error(r);
 
                 has_roothash = has_roothash || !isempty(rh);
                 has_dropin_files = has_dropin_files || !strv_isempty(p->drop_in_files);
+                has_split_path = has_split_path || !isempty(p->split_path);
         }
 
         if ((arg_json_format_flags & JSON_FORMAT_OFF) && (sum_padding > 0 || sum_size > 0)) {
@@ -2457,6 +2504,7 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_STRING, b,
                                 TABLE_EMPTY,
                                 TABLE_EMPTY,
+                                TABLE_EMPTY,
                                 TABLE_EMPTY);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -2474,6 +2522,12 @@ static int context_dump_partitions(Context *context, const char *node) {
                         return log_error_errno(r, "Failed to set columns to display: %m");
         }
 
+        if (!has_split_path) {
+                r = table_hide_column_from_display(t, split_path_col);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set columns to display: %m");
+        }
+
         return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
 }
 
@@ -2559,7 +2613,7 @@ done:
         return 0;
 }
 
-static int context_dump_partition_bar(Context *context, const char *node) {
+static int context_dump_partition_bar(Context *context) {
         _cleanup_free_ Partition **bar = NULL;
         _cleanup_free_ size_t *start_array = NULL;
         Partition *last = NULL;
@@ -2635,7 +2689,7 @@ static int context_dump_partition_bar(Context *context, const char *node) {
                         } else if (i == context->n_partitions - j) {
                                 _cleanup_free_ char *hint = NULL;
 
-                                (void) partition_hint(p, node, &hint);
+                                (void) partition_hint(p, context->node, &hint);
 
                                 if (streq_ptr(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_VERTICAL)))
                                         d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), " ", strna(hint));
@@ -2680,11 +2734,10 @@ static bool context_has_roothash(Context *context) {
         return false;
 }
 
-static int context_dump(Context *context, const char *node, bool late) {
+static int context_dump(Context *context, bool late) {
         int r;
 
         assert(context);
-        assert(node);
 
         if (arg_pretty == 0 && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
                 return 0;
@@ -2699,7 +2752,7 @@ static int context_dump(Context *context, const char *node, bool late) {
         if (late && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !context_has_roothash(context))
                 return 0;
 
-        r = context_dump_partitions(context, node);
+        r = context_dump_partitions(context);
         if (r < 0)
                 return r;
 
@@ -2708,7 +2761,7 @@ static int context_dump(Context *context, const char *node, bool late) {
         if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !late) {
                 putc('\n', stdout);
 
-                r = context_dump_partition_bar(context, node);
+                r = context_dump_partition_bar(context);
                 if (r < 0)
                         return r;
 
@@ -2951,7 +3004,7 @@ static int context_discard_gap_after(Context *context, Partition *p) {
         return 0;
 }
 
-static int context_wipe_and_discard(Context *context, bool from_scratch) {
+static int context_wipe_and_discard(Context *context) {
         int r;
 
         assert(context);
@@ -2965,14 +3018,14 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) {
                 if (!p->allocated_to_area)
                         continue;
 
-                if (partition_skip(p))
+                if (partition_defer(p))
                         continue;
 
                 r = context_wipe_partition(context, p);
                 if (r < 0)
                         return r;
 
-                if (!from_scratch) {
+                if (!context->from_scratch) {
                         r = context_discard_partition(context, p);
                         if (r < 0)
                                 return r;
@@ -2983,7 +3036,7 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) {
                 }
         }
 
-        if (!from_scratch) {
+        if (!context->from_scratch) {
                 r = context_discard_gap_after(context, NULL);
                 if (r < 0)
                         return r;
@@ -3420,7 +3473,7 @@ static int partition_format_verity_hash(
         if (p->verity != VERITY_HASH)
                 return 0;
 
-        if (partition_skip(p))
+        if (partition_defer(p))
                 return 0;
 
         assert_se(dp = p->siblings[VERITY_DATA]);
@@ -3552,7 +3605,7 @@ static int partition_format_verity_sig(Context *context, Partition *p) {
         if (PARTITION_EXISTS(p))
                 return 0;
 
-        if (partition_skip(p))
+        if (partition_defer(p))
                 return 0;
 
         assert_se(hp = p->siblings[VERITY_HASH]);
@@ -3626,7 +3679,7 @@ static int context_copy_blocks(Context *context) {
                 if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
                         continue;
 
-                if (partition_skip(p))
+                if (partition_defer(p))
                         continue;
 
                 assert(p->new_size != UINT64_MAX);
@@ -3950,7 +4003,7 @@ static int context_mkfs(Context *context) {
                 if (p->copy_blocks_fd >= 0)
                         continue;
 
-                if (partition_skip(p))
+                if (partition_defer(p))
                         continue;
 
                 assert(p->offset != UINT64_MAX);
@@ -4335,7 +4388,7 @@ static int context_mangle_partitions(Context *context) {
                 if (p->dropped)
                         continue;
 
-                if (partition_skip(p))
+                if (partition_defer(p))
                         continue;
 
                 assert(p->new_size != UINT64_MAX);
@@ -4456,7 +4509,7 @@ static int context_mangle_partitions(Context *context) {
         return 0;
 }
 
-static int split_name_printf(Partition *p) {
+static int split_name_printf(Partition *p, char **ret) {
         assert(p);
 
         const Specifier table[] = {
@@ -4469,87 +4522,104 @@ static int split_name_printf(Partition *p) {
                 {}
         };
 
-        return specifier_printf(p->split_name_format, NAME_MAX, table, arg_root, p, &p->split_name_resolved);
+        return specifier_printf(p->split_name_format, NAME_MAX, table, arg_root, p, ret);
+}
+
+static int split_node(const char *node, char **ret_base, char **ret_ext) {
+        _cleanup_free_ char *base = NULL, *ext = NULL;
+        char *e;
+        int r;
+
+        assert(node);
+        assert(ret_base);
+        assert(ret_ext);
+
+        r = path_extract_filename(node, &base);
+        if (r == O_DIRECTORY || r == -EADDRNOTAVAIL)
+                return log_error_errno(r, "Device node %s cannot be a directory", node);
+        if (r < 0)
+                return log_error_errno(r, "Failed to extract filename from %s: %m", node);
+
+        e = endswith(base, ".raw");
+        if (e) {
+                ext = strdup(e);
+                if (!ext)
+                        return log_oom();
+
+                *e = 0;
+        }
+
+        *ret_base = TAKE_PTR(base);
+        *ret_ext = TAKE_PTR(ext);
+
+        return 0;
 }
 
 static int split_name_resolve(Context *context) {
+        _cleanup_free_ char *parent = NULL, *base = NULL, *ext = NULL;
         int r;
 
+        assert(context);
+
+        r = path_extract_directory(context->node, &parent);
+        if (r < 0 && r != -EDESTADDRREQ)
+                return log_error_errno(r, "Failed to extract directory from %s: %m", context->node);
+
+        r = split_node(context->node, &base, &ext);
+        if (r < 0)
+                return r;
+
         LIST_FOREACH(partitions, p, context->partitions) {
+                _cleanup_free_ char *resolved = NULL;
+
                 if (p->dropped)
                         continue;
 
                 if (!p->split_name_format)
                         continue;
 
-                r = split_name_printf(p);
+                r = split_name_printf(p, &resolved);
                 if (r < 0)
                         return log_error_errno(r, "Failed to resolve specifiers in %s: %m", p->split_name_format);
+
+                if (parent)
+                        p->split_path = strjoin(parent, "/", base, ".", resolved, ext);
+                else
+                        p->split_path = strjoin(base, ".", resolved, ext);
+                if (!p->split_path)
+                        return log_oom();
         }
 
         LIST_FOREACH(partitions, p, context->partitions) {
-                if (!p->split_name_resolved)
+                if (!p->split_path)
                         continue;
 
                 LIST_FOREACH(partitions, q, context->partitions) {
                         if (p == q)
                                 continue;
 
-                        if (!q->split_name_resolved)
+                        if (!q->split_path)
                                 continue;
 
-                        if (!streq(p->split_name_resolved, q->split_name_resolved))
+                        if (!streq(p->split_path, q->split_path))
                                 continue;
 
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
                                                "%s and %s have the same resolved split name \"%s\", refusing",
-                                               p->definition_path, q->definition_path, p->split_name_resolved);
+                                               p->definition_path, q->definition_path, p->split_path);
                 }
         }
 
         return 0;
 }
 
-static int split_node(const char *node, char **ret_base, char **ret_ext) {
-        _cleanup_free_ char *base = NULL, *ext = NULL;
-        char *e;
-        int r;
-
-        assert(node);
-        assert(ret_base);
-        assert(ret_ext);
-
-        r = path_extract_filename(node, &base);
-        if (r == O_DIRECTORY || r == -EADDRNOTAVAIL)
-                return log_error_errno(r, "Device node %s cannot be a directory", arg_node);
-        if (r < 0)
-                return log_error_errno(r, "Failed to extract filename from %s: %m", arg_node);
-
-        e = endswith(base, ".raw");
-        if (e) {
-                ext = strdup(e);
-                if (!ext)
-                        return log_oom();
-
-                *e = 0;
-        }
-
-        *ret_base = TAKE_PTR(base);
-        *ret_ext = TAKE_PTR(ext);
-
-        return 0;
-}
-
 static int context_split(Context *context) {
-        _cleanup_free_ char *base = NULL, *ext = NULL;
-        _cleanup_close_ int dir_fd = -1;
         int fd = -1, r;
 
         if (!arg_split)
                 return 0;
 
         assert(context);
-        assert(arg_node);
 
         /* We can't do resolution earlier because the partition UUIDs for verity partitions are only filled
          * in after they've been generated. */
@@ -4558,36 +4628,21 @@ static int context_split(Context *context) {
         if (r < 0)
                 return r;
 
-        r = split_node(arg_node, &base, &ext);
-        if (r < 0)
-                return r;
-
-        dir_fd = r = open_parent(arg_node, O_PATH|O_CLOEXEC, 0);
-        if (r == -EDESTADDRREQ)
-                dir_fd = AT_FDCWD;
-        else if (r < 0)
-                return log_error_errno(r, "Failed to open parent directory of %s: %m", arg_node);
-
         LIST_FOREACH(partitions, p, context->partitions) {
-                _cleanup_free_ char *fname = NULL;
                 _cleanup_close_ int fdt = -1;
 
                 if (p->dropped)
                         continue;
 
-                if (!p->split_name_resolved)
+                if (!p->split_path)
                         continue;
 
-                if (partition_skip(p))
+                if (partition_defer(p))
                         continue;
 
-                fname = strjoin(base, ".", p->split_name_resolved, ext);
-                if (!fname)
-                        return log_oom();
-
-                fdt = openat(dir_fd, fname, O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW|O_CREAT|O_EXCL, 0666);
+                fdt = open(p->split_path, O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW|O_CREAT|O_EXCL, 0666);
                 if (fdt < 0)
-                        return log_error_errno(errno, "Failed to open %s: %m", fname);
+                        return log_error_errno(fdt, "Failed to open split partition file %s: %m", p->split_path);
 
                 if (fd < 0)
                         assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
@@ -4597,23 +4652,19 @@ static int context_split(Context *context) {
 
                 r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to copy to split partition %s: %m", fname);
+                        return log_error_errno(r, "Failed to copy to split partition %s: %m", p->split_path);
         }
 
         return 0;
 }
 
-static int context_write_partition_table(
-                Context *context,
-                const char *node,
-                bool from_scratch) {
-
+static int context_write_partition_table(Context *context) {
         _cleanup_(fdisk_unref_tablep) struct fdisk_table *original_table = NULL;
         int capable, r;
 
         assert(context);
 
-        if (!from_scratch && !context_changed(context)) {
+        if (!context->from_scratch && !context_changed(context)) {
                 log_info("No changes.");
                 return 0;
         }
@@ -4625,7 +4676,7 @@ static int context_write_partition_table(
 
         log_info("Applying changes.");
 
-        if (from_scratch) {
+        if (context->from_scratch) {
                 r = context_wipe_range(context, 0, context->total);
                 if (r < 0)
                         return r;
@@ -4649,7 +4700,7 @@ static int context_write_partition_table(
 
         /* Wipe fs signatures and discard sectors where the new partitions are going to be placed and in the
          * gaps between partitions, just to be sure. */
-        r = context_wipe_and_discard(context, from_scratch);
+        r = context_wipe_and_discard(context);
         if (r < 0)
                 return r;
 
@@ -4679,7 +4730,7 @@ static int context_write_partition_table(
         else if (capable > 0) {
                 log_info("Telling kernel to reread partition table.");
 
-                if (from_scratch)
+                if (context->from_scratch)
                         r = fdisk_reread_partition_table(context->fdisk_context);
                 else
                         r = fdisk_reread_changes(context->fdisk_context, original_table);
@@ -4710,8 +4761,8 @@ static int context_read_seed(Context *context, const char *root) {
                 else if (fd < 0)
                         return log_error_errno(fd, "Failed to determine machine ID of image: %m");
                 else {
-                        r = id128_read_fd(fd, ID128_PLAIN_OR_UNINIT, &context->seed);
-                        if (r == -ENOMEDIUM)
+                        r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &context->seed);
+                        if (IN_SET(r, -ENOMEDIUM, -ENOPKG))
                                 log_info("No machine ID set, using randomized partition UUIDs.");
                         else if (r < 0)
                                 return log_error_errno(r, "Failed to parse machine ID of image: %m");
@@ -4727,7 +4778,7 @@ static int context_read_seed(Context *context, const char *root) {
         return 0;
 }
 
-static int context_factory_reset(Context *context, bool from_scratch) {
+static int context_factory_reset(Context *context) {
         size_t n = 0;
         int r;
 
@@ -4736,7 +4787,7 @@ static int context_factory_reset(Context *context, bool from_scratch) {
         if (arg_factory_reset <= 0)
                 return 0;
 
-        if (from_scratch) /* Nothing to reset if we start from scratch */
+        if (context->from_scratch) /* Nothing to reset if we start from scratch */
                 return 0;
 
         if (arg_dry_run) {
@@ -5251,7 +5302,7 @@ static int context_minimize(Context *context) {
                 if (!p->format)
                         continue;
 
-                if (!p->minimize)
+                if (p->minimize == MINIMIZE_OFF)
                         continue;
 
                 if (!partition_needs_populate(p))
@@ -5306,6 +5357,7 @@ static int context_minimize(Context *context) {
                  * loopback file for us. */
                 if (fstype_is_ro(p->format)) {
                         p->copy_blocks_path = TAKE_PTR(temp);
+                        p->copy_blocks_path_is_our_file = true;
                         continue;
                 }
 
@@ -5363,6 +5415,7 @@ static int context_minimize(Context *context) {
                 }
 
                 p->copy_blocks_path = TAKE_PTR(temp);
+                p->copy_blocks_path_is_our_file = true;
         }
 
         return 0;
@@ -5443,7 +5496,7 @@ static int help(void) {
                "                          Ignore partitions not of the specified types\n"
                "     --exclude-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
                "                          Ignore partitions of the specified types\n"
-               "     --skip-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
+               "     --defer-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
                "                          Take partitions of the specified types into account\n"
                "                          but don't populate them yet\n"
                "\nSee the %s for details.\n",
@@ -5483,7 +5536,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SPLIT,
                 ARG_INCLUDE_PARTITIONS,
                 ARG_EXCLUDE_PARTITIONS,
-                ARG_SKIP_PARTITIONS,
+                ARG_DEFER_PARTITIONS,
         };
 
         static const struct option options[] = {
@@ -5513,7 +5566,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "split",                required_argument, NULL, ARG_SPLIT                },
                 { "include-partitions",   required_argument, NULL, ARG_INCLUDE_PARTITIONS   },
                 { "exclude-partitions",   required_argument, NULL, ARG_EXCLUDE_PARTITIONS   },
-                { "skip-partitions",      required_argument, NULL, ARG_SKIP_PARTITIONS      },
+                { "defer-partitions",     required_argument, NULL, ARG_DEFER_PARTITIONS     },
                 {}
         };
 
@@ -5794,8 +5847,8 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
-                case ARG_SKIP_PARTITIONS:
-                        r = parse_partition_types(optarg, &arg_skip_partitions, &arg_n_skip_partitions);
+                case ARG_DEFER_PARTITIONS:
+                        r = parse_partition_types(optarg, &arg_defer_partitions, &arg_n_defer_partitions);
                         if (r < 0)
                                 return r;
 
@@ -6002,12 +6055,11 @@ static int acquire_root_devno(
         return 0;
 }
 
-static int find_root(char **ret, int *ret_fd) {
+static int find_root(Context *context) {
         _cleanup_free_ char *device = NULL;
         int r;
 
-        assert(ret);
-        assert(ret_fd);
+        assert(context);
 
         if (arg_node) {
                 if (arg_empty == EMPTY_CREATE) {
@@ -6022,14 +6074,15 @@ static int find_root(char **ret, int *ret_fd) {
                         if (fd < 0)
                                 return log_error_errno(errno, "Failed to create '%s': %m", arg_node);
 
-                        *ret = TAKE_PTR(s);
-                        *ret_fd = TAKE_FD(fd);
+                        context->node = TAKE_PTR(s);
+                        context->node_is_our_file = true;
+                        context->backing_fd = TAKE_FD(fd);
                         return 0;
                 }
 
                 /* Note that we don't specify a root argument here: if the user explicitly configured a node
                  * we'll take it relative to the host, not the image */
-                r = acquire_root_devno(arg_node, NULL, O_RDONLY|O_CLOEXEC, ret, ret_fd);
+                r = acquire_root_devno(arg_node, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
                 if (r == -EUCLEAN)
                         return btrfs_log_dev_root(LOG_ERR, r, arg_node);
                 if (r < 0)
@@ -6051,7 +6104,8 @@ static int find_root(char **ret, int *ret_fd) {
 
                 FOREACH_STRING(p, "/", "/usr") {
 
-                        r = acquire_root_devno(p, arg_root, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret, ret_fd);
+                        r = acquire_root_devno(p, arg_root, O_RDONLY|O_DIRECTORY|O_CLOEXEC, &context->node,
+                                               &context->backing_fd);
                         if (r < 0) {
                                 if (r == -EUCLEAN)
                                         return btrfs_log_dev_root(LOG_ERR, r, p);
@@ -6063,7 +6117,7 @@ static int find_root(char **ret, int *ret_fd) {
         } else if (r < 0)
                 return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
         else {
-                r = acquire_root_devno(device, NULL, O_RDONLY|O_CLOEXEC, ret, ret_fd);
+                r = acquire_root_devno(device, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
                 if (r == -EUCLEAN)
                         return btrfs_log_dev_root(LOG_ERR, r, device);
                 if (r < 0)
@@ -6269,9 +6323,7 @@ static int run(int argc, char *argv[]) {
         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
         _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
         _cleanup_(context_freep) Context* context = NULL;
-        _cleanup_free_ char *node = NULL;
-        _cleanup_close_ int backing_fd = -1;
-        bool from_scratch, node_is_our_loop = false;
+        bool node_is_our_loop = false;
         int r;
 
         log_show_color(true);
@@ -6337,27 +6389,27 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        r = find_root(&node, &backing_fd);
+        r = find_root(context);
         if (r < 0)
                 return r;
 
         if (arg_size != UINT64_MAX) {
                 r = resize_backing_fd(
-                                node,
-                                &backing_fd,
+                                context->node,
+                                &context->backing_fd,
                                 node_is_our_loop ? arg_image : NULL,
                                 node_is_our_loop ? loop_device : NULL);
                 if (r < 0)
                         return r;
         }
 
-        r = context_load_partition_table(context, node, &backing_fd);
+        r = context_load_partition_table(context);
         if (r == -EHWPOISON)
                 return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't
                             * really an error when called at boot. */
         if (r < 0)
                 return r;
-        from_scratch = r > 0; /* Starting from scratch */
+        context->from_scratch = r > 0; /* Starting from scratch */
 
         if (arg_can_factory_reset) {
                 r = context_can_factory_reset(context);
@@ -6369,7 +6421,7 @@ static int run(int argc, char *argv[]) {
                 return 0;
         }
 
-        r = context_factory_reset(context, from_scratch);
+        r = context_factory_reset(context);
         if (r < 0)
                 return r;
         if (r > 0) {
@@ -6380,16 +6432,11 @@ static int run(int argc, char *argv[]) {
 
                 /* Reload the reduced partition table */
                 context_unload_partition_table(context);
-                r = context_load_partition_table(context, node, &backing_fd);
+                r = context_load_partition_table(context);
                 if (r < 0)
                         return r;
         }
 
-#if 0
-        (void) context_dump_partitions(context, node);
-        putchar('\n');
-#endif
-
         r = context_read_seed(context, arg_root);
         if (r < 0)
                 return r;
@@ -6422,14 +6469,14 @@ static int run(int argc, char *argv[]) {
 
                 assert(arg_size != UINT64_MAX);
                 r = resize_backing_fd(
-                                node,
-                                &backing_fd,
+                                context->node,
+                                &context->backing_fd,
                                 node_is_our_loop ? arg_image : NULL,
                                 node_is_our_loop ? loop_device : NULL);
                 if (r < 0)
                         return r;
 
-                r = context_load_partition_table(context, node, &backing_fd);
+                r = context_load_partition_table(context);
                 if (r < 0)
                         return r;
         }
@@ -6458,9 +6505,9 @@ static int run(int argc, char *argv[]) {
         /* Now calculate where each new partition gets placed */
         context_place_partitions(context);
 
-        (void) context_dump(context, node, /*late=*/ false);
+        (void) context_dump(context, /*late=*/ false);
 
-        r = context_write_partition_table(context, node, from_scratch);
+        r = context_write_partition_table(context);
         if (r < 0)
                 return r;
 
@@ -6468,7 +6515,12 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        (void) context_dump(context, node, /*late=*/ true);
+        (void) context_dump(context, /*late=*/ true);
+
+        context->node = mfree(context->node);
+
+        LIST_FOREACH(partitions, p, context->partitions)
+                p->split_path = mfree(p->split_path);
 
         return 0;
 }
index f8e6582d5bf2dcc47dbf11f17596f5be37a6d1ce..6c3d5775077a07e8a1134397a5b23b8c1b4e960b 100644 (file)
@@ -114,7 +114,7 @@ static int compare_pstore_entries(const PStoreEntry *a, const PStoreEntry *b) {
         return strcmp(a->dirent.d_name, b->dirent.d_name);
 }
 
-static int move_file(PStoreEntry *pe, const char *subdir) {
+static int move_file(PStoreEntry *pe, const char *subdir1, const char *subdir2) {
         _cleanup_free_ char *ifd_path = NULL, *ofd_path = NULL;
         _cleanup_free_ void *field = NULL;
         const char *suffix, *message;
@@ -128,7 +128,7 @@ static int move_file(PStoreEntry *pe, const char *subdir) {
         if (!ifd_path)
                 return log_oom();
 
-        ofd_path = path_join(arg_archivedir, subdir, pe->dirent.d_name);
+        ofd_path = path_join(arg_archivedir, subdir1, subdir2, pe->dirent.d_name);
         if (!ofd_path)
                 return log_oom();
 
@@ -171,153 +171,115 @@ static int move_file(PStoreEntry *pe, const char *subdir) {
         return 0;
 }
 
-static int write_dmesg(const char *dmesg, size_t size, const char *id) {
-        _cleanup_(unlink_and_freep) char *tmp_path = NULL;
+static int append_dmesg(PStoreEntry *pe, const char *subdir1, const char *subdir2) {
+        /* Append dmesg chunk to end, create if needed */
         _cleanup_free_ char *ofd_path = NULL;
         _cleanup_close_ int ofd = -1;
         ssize_t wr;
-        int r;
 
-        if (size == 0)
-                return 0;
+        assert(pe);
 
-        assert(dmesg);
+        if (pe->content_size == 0)
+                return 0;
 
-        ofd_path = path_join(arg_archivedir, id, "dmesg.txt");
+        ofd_path = path_join(arg_archivedir, subdir1, subdir2, "dmesg.txt");
         if (!ofd_path)
                 return log_oom();
 
-        ofd = open_tmpfile_linkable(ofd_path, O_CLOEXEC|O_CREAT|O_TRUNC|O_WRONLY, &tmp_path);
+        ofd = open(ofd_path, O_CREAT|O_NOFOLLOW|O_NOCTTY|O_CLOEXEC|O_APPEND|O_WRONLY, 0640);
         if (ofd < 0)
-                return log_error_errno(ofd, "Failed to open temporary file %s: %m", ofd_path);
-        wr = write(ofd, dmesg, size);
+                return log_error_errno(ofd, "Failed to open file %s: %m", ofd_path);
+        wr = write(ofd, pe->content, pe->content_size);
         if (wr < 0)
                 return log_error_errno(errno, "Failed to store dmesg to %s: %m", ofd_path);
-        if (wr != (ssize_t)size)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to store dmesg to %s. %zu bytes are lost.", ofd_path, size - wr);
-        r = link_tmpfile(ofd, tmp_path, ofd_path);
-        if (r < 0)
-                return log_error_errno(r, "Failed to write temporary file %s: %m", ofd_path);
-        tmp_path = mfree(tmp_path);
+        if ((size_t)wr != pe->content_size)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to store dmesg to %s. %zu bytes are lost.", ofd_path, pe->content_size - wr);
 
         return 0;
 }
 
-static void process_dmesg_files(PStoreList *list) {
+static int process_dmesg_files(PStoreList *list) {
         /* Move files, reconstruct dmesg.txt */
-        _cleanup_free_ char *dmesg = NULL, *dmesg_id = NULL;
-        size_t dmesg_size = 0;
-        bool dmesg_bad = false;
-        PStoreEntry *pe;
+        _cleanup_free_ char *erst_subdir = NULL;
+        uint64_t last_record_id = 0;
+
+        /* When dmesg is written into pstore, it is done so in small chunks, whatever the exchange buffer
+         * size is with the underlying pstore backend (ie. EFI may be ~2KiB), which means an example
+         * pstore with approximately 64KB of storage may have up to roughly 32 dmesg files, some likely
+         * related.
+         *
+         * Here we look at the dmesg filename and try to discern if files are part of a related group,
+         * meaning the same original dmesg.
+         *
+         * The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER,
+         * record id, a 64-bit number.
+         *
+         * Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg.*/
 
-        /* Handle each dmesg file: files processed in reverse
-         * order so as to properly reconstruct original dmesg */
         for (size_t n = list->n_entries; n > 0; n--) {
-                bool move_file_and_continue = false;
-                _cleanup_free_ char *pe_id = NULL;
+                PStoreEntry *pe;
                 char *p;
-                size_t plen;
 
                 pe = &list->entries[n-1];
 
                 if (pe->handled)
                         continue;
-                if (!startswith(pe->dirent.d_name, "dmesg-"))
-                        continue;
-
                 if (endswith(pe->dirent.d_name, ".enc.z")) /* indicates a problem */
-                        move_file_and_continue = true;
-                p = strrchr(pe->dirent.d_name, '-');
-                if (!p)
-                        move_file_and_continue = true;
-
-                if (move_file_and_continue) {
-                        /* A dmesg file on which we do NO additional processing */
-                        (void) move_file(pe, NULL);
-                        continue;
-                }
-
-                /* See if this file is one of a related group of files
-                 * in order to reconstruct dmesg */
-
-                /* When dmesg is written into pstore, it is done so in
-                 * small chunks, whatever the exchange buffer size is
-                 * with the underlying pstore backend (ie. EFI may be
-                 * ~2KiB), which means an example pstore with approximately
-                 * 64KB of storage may have up to roughly 32 dmesg files
-                 * that could be related, depending upon the size of the
-                 * original dmesg.
-                 *
-                 * Here we look at the dmesg filename and try to discern
-                 * if files are part of a related group, meaning the same
-                 * original dmesg.
-                 *
-                 * The two known pstore backends are EFI and ERST. These
-                 * backends store data in the Common Platform Error
-                 * Record, CPER, format. The dmesg- filename contains the
-                 * CPER record id, a 64bit number (in decimal notation).
-                 * In Linux, the record id is encoded with two digits for
-                 * the dmesg part (chunk) number and 3 digits for the
-                 * count number. So allowing an additional digit to
-                 * compensate for advancing time, this code ignores the
-                 * last six digits of the filename in determining the
-                 * record id.
-                 *
-                 * For the EFI backend, the record id encodes an id in the
-                 * upper 32 bits, and a timestamp in the lower 32-bits.
-                 * So ignoring the least significant 6 digits has proven
-                 * to generally identify related dmesg entries.  */
-#define PSTORE_FILENAME_IGNORE 6
-
-                /* determine common portion of record id */
-                ++p; /* move beyond dmesg- */
-                plen = strlen(p);
-                if (plen > PSTORE_FILENAME_IGNORE) {
-                        pe_id = memdup_suffix0(p, plen - PSTORE_FILENAME_IGNORE);
-                        if (!pe_id) {
-                                log_oom();
-                                return;
-                        }
-                } else
-                        pe_id = mfree(pe_id);
-
-                /* Now move file from pstore to archive storage */
-                move_file(pe, pe_id);
-
-                if (dmesg_bad)
                         continue;
-
-                /* If the current record id is NOT the same as the
-                 * previous record id, then start a new dmesg.txt file */
-                if (!streq_ptr(pe_id, dmesg_id)) {
-                        /* Encountered a new dmesg group, close out old one, open new one */
-                        (void) write_dmesg(dmesg, dmesg_size, dmesg_id);
-                        dmesg_size = 0;
-
-                        /* now point dmesg_id to storage of pe_id */
-                        free_and_replace(dmesg_id, pe_id);
-                }
-
-                /* Reconstruction of dmesg is done as a useful courtesy: do not fail, but don't write garbled
-                 * output either. */
-                size_t needed = strlen(pe->dirent.d_name) + strlen(":\n") + pe->content_size + 1;
-                if (!GREEDY_REALLOC(dmesg, dmesg_size + needed)) {
-                        log_oom();
-                        dmesg_bad = true;
+                if (!startswith(pe->dirent.d_name, "dmesg-"))
                         continue;
-                }
-
-                dmesg_size += sprintf(dmesg + dmesg_size, "%s:\n", pe->dirent.d_name);
-                if (pe->content) {
-                        memcpy(dmesg + dmesg_size, pe->content, pe->content_size);
-                        dmesg_size += pe->content_size;
-                }
 
-                pe_id = mfree(pe_id);
+                if ((p = startswith(pe->dirent.d_name, "dmesg-efi-"))) {
+                        /* For the EFI backend, the 3 least significant digits of record id encodes a
+                         * "count" number, the next 2 least significant digits for the dmesg part
+                         * (chunk) number, and the remaining digits as the timestamp.  See
+                         * linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write(). */
+                        _cleanup_free_ char *subdir1 = NULL, *subdir2 = NULL;
+                        size_t plen = strlen(p);
+
+                        if (plen < 6)
+                                continue;
+
+                        /* Extract base record id */
+                        subdir1 = strndup(p, plen - 5);
+                        if (!subdir1)
+                                return log_oom();
+                        /* Extract "count" field */
+                        subdir2 = strndup(p + plen - 3, 3);
+                        if (!subdir2)
+                                return log_oom();
+
+                        /* Now move file from pstore to archive storage */
+                        (void) move_file(pe, subdir1, subdir2);
+
+                        /* Append to the dmesg */
+                        (void) append_dmesg(pe, subdir1, subdir2);
+                } else if ((p = startswith(pe->dirent.d_name, "dmesg-erst-"))) {
+                        /* For the ERST backend, the record is a monotonically increasing number, seeded as
+                         * a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer(). */
+                        uint64_t record_id;
+
+                        if (safe_atou64(p, &record_id) < 0)
+                                continue;
+                        if (last_record_id - 1 != record_id)
+                                /* A discontinuity in the number has been detected, this current record id
+                                 * will become the directory name for all pieces of the dmesg in this
+                                 * series. */
+                                if (free_and_strdup(&erst_subdir, p) < 0)
+                                        return log_oom();
+
+                        /* Now move file from pstore to archive storage */
+                        (void) move_file(pe, erst_subdir, NULL);
+
+                        /* Append to the dmesg */
+                        (void) append_dmesg(pe, erst_subdir, NULL);
+
+                        /* Update, but keep erst_subdir for next file */
+                        last_record_id = record_id;
+                } else
+                        log_debug("Unknown backend, ignoring \"%s\".", pe->dirent.d_name);
         }
-
-        if (!dmesg_bad)
-                (void) write_dmesg(dmesg, dmesg_size, dmesg_id);
+        return 0;
 }
 
 static int list_files(PStoreList *list, const char *sourcepath) {
@@ -393,11 +355,11 @@ static int run(int argc, char *argv[]) {
         typesafe_qsort(list.entries, list.n_entries, compare_pstore_entries);
 
         /* Process known file types */
-        process_dmesg_files(&list);
+        (void) process_dmesg_files(&list);
 
         /* Move left over files out of pstore */
         for (size_t n = 0; n < list.n_entries; n++)
-                move_file(&list.entries[n], NULL);
+                (void) move_file(&list.entries[n], NULL, NULL);
 
         return 0;
 }
index 050c85dab4cd506bb2116ca4bc572cbf5648636d..bd6d0a8d5d22c807be27f187a8e1c120834e35af 100644 (file)
@@ -6,7 +6,7 @@
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         _cleanup_fclose_ FILE *f = NULL;
-        _cleanup_(etc_hosts_free) EtcHosts h = {};
+        _cleanup_(etc_hosts_clear) EtcHosts h = {};
 
         if (!getenv("SYSTEMD_LOG_LEVEL"))
                 log_set_max_level(LOG_CRIT);
diff --git a/src/resolve/fuzz-resource-record.c b/src/resolve/fuzz-resource-record.c
new file mode 100644 (file)
index 0000000..15c4659
--- /dev/null
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "fuzz.h"
+#include "memory-util.h"
+#include "resolved-dns-packet.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+        _cleanup_free_ char *out = NULL; /* out should be freed after f */
+        size_t out_size;
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *copy = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+        if (outside_size_range(size, 0, DNS_PACKET_SIZE_MAX))
+                return 0;
+
+        if (dns_resource_record_new_from_raw(&rr, data, size) < 0)
+                return 0;
+
+        assert_se(copy = dns_resource_record_copy(rr));
+        assert_se(dns_resource_record_equal(copy, rr) > 0);
+
+        assert_se(f = open_memstream_unlocked(&out, &out_size));
+        (void) fprintf(f, "%s", strna(dns_resource_record_to_string(rr)));
+
+        if (dns_resource_record_to_json(rr, &v) < 0)
+                return 0;
+
+        (void) json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, f, NULL);
+        (void) dns_resource_record_to_wire_format(rr, false);
+        (void) dns_resource_record_to_wire_format(rr, true);
+
+        return 0;
+}
index e11aefce7acb7418b2fed984ab926e5924a12d92..cd02c880396eea4cc612f9f43c264293f6bdfecd 100644 (file)
@@ -237,6 +237,11 @@ fuzzers += [
           libshared],
          [lib_openssl_or_gcrypt,
           libm]],
+        [files('fuzz-resource-record.c'),
+         [libsystemd_resolve_core,
+          libshared],
+         [lib_openssl_or_gcrypt,
+          libm]],
 ]
 
 systemd_resolved_sources += files('resolved.c')
index d47cdbbd8e2753637831f3824079f5ac22bfcaf4..d3175b1b9de30c265ae23c520b344f8545e11a0b 100644 (file)
@@ -1137,12 +1137,15 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
                 break;
 
         default:
-                t = hexmem(rr->generic.data, rr->generic.data_size);
-                if (!t)
-                        return NULL;
-
                 /* Format as documented in RFC 3597, Section 5 */
-                r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.data_size, t);
+                if (rr->generic.data_size == 0)
+                        r = asprintf(&s, "%s \\# 0", k);
+                else {
+                        t = hexmem(rr->generic.data, rr->generic.data_size);
+                        if (!t)
+                                return NULL;
+                        r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.data_size, t);
+                }
                 if (r < 0)
                         return NULL;
                 break;
@@ -1765,36 +1768,30 @@ int dns_resource_record_get_cname_target(DnsResourceKey *key, DnsResourceRecord
         return 0;
 }
 
-DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
-        DnsTxtItem *n;
-
-        if (!i)
-                return NULL;
-
-        n = i->items_next;
+DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *first) {
+        LIST_FOREACH(items, i, first)
+                free(i);
 
-        free(i);
-        return dns_txt_item_free_all(n);
+        return NULL;
 }
 
 bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
+        DnsTxtItem *bb = b;
 
         if (a == b)
                 return true;
 
-        if (!a != !b)
-                return false;
+        LIST_FOREACH(items, aa, a) {
+                if (!bb)
+                        return false;
 
-        if (!a)
-                return true;
-
-        if (a->length != b->length)
-                return false;
+                if (memcmp_nn(aa->data, aa->length, bb->data, bb->length) != 0)
+                        return false;
 
-        if (memcmp(a->data, b->data, a->length) != 0)
-                return false;
+                bb = bb->items_next;
+        }
 
-        return dns_txt_item_equal(a->items_next, b->items_next);
+        return !bb;
 }
 
 DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) {
@@ -1804,10 +1801,8 @@ DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) {
                 DnsTxtItem *j;
 
                 j = memdup(i, offsetof(DnsTxtItem, data) + i->length + 1);
-                if (!j) {
-                        dns_txt_item_free_all(copy);
-                        return NULL;
-                }
+                if (!j)
+                        return dns_txt_item_free_all(copy);
 
                 LIST_INSERT_AFTER(items, copy, end, j);
                 end = j;
@@ -1819,6 +1814,8 @@ DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) {
 int dns_txt_item_new_empty(DnsTxtItem **ret) {
         DnsTxtItem *i;
 
+        assert(ret);
+
         /* RFC 6763, section 6.1 suggests to treat
          * empty TXT RRs as equivalent to a TXT record
          * with a single empty string. */
@@ -1828,7 +1825,6 @@ int dns_txt_item_new_empty(DnsTxtItem **ret) {
                 return -ENOMEM;
 
         *ret = i;
-
         return 0;
 }
 
@@ -1886,6 +1882,36 @@ static int type_bitmap_to_json(Bitmap *b, JsonVariant **ret) {
         return 0;
 }
 
+static int txt_to_json(DnsTxtItem *items, JsonVariant **ret) {
+        JsonVariant **elements = NULL;
+        size_t n = 0;
+        int r;
+
+        assert(ret);
+
+        LIST_FOREACH(items, i, items) {
+                if (!GREEDY_REALLOC(elements, n + 1)) {
+                        r = -ENOMEM;
+                        goto finalize;
+                }
+
+                r = json_variant_new_octescape(elements + n, i->data, i->length);
+                if (r < 0)
+                        goto finalize;
+
+                n++;
+        }
+
+        r = json_variant_new_array(ret, elements, n);
+
+finalize:
+        for (size_t i = 0; i < n; i++)
+                json_variant_unref(elements[i]);
+
+        free(elements);
+        return r;
+}
+
 int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret) {
         _cleanup_(json_variant_unrefp) JsonVariant *k = NULL;
         int r;
@@ -1928,23 +1954,9 @@ int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret) {
         case DNS_TYPE_TXT: {
                 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
 
-                LIST_FOREACH(items, i, rr->txt.items) {
-                        _cleanup_(json_variant_unrefp) JsonVariant *b = NULL;
-
-                        r = json_variant_new_octescape(&b, i->data, i->length);
-                        if (r < 0)
-                                return r;
-
-                        r = json_variant_append_array(&l, b);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (!l) {
-                        r = json_variant_new_array(&l, NULL, 0);
-                        if (r < 0)
-                                return r;
-                }
+                r = txt_to_json(rr->txt.items, &l);
+                if (r < 0)
+                        return r;
 
                 return json_build(ret,
                                   JSON_BUILD_OBJECT(
index 8a58db4a21a1f630032f5f5696bd096572e943ad..cf0e2be5fa0b86aff1c7a5829d6e1f3a6227f4aa 100644 (file)
 /* Recheck /etc/hosts at most once every 2s */
 #define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC)
 
-static void etc_hosts_item_free(EtcHostsItem *item) {
-        strv_free(item->names);
-        free(item);
+static EtcHostsItemByAddress *etc_hosts_item_by_address_free(EtcHostsItemByAddress *item) {
+        if (!item)
+                return NULL;
+
+        set_free(item->names);
+        return mfree(item);
 }
 
-static void etc_hosts_item_by_name_free(EtcHostsItemByName *item) {
+DEFINE_TRIVIAL_CLEANUP_FUNC(EtcHostsItemByAddress*, etc_hosts_item_by_address_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        by_address_hash_ops,
+        struct in_addr_data,
+        in_addr_data_hash_func,
+        in_addr_data_compare_func,
+        EtcHostsItemByAddress,
+        etc_hosts_item_by_address_free);
+
+static EtcHostsItemByName *etc_hosts_item_by_name_free(EtcHostsItemByName *item) {
+        if (!item)
+                return NULL;
+
         free(item->name);
-        free(item->addresses);
-        free(item);
+        set_free(item->addresses);
+        return mfree(item);
 }
 
-void etc_hosts_free(EtcHosts *hosts) {
-        hosts->by_address = hashmap_free_with_destructor(hosts->by_address, etc_hosts_item_free);
-        hosts->by_name = hashmap_free_with_destructor(hosts->by_name, etc_hosts_item_by_name_free);
-        hosts->no_address = set_free_free(hosts->no_address);
+DEFINE_TRIVIAL_CLEANUP_FUNC(EtcHostsItemByName*, etc_hosts_item_by_name_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        by_name_hash_ops,
+        char,
+        dns_name_hash_func,
+        dns_name_compare_func,
+        EtcHostsItemByName,
+        etc_hosts_item_by_name_free);
+
+void etc_hosts_clear(EtcHosts *hosts) {
+        assert(hosts);
+
+        hosts->by_address = hashmap_free(hosts->by_address);
+        hosts->by_name = hashmap_free(hosts->by_name);
+        hosts->no_address = set_free(hosts->no_address);
 }
 
 void manager_etc_hosts_flush(Manager *m) {
-        etc_hosts_free(&m->etc_hosts);
+        etc_hosts_clear(&m->etc_hosts);
         m->etc_hosts_stat = (struct stat) {};
 }
 
@@ -44,7 +72,7 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
         _cleanup_free_ char *address_str = NULL;
         struct in_addr_data address = {};
         bool found = false;
-        EtcHostsItem *item;
+        EtcHostsItemByAddress *item;
         int r;
 
         assert(hosts);
@@ -75,23 +103,21 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
 
                 item = hashmap_get(hosts->by_address, &address);
                 if (!item) {
-                        r = hashmap_ensure_allocated(&hosts->by_address, &in_addr_data_hash_ops);
-                        if (r < 0)
-                                return log_oom();
+                        _cleanup_(etc_hosts_item_by_address_freep) EtcHostsItemByAddress *new_item = NULL;
 
-                        item = new(EtcHostsItem, 1);
-                        if (!item)
+                        new_item = new(EtcHostsItemByAddress, 1);
+                        if (!new_item)
                                 return log_oom();
 
-                        *item = (EtcHostsItem) {
+                        *new_item = (EtcHostsItemByAddress) {
                                 .address = address,
                         };
 
-                        r = hashmap_put(hosts->by_address, &item->address, item);
-                        if (r < 0) {
-                                free(item);
+                        r = hashmap_ensure_put(&hosts->by_address, &by_address_hash_ops, &new_item->address, new_item);
+                        if (r < 0)
                                 return log_oom();
-                        }
+
+                        item = TAKE_PTR(new_item);
                 }
         }
 
@@ -105,8 +131,6 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
                 if (r == 0)
                         break;
 
-                found = true;
-
                 r = dns_name_is_valid_ldh(name);
                 if (r <= 0) {
                         if (r < 0)
@@ -116,48 +140,62 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
                         continue;
                 }
 
+                found = true;
+
                 if (!item) {
                         /* Optimize the case where we don't need to store any addresses, by storing
                          * only the name in a dedicated Set instead of the hashmap */
 
-                        r = set_ensure_consume(&hosts->no_address, &dns_name_hash_ops, TAKE_PTR(name));
+                        r = set_ensure_consume(&hosts->no_address, &dns_name_hash_ops_free, TAKE_PTR(name));
                         if (r < 0)
-                                return r;
+                                return log_oom();
 
                         continue;
                 }
 
-                r = strv_extend_with_size(&item->names, &item->n_names, name);
-                if (r < 0)
-                        return log_oom();
-
                 bn = hashmap_get(hosts->by_name, name);
                 if (!bn) {
-                        r = hashmap_ensure_allocated(&hosts->by_name, &dns_name_hash_ops);
-                        if (r < 0)
+                        _cleanup_(etc_hosts_item_by_name_freep) EtcHostsItemByName *new_item = NULL;
+                        _cleanup_free_ char *name_copy = NULL;
+
+                        name_copy = strdup(name);
+                        if (!name_copy)
                                 return log_oom();
 
-                        bn = new0(EtcHostsItemByName, 1);
-                        if (!bn)
+                        new_item = new(EtcHostsItemByName, 1);
+                        if (!new_item)
                                 return log_oom();
 
-                        r = hashmap_put(hosts->by_name, name, bn);
-                        if (r < 0) {
-                                free(bn);
+                        *new_item = (EtcHostsItemByName) {
+                                .name = TAKE_PTR(name_copy),
+                        };
+
+                        r = hashmap_ensure_put(&hosts->by_name, &by_name_hash_ops, new_item->name, new_item);
+                        if (r < 0)
                                 return log_oom();
-                        }
 
-                        bn->name = TAKE_PTR(name);
+                        bn = TAKE_PTR(new_item);
                 }
 
-                if (!GREEDY_REALLOC(bn->addresses, bn->n_addresses + 1))
-                        return log_oom();
+                if (!set_contains(bn->addresses, &address)) {
+                        _cleanup_free_ struct in_addr_data *address_copy = NULL;
+
+                        address_copy = newdup(struct in_addr_data, &address, 1);
+                        if (!address_copy)
+                                return log_oom();
 
-                bn->addresses[bn->n_addresses++] = &item->address;
+                        r = set_ensure_consume(&bn->addresses, &in_addr_data_hash_ops_free, TAKE_PTR(address_copy));
+                        if (r < 0)
+                                return log_oom();
+                }
+
+                r = set_ensure_consume(&item->names, &dns_name_hash_ops_free, TAKE_PTR(name));
+                if (r < 0)
+                        return log_oom();
         }
 
         if (!found)
-                log_warning("/etc/hosts:%u: line is missing any hostnames", nr);
+                log_warning("/etc/hosts:%u: line is missing any valid hostnames", nr);
 
         return 0;
 }
@@ -179,8 +217,6 @@ static void strip_localhost(EtcHosts *hosts) {
                 },
         };
 
-        EtcHostsItem *item;
-
         assert(hosts);
 
         /* Removes the 'localhost' entry from what we loaded. But only if the mapping is exclusively between
@@ -191,7 +227,9 @@ static void strip_localhost(EtcHosts *hosts) {
          * mappings.  */
 
         for (size_t j = 0; j < ELEMENTSOF(local_in_addrs); j++) {
-                bool all_localhost, in_order;
+                bool all_localhost, all_local_address;
+                EtcHostsItemByAddress *item;
+                const char *name;
 
                 item = hashmap_get(hosts->by_address, local_in_addrs + j);
                 if (!item)
@@ -199,8 +237,8 @@ static void strip_localhost(EtcHosts *hosts) {
 
                 /* Check whether all hostnames the loopback address points to are localhost ones */
                 all_localhost = true;
-                STRV_FOREACH(i, item->names)
-                        if (!is_localhost(*i)) {
+                SET_FOREACH(name, item->names)
+                        if (!is_localhost(name)) {
                                 all_localhost = false;
                                 break;
                         }
@@ -210,48 +248,40 @@ static void strip_localhost(EtcHosts *hosts) {
 
                 /* Now check if the names listed for this address actually all point back just to this
                  * address (or the other loopback address). If not, let's stay away from this too. */
-                in_order = true;
-                STRV_FOREACH(i, item->names) {
+                all_local_address = true;
+                SET_FOREACH(name, item->names) {
                         EtcHostsItemByName *n;
-                        bool all_local_address;
+                        struct in_addr_data *a;
 
-                        n = hashmap_get(hosts->by_name, *i);
+                        n = hashmap_get(hosts->by_name, name);
                         if (!n) /* No reverse entry? Then almost certainly the entry already got deleted from
                                  * the previous iteration of this loop, i.e. via the other protocol */
                                 break;
 
                         /* Now check if the addresses of this item are all localhost addresses */
-                        all_local_address = true;
-                        for (size_t m = 0; m < n->n_addresses; m++)
-                                if (!in_addr_is_localhost(n->addresses[m]->family, &n->addresses[m]->address)) {
+                        SET_FOREACH(a, n->addresses)
+                                if (!in_addr_is_localhost(a->family, &a->address)) {
                                         all_local_address = false;
                                         break;
                                 }
 
-                        if (!all_local_address) {
-                                in_order = false;
+                        if (!all_local_address)
                                 break;
-                        }
                 }
 
-                if (!in_order)
+                if (!all_local_address)
                         continue;
 
-                STRV_FOREACH(i, item->names) {
-                        EtcHostsItemByName *n;
-
-                        n = hashmap_remove(hosts->by_name, *i);
-                        if (n)
-                                etc_hosts_item_by_name_free(n);
-                }
+                SET_FOREACH(name, item->names)
+                        etc_hosts_item_by_name_free(hashmap_remove(hosts->by_name, name));
 
                 assert_se(hashmap_remove(hosts->by_address, local_in_addrs + j) == item);
-                etc_hosts_item_free(item);
+                etc_hosts_item_by_address_free(item);
         }
 }
 
 int etc_hosts_parse(EtcHosts *hosts, FILE *f) {
-        _cleanup_(etc_hosts_free) EtcHosts t = {};
+        _cleanup_(etc_hosts_clear) EtcHosts t = {};
         unsigned nr = 0;
         int r;
 
@@ -282,7 +312,7 @@ int etc_hosts_parse(EtcHosts *hosts, FILE *f) {
 
         strip_localhost(&t);
 
-        etc_hosts_free(hosts);
+        etc_hosts_clear(hosts);
         *hosts = t;
         t = (EtcHosts) {}; /* prevent cleanup */
         return 0;
@@ -341,88 +371,97 @@ static int manager_etc_hosts_read(Manager *m) {
         return 1;
 }
 
-int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
-        bool found_a = false, found_aaaa = false;
-        struct in_addr_data k = {};
-        EtcHostsItemByName *bn;
-        DnsResourceKey *t;
-        const char *name;
-        unsigned i;
+static int etc_hosts_lookup_by_address(
+                EtcHosts *hosts,
+                DnsQuestion *q,
+                const char *name,
+                const struct in_addr_data *address,
+                DnsAnswer **answer) {
+
+        DnsResourceKey *t, *found_ptr = NULL;
+        EtcHostsItemByAddress *item;
         int r;
 
-        assert(m);
+        assert(hosts);
         assert(q);
+        assert(name);
+        assert(address);
         assert(answer);
 
-        if (!m->read_etc_hosts)
+        item = hashmap_get(hosts->by_address, address);
+        if (!item)
                 return 0;
 
-        (void) manager_etc_hosts_read(m);
+        /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data
+         * we'll only return if the request was for PTR. */
 
-        name = dns_question_first_name(q);
-        if (!name)
-                return 0;
+        DNS_QUESTION_FOREACH(t, q) {
+                if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY))
+                        continue;
+                if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
+                        continue;
 
-        r = dns_name_address(name, &k.family, &k.address);
-        if (r > 0) {
-                EtcHostsItem *item;
-                DnsResourceKey *found_ptr = NULL;
+                r = dns_name_equal(dns_resource_key_name(t), name);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        found_ptr = t;
+                        break;
+                }
+        }
 
-                item = hashmap_get(m->etc_hosts.by_address, &k);
-                if (!item)
-                        return 0;
+        if (found_ptr) {
+                const char *n;
+
+                r = dns_answer_reserve(answer, set_size(item->names));
+                if (r < 0)
+                        return r;
 
-                /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data
-                 * we'll only return if the request was for PTR. */
+                SET_FOREACH(n, item->names) {
+                        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
 
-                DNS_QUESTION_FOREACH(t, q) {
-                        if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY))
-                                continue;
-                        if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
-                                continue;
+                        rr = dns_resource_record_new(found_ptr);
+                        if (!rr)
+                                return -ENOMEM;
 
-                        r = dns_name_equal(dns_resource_key_name(t), name);
-                        if (r < 0)
-                                return r;
-                        if (r > 0) {
-                                found_ptr = t;
-                                break;
-                        }
-                }
+                        rr->ptr.name = strdup(n);
+                        if (!rr->ptr.name)
+                                return -ENOMEM;
 
-                if (found_ptr) {
-                        r = dns_answer_reserve(answer, item->n_names);
+                        r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED, NULL);
                         if (r < 0)
                                 return r;
+                }
+        }
 
-                        STRV_FOREACH(n, item->names) {
-                                _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
-                                rr = dns_resource_record_new(found_ptr);
-                                if (!rr)
-                                        return -ENOMEM;
+        return 1;
+}
 
-                                rr->ptr.name = strdup(*n);
-                                if (!rr->ptr.name)
-                                        return -ENOMEM;
+static int etc_hosts_lookup_by_name(
+                EtcHosts *hosts,
+                DnsQuestion *q,
+                const char *name,
+                DnsAnswer **answer) {
 
-                                r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED, NULL);
-                                if (r < 0)
-                                        return r;
-                        }
-                }
+        bool found_a = false, found_aaaa = false;
+        const struct in_addr_data *a;
+        EtcHostsItemByName *item;
+        DnsResourceKey *t;
+        int r;
 
-                return 1;
-        }
+        assert(hosts);
+        assert(q);
+        assert(name);
+        assert(answer);
 
-        bn = hashmap_get(m->etc_hosts.by_name, name);
-        if (bn) {
-                r = dns_answer_reserve(answer, bn->n_addresses);
+        item = hashmap_get(hosts->by_name, name);
+        if (item) {
+                r = dns_answer_reserve(answer, set_size(item->addresses));
                 if (r < 0)
                         return r;
         } else {
                 /* Check if name was listed with no address. If yes, continue to return an answer. */
-                if (!set_contains(m->etc_hosts.no_address, name))
+                if (!set_contains(hosts->no_address, name))
                         return 0;
         }
 
@@ -447,14 +486,14 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
                         break;
         }
 
-        for (i = 0; bn && i < bn->n_addresses; i++) {
+        SET_FOREACH(a, item ? item->addresses : NULL) {
                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
 
-                if ((!found_a && bn->addresses[i]->family == AF_INET) ||
-                    (!found_aaaa && bn->addresses[i]->family == AF_INET6))
+                if ((!found_a && a->family == AF_INET) ||
+                    (!found_aaaa && a->family == AF_INET6))
                         continue;
 
-                r = dns_resource_record_new_address(&rr, bn->addresses[i]->family, &bn->addresses[i]->address, bn->name);
+                r = dns_resource_record_new_address(&rr, a->family, &a->address, item->name);
                 if (r < 0)
                         return r;
 
@@ -465,3 +504,26 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
 
         return found_a || found_aaaa;
 }
+
+int manager_etc_hosts_lookup(Manager *m, DnsQuestion *q, DnsAnswer **answer) {
+        struct in_addr_data k;
+        const char *name;
+
+        assert(m);
+        assert(q);
+        assert(answer);
+
+        if (!m->read_etc_hosts)
+                return 0;
+
+        (void) manager_etc_hosts_read(m);
+
+        name = dns_question_first_name(q);
+        if (!name)
+                return 0;
+
+        if (dns_name_address(name, &k.family, &k.address) > 0)
+                return etc_hosts_lookup_by_address(&m->etc_hosts, q, name, &k, answer);
+
+        return etc_hosts_lookup_by_name(&m->etc_hosts, q, name, answer);
+}
index df66e714da5e5a773e772cc5cf549038fc9461b8..e1a7249f29866344239e35b8da353e50cbec3e29 100644 (file)
@@ -5,22 +5,18 @@
 #include "resolved-dns-question.h"
 #include "resolved-dns-answer.h"
 
-typedef struct EtcHostsItem {
+typedef struct EtcHostsItemByAddress {
         struct in_addr_data address;
-
-        char **names;
-        size_t n_names;
-} EtcHostsItem;
+        Set *names;
+} EtcHostsItemByAddress;
 
 typedef struct EtcHostsItemByName {
         char *name;
-
-        struct in_addr_data **addresses;
-        size_t n_addresses;
+        Set *addresses;
 } EtcHostsItemByName;
 
 int etc_hosts_parse(EtcHosts *hosts, FILE *f);
-void etc_hosts_free(EtcHosts *hosts);
+void etc_hosts_clear(EtcHosts *hosts);
 
 void manager_etc_hosts_flush(Manager *m);
 int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
index d6cd184b5e2d8ed4fec6d757745fae8112964a79..19b2991a356ae26ec00b2c07ed40d2ee1332a21f 100644 (file)
@@ -23,17 +23,15 @@ TEST(parse_etc_hosts_system) {
                 return;
         }
 
-        _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+        _cleanup_(etc_hosts_clear) EtcHosts hosts = {};
         assert_se(etc_hosts_parse(&hosts, f) == 0);
 }
 
-#define address_equal_4(_addr, _address)                                \
-        ((_addr)->family == AF_INET &&                                  \
-         !memcmp(&(_addr)->address.in, &(struct in_addr) { .s_addr = (_address) }, 4))
+#define has_4(_set, _address_str)                                       \
+        set_contains(_set, &(struct in_addr_data) { .family = AF_INET, .address.in = { .s_addr = inet_addr(_address_str) } })
 
-#define address_equal_6(_addr, ...)                                     \
-        ((_addr)->family == AF_INET6 &&                                 \
-         !memcmp(&(_addr)->address.in6, &(struct in6_addr) { .s6_addr = __VA_ARGS__}, 16) )
+#define has_6(_set, ...)                                           \
+        set_contains(_set, &(struct in_addr_data) { .family = AF_INET6, .address.in6 = { .s6_addr = __VA_ARGS__ } })
 
 TEST(parse_etc_hosts) {
         _cleanup_(unlink_tempfilep) char
@@ -67,38 +65,34 @@ TEST(parse_etc_hosts) {
         assert_se(fflush_and_check(f) >= 0);
         rewind(f);
 
-        _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+        _cleanup_(etc_hosts_clear) EtcHosts hosts = {};
         assert_se(etc_hosts_parse(&hosts, f) == 0);
 
         EtcHostsItemByName *bn;
         assert_se(bn = hashmap_get(hosts.by_name, "some.where"));
-        assert_se(bn->n_addresses == 3);
-        assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 3);
-        assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.4")));
-        assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.5")));
-        assert_se(address_equal_6(bn->addresses[2], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
+        assert_se(set_size(bn->addresses) == 3);
+        assert_se(has_4(bn->addresses, "1.2.3.4"));
+        assert_se(has_4(bn->addresses, "1.2.3.5"));
+        assert_se(has_6(bn->addresses, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
 
         assert_se(bn = hashmap_get(hosts.by_name, "dash"));
-        assert_se(bn->n_addresses == 1);
-        assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 1);
-        assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6")));
+        assert_se(set_size(bn->addresses) == 1);
+        assert_se(has_4(bn->addresses, "1.2.3.6"));
 
         assert_se(bn = hashmap_get(hosts.by_name, "dash-dash.where-dash"));
-        assert_se(bn->n_addresses == 1);
-        assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 1);
-        assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6")));
+        assert_se(set_size(bn->addresses) == 1);
+        assert_se(has_4(bn->addresses, "1.2.3.6"));
 
         /* See https://tools.ietf.org/html/rfc1035#section-2.3.1 */
         FOREACH_STRING(s, "bad-dash-", "-bad-dash", "-bad-dash.bad-")
                 assert_se(!hashmap_get(hosts.by_name, s));
 
         assert_se(bn = hashmap_get(hosts.by_name, "before.comment"));
-        assert_se(bn->n_addresses == 4);
-        assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 4);
-        assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.9")));
-        assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.10")));
-        assert_se(address_equal_4(bn->addresses[2], inet_addr("1.2.3.11")));
-        assert_se(address_equal_4(bn->addresses[3], inet_addr("1.2.3.12")));
+        assert_se(set_size(bn->addresses) == 4);
+        assert_se(has_4(bn->addresses, "1.2.3.9"));
+        assert_se(has_4(bn->addresses, "1.2.3.10"));
+        assert_se(has_4(bn->addresses, "1.2.3.11"));
+        assert_se(has_4(bn->addresses, "1.2.3.12"));
 
         assert_se(!hashmap_get(hosts.by_name, "within.comment"));
         assert_se(!hashmap_get(hosts.by_name, "within.comment2"));
@@ -113,9 +107,8 @@ TEST(parse_etc_hosts) {
         assert_se(!set_contains(hosts.no_address, "multi.colon"));
 
         assert_se(bn = hashmap_get(hosts.by_name, "some.other"));
-        assert_se(bn->n_addresses == 1);
-        assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 1);
-        assert_se(address_equal_6(bn->addresses[0], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
+        assert_se(set_size(bn->addresses) == 1);
+        assert_se(has_6(bn->addresses, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
 
         assert_se( set_contains(hosts.no_address, "some.where"));
         assert_se( set_contains(hosts.no_address, "some.other"));
@@ -124,7 +117,7 @@ TEST(parse_etc_hosts) {
 }
 
 static void test_parse_file_one(const char *fname) {
-        _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+        _cleanup_(etc_hosts_clear) EtcHosts hosts = {};
         _cleanup_fclose_ FILE *f;
 
         log_info("/* %s(\"%s\") */", __func__, fname);
index ab8cdc0ff96db92e08ddc6bc3d9617e69d305405..b35d952fab0d3d0c5a7107c07f28e47cbcd8c492 100755 (executable)
@@ -19,21 +19,21 @@ case "$command" in
 
     remove-system-units)
         if [ -d /run/systemd/system ]; then
-            systemctl --no-reload disable --now "$@"
+            systemctl --no-reload disable --now --no-warn "$@"
         else
-            systemctl --no-reload disable "$@"
+            systemctl --no-reload disable --no-warn "$@"
         fi
         ;;
 
     remove-user-units)
-        systemctl --global disable "$@"
+        systemctl --global disable --no-warn "$@"
 
         [ -d /run/systemd/system ] || exit 0
 
         users=$(systemctl list-units 'user@*' --legend=no | sed -n -r 's/.*user@([0-9]+).service.*/\1/p')
         for user in $users; do
             SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT}} \
-                    systemctl --user -M "$user@" disable --now "$@" &
+                    systemctl --user -M "$user@" disable --now --no-warn "$@" &
         done
         wait
         ;;
index c6c673984d20672f094539d84063089794757f97..b734ee1e0c97f1d7095fe0ce3e2db6dc51884812 100644 (file)
@@ -90,6 +90,7 @@ int add_base_acls_if_needed(acl_t *acl_p, const char *path) {
         _cleanup_(acl_freep) acl_t basic = NULL;
 
         assert(acl_p);
+        assert(path);
 
         for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i);
              r > 0;
@@ -208,12 +209,16 @@ int acl_search_groups(const char *path, char ***ret_groups) {
         return ret;
 }
 
-int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
+int parse_acl(const char *text, acl_t *ret_acl_access, acl_t *ret_acl_default, bool want_mask) {
         _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not freed */
         _cleanup_strv_free_ char **split = NULL;
         int r = -EINVAL;
         _cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL;
 
+        assert(text);
+        assert(ret_acl_access);
+        assert(ret_acl_default);
+
         split = strv_split(text, ",");
         if (!split)
                 return -ENOMEM;
@@ -266,8 +271,8 @@ int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want
                 }
         }
 
-        *acl_access = TAKE_PTR(a_acl);
-        *acl_default = TAKE_PTR(d_acl);
+        *ret_acl_access = TAKE_PTR(a_acl);
+        *ret_acl_default = TAKE_PTR(d_acl);
 
         return 0;
 }
@@ -322,7 +327,7 @@ static int acl_entry_equal(acl_entry_t a, acl_entry_t b) {
         }
 }
 
-static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) {
+static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *ret) {
         acl_entry_t i;
         int r;
 
@@ -334,36 +339,40 @@ static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) {
                 if (r < 0)
                         return r;
                 if (r > 0) {
-                        *out = i;
-                        return 1;
+                        if (ret)
+                                *ret = i;
+                        return 0;
                 }
         }
         if (r < 0)
                 return -errno;
-        return 0;
+
+        return -ENOENT;
 }
 
-int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
-        _cleanup_(acl_freep) acl_t old;
+int acls_for_file(const char *path, acl_type_t type, acl_t acl, acl_t *ret) {
+        _cleanup_(acl_freep) acl_t applied = NULL;
         acl_entry_t i;
         int r;
 
-        old = acl_get_file(path, type);
-        if (!old)
+        assert(path);
+
+        applied = acl_get_file(path, type);
+        if (!applied)
                 return -errno;
 
-        for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
+        for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
              r > 0;
-             r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
+             r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
 
                 acl_entry_t j;
 
-                r = find_acl_entry(old, i, &j);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        if (acl_create_entry(&old, &j) < 0)
+                r = find_acl_entry(applied, i, &j);
+                if (r == -ENOENT) {
+                        if (acl_create_entry(&applied, &j) < 0)
                                 return -errno;
+                } else if (r < 0)
+                        return r;
 
                 if (acl_copy_entry(j, i) < 0)
                         return -errno;
@@ -371,7 +380,8 @@ int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
         if (r < 0)
                 return -errno;
 
-        *acl = TAKE_PTR(old);
+        if (ret)
+                *ret = TAKE_PTR(applied);
 
         return 0;
 }
index 03595c665152ba5e6936a13be08ef752af7c8cde..d3a341fbe612f4062f5d8750e92fb2f40c106905 100644 (file)
@@ -15,8 +15,8 @@ int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry);
 int calc_acl_mask_if_needed(acl_t *acl_p);
 int add_base_acls_if_needed(acl_t *acl_p, const char *path);
 int acl_search_groups(const char* path, char ***ret_groups);
-int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask);
-int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl);
+int parse_acl(const char *text, acl_t *ret_acl_access, acl_t *ret_acl_default, bool want_mask);
+int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *ret);
 int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask);
 
 /* acl_free takes multiple argument types.
index 1ad5ddd50327c81593519c9374f234a5d5a500f2..0b1b9c6861b13f14a7c3aef75a075a0203ca3b5a 100644 (file)
@@ -687,7 +687,7 @@ static int create_socket(char **ret) {
                 return r;
         sa_len = r;
 
-        RUN_WITH_UMASK(0177)
+        WITH_UMASK(0177)
                 if (bind(fd, &sa.sa, sa_len) < 0)
                         return -errno;
 
index c2902d35082e40b9d26a69269a64e634885ae518..9b7270ae535b9bac4b83a160389e319ea7498deb 100644 (file)
@@ -178,7 +178,7 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
                         continue;
                 }
 
-                RUN_WITH_UMASK(0000)
+                WITH_UMASK(0000)
                         r = mkdirat(fd, table[i].dir, table[i].mode);
                 if (r < 0) {
                         log_full_errno(IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure ? LOG_DEBUG : LOG_ERR, errno,
index 724d7f27d999ac2324f3af808aae84b568ce0900..a26175474b369932675c796b16e842f24e973874 100644 (file)
@@ -5,10 +5,30 @@
 #include <sys/vfs.h>
 
 #include "binfmt-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "missing_magic.h"
 #include "stat-util.h"
 
+int binfmt_mounted(void) {
+        _cleanup_close_ int fd = -EBADF;
+        int r;
+
+        fd = RET_NERRNO(open("/proc/sys/fs/binfmt_misc", O_CLOEXEC | O_DIRECTORY | O_PATH));
+        if (fd == -ENOENT)
+                return false;
+        if (fd < 0)
+                return fd;
+
+        r = fd_is_fs_type(fd, BINFMTFS_MAGIC);
+        if (r <= 0)
+                return r;
+
+        return access_fd(fd, W_OK) >= 0;
+}
+
 int disable_binfmt(void) {
         int r;
 
@@ -18,13 +38,13 @@ int disable_binfmt(void) {
          * We are a bit careful here, since binfmt_misc might still be an autofs which we don't want to
          * trigger. */
 
-        r = path_is_fs_type("/proc/sys/fs/binfmt_misc", BINFMTFS_MAGIC);
-        if (r == 0 || r == -ENOENT) {
-                log_debug("binfmt_misc is not mounted, not detaching entries.");
-                return 0;
-        }
+        r = binfmt_mounted();
         if (r < 0)
                 return log_warning_errno(r, "Failed to determine whether binfmt_misc is mounted: %m");
+        if (r == 0) {
+                log_debug("binfmt_misc is not mounted in read-write mode, not detaching entries.");
+                return 0;
+        }
 
         r = write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", WRITE_STRING_FILE_DISABLE_BUFFER);
         if (r < 0)
index 2f008d12ffe3d1aa18592a4ddcb0a5a7ac8e5fe8..13f4548b7ce8c4283502e61fd52d18ca74675284 100644 (file)
@@ -1,4 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+int binfmt_mounted(void);
 int disable_binfmt(void);
index 56274a0d4d35ab200daf451cda411e46cb3051ed..4cced23adcbd3303f4c48a1ba537d6c0d38ddee2 100644 (file)
@@ -980,6 +980,8 @@ static int boot_config_find(const BootConfig *config, const char *id) {
         if (id[0] == '@') {
                 if (!strcaseeq(id, "@saved"))
                         return -1;
+                if (!config->entry_selected)
+                        return -1;
                 id = config->entry_selected;
         }
 
@@ -1402,6 +1404,8 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
         assert(config);
 
         if (!FLAGS_SET(json_format, JSON_FORMAT_OFF)) {
+                _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+
                 for (size_t i = 0; i < config->n_entries; i++) {
                         _cleanup_free_ char *opts = NULL;
                         const BootEntry *e = config->entries + i;
@@ -1441,9 +1445,13 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
                         if (r < 0)
                                 return log_oom();
 
-                        json_variant_dump(v, json_format, stdout, NULL);
+                        r = json_variant_append_array(&array, v);
+                        if (r < 0)
+                                return log_oom();
                 }
 
+                json_variant_dump(array, json_format | JSON_FORMAT_EMPTY_ARRAY, NULL, NULL);
+
         } else {
                 for (size_t n = 0; n < config->n_entries; n++) {
                         r = show_boot_entry(
index ba02de17f8523876c54e790ba59393f4f025e276..b1aaf746cf91e251e7aa70c0c1e0c62de8db3fae 100644 (file)
@@ -511,10 +511,7 @@ int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) {
         }
 
 finish:
-        if (!found)
-                return -ENODATA;
-
-        return 0;
+        return found ? 0 : -ENODATA;
 }
 
 int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
index e6265e2c9663f6ce7502483dc1d321629e89c21c..e2ce4b29074bfc2ad8a209f5944f3bd92793ab68 100644 (file)
@@ -1357,7 +1357,7 @@ int copy_file_full(
         if (r < 0)
                 return r;
 
-        RUN_WITH_UMASK(0000) {
+        WITH_UMASK(0000) {
                 if (copy_flags & COPY_MAC_CREATE) {
                         r = mac_selinux_create_file_prepare(to, S_IFREG);
                         if (r < 0)
index b6eb3d9f4185017d4a44dc50733879a9af84e025..58ee1845afd6ca61117edfc31876b9ef03d1b326 100644 (file)
@@ -4,6 +4,9 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#if HAVE_LINUX_MEMFD_H
+#include <linux/memfd.h>
+#endif
 
 #include "alloc-util.h"
 #include "copy.h"
@@ -12,6 +15,8 @@
 #include "fs-util.h"
 #include "io-util.h"
 #include "memfd-util.h"
+#include "missing_mman.h"
+#include "missing_syscall.h"
 #include "tmpfile-util.h"
 
 /* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
@@ -343,3 +348,50 @@ finish:
 
         return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
 }
+
+int memfd_clone_fd(int fd, const char *name, int mode) {
+        _cleanup_close_ int mfd = -EBADF;
+        bool ro;
+        int r;
+
+        /* Creates a clone of a regular file in a memfd. Unlike copy_data_fd() this returns strictly a memfd
+         * (and if it can't it will fail). Thus the resulting fd is seekable, and definitely reports as
+         * S_ISREG. */
+
+        assert(fd >= 0);
+        assert(name);
+        assert(IN_SET(mode & O_ACCMODE, O_RDONLY, O_RDWR));
+        assert((mode & ~(O_RDONLY|O_RDWR|O_CLOEXEC)) == 0);
+
+        ro = (mode & O_ACCMODE) == O_RDONLY;
+
+        mfd = memfd_create(name,
+                           ((FLAGS_SET(mode, O_CLOEXEC) || ro) ? MFD_CLOEXEC : 0) |
+                           (ro ? MFD_ALLOW_SEALING : 0));
+        if (mfd < 0)
+                return -errno;
+
+        r = copy_bytes(fd, mfd, UINT64_MAX, COPY_REFLINK);
+        if (r < 0)
+                return r;
+
+        if (ro) {
+                _cleanup_close_ int rfd = -1;
+
+                r = memfd_set_sealed(mfd);
+                if (r < 0)
+                        return r;
+
+                rfd = fd_reopen(mfd, mode);
+                if (rfd < 0)
+                        return rfd;
+
+                return TAKE_FD(rfd);
+        }
+
+        off_t f = lseek(mfd, 0, SEEK_SET);
+        if (f < 0)
+                return -errno;
+
+        return TAKE_FD(mfd);
+}
index 827e149662d6a49671585cef43414cc9783f82ca..f0e7923556f7a4a339892099367857d7822703db 100644 (file)
@@ -5,3 +5,4 @@
 
 int acquire_data_fd(const void *data, size_t size, unsigned flags);
 int copy_data_fd(int fd);
+int memfd_clone_fd(int fd, const char *name, int mode);
index 9a7a1c990a74b6b1b4d9c5b072f639c00db61b05..eb4d23f0e8a71afe635e235735d708f24beb26c8 100644 (file)
@@ -123,6 +123,7 @@ static char *image_roothash_path(Image *image) {
 
 static int image_new(
                 ImageType t,
+                ImageClass c,
                 const char *pretty,
                 const char *path,
                 const char *filename,
@@ -146,6 +147,7 @@ static int image_new(
         *i = (Image) {
                 .n_ref = 1,
                 .type = t,
+                .class = c,
                 .read_only = read_only,
                 .crtime = crtime,
                 .mtime = mtime,
@@ -203,6 +205,7 @@ static int extract_pretty(const char *path, const char *suffix, char **ret) {
 }
 
 static int image_make(
+                ImageClass c,
                 const char *pretty,
                 int dfd,
                 const char *path,
@@ -278,6 +281,7 @@ static int image_make(
                                         return r;
 
                                 r = image_new(IMAGE_SUBVOLUME,
+                                              c,
                                               pretty,
                                               path,
                                               filename,
@@ -314,6 +318,7 @@ static int image_make(
 
                 /* It's just a normal directory. */
                 r = image_new(IMAGE_DIRECTORY,
+                              c,
                               pretty,
                               path,
                               filename,
@@ -345,6 +350,7 @@ static int image_make(
                 }
 
                 r = image_new(IMAGE_RAW,
+                              c,
                               pretty,
                               path,
                               filename,
@@ -405,6 +411,7 @@ static int image_make(
                 }
 
                 r = image_new(IMAGE_BLOCK,
+                              c,
                               pretty,
                               path,
                               filename,
@@ -475,13 +482,13 @@ int image_find(ImageClass class,
                         if (!S_ISREG(st.st_mode))
                                 continue;
 
-                        r = image_make(name, dirfd(d), resolved, raw, &st, ret);
+                        r = image_make(class, name, dirfd(d), resolved, raw, &st, ret);
 
                 } else {
                         if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode))
                                 continue;
 
-                        r = image_make(name, dirfd(d), resolved, name, &st, ret);
+                        r = image_make(class, name, dirfd(d), resolved, name, &st, ret);
                 }
                 if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
                         continue;
@@ -495,7 +502,7 @@ int image_find(ImageClass class,
         }
 
         if (class == IMAGE_MACHINE && streq(name, ".host")) {
-                r = image_make(".host", AT_FDCWD, NULL, empty_to_root(root), NULL, ret);
+                r = image_make(class, ".host", AT_FDCWD, NULL, empty_to_root(root), NULL, ret);
                 if (r < 0)
                         return r;
 
@@ -515,9 +522,9 @@ int image_from_path(const char *path, Image **ret) {
          * overridden by another, different image earlier in the search path */
 
         if (path_equal(path, "/"))
-                return image_make(".host", AT_FDCWD, NULL, "/", NULL, ret);
+                return image_make(IMAGE_MACHINE, ".host", AT_FDCWD, NULL, "/", NULL, ret);
 
-        return image_make(NULL, AT_FDCWD, NULL, path, NULL, ret);
+        return image_make(_IMAGE_CLASS_INVALID, NULL, AT_FDCWD, NULL, path, NULL, ret);
 }
 
 int image_find_harder(ImageClass class, const char *name_or_path, const char *root, Image **ret) {
@@ -591,7 +598,7 @@ int image_discover(
                         if (hashmap_contains(h, pretty))
                                 continue;
 
-                        r = image_make(pretty, dirfd(d), resolved, de->d_name, &st, &image);
+                        r = image_make(class, pretty, dirfd(d), resolved, de->d_name, &st, &image);
                         if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
                                 continue;
                         if (r < 0)
@@ -610,7 +617,7 @@ int image_discover(
         if (class == IMAGE_MACHINE && !hashmap_contains(h, ".host")) {
                 _cleanup_(image_unrefp) Image *image = NULL;
 
-                r = image_make(".host", AT_FDCWD, NULL, empty_to_root("/"), NULL, &image);
+                r = image_make(IMAGE_MACHINE, ".host", AT_FDCWD, NULL, empty_to_root("/"), NULL, &image);
                 if (r < 0)
                         return r;
 
@@ -1151,7 +1158,7 @@ int image_read_metadata(Image *i) {
                         if (fd < 0)
                                 log_debug_errno(errno, "Failed to open %s: %m", path);
                         else {
-                                r = id128_read_fd(fd, ID128_PLAIN, &machine_id);
+                                r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &machine_id);
                                 if (r < 0)
                                         log_debug_errno(r, "Image %s contains invalid machine ID.", i->name);
                         }
@@ -1303,3 +1310,11 @@ static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);
+
+static const char* const image_class_table[_IMAGE_CLASS_MAX] = {
+        [IMAGE_MACHINE] = "machine",
+        [IMAGE_PORTABLE] = "portable",
+        [IMAGE_EXTENSION] = "extension",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(image_class, ImageClass);
index 3726e98d30c3d4224d8d656247c2a25dd3d1a288..a8874228dcf70ae30e3a8cb5c9c72f1dbeb7f884 100644 (file)
@@ -34,6 +34,7 @@ typedef struct Image {
         unsigned n_ref;
 
         ImageType type;
+        ImageClass class;
         char *name;
         char *path;
         bool read_only;
@@ -76,6 +77,9 @@ int image_read_only(Image *i, bool b);
 const char* image_type_to_string(ImageType t) _const_;
 ImageType image_type_from_string(const char *s) _pure_;
 
+const char* image_class_to_string(ImageClass cl) _const_;
+ImageClass image_class_from_string(const char *s) _pure_;
+
 int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local);
 int image_name_lock(const char *name, int operation, LockFile *ret);
 
index ddd6c5430a82e866d77a12da77a3133dad114541..b3d35e9fbf34c6545160a80b4fbf80b2640c3f4d 100644 (file)
@@ -513,13 +513,10 @@ static int dissect_image(
                         m->encrypted = streq_ptr(fstype, "crypto_LUKS");
 
                         m->has_verity = verity && verity->data_path;
-                        m->verity_ready = m->has_verity &&
-                                verity->root_hash &&
-                                (verity->designator < 0 || verity->designator == PARTITION_ROOT);
+                        m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT);
 
                         m->has_verity_sig = false; /* signature not embedded, must be specified */
-                        m->verity_sig_ready = m->verity_ready &&
-                                verity->root_hash_sig;
+                        m->verity_sig_ready = m->verity_ready && verity->root_hash_sig;
 
                         m->image_uuid = uuid;
 
@@ -862,7 +859,9 @@ static int dissect_image(
                                                 return r;
 
                                         if (!sd_id128_equal(var_uuid, id)) {
-                                                log_debug("Found a /var/ partition, but its UUID didn't match our expectations, ignoring.");
+                                                log_debug("Found a /var/ partition, but its UUID didn't match our expectations "
+                                                          "(found: " SD_ID128_UUID_FORMAT_STR ", expected: " SD_ID128_UUID_FORMAT_STR "), ignoring.",
+                                                          SD_ID128_FORMAT_VAL(id), SD_ID128_FORMAT_VAL(var_uuid));
                                                 continue;
                                         }
                                 }
@@ -932,6 +931,7 @@ static int dissect_image(
                                         .mount_node_fd = TAKE_FD(mount_node_fd),
                                         .offset = (uint64_t) start * 512,
                                         .size = (uint64_t) size * 512,
+                                        .gpt_flags = pflags,
                                 };
                         }
 
index 2f77228a1d5a7c29929de921e1d2f697519a9904..059b9aecbb9e36dcfc4faf32157995698c8385f3 100644 (file)
@@ -33,6 +33,7 @@ struct DissectedPartition {
         int mount_node_fd;
         uint64_t size;
         uint64_t offset;
+        uint64_t gpt_flags;
 };
 
 #define DISSECTED_PARTITION_NULL                                        \
@@ -165,6 +166,14 @@ int dissected_image_relinquish(DissectedImage *m);
 int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
 void verity_settings_done(VeritySettings *verity);
 
+static inline bool verity_settings_data_covers(const VeritySettings *verity, PartitionDesignator d) {
+        /* Returns true if the verity settings contain sufficient information to cover the specified partition */
+        return verity &&
+                ((d >= 0 && verity->designator == d) || (d == PARTITION_ROOT && verity->designator < 0)) &&
+                verity->root_hash &&
+                verity->data_path;
+}
+
 int dissected_image_load_verity_sig_partition(DissectedImage *m, int fd, VeritySettings *verity);
 
 bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator d);
index ebf86d24050e864d4039dd1e91798142e5fa93d3..a071976442310d58237155b6eea223a9d20cf287 100644 (file)
@@ -527,7 +527,18 @@ int dns_name_compare_func(const char *a, const char *b) {
         }
 }
 
-DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
+DEFINE_HASH_OPS(
+        dns_name_hash_ops,
+        char,
+        dns_name_hash_func,
+        dns_name_compare_func);
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+        dns_name_hash_ops_free,
+        char,
+        dns_name_hash_func,
+        dns_name_compare_func,
+        free);
 
 int dns_name_equal(const char *x, const char *y) {
         int r, q;
index 5421c60ee7ebd68a7ad0b3efe45c1ae9b22ed85a..7a35ae43bcb56cb8460fd257bd4409c96abd7252 100644 (file)
@@ -67,6 +67,7 @@ static inline bool dns_name_is_empty(const char *s) {
 void dns_name_hash_func(const char *s, struct siphash *state);
 int dns_name_compare_func(const char *a, const char *b);
 extern const struct hash_ops dns_name_hash_ops;
+extern const struct hash_ops dns_name_hash_ops_free;
 
 int dns_name_between(const char *a, const char *b, const char *c);
 int dns_name_equal(const char *x, const char *y);
index 779154541667d668256ec7a4eb6084fa97fe243f..0c694d79f3a61b54cc17bcbb1466d83a29b431c5 100644 (file)
@@ -15,6 +15,7 @@
 #include "dlfcn-util.h"
 #include "elf-util.h"
 #include "errno-util.h"
+#include "escape.h"
 #include "fileio.h"
 #include "fd-util.h"
 #include "format-util.h"
@@ -396,8 +397,10 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e
                         }
 
                         r = json_parse(payload, 0, &v, NULL, NULL);
-                        if (r < 0)
-                                return log_error_errno(r, "json_parse on %s failed: %m", payload);
+                        if (r < 0) {
+                                _cleanup_free_ char *esc = cescape(payload);
+                                return log_error_errno(r, "json_parse on \"%s\" failed: %m", strnull(esc));
+                        }
 
                         /* If we have a build-id, merge it in the same JSON object so that it appears all
                          * nicely together in the logs/metadata. */
index 99795530bd94741fd6277f6788e3758b1d05e96b..3e1a385124dd5de1796bd533e23e187cd79bc738 100644 (file)
@@ -58,6 +58,33 @@ PartitionDesignator partition_verity_sig_of(PartitionDesignator p) {
         }
 }
 
+PartitionDesignator partition_verity_to_data(PartitionDesignator d) {
+        switch (d) {
+
+        case PARTITION_ROOT_VERITY:
+                return PARTITION_ROOT;
+
+        case PARTITION_USR_VERITY:
+                return PARTITION_USR;
+
+        default:
+                return _PARTITION_DESIGNATOR_INVALID;
+        }
+}
+
+PartitionDesignator partition_verity_sig_to_data(PartitionDesignator d) {
+        switch (d) {
+
+        case PARTITION_ROOT_VERITY_SIG:
+                return PARTITION_ROOT;
+
+        case PARTITION_USR_VERITY_SIG:
+                return PARTITION_USR;
+
+        default:
+                return _PARTITION_DESIGNATOR_INVALID;
+        }
+}
 
 static const char *const partition_designator_table[_PARTITION_DESIGNATOR_MAX] = {
         [PARTITION_ROOT]                      = "root",
index 03af12c9e3d0cdd66f6901b113ea660b8b576b2a..bebfbc61167437fb29531c5d3fc10b142ffe384d 100644 (file)
@@ -32,6 +32,8 @@ bool partition_designator_is_versioned(PartitionDesignator d);
 
 PartitionDesignator partition_verity_of(PartitionDesignator p);
 PartitionDesignator partition_verity_sig_of(PartitionDesignator p);
+PartitionDesignator partition_verity_to_data(PartitionDesignator d);
+PartitionDesignator partition_verity_sig_to_data(PartitionDesignator d);
 
 const char* partition_designator_to_string(PartitionDesignator d) _const_;
 PartitionDesignator partition_designator_from_string(const char *name) _pure_;
index 51aa60bb5253cf8e4bca64b52a7068af7e2af8bb..c38ba1bd7aa4aa35b99f634b22f50a4f0d597508 100644 (file)
@@ -2790,25 +2790,38 @@ static int do_unit_file_disable(
 
         _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+        InstallInfo *info;
+        bool has_install_info = false;
         int r;
 
         STRV_FOREACH(name, names) {
                 if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
                         return install_changes_add(changes, n_changes, -EUCLEAN, *name, NULL);
 
-                r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, NULL);
+                r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, &info);
+                if (r >= 0)
+                        r = install_info_traverse(&ctx, lp, info, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
+
                 if (r < 0)
-                        return r;
+                        return install_changes_add(changes, n_changes, r, *name, NULL);
+
+                /* If we enable multiple units, some with install info and others without,
+                 * the "empty [Install] section" warning is not shown. Let's make the behavior
+                 * of disable align with that. */
+                has_install_info = has_install_info || install_info_has_rules(info) || install_info_has_also(info);
         }
 
         r = install_context_mark_for_removal(&ctx, lp, &remove_symlinks_to, config_path, changes, n_changes);
+        if (r >= 0)
+                r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes);
+
         if (r < 0)
                 return r;
 
-        return remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes);
+        /* The warning is shown only if it's a no-op */
+        return install_changes_have_modification(*changes, *n_changes) || has_install_info;
 }
 
-
 int unit_file_disable(
                 LookupScope scope,
                 UnitFileFlags flags,
index 94d7b3155705cf29e22b5f7921b879a14697b1e8..b1ef0ed34992def5bbbae23b68e02317023c9055 100644 (file)
@@ -553,9 +553,34 @@ static void json_variant_copy_source(JsonVariant *v, JsonVariant *from) {
         v->source = json_source_ref(from->source);
 }
 
+static int _json_variant_array_put_element(JsonVariant *array, JsonVariant *element) {
+        assert(array);
+        JsonVariant *w = array + 1 + array->n_elements;
+
+        uint16_t d = json_variant_depth(element);
+        if (d >= DEPTH_MAX) /* Refuse too deep nesting */
+                return -ELNRNG;
+        if (d >= array->depth)
+                array->depth = d + 1;
+        array->n_elements ++;
+
+        *w = (JsonVariant) {
+                .is_embedded = true,
+                .parent = array,
+        };
+
+        json_variant_set(w, element);
+        json_variant_copy_source(w, element);
+
+        if (!json_variant_is_normalized(element))
+                array->normalized = false;
+
+        return 0;
+}
+
 int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-        bool normalized = true;
+        int r;
 
         assert_return(ret, -EINVAL);
         if (n == 0) {
@@ -571,33 +596,15 @@ int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) {
         *v = (JsonVariant) {
                 .n_ref = 1,
                 .type = JSON_VARIANT_ARRAY,
+                .normalized = true,
         };
 
-        for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
-                JsonVariant *w = v + 1 + v->n_elements,
-                            *c = array[v->n_elements];
-                uint16_t d;
-
-                d = json_variant_depth(c);
-                if (d >= DEPTH_MAX) /* Refuse too deep nesting */
-                        return -ELNRNG;
-                if (d >= v->depth)
-                        v->depth = d + 1;
-
-                *w = (JsonVariant) {
-                        .is_embedded = true,
-                        .parent = v,
-                };
-
-                json_variant_set(w, c);
-                json_variant_copy_source(w, c);
-
-                if (!json_variant_is_normalized(c))
-                        normalized = false;
+        while (v->n_elements < n) {
+                r = _json_variant_array_put_element(v, array[v->n_elements]);
+                if (r < 0)
+                        return r;
         }
 
-        v->normalized = normalized;
-
         *ret = TAKE_PTR(v);
         return 0;
 }
@@ -823,6 +830,19 @@ static void json_variant_free_inner(JsonVariant *v, bool force_sensitive) {
                 explicit_bzero_safe(v, json_variant_size(v));
 }
 
+static unsigned json_variant_n_ref(const JsonVariant *v) {
+        /* Return the number of references to v.
+         * 0  => NULL or not a regular object or embedded.
+         * >0 => number of references
+         */
+
+        if (!v || !json_variant_is_regular(v) || v->is_embedded)
+                return 0;
+
+        assert(v->n_ref > 0);
+        return v->n_ref;
+}
+
 JsonVariant *json_variant_ref(JsonVariant *v) {
         if (!v)
                 return NULL;
@@ -1790,8 +1810,12 @@ int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
 }
 
 int json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix) {
-        if (!v)
-                return 0;
+        if (!v) {
+                if (flags & JSON_FORMAT_EMPTY_ARRAY)
+                        v = JSON_VARIANT_MAGIC_EMPTY_ARRAY;
+                else
+                        return 0;
+        }
 
         if (!f)
                 f = stdout;
@@ -2072,28 +2096,54 @@ int json_variant_append_array(JsonVariant **v, JsonVariant *element) {
 
         if (!*v || json_variant_is_null(*v))
                 blank = true;
-        else if (!json_variant_is_array(*v))
-                return -EINVAL;
-        else
+        else if (json_variant_is_array(*v))
                 blank = json_variant_elements(*v) == 0;
+        else
+                return -EINVAL;
 
-        if (blank)
+        if (blank) {
                 r = json_variant_new_array(&nv, (JsonVariant*[]) { element }, 1);
-        else {
-                _cleanup_free_ JsonVariant **array = new(JsonVariant*, json_variant_elements(*v) + 1);
+                if (r < 0)
+                        return r;
+        } else if (json_variant_n_ref(*v) == 1) {
+                /* Let's bump the reference count on element. We can't do the realloc if we're appending *v
+                 * to itself, or one of the objects embedded in *v to *v. If the reference count grows, we
+                 * need to fall back to the other method below. */
+
+                _unused_ _cleanup_(json_variant_unrefp) JsonVariant *dummy = json_variant_ref(element);
+                if (json_variant_n_ref(*v) == 1) {
+                        /* We hold the only reference. Let's mutate the object. */
+                        size_t size = json_variant_elements(*v);
+                        void *old = *v;
+
+                        if (!GREEDY_REALLOC(*v, size + 1 + 1))
+                                return -ENOMEM;
+
+                        if (old != *v)
+                                /* Readjust the parent pointers to the new address */
+                                for (size_t i = 1; i < size; i++)
+                                        (*v)[1 + i].parent = *v;
+
+                        return _json_variant_array_put_element(*v, element);
+                }
+        }
+
+        if (!blank) {
+                size_t size = json_variant_elements(*v);
+
+                _cleanup_free_ JsonVariant **array = new(JsonVariant*, size + 1);
                 if (!array)
                         return -ENOMEM;
 
-                size_t size = json_variant_elements(*v);
                 for (size_t i = 0; i < size; i++)
                         array[i] = json_variant_by_index(*v, i);
 
                 array[size] = element;
 
                 r = json_variant_new_array(&nv, array, size + 1);
+                if (r < 0)
+                        return r;
         }
-        if (r < 0)
-                return r;
 
         json_variant_propagate_sensitive(*v, nv);
         JSON_VARIANT_REPLACE(*v, TAKE_PTR(nv));
@@ -3180,16 +3230,53 @@ finish:
         return r;
 }
 
-int json_parse(const char *input, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
-        return json_parse_internal(&input, NULL, flags, ret, ret_line, ret_column, false);
+int json_parse_with_source(
+                const char *input,
+                const char *source,
+                JsonParseFlags flags,
+                JsonVariant **ret,
+                unsigned *ret_line,
+                unsigned *ret_column) {
+
+        _cleanup_(json_source_unrefp) JsonSource *s = NULL;
+
+        if (source) {
+                s = json_source_new(source);
+                if (!s)
+                        return -ENOMEM;
+        }
+
+        return json_parse_internal(&input, s, flags, ret, ret_line, ret_column, false);
 }
 
-int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
-        return json_parse_internal(p, NULL, flags, ret, ret_line, ret_column, true);
+int json_parse_with_source_continue(
+                const char **p,
+                const char *source,
+                JsonParseFlags flags,
+                JsonVariant **ret,
+                unsigned *ret_line,
+                unsigned *ret_column) {
+
+        _cleanup_(json_source_unrefp) JsonSource *s = NULL;
+
+        if (source) {
+                s = json_source_new(source);
+                if (!s)
+                        return -ENOMEM;
+        }
+
+        return json_parse_internal(p, s, flags, ret, ret_line, ret_column, true);
 }
 
-int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
-        _cleanup_(json_source_unrefp) JsonSource *source = NULL;
+int json_parse_file_at(
+                FILE *f,
+                int dir_fd,
+                const char *path,
+                JsonParseFlags flags,
+                JsonVariant **ret,
+                unsigned *ret_line,
+                unsigned *ret_column) {
+
         _cleanup_free_ char *text = NULL;
         int r;
 
@@ -3205,14 +3292,7 @@ int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags fla
         if (isempty(text))
                 return -ENODATA;
 
-        if (path) {
-                source = json_source_new(path);
-                if (!source)
-                        return -ENOMEM;
-        }
-
-        const char *p = text;
-        return json_parse_internal(&p, source, flags, ret, ret_line, ret_column, false);
+        return json_parse_with_source(text, path, flags, ret, ret_line, ret_column);
 }
 
 int json_buildv(JsonVariant **ret, va_list ap) {
index c5f052a9d569bbcafc83c3afca3a61c619611ae7..8d060e78777de8fa97145435a16a84047b460b8c 100644 (file)
@@ -194,7 +194,8 @@ typedef enum JsonFormatFlags {
         JSON_FORMAT_SSE         = 1 << 6, /* prefix/suffix with W3C server-sent events */
         JSON_FORMAT_SEQ         = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
         JSON_FORMAT_FLUSH       = 1 << 8, /* call fflush() after dumping JSON */
-        JSON_FORMAT_OFF         = 1 << 9, /* make json_variant_format() fail with -ENOEXEC */
+        JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */
+        JSON_FORMAT_OFF         = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
 } JsonFormatFlags;
 
 int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
@@ -222,8 +223,16 @@ typedef enum JsonParseFlags {
         JSON_PARSE_SENSITIVE = 1 << 0, /* mark variant as "sensitive", i.e. something containing secret key material or such */
 } JsonParseFlags;
 
-int json_parse(const char *string, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
-int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
+int json_parse_with_source(const char *string, const char *source, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
+int json_parse_with_source_continue(const char **p, const char *source, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
+
+static inline int json_parse(const char *string, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
+        return json_parse_with_source(string, NULL, flags, ret, ret_line, ret_column);
+}
+static inline int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
+        return json_parse_with_source_continue(p, NULL, flags, ret, ret_line, ret_column);
+}
+
 int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
 
 static inline int json_parse_file(FILE *f, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
index a2bf14ec487299fc450491422fb3b2277f13b94b..aa2d8b3ff1b7e15fbdce4acb167c2c59a4815d0b 100644 (file)
@@ -262,7 +262,6 @@ static int fido2_is_cred_in_specific_token(
                 const char *rp_id,
                 const void *cid,
                 size_t cid_size,
-                char **pins,
                 Fido2EnrollFlags flags) {
 
         assert(path);
@@ -285,6 +284,10 @@ static int fido2_is_cred_in_specific_token(
                                        "Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
 
         r = verify_features(d, path, LOG_ERR, NULL, NULL, &has_up, &has_uv);
+        if (r == -ENODEV) { /* Not a FIDO2 device or lacking HMAC-SECRET extension */
+                log_debug_errno(r, "%s is not a FIDO2 device, or it lacks the hmac-secret extension", path);
+                return false;
+        }
         if (r < 0)
                 return r;
 
@@ -296,6 +299,17 @@ static int fido2_is_cred_in_specific_token(
         if (r < 0)
                 return r;
 
+        /* FIDO2 devices may not support pre-flight requests with UV, at least not
+         * without user interaction [1]. As a result, let's just return true
+         * here and go ahead with trying the unlock directly.
+         * Reference:
+         * 1: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-getAssert-authnr-alg
+         *    See section 7.4 */
+        if (has_uv && FLAGS_SET(flags, FIDO2ENROLL_UV)) {
+                log_debug("Pre-flight requests with UV are unsupported, device: %s", path);
+                return true;
+        }
+
         /* According to CTAP 2.1 specification, to do pre-flight we need to set up option to false
          * with optionally pinUvAuthParam in assertion[1]. But for authenticator that doesn't support
          * user presence, once up option is present, the authenticator may return CTAP2_ERR_UNSUPPORTED_OPTION[2].
@@ -312,28 +326,7 @@ static int fido2_is_cred_in_specific_token(
                 return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                        "Failed to set assertion user presence: %s", sym_fido_strerr(r));
 
-
-        /* According to CTAP 2.1 specification, if the credential is marked as userVerificationRequired,
-         * we may pass pinUvAuthParam parameter or set uv option to true in order to check whether the
-         * credential is in the token. Here we choose to use PIN (pinUvAuthParam) to achieve this.
-         * If we don't do that, the authenticator will remove the credential from the applicable
-         * credentials list, hence CTAP2_ERR_NO_CREDENTIALS error will be returned.
-         * Please see
-         * https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-getAssert-authnr-alg
-         * step 7.4 for detailed information.
-         */
-        if (FLAGS_SET(flags, FIDO2ENROLL_UV) && has_uv) {
-                if (strv_isempty(pins))
-                        r = FIDO_ERR_PIN_REQUIRED;
-                else
-                        STRV_FOREACH(i, pins) {
-                                r = sym_fido_dev_get_assert(d, a, *i);
-                                if (r != FIDO_ERR_PIN_INVALID)
-                                        break;
-                        }
-
-        } else
-                r = sym_fido_dev_get_assert(d, a, NULL);
+        r = sym_fido_dev_get_assert(d, a, NULL);
 
         switch (r) {
                 case FIDO_OK:
@@ -596,8 +589,18 @@ int fido2_use_hmac_hash(
         if (r < 0)
                 return log_error_errno(r, "FIDO2 support is not installed.");
 
-        if (device)
+        if (device) {
+                r = fido2_is_cred_in_specific_token(device, rp_id, cid, cid_size, required);
+                if (r == 0)
+                        /* The caller is expected to attempt other key slots in this case,
+                         * therefore, do not spam the console with error logs here. */
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADSLT),
+                                               "The credential is not in the token %s.", device);
+                if (r < 0)
+                        return log_error_errno(r, "Token returned error during pre-flight: %m");
+
                 return fido2_use_hmac_hash_specific_token(device, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size);
+        }
 
         di = sym_fido_dev_info_new(allocated);
         if (!di)
@@ -632,26 +635,12 @@ int fido2_use_hmac_hash(
                         goto finish;
                 }
 
-                r = fido2_is_cred_in_specific_token(path, rp_id, cid, cid_size, pins, required);
-                /* We handle PIN related errors here since we have used PIN in the check.
-                 * If PIN error happens, we can avoid pin retries decreased the second time. */
-                if (IN_SET(r, -ENOANO,        /* → pin required */
-                              -ENOLCK,        /* → pin incorrect */
-                              -EOWNERDEAD)) { /* → uv blocked */
-                        /* If it's not the last device, try next one */
-                        if (i < found - 1)
-                                continue;
-
-                        /* -EOWNERDEAD is returned when uv is blocked. Map it to EAGAIN so users can reinsert
-                         * the token and try again. */
-                        if (r == -EOWNERDEAD)
-                                r = -EAGAIN;
+                r = fido2_is_cred_in_specific_token(path, rp_id, cid, cid_size, required);
+                if (r < 0) {
+                        log_error_errno(r, "Token returned error during pre-flight: %m");
                         goto finish;
-                } else if (r == -ENODEV) /* not a FIDO2 device or lacking HMAC-SECRET extension */
-                        continue;
-                else if (r < 0)
-                        log_error_errno(r, "Failed to determine whether the credential is in the token, trying anyway: %m");
-                else if (r == 0) {
+                }
+                if (r == 0) {
                         log_debug("The credential is not in the token %s, skipping.", path);
                         continue;
                 }
index fb7e80b1b5c0c2c9a7f08a01bb82322531812f67..ed31454e313d08510f8c646fca291df6184da225 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "alloc-util.h"
 #include "blockdev-util.h"
+#include "data-fd-util.h"
 #include "device-util.h"
 #include "devnum-util.h"
 #include "env-util.h"
@@ -640,6 +641,47 @@ int loop_device_make_by_path(
         return loop_device_make_internal(path, fd, open_flags, 0, 0, 0, loop_flags, lock_op, ret);
 }
 
+int loop_device_make_by_path_memory(
+                const char *path,
+                int open_flags,
+                uint32_t loop_flags,
+                int lock_op,
+                LoopDevice **ret) {
+
+        _cleanup_close_ int fd = -EBADF, mfd = -EBADF;
+        _cleanup_free_ char *fn = NULL;
+        struct stat st;
+        int r;
+
+        assert(path);
+        assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
+        assert(ret);
+
+        loop_flags &= ~LO_FLAGS_DIRECT_IO; /* memfds don't support O_DIRECT, hence LO_FLAGS_DIRECT_IO can't be used either */
+
+        fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|O_RDONLY);
+        if (fd < 0)
+                return -errno;
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
+                return -EBADF;
+
+        r = path_extract_filename(path, &fn);
+        if (r < 0)
+                return r;
+
+        mfd = memfd_clone_fd(fd, fn, open_flags|O_CLOEXEC);
+        if (mfd < 0)
+                return mfd;
+
+        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);
+}
+
 static LoopDevice* loop_device_free(LoopDevice *d) {
         _cleanup_close_ int control = -1;
         int r;
index e466a5abbdf9dd3e66bffa6799d25407c439b923..512b5ab4a22d6510b5388bf07a0fb42130d06995 100644 (file)
@@ -30,6 +30,7 @@ struct LoopDevice {
 
 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_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 c694a185ba9592847a0d946e36a5e3656427132a..0bdaba00e64d2c6e0648e2e05d6073072abd1c4b 100644 (file)
@@ -37,7 +37,7 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) {
         dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id");
         fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
         if (fd >= 0) {
-                if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) {
+                if (id128_read_fd(fd, ID128_FORMAT_PLAIN, ret) >= 0) {
                         log_info("Initializing machine ID from D-Bus machine ID.");
                         return 0;
                 }
@@ -89,7 +89,7 @@ int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_
 
         etc_machine_id = prefix_roota(root, "/etc/machine-id");
 
-        RUN_WITH_UMASK(0000) {
+        WITH_UMASK(0000) {
                 /* We create this 0444, to indicate that this isn't really
                  * something you should ever modify. Of course, since the file
                  * will be owned by root it doesn't matter much, but maybe
@@ -122,7 +122,7 @@ int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_
         if (sd_id128_is_null(machine_id)) {
 
                 /* Try to read any existing machine ID */
-                if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0)
+                if (id128_read_fd(fd, ID128_FORMAT_PLAIN, ret) >= 0)
                         return 0;
 
                 /* Hmm, so, the id currently stored is not useful, then let's generate one */
@@ -151,7 +151,7 @@ int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_
                         if (r < 0)
                                 return log_error_errno(r, "Failed to sync %s: %m", etc_machine_id);
                 } else {
-                        r = id128_write_fd(fd, ID128_PLAIN, machine_id, true);
+                        r = id128_write_fd(fd, ID128_FORMAT_PLAIN | ID128_SYNC_ON_WRITE, machine_id);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to write %s: %m", etc_machine_id);
                         else
@@ -166,8 +166,8 @@ int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_
 
         run_machine_id = prefix_roota(root, "/run/machine-id");
 
-        RUN_WITH_UMASK(0022)
-                r = id128_write(run_machine_id, ID128_PLAIN, machine_id, false);
+        WITH_UMASK(0022)
+                r = id128_write(run_machine_id, ID128_FORMAT_PLAIN, machine_id);
         if (r < 0) {
                 (void) unlink(run_machine_id);
                 return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
@@ -239,7 +239,7 @@ int machine_id_commit(const char *root) {
                                        "%s is not on a temporary file system.",
                                        etc_machine_id);
 
-        r = id128_read_fd(fd, ID128_PLAIN, &id);
+        r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &id);
         if (r < 0)
                 return log_error_errno(r, "We didn't find a valid machine ID in %s: %m", etc_machine_id);
 
@@ -260,7 +260,7 @@ int machine_id_commit(const char *root) {
                 return r;
 
         /* Update a persistent version of etc_machine_id */
-        r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
+        r = id128_write(etc_machine_id, ID128_FORMAT_PLAIN | ID128_SYNC_ON_WRITE, id);
         if (r < 0)
                 return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
 
index 1f0b0b47303e16f084ae08395ca9eb25c05f3b71..fb0b2f5adcc714c379b6e1c8d34eadfc6045716b 100644 (file)
@@ -22,7 +22,7 @@ static int check_btrfs(void) {
         return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
 }
 
-int setup_machine_directory(sd_bus_error *error) {
+int setup_machine_directory(sd_bus_error *error, bool use_btrfs_subvol, bool use_btrfs_quota) {
         int r;
 
         r = check_btrfs();
@@ -31,8 +31,14 @@ int setup_machine_directory(sd_bus_error *error) {
         if (r == 0)
                 return 0;
 
+        if (!use_btrfs_subvol)
+                return 0;
+
         (void) btrfs_subvol_make_label("/var/lib/machines");
 
+        if (!use_btrfs_quota)
+                return 0;
+
         r = btrfs_quota_enable("/var/lib/machines", true);
         if (r < 0)
                 log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m");
@@ -41,5 +47,5 @@ int setup_machine_directory(sd_bus_error *error) {
         if (r < 0)
                 log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");
 
-        return 1;
+        return 0;
 }
index 3f528ab060e2b29374bea3e3efbede103d140efb..c57e47878fbbdb1cbb422a3a26f2e5004c78b18e 100644 (file)
@@ -5,4 +5,4 @@
 
 #include "sd-bus.h"
 
-int setup_machine_directory(sd_bus_error *error);
+int setup_machine_directory(sd_bus_error *error, bool use_btrfs_subvol, bool use_btrfs_quota);
index 68e8f12eabb42176ebd8db5800c50ad1c8ba025f..97c3ff8da21a7fba72efb3f8f109d84e9e4a3ed4 100644 (file)
@@ -335,6 +335,14 @@ int make_filesystem(
                         return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mksquashfs binary not available.");
                 if (r < 0)
                         return log_error_errno(r, "Failed to determine whether mksquashfs binary exists: %m");
+
+        } else if (streq(fstype, "erofs")) {
+                r = find_executable("mkfs.erofs", &mkfs);
+                if (r == -ENOENT)
+                        return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs.erofs binary not available.");
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine whether mkfs.erofs binary exists: %m");
+
         } else if (fstype_is_ro(fstype)) {
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                                        "Don't know how to create read-only file system '%s', refusing.",
@@ -501,6 +509,12 @@ int make_filesystem(
                                 root, node,
                                 "-quiet",
                                 "-noappend");
+
+        else if (streq(fstype, "erofs"))
+
+                argv = strv_new(mkfs,
+                                "-U", vol_id,
+                                node, root);
         else
                 /* Generic fallback for all other file systems */
                 argv = strv_new(mkfs, node);
@@ -539,6 +553,9 @@ int make_filesystem(
         if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "f2fs", "xfs", "vfat", "swap"))
                 log_info("%s successfully formatted as %s (label \"%s\", uuid %s)",
                          node, fstype, label, vol_id);
+        else if (streq(fstype, "erofs"))
+                log_info("%s successfully formatted as %s (uuid %s, no label)",
+                         node, fstype, vol_id);
         else
                 log_info("%s successfully formatted as %s (no label or uuid specified)",
                          node, fstype);
index 975c027f47c9c067626b52806a8edf522db77021..f1ee5282325079f276da74692e19fcb5635e5218 100644 (file)
@@ -62,53 +62,55 @@ typedef struct MountPoint {
 #endif
 
 static const MountPoint mount_table[] = {
-        { "proc",        "/proc",                     "proc",       NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "proc",        "/proc",                     "proc",       NULL,                                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER|MNT_FOLLOW_SYMLINK },
-        { "sysfs",       "/sys",                      "sysfs",      NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "sysfs",       "/sys",                      "sysfs",      NULL,                                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "devtmpfs",    "/dev",                      "devtmpfs",   "mode=755" TMPFS_LIMITS_DEV,               MS_NOSUID|MS_STRICTATIME,
+        { "devtmpfs",    "/dev",                      "devtmpfs",   "mode=0755" TMPFS_LIMITS_DEV,               MS_NOSUID|MS_STRICTATIME,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "securityfs",  "/sys/kernel/security",      "securityfs", NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "securityfs",  "/sys/kernel/security",      "securityfs", NULL,                                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_NONE                   },
 #if ENABLE_SMACK
-        { "smackfs",     "/sys/fs/smackfs",           "smackfs",    "smackfsdef=*",                            MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "smackfs",     "/sys/fs/smackfs",           "smackfs",    "smackfsdef=*",                             MS_NOSUID|MS_NOEXEC|MS_NODEV,
           mac_smack_use, MNT_FATAL                  },
-        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777,smackfsroot=*",                 MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=01777,smackfsroot=*",                 MS_NOSUID|MS_NODEV|MS_STRICTATIME,
           mac_smack_use, MNT_FATAL                  },
 #endif
-        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777",                               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=01777",                               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "devpts",      "/dev/pts",                  "devpts",     "mode=620,gid=" STRINGIFY(TTY_GID),        MS_NOSUID|MS_NOEXEC,
+        { "devpts",      "/dev/pts",                  "devpts",     "mode=0620,gid=" STRINGIFY(TTY_GID),        MS_NOSUID|MS_NOEXEC,
           NULL,          MNT_IN_CONTAINER           },
 #if ENABLE_SMACK
-        { "tmpfs",       "/run",                      "tmpfs",      "mode=755,smackfsroot=*" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/run",                      "tmpfs",      "mode=0755,smackfsroot=*" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
           mac_smack_use, MNT_FATAL                  },
 #endif
-        { "tmpfs",       "/run",                      "tmpfs",      "mode=755" TMPFS_LIMITS_RUN,               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/run",                      "tmpfs",      "mode=0755" TMPFS_LIMITS_RUN,               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate,memory_recursiveprot",         MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate,memory_recursiveprot",          MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate",                              MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate",                               MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    NULL,                                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "tmpfs",       "/sys/fs/cgroup",            "tmpfs",      "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP,     MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/sys/fs/cgroup",            "tmpfs",      "mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP,     MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
-        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    "nsdelegate",                              MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    "nsdelegate",                               MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    NULL,                                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd,xattr",                 MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd,xattr",                  MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_legacy_wanted, MNT_IN_CONTAINER     },
-        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd",                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd",                        MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
-        { "pstore",      "/sys/fs/pstore",            "pstore",     NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+#if ENABLE_PSTORE
+        { "pstore",      "/sys/fs/pstore",            "pstore",     NULL,                                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_NONE                   },
+#endif
 #if ENABLE_EFI
-        { "efivarfs",    "/sys/firmware/efi/efivars", "efivarfs",   NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "efivarfs",    "/sys/firmware/efi/efivars", "efivarfs",   NULL,                                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
           is_efi_boot,   MNT_NONE                   },
 #endif
-        { "bpf",         "/sys/fs/bpf",               "bpf",        "mode=700",                                MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "bpf",         "/sys/fs/bpf",               "bpf",        "mode=0700",                                MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_NONE,                  },
 };
 
@@ -354,7 +356,9 @@ int mount_cgroup_controllers(void) {
         }
 
         /* Now that we mounted everything, let's make the tmpfs the cgroup file systems are mounted into read-only. */
-        (void) mount_nofollow("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP);
+        (void) mount_nofollow("tmpfs", "/sys/fs/cgroup", "tmpfs",
+                              MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY,
+                              "mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP);
 
         return 0;
 }
index 681d698800b1ab8477f9093b3c8a7eddd81c2787..dec12ea300063f180413044c3d64092ed79bba35 100644 (file)
 #include "set.h"
 #include "stat-util.h"
 #include "stdio-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tmpfile-util.h"
 #include "user-util.h"
 
-int mount_fd(const char *source,
-             int target_fd,
-             const char *filesystemtype,
-             unsigned long mountflags,
-             const void *data) {
-
-        if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
-                if (errno != ENOENT)
-                        return -errno;
-
-                /* ENOENT can mean two things: either that the source is missing, or that /proc/ isn't
-                 * mounted. Check for the latter to generate better error messages. */
-                if (proc_mounted() == 0)
-                        return -ENOSYS;
-
-                return -ENOENT;
-        }
-
-        return 0;
-}
-
-int mount_nofollow(
-                const char *source,
-                const char *target,
-                const char *filesystemtype,
-                unsigned long mountflags,
-                const void *data) {
-
-        _cleanup_close_ int fd = -1;
-
-        /* In almost all cases we want to manipulate the mount table without following symlinks, hence
-         * mount_nofollow() is usually the way to go. The only exceptions are environments where /proc/ is
-         * not available yet, since we need /proc/self/fd/ for this logic to work. i.e. during the early
-         * initialization of namespacing/container stuff where /proc is not yet mounted (and maybe even the
-         * fs to mount) we can only use traditional mount() directly.
-         *
-         * Note that this disables following only for the final component of the target, i.e symlinks within
-         * the path of the target are honoured, as are symlinks in the source path everywhere. */
-
-        fd = open(target, O_PATH|O_CLOEXEC|O_NOFOLLOW);
-        if (fd < 0)
-                return -errno;
-
-        return mount_fd(source, fd, filesystemtype, mountflags, data);
-}
-
 int umount_recursive(const char *prefix, int flags) {
         int n = 0, r;
         bool again;
@@ -475,47 +430,41 @@ int bind_remount_one_with_mountinfo(
         return 0;
 }
 
-int mount_move_root(const char *path) {
-        assert(path);
-
-        if (chdir(path) < 0)
-                return -errno;
-
-        if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
-                return -errno;
+static const char *const mount_attr_propagation_type_table[_MOUNT_ATTR_PROPAGATION_TYPE_MAX] = {
+        [MOUNT_ATTR_PROPAGATION_INHERIT]   = "inherited",
+        [MOUNT_ATTR_PROPAGATION_PRIVATE]   = "private",
+        [MOUNT_ATTR_PROPAGATION_DEPENDENT] = "dependent",
+        [MOUNT_ATTR_PROPAGATION_SHARED]    = "shared",
+};
 
-        if (chroot(".") < 0)
-                return -errno;
+DEFINE_STRING_TABLE_LOOKUP(mount_attr_propagation_type, MountAttrPropagationType);
 
-        return RET_NERRNO(chdir("/"));
+unsigned int mount_attr_propagation_type_to_flag(MountAttrPropagationType t) {
+        switch (t) {
+        case MOUNT_ATTR_PROPAGATION_INHERIT:
+                return 0;
+        case MOUNT_ATTR_PROPAGATION_PRIVATE:
+                return MS_PRIVATE;
+        case MOUNT_ATTR_PROPAGATION_DEPENDENT:
+                return MS_SLAVE;
+        case MOUNT_ATTR_PROPAGATION_SHARED:
+                return MS_SHARED;
+        default:
+                assert_not_reached();
+        }
 }
 
-int mount_pivot_root(const char *path) {
-        _cleanup_close_ int fd_oldroot = -EBADF, fd_newroot = -EBADF;
-
-        assert(path);
-
-        /* pivot_root() isn't currently supported in the initramfs. */
-        if (in_initrd())
-                return mount_move_root(path);
+static inline int mount_switch_root_pivot(const char *path, int fd_newroot) {
+        _cleanup_close_ int fd_oldroot = -EBADF;
 
         fd_oldroot = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
         if (fd_oldroot < 0)
                 return log_debug_errno(errno, "Failed to open old rootfs");
 
-        fd_newroot = open(path, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
-        if (fd_newroot < 0)
-                return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path);
-
-        /* Change into the new rootfs. */
-        if (fchdir(fd_newroot) < 0)
-                return log_debug_errno(errno, "Failed to change into new rootfs '%s': %m", path);
-
         /* Let the kernel tuck the new root under the old one. */
         if (pivot_root(".", ".") < 0)
                 return log_debug_errno(errno, "Failed to pivot root to new rootfs '%s': %m", path);
 
-
         /* At this point the new root is tucked under the old root. If we want
          * to unmount it we cannot be fchdir()ed into it. So escape back to the
          * old root. */
@@ -535,6 +484,52 @@ int mount_pivot_root(const char *path) {
         return 0;
 }
 
+static inline int mount_switch_root_move(const char *path) {
+        if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
+                return log_debug_errno(errno, "Failed to move new rootfs '%s': %m", path);
+
+        if (chroot(".") < 0)
+                return log_debug_errno(errno, "Failed to chroot to new rootfs '%s': %m", path);
+
+        if (chdir("/"))
+                return log_debug_errno(errno, "Failed to chdir to new rootfs '%s': %m", path);
+
+        return 0;
+}
+
+int mount_switch_root(const char *path, MountAttrPropagationType type) {
+        int r;
+        _cleanup_close_ int fd_newroot = -EBADF;
+        unsigned int flags;
+
+        assert(path);
+
+        fd_newroot = open(path, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+        if (fd_newroot < 0)
+                return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path);
+
+        /* Change into the new rootfs. */
+        if (fchdir(fd_newroot) < 0)
+                return log_debug_errno(errno, "Failed to change into new rootfs '%s': %m", path);
+
+        r = mount_switch_root_pivot(path, fd_newroot);
+        if (r < 0) {
+                /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the
+                 * rootfs is an initramfs in which case pivot_root() isn't supported. */
+                log_debug_errno(r, "Failed to pivot into new rootfs '%s': %m", path);
+                r = mount_switch_root_move(path);
+        }
+        if (r < 0)
+                return log_debug_errno(r, "Failed to switch to new rootfs '%s': %m", path);
+
+        /* Finally, let's establish the requested propagation type. */
+        flags = mount_attr_propagation_type_to_flag(type);
+        if ((flags != 0) && mount(NULL, ".", NULL, flags|MS_REC, 0) < 0)
+                return log_debug_errno(errno, "Failed to turn new rootfs '%s' into %s mount: %m",
+                                       mount_attr_propagation_type_to_string(type), path);
+
+        return 0;
+}
 
 int repeat_unmount(const char *path, int flags) {
         bool done = false;
@@ -768,9 +763,9 @@ int mount_option_mangle(
         /* This extracts mount flags from the mount options, and stores
          * non-mount-flag options to '*ret_remaining_options'.
          * E.g.,
-         * "rw,nosuid,nodev,relatime,size=1630748k,mode=700,uid=1000,gid=1000"
+         * "rw,nosuid,nodev,relatime,size=1630748k,mode=0700,uid=1000,gid=1000"
          * is split to MS_NOSUID|MS_NODEV|MS_RELATIME and
-         * "size=1630748k,mode=700,uid=1000,gid=1000".
+         * "size=1630748k,mode=0700,uid=1000,gid=1000".
          * See more examples in test-mount-util.c.
          *
          * If 'options' does not contain any non-mount-flag options,
index 29b9ed02f7ca5efda2db43b58caa09469805a581..f6de42e1a027bab4322c20232e8c25545a340308 100644 (file)
 #include "errno-util.h"
 #include "macro.h"
 
-/* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't
- * consume any space and udev isn't supposed to create regular file either. There's no limit on the
- * max number of inodes since such limit is hard to guess especially on large storage array
- * systems. */
-#define TMPFS_LIMITS_DEV             ",size=4m"
-
-/* The limit used for /dev in private namespaces. 4MB for contents of regular files. The number of
- * inodes should be relatively low in private namespaces but for now use a 64k limit. */
-#define TMPFS_LIMITS_PRIVATE_DEV     ",size=4m,nr_inodes=64k"
-
-/* Very little, if any use expected */
-#define TMPFS_LIMITS_EMPTY_OR_ALMOST ",size=4m,nr_inodes=1k"
-#define TMPFS_LIMITS_SYS             TMPFS_LIMITS_EMPTY_OR_ALMOST
-#define TMPFS_LIMITS_SYS_FS_CGROUP   TMPFS_LIMITS_EMPTY_OR_ALMOST
-
-/* On an extremely small device with only 256MB of RAM, 20% of RAM should be enough for the re-execution of
- * PID1 because 16MB of free space is required. */
-#define TMPFS_LIMITS_RUN             ",size=20%,nr_inodes=800k"
-
-/* The limit used for various nested tmpfs mounts, in particular for guests started by systemd-nspawn.
- * 10% of RAM (using 16GB of RAM as a baseline) translates to 400k inodes (assuming 4k each) and 25%
- * translates to 1M inodes.
- * (On the host, /tmp is configured through a .mount unit file.) */
-#define NESTED_TMPFS_LIMITS          ",size=10%,nr_inodes=400k"
-
-/* More space for volatile root and /var */
-#define TMPFS_LIMITS_VAR             ",size=25%,nr_inodes=1m"
-#define TMPFS_LIMITS_ROOTFS          TMPFS_LIMITS_VAR
-#define TMPFS_LIMITS_VOLATILE_STATE  TMPFS_LIMITS_VAR
-
-int mount_fd(const char *source, int target_fd, const char *filesystemtype, unsigned long mountflags, const void *data);
-int mount_nofollow(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data);
+typedef enum MountAttrPropagationType {
+        MOUNT_ATTR_PROPAGATION_INHERIT,   /* no special MS_* propagation flags */
+        MOUNT_ATTR_PROPAGATION_PRIVATE,   /* MS_PRIVATE */
+        MOUNT_ATTR_PROPAGATION_DEPENDENT, /* MS_SLAVE */
+        MOUNT_ATTR_PROPAGATION_SHARED,    /* MS_SHARE */
+
+        _MOUNT_ATTR_PROPAGATION_TYPE_MAX,
+        _MOUNT_ATTR_PROPAGATION_TYPE_INVALID = -EINVAL,
+} MountAttrPropagationType;
+
+const char* mount_attr_propagation_type_to_string(MountAttrPropagationType t) _const_;
+MountAttrPropagationType mount_attr_propagation_type_from_string(const char *s) _pure_;
+unsigned int mount_attr_propagation_type_to_flag(MountAttrPropagationType t);
 
 int repeat_unmount(const char *path, int flags);
 int umount_recursive(const char *target, int flags);
@@ -54,8 +35,7 @@ static inline int bind_remount_recursive(const char *prefix, unsigned long new_f
 
 int bind_remount_one_with_mountinfo(const char *path, unsigned long new_flags, unsigned long flags_mask, FILE *proc_self_mountinfo);
 
-int mount_move_root(const char *path);
-int mount_pivot_root(const char *path);
+int mount_switch_root(const char *path, MountAttrPropagationType type);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, endmntent, NULL);
 #define _cleanup_endmntent_ _cleanup_(endmntentp)
index 7fa3ff98b69deedc97e4951ac0709afb430054ed..b11040556830574a4df15f094cb512aba842964d 100644 (file)
@@ -42,7 +42,7 @@ int update_reboot_parameter_and_warn(const char *parameter, bool keep) {
                 return 0;
         }
 
-        RUN_WITH_UMASK(0022) {
+        WITH_UMASK(0022) {
                 r = write_string_file("/run/systemd/reboot-param", parameter,
                                       WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
                 if (r < 0)
index f7d8353b49c182ab8d28439f75e7b2403dc520a9..e240cdc2c32702a616189fc58b8a18eb81ef7959 100644 (file)
@@ -277,7 +277,7 @@ static int selinux_fix_fd(
                         return 0;
 
                 /* If the old label is identical to the new one, suppress any kind of error */
-                if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq(fcon, oldcon))
+                if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq_ptr(fcon, oldcon))
                         return 0;
 
                 return log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", label_path);
@@ -381,9 +381,13 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
 
         if (getcon_raw(&mycon) < 0)
                 return -errno;
+        if (!mycon)
+                return -EOPNOTSUPP;
 
         if (getfilecon_raw(exe, &fcon) < 0)
                 return -errno;
+        if (!fcon)
+                return -EOPNOTSUPP;
 
         sclass = string_to_security_class("process");
         if (sclass == 0)
@@ -395,14 +399,21 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
 #endif
 }
 
-int mac_selinux_get_our_label(char **label) {
-#if HAVE_SELINUX
-        assert(label);
+int mac_selinux_get_our_label(char **ret) {
+        assert(ret);
 
+#if HAVE_SELINUX
         if (!mac_selinux_use())
                 return -EOPNOTSUPP;
 
-        return RET_NERRNO(getcon_raw(label));
+        _cleanup_freecon_ char *con = NULL;
+        if (getcon_raw(&con) < 0)
+                return -errno;
+        if (!con)
+                return -EOPNOTSUPP;
+
+        *ret = TAKE_PTR(con);
+        return 0;
 #else
         return -EOPNOTSUPP;
 #endif
@@ -424,13 +435,20 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
 
         if (getcon_raw(&mycon) < 0)
                 return -errno;
+        if (!mycon)
+                return -EOPNOTSUPP;
 
         if (getpeercon_raw(socket_fd, &peercon) < 0)
                 return -errno;
+        if (!peercon)
+                return -EOPNOTSUPP;
 
-        if (!exec_label) /* If there is no context set for next exec let's use context of target executable */
+        if (!exec_label) /* If there is no context set for next exec let's use context of target executable */
                 if (getfilecon_raw(exe, &fcon) < 0)
                         return -errno;
+                if (!fcon)
+                        return -EOPNOTSUPP;
+        }
 
         bcon = context_new(mycon);
         if (!bcon)
index 8094ad764345f2da96019617f37689f7eca92b94..5195ca410c99c37d419b584c78569b3d2e185c59 100644 (file)
@@ -101,7 +101,7 @@ int socket_address_listen(
                 (void) mkdir_parents_label(p, directory_mode);
 
                 /* Enforce the right access mode for the socket */
-                RUN_WITH_UMASK(~socket_mode) {
+                WITH_UMASK(~socket_mode) {
                         r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
                         if (r == -EADDRINUSE) {
                                 /* Unlink and try again */
index d54ab9f5a98f283fd038e2971cb0d6378e1f0588..cd651768bd80725c11e4bead8c942cca370f890d 100644 (file)
@@ -195,7 +195,7 @@ int specifier_machine_id(char specifier, const void *data, const char *root, con
                         /* Translate error for missing os-release file to EUNATCH. */
                         return fd == -ENOENT ? -EUNATCH : fd;
 
-                r = id128_read_fd(fd, ID128_PLAIN, &id);
+                r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &id);
         } else
                 r = sd_id128_get_machine(&id);
         if (r < 0)
index 327caa439f0b3a71052ca5d93056cb0ef25c6bed..ba8dfb041d8b07ffb2e177b19cc6326c8359b159 100644 (file)
@@ -1565,6 +1565,8 @@ finish:
         return r;
 }
 
+#define RETRY_UNSEAL_MAX 30u
+
 int tpm2_unseal(const char *device,
                 uint32_t hash_pcr_mask,
                 uint16_t pcr_bank,
@@ -1676,44 +1678,53 @@ int tpm2_unseal(const char *device,
         if (r < 0)
                 goto finish;
 
-        r = tpm2_make_policy_session(
-                        c.esys_context,
-                        primary,
-                        hmac_session,
-                        TPM2_SE_POLICY,
-                        hash_pcr_mask,
-                        pcr_bank,
-                        pubkey, pubkey_size,
-                        pubkey_pcr_mask,
-                        signature,
-                        !!pin,
-                        &session,
-                        &policy_digest,
-                        /* ret_pcr_bank= */ NULL);
-        if (r < 0)
-                goto finish;
+        for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
+                r = tpm2_make_policy_session(
+                                c.esys_context,
+                                primary,
+                                hmac_session,
+                                TPM2_SE_POLICY,
+                                hash_pcr_mask,
+                                pcr_bank,
+                                pubkey, pubkey_size,
+                                pubkey_pcr_mask,
+                                signature,
+                                !!pin,
+                                &session,
+                                &policy_digest,
+                                /* ret_pcr_bank= */ NULL);
+                if (r < 0)
+                        goto finish;
 
-        /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
-         * wait until the TPM2 tells us to go away. */
-        if (known_policy_hash_size > 0 &&
-                memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
-                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
-                                               "Current policy digest does not match stored policy digest, cancelling "
-                                               "TPM2 authentication attempt.");
+                /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
+                 * wait until the TPM2 tells us to go away. */
+                if (known_policy_hash_size > 0 &&
+                        memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+                                                       "Current policy digest does not match stored policy digest, cancelling "
+                                                       "TPM2 authentication attempt.");
 
-        log_debug("Unsealing HMAC key.");
+                log_debug("Unsealing HMAC key.");
 
-        rc = sym_Esys_Unseal(
-                        c.esys_context,
-                        hmac_key,
-                        session,
-                        hmac_session, /* use HMAC session to enable parameter encryption */
-                        ESYS_TR_NONE,
-                        &unsealed);
-        if (rc != TSS2_RC_SUCCESS) {
-                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                    "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
-                goto finish;
+                rc = sym_Esys_Unseal(
+                                c.esys_context,
+                                hmac_key,
+                                session,
+                                hmac_session, /* use HMAC session to enable parameter encryption */
+                                ESYS_TR_NONE,
+                                &unsealed);
+                if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
+                        log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
+                        session = tpm2_flush_context_verbose(c.esys_context, session);
+                        continue;
+                }
+                if (rc != TSS2_RC_SUCCESS) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                            "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
+                        goto finish;
+                }
+
+                break;
         }
 
         secret = memdup(unsealed->buffer, unsealed->size);
index e89c0de1207968b7b24a5d1a94dfde26b6a219de..da181c6078af14559d2436f9ab92c5cb4aaa7834 100644 (file)
@@ -314,6 +314,8 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
                         printf("  PBKDF Type: %s\n", hr->luks_pbkdf_type);
                 if (hr->luks_pbkdf_hash_algorithm)
                         printf("  PBKDF Hash: %s\n", hr->luks_pbkdf_hash_algorithm);
+                if (hr->luks_pbkdf_force_iterations != UINT64_MAX)
+                        printf(" PBKDF Iters: %" PRIu64 "\n", hr->luks_pbkdf_force_iterations);
                 if (hr->luks_pbkdf_time_cost_usec != UINT64_MAX)
                         printf("  PBKDF Time: %s\n", FORMAT_TIMESPAN(hr->luks_pbkdf_time_cost_usec, 0));
                 if (hr->luks_pbkdf_memory_cost != UINT64_MAX)
index 84cbdb1d30a998c8de6292db4f5bc4d8194c34c8..06bc699572f28e7ff2dc11a60a6d5476b0939ff8 100644 (file)
@@ -55,6 +55,7 @@ UserRecord* user_record_new(void) {
                 .luks_discard = -1,
                 .luks_offline_discard = -1,
                 .luks_volume_key_size = UINT64_MAX,
+                .luks_pbkdf_force_iterations = UINT64_MAX,
                 .luks_pbkdf_time_cost_usec = UINT64_MAX,
                 .luks_pbkdf_memory_cost = UINT64_MAX,
                 .luks_pbkdf_parallel_threads = UINT64_MAX,
@@ -1213,6 +1214,7 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp
                 { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_volume_key_size),          0         },
                 { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
                 { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
+                { "luksPbkdfForceIterations",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_force_iterations),   0         },
                 { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
                 { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
                 { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
@@ -1566,6 +1568,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
                 { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_volume_key_size),          0         },
                 { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
                 { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
+                { "luksPbkdfForceIterations",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_force_iterations),   0         },
                 { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
                 { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
                 { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
@@ -1842,6 +1845,17 @@ const char* user_record_luks_pbkdf_type(UserRecord *h) {
         return h->luks_pbkdf_type ?: "argon2id";
 }
 
+uint64_t user_record_luks_pbkdf_force_iterations(UserRecord *h) {
+        assert(h);
+
+        /* propagate default "benchmark" mode as itself */
+        if (h->luks_pbkdf_force_iterations == UINT64_MAX)
+                return UINT64_MAX;
+
+        /* clamp everything else to actually accepted number of iterations of libcryptsetup */
+        return CLAMP(h->luks_pbkdf_force_iterations, 1U, UINT32_MAX);
+}
+
 uint64_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h) {
         assert(h);
 
index 47f4035d4531e512e5cf587d6e236226cfb6a271..73fb86cc94520e6a8976a49ec743b43d85befbdf 100644 (file)
@@ -309,6 +309,7 @@ typedef struct UserRecord {
         uint64_t luks_volume_key_size;
         char *luks_pbkdf_hash_algorithm;
         char *luks_pbkdf_type;
+        uint64_t luks_pbkdf_force_iterations;
         uint64_t luks_pbkdf_time_cost_usec;
         uint64_t luks_pbkdf_memory_cost;
         uint64_t luks_pbkdf_parallel_threads;
@@ -394,6 +395,7 @@ const char *user_record_luks_cipher(UserRecord *h);
 const char *user_record_luks_cipher_mode(UserRecord *h);
 uint64_t user_record_luks_volume_key_size(UserRecord *h);
 const char* user_record_luks_pbkdf_type(UserRecord *h);
+uint64_t user_record_luks_pbkdf_force_iterations(UserRecord *h);
 usec_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h);
 uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h);
 uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h);
index 4d2cfee4918aa9377debad71d59b381d01d1456a..7ff81973e1c1d3b55387991f193751ec1d393626 100644 (file)
@@ -2326,7 +2326,7 @@ int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t
 
         (void) sockaddr_un_unlink(&sockaddr.un);
 
-        RUN_WITH_UMASK(~m & 0777) {
+        WITH_UMASK(~m & 0777) {
                 r = mac_selinux_bind(fd, &sockaddr.sa, sockaddr_len);
                 if (r < 0)
                         return r;
index 6785ae2330af61f7ed090e4340c4edb8c7c8f81d..9b69a2a10d1fcd315c87d7bb083b2d07ec670d12 100644 (file)
@@ -366,6 +366,9 @@ static int freeze_thaw_user_slice(const char **method) {
         if (r < 0)
                 return log_debug_errno(r, "Failed to open connection to systemd: %m");
 
+        /* Wait for 1.5 seconds at maximum for freeze operation */
+        (void) sd_bus_set_method_call_timeout(bus, 1500 * USEC_PER_MSEC);
+
         r = bus_call_method(bus, bus_systemd_mgr, *method, &error, NULL, "s", SPECIAL_USER_SLICE);
         if (r < 0)
                 return log_debug_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
@@ -374,7 +377,7 @@ static int freeze_thaw_user_slice(const char **method) {
 }
 
 static int execute_s2h(const SleepConfig *sleep_config) {
-        _unused_ _cleanup_(freeze_thaw_user_slice) const char *auto_method_thaw = NULL;
+        _unused_ _cleanup_(freeze_thaw_user_slice) const char *auto_method_thaw = "ThawUnit";
         int r, k;
 
         assert(sleep_config);
@@ -382,8 +385,6 @@ static int execute_s2h(const SleepConfig *sleep_config) {
         r = freeze_thaw_user_slice(&(const char*) { "FreezeUnit" });
         if (r < 0)
                 log_debug_errno(r, "Failed to freeze unit user.slice, ignoring: %m");
-        else
-                auto_method_thaw = "ThawUnit"; /* from now on we want automatic thawing */;
 
         r = check_wakeup_type();
         if (r < 0)
index 92f6ecaa8d044145b87ae87f91ec71c66e48f7d8..606f602c3acf3720c06e8373a2a736c2238a3028 100644 (file)
@@ -19,6 +19,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         _cleanup_close_ int orig_stdout_fd = -1;
         int r;
 
+        if (size > 16*1024)
+                return 0; /* See the comment below about the limit for strv_length(). */
+
         /* We don't want to fill the logs with messages about parse errors.
          * Disable most logging if not running standalone */
         if (!getenv("SYSTEMD_LOG_LEVEL"))
index 5be4c0c7250faee6d028a471f942674519a60cbe..86d9f602fa0c5493d9f9a3c402fec727020dff94 100644 (file)
@@ -67,7 +67,7 @@ int verb_enable(int argc, char *argv[], void *userdata) {
         InstallChange *changes = NULL;
         size_t n_changes = 0;
         int carries_install_info = -1;
-        bool ignore_carries_install_info = arg_quiet;
+        bool ignore_carries_install_info = arg_quiet || arg_no_warn;
         int r;
 
         if (!argv[1])
@@ -109,9 +109,10 @@ int verb_enable(int argc, char *argv[], void *userdata) {
                 if (streq(verb, "enable")) {
                         r = unit_file_enable(arg_scope, flags, arg_root, names, &changes, &n_changes);
                         carries_install_info = r;
-                } else if (streq(verb, "disable"))
+                } else if (streq(verb, "disable")) {
                         r = unit_file_disable(arg_scope, flags, arg_root, names, &changes, &n_changes);
-                else if (streq(verb, "reenable")) {
+                        carries_install_info = r;
+                } else if (streq(verb, "reenable")) {
                         r = unit_file_reenable(arg_scope, flags, arg_root, names, &changes, &n_changes);
                         carries_install_info = r;
                 } else if (streq(verb, "link"))
@@ -165,7 +166,8 @@ int verb_enable(int argc, char *argv[], void *userdata) {
                         method = "EnableUnitFiles";
                         expect_carries_install_info = true;
                 } else if (streq(verb, "disable")) {
-                        method = "DisableUnitFiles";
+                        method = "DisableUnitFilesWithFlagsAndInstallInfo";
+                        expect_carries_install_info = true;
                         send_force = false;
                 } else if (streq(verb, "reenable")) {
                         method = "ReenableUnitFiles";
@@ -208,7 +210,10 @@ int verb_enable(int argc, char *argv[], void *userdata) {
                 }
 
                 if (send_runtime) {
-                        r = sd_bus_message_append(m, "b", arg_runtime);
+                        if (streq(method, "DisableUnitFilesWithFlagsAndInstallInfo"))
+                                r = sd_bus_message_append(m, "t", arg_runtime ? UNIT_FILE_RUNTIME : 0);
+                        else
+                                r = sd_bus_message_append(m, "b", arg_runtime);
                         if (r < 0)
                                 return bus_log_create_error(r);
                 }
@@ -245,7 +250,7 @@ int verb_enable(int argc, char *argv[], void *userdata) {
         if (carries_install_info == 0 && !ignore_carries_install_info)
                 log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, Also=,\n"
                            "Alias= settings in the [Install] section, and DefaultInstance= for template\n"
-                           "units). This means they are not meant to be enabled using systemctl.\n"
+                           "units). This means they are not meant to be enabled or disabled using systemctl.\n"
                            " \n" /* trick: the space is needed so that the line does not get stripped from output */
                            "Possible reasons for having this kind of units are:\n"
                            "%1$s A unit may be statically enabled by being symlinked from another unit's\n"
index 7218e900154f78b2edcf7bbda375d2087f6d2056..15e366ccae227bd65259523b3c30b7db1f292df3 100644 (file)
@@ -13,8 +13,8 @@ static int check_unit_generic(int code, const UnitActiveState good_states[], int
         _cleanup_strv_free_ char **names = NULL;
         UnitActiveState active_state;
         sd_bus *bus;
+        bool not_found = true, ok = false;
         int r;
-        bool found = false;
 
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
@@ -25,21 +25,32 @@ static int check_unit_generic(int code, const UnitActiveState good_states[], int
                 return log_error_errno(r, "Failed to expand names: %m");
 
         STRV_FOREACH(name, names) {
+                _cleanup_free_ char *load_state = NULL;
+
                 r = get_state_one_unit(bus, *name, &active_state);
                 if (r < 0)
                         return r;
 
+                r = unit_load_state(bus, *name, &load_state);
+                if (r < 0)
+                        return r;
+
                 if (!arg_quiet)
                         puts(unit_active_state_to_string(active_state));
 
                 for (int i = 0; i < nb_states; ++i)
-                        if (good_states[i] == active_state)
-                                found = true;
+                        if (good_states[i] == active_state) {
+                                ok = true;
+                                break;
+                        }
+
+                if (!streq(load_state, "not-found"))
+                        not_found = false;
         }
 
-        /* use the given return code for the case that we won't find
-         * any unit which matches the list */
-        return found ? 0 : code;
+        /* We use LSB code 4 ("program or service status is unknown")
+         * when the corresponding unit file doesn't exist. */
+        return ok ? EXIT_SUCCESS : not_found ? EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN : code;
 }
 
 int verb_is_active(int argc, char *argv[], void *userdata) {
index 2d33313eb826aca8d611ba793566dead6cf8d6db..e3bfd62c3f5a76de62f0fa3ae06e51ad797841a2 100644 (file)
@@ -58,7 +58,7 @@ static int show_installation_targets(sd_bus *bus, const char *name) {
 
 int verb_is_enabled(int argc, char *argv[], void *userdata) {
         _cleanup_strv_free_ char **names = NULL;
-        bool enabled;
+        bool not_found, enabled;
         int r;
 
         r = mangle_names("to check", strv_skip(argv, 1), &names);
@@ -69,15 +69,22 @@ int verb_is_enabled(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        enabled = r > 0;
+        not_found = r == 0; /* Doesn't have SysV support or SYSV_UNIT_NOT_FOUND */
+        enabled = r == SYSV_UNIT_ENABLED;
 
         if (install_client_side()) {
                 STRV_FOREACH(name, names) {
                         UnitFileState state;
 
                         r = unit_file_get_state(arg_scope, arg_root, *name, &state);
-                        if (r < 0)
+                        if (r == -ENOENT) {
+                                if (!arg_quiet)
+                                        puts("not-found");
+                                continue;
+                        } else if (r < 0)
                                 return log_error_errno(r, "Failed to get unit file state for %s: %m", *name);
+                        else
+                                not_found = false;
 
                         if (IN_SET(state,
                                    UNIT_FILE_ENABLED,
@@ -112,8 +119,19 @@ int verb_is_enabled(int argc, char *argv[], void *userdata) {
                         const char *s;
 
                         r = bus_call_method(bus, bus_systemd_mgr, "GetUnitFileState", &error, &reply, "s", *name);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
+                        if (r == -ENOENT) {
+                                sd_bus_error_free(&error);
+
+                                if (!arg_quiet)
+                                        puts("not-found");
+                                continue;
+                        } else if (r < 0)
+                                return log_error_errno(r,
+                                                       "Failed to get unit file state for %s: %s",
+                                                       *name,
+                                                       bus_error_message(&error, r));
+                        else
+                                not_found = false;
 
                         r = sd_bus_message_read(reply, "s", &s);
                         if (r < 0)
@@ -133,5 +151,5 @@ int verb_is_enabled(int argc, char *argv[], void *userdata) {
                 }
         }
 
-        return enabled ? EXIT_SUCCESS : EXIT_FAILURE;
+        return enabled ? EXIT_SUCCESS : not_found ? EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN : EXIT_FAILURE;
 }
index f6889993ed31276037aa0ca586a8aede8b87ba79..0412b0c62661c5c5b4377fd6ee5f9bb14568f73d 100644 (file)
@@ -113,6 +113,7 @@ int enable_sysv_units(const char *verb, char **args) {
 #if HAVE_SYSV_COMPAT
         _cleanup_(lookup_paths_free) LookupPaths paths = {};
         unsigned f = 0;
+        SysVUnitEnableState enable_state = SYSV_UNIT_NOT_FOUND;
 
         /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */
 
@@ -226,10 +227,12 @@ int enable_sysv_units(const char *verb, char **args) {
                         if (j == EXIT_SUCCESS) {
                                 if (!arg_quiet)
                                         puts("enabled");
-                                r = 1;
+                                enable_state = SYSV_UNIT_ENABLED;
                         } else {
                                 if (!arg_quiet)
                                         puts("disabled");
+                                if (enable_state != SYSV_UNIT_ENABLED)
+                                        enable_state = SYSV_UNIT_DISABLED;
                         }
 
                 } else if (j != EXIT_SUCCESS)
@@ -245,6 +248,8 @@ int enable_sysv_units(const char *verb, char **args) {
                 strv_remove(args + f, name);
         }
 
+        if (streq(verb, "is-enabled"))
+                return enable_state;
 #endif
         return r;
 }
index 86fd3ec1861cf3c8ba7056c99309f15c70156538..05db6ec6f2276aca63e941b6f4f964a4c070580b 100644 (file)
@@ -30,6 +30,12 @@ enum {
         EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN   = 4,
 };
 
+typedef enum SysVUnitEnableState {
+        SYSV_UNIT_NOT_FOUND = 0,
+        SYSV_UNIT_DISABLED,
+        SYSV_UNIT_ENABLED,
+} SysVUnitEnableState;
+
 int enable_sysv_units(const char *verb, char **args);
 
 int action_to_runlevel(void) _pure_;
index 4f2637f0f1e44015ce3f78ce1f3be05707692383..d13c7867e228e844b46227a13fa28924eb5dec5e 100644 (file)
@@ -22,6 +22,7 @@
 #include "rlimit-util.h"
 #include "sigbus.h"
 #include "signal-util.h"
+#include "stat-util.h"
 #include "string-table.h"
 #include "systemctl-add-dependency.h"
 #include "systemctl-cancel-job.h"
@@ -84,6 +85,7 @@ bool arg_show_types = false;
 int arg_check_inhibitors = -1;
 bool arg_dry_run = false;
 bool arg_quiet = false;
+bool arg_no_warn = false;
 bool arg_full = false;
 bool arg_recursive = false;
 bool arg_with_dependencies = false;
@@ -277,6 +279,8 @@ 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"
                "     --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"
@@ -433,6 +437,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_READ_ONLY,
                 ARG_MKDIR,
                 ARG_MARKED,
+                ARG_NO_WARN,
         };
 
         static const struct option options[] = {
@@ -465,6 +470,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "no-wall",             no_argument,       NULL, ARG_NO_WALL             },
                 { "dry-run",             no_argument,       NULL, ARG_DRY_RUN             },
                 { "quiet",               no_argument,       NULL, 'q'                     },
+                { "no-warn",             no_argument,       NULL, ARG_NO_WARN             },
                 { "root",                required_argument, NULL, ARG_ROOT                },
                 { "image",               required_argument, NULL, ARG_IMAGE               },
                 { "force",               no_argument,       NULL, 'f'                     },
@@ -926,6 +932,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_marked = true;
                         break;
 
+                case ARG_NO_WARN:
+                        arg_no_warn = true;
+                        break;
+
                 case '.':
                         /* Output an error mimicking getopt, and print a hint afterwards */
                         log_error("%s: invalid option -- '.'", program_invocation_name);
@@ -1148,6 +1158,13 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 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() ? " " : "");
+
         if (arg_action != ACTION_SYSTEMCTL && running_in_chroot() > 0) {
                 if (!arg_quiet)
                         log_info("Running in chroot, ignoring request.");
index 2454c4c714f7f26f79d76c396743daacc6e28ff9..1a7a6e28d39c961c7320bc8c8c3fe18566e4f399 100644 (file)
@@ -65,6 +65,7 @@ extern bool arg_show_types;
 extern int arg_check_inhibitors;
 extern bool arg_dry_run;
 extern bool arg_quiet;
+extern bool arg_no_warn;
 extern bool arg_full;
 extern bool arg_recursive;
 extern bool arg_with_dependencies;
index 5144eb4687298a55433ef60ae202120e84edd955..901d80eb3d54bc2868982be37e08fe66a1c3755a 100644 (file)
@@ -250,7 +250,7 @@ TEST(condition_test_host) {
         int r;
 
         r = sd_id128_get_machine(&id);
-        if (IN_SET(r, -ENOENT, -ENOMEDIUM))
+        if (IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG))
                 return (void) log_tests_skipped("/etc/machine-id missing");
         assert_se(r >= 0);
 
index da19272624fbd27f6b8b54c53d36430c2661dfba..de82020d5fe684105a35a5588ef4ddae5f3fe1cb 100644 (file)
@@ -219,4 +219,20 @@ TEST(quote_command_line) {
                                     "true \"\\$dollar\"");
 }
 
+static void test_octescape_one(const char *s, const char *expected) {
+        _cleanup_free_ char *ret;
+
+        assert_se(ret = octescape(s, strlen_ptr(s)));
+        log_debug("octescape(\"%s\") → \"%s\" (expected: \"%s\")", strnull(s), ret, expected);
+        assert_se(streq(ret, expected));
+}
+
+TEST(octescape) {
+        test_octescape_one(NULL, "");
+        test_octescape_one("", "");
+        test_octescape_one("foo", "foo");
+        test_octescape_one("\"\\\"", "\\042\\134\\042");
+        test_octescape_one("\123\213\222", "\123\\213\\222");
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 4bf0a5daf8765c85d4fcf8b9389addf166cf8a8a..69f630399021cc4cbcf2023cbe7bf4a818cd35d0 100644 (file)
@@ -27,7 +27,7 @@
 static const char *arg_test_dir = NULL;
 
 TEST(chase_symlinks) {
-        _cleanup_free_ char *result = NULL;
+        _cleanup_free_ char *result = NULL, *pwd = NULL;
         _cleanup_close_ int pfd = -1;
         char *temp;
         const char *top, *p, *pslash, *q, *qslash;
@@ -245,6 +245,30 @@ TEST(chase_symlinks) {
         assert_se(path_equal(result, p));
         result = mfree(result);
 
+        /* Relative paths */
+
+        assert_se(safe_getcwd(&pwd) >= 0);
+
+        assert_se(chdir(temp) >= 0);
+
+        p = "this/is/a/relative/path";
+        r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result, NULL);
+        assert_se(r == 0);
+
+        p = strjoina(temp, "/", p);
+        assert_se(path_equal(result, p));
+        result = mfree(result);
+
+        p = "this/is/a/relative/path";
+        r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result, NULL);
+        assert_se(r == 0);
+
+        p = strjoina(temp, "/", p);
+        assert_se(path_equal(result, p));
+        result = mfree(result);
+
+        assert_se(chdir(pwd) >= 0);
+
         /* Path which doesn't exist, but contains weird stuff */
 
         p = strjoina(temp, "/idontexist/..");
@@ -291,7 +315,7 @@ TEST(chase_symlinks) {
         assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
 
         r = chase_symlinks(p, NULL, 0, NULL, &pfd);
-        if (r != -ENOENT) {
+        if (r != -ENOENT && sd_id128_get_machine(NULL) >= 0) {
                 _cleanup_close_ int fd = -1;
                 sd_id128_t a, b;
 
@@ -301,7 +325,7 @@ TEST(chase_symlinks) {
                 assert_se(fd >= 0);
                 safe_close(pfd);
 
-                assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
+                assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &a) >= 0);
                 assert_se(sd_id128_get_machine(&b) >= 0);
                 assert_se(sd_id128_equal(a, b));
         }
index 377b79f1556d36cad898eca2e69187df27c0b87e..b8a54055c2b4e5ab65f3ed6c49e74300a18112ba 100644 (file)
@@ -46,4 +46,22 @@ TEST(gpt_types_against_architectures) {
                         }
 }
 
+TEST(verity_mappings) {
+        for (PartitionDesignator p = 0; p < _PARTITION_DESIGNATOR_MAX; p++) {
+                PartitionDesignator q;
+
+                q = partition_verity_of(p);
+                assert_se(q < 0 || partition_verity_to_data(q) == p);
+
+                q = partition_verity_sig_of(p);
+                assert_se(q < 0 || partition_verity_sig_to_data(q) == p);
+
+                q = partition_verity_to_data(p);
+                assert_se(q < 0 || partition_verity_of(q) == p);
+
+                q = partition_verity_sig_to_data(p);
+                assert_se(q < 0 || partition_verity_sig_of(q) == p);
+        }
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index cc9a7cb8381e5ffecac26619fd6e17c6684d7bff..afdc3b543681e37f351d2bca548abb2dd0148b6d 100644 (file)
@@ -73,6 +73,25 @@ TEST(undecchar) {
         assert_se(undecchar('9') == 9);
 }
 
+static void test_hexmem_one(const char *in, const char *expected) {
+        _cleanup_free_ char *result = NULL;
+        _cleanup_free_ void *mem = NULL;
+        size_t len;
+
+        assert_se(result = hexmem(in, strlen_ptr(in)));
+        log_debug("hexmem(\"%s\") → \"%s\" (expected: \"%s\")", strnull(in), result, expected);
+        assert_se(streq(result, expected));
+
+        assert_se(unhexmem(result, SIZE_MAX, &mem, &len) >= 0);
+        assert_se(memcmp_safe(mem, in, len) == 0);
+}
+
+TEST(hexmem) {
+        test_hexmem_one(NULL, "");
+        test_hexmem_one("", "");
+        test_hexmem_one("foo", "666f6f");
+}
+
 static void test_unhexmem_one(const char *s, size_t l, int retval) {
         _cleanup_free_ char *hex = NULL;
         _cleanup_free_ void *mem = NULL;
@@ -308,6 +327,120 @@ TEST(base64mem_linebreak) {
         }
 }
 
+static void test_base64_append_one(char **buf, size_t *len, const char *in, const char *expected) {
+        ssize_t new_len;
+
+        new_len = base64_append(buf, *len, in, strlen_ptr(in), 8, 12);
+        assert_se(new_len >= 0);
+        log_debug("base64_append_one(\"%s\")\nresult:\n%s\nexpected:\n%s", in, strnull(*buf), strnull(expected));
+        assert_se((size_t) new_len == strlen_ptr(*buf));
+        assert_se(streq_ptr(*buf, expected));
+        *len = new_len;
+}
+
+TEST(base64_append) {
+        _cleanup_free_ char *buf = NULL;
+        size_t len = 0;
+
+        test_base64_append_one(&buf, &len, "", NULL);
+        test_base64_append_one(&buf, &len, "f",
+                               "Zg==");
+        test_base64_append_one(&buf, &len, "fo",
+                               "Zg== Zm8=");
+        test_base64_append_one(&buf, &len, "foo",
+                               "Zg== Zm8=\n"
+                               "        Zm9v");
+        test_base64_append_one(&buf, &len, "foob",
+                               "Zg== Zm8=\n"
+                               "        Zm9v\n"
+                               "        Zm9v\n"
+                               "        Yg==");
+        test_base64_append_one(&buf, &len, "fooba",
+                               "Zg== Zm8=\n"
+                               "        Zm9v\n"
+                               "        Zm9v\n"
+                               "        Yg==\n"
+                               "        Zm9v\n"
+                               "        YmE=");
+        test_base64_append_one(&buf, &len, "foobar",
+                               "Zg== Zm8=\n"
+                               "        Zm9v\n"
+                               "        Zm9v\n"
+                               "        Yg==\n"
+                               "        Zm9v\n"
+                               "        YmE=\n"
+                               "        Zm9v\n"
+                               "        YmFy");
+
+        assert_se(free_and_strdup(&buf, "hogehogehogehoge") >= 0);
+        len = strlen(buf);
+
+        test_base64_append_one(&buf, &len, "",
+                               "hogehogehogehoge");
+        test_base64_append_one(&buf, &len, "f",
+                               "hogehogehogehoge\n"
+                               "        Zg==");
+        test_base64_append_one(&buf, &len, "fo",
+                               "hogehogehogehoge\n"
+                               "        Zg==\n"
+                               "        Zm8=");
+        test_base64_append_one(&buf, &len, "foo",
+                               "hogehogehogehoge\n"
+                               "        Zg==\n"
+                               "        Zm8=\n"
+                               "        Zm9v");
+        test_base64_append_one(&buf, &len, "foob",
+                               "hogehogehogehoge\n"
+                               "        Zg==\n"
+                               "        Zm8=\n"
+                               "        Zm9v\n"
+                               "        Zm9v\n"
+                               "        Yg==");
+        test_base64_append_one(&buf, &len, "fooba",
+                               "hogehogehogehoge\n"
+                               "        Zg==\n"
+                               "        Zm8=\n"
+                               "        Zm9v\n"
+                               "        Zm9v\n"
+                               "        Yg==\n"
+                               "        Zm9v\n"
+                               "        YmE=");
+        test_base64_append_one(&buf, &len, "foobar",
+                               "hogehogehogehoge\n"
+                               "        Zg==\n"
+                               "        Zm8=\n"
+                               "        Zm9v\n"
+                               "        Zm9v\n"
+                               "        Yg==\n"
+                               "        Zm9v\n"
+                               "        YmE=\n"
+                               "        Zm9v\n"
+                               "        YmFy");
+
+        assert_se(free_and_strdup(&buf, "hogehogehogehoge") >= 0);
+        len = strlen(buf);
+
+        test_base64_append_one(&buf, &len, "foobarfoobarfoobarfoobar",
+                               "hogehogehogehoge\n"
+                               "        Zm9v\n"
+                               "        YmFy\n"
+                               "        Zm9v\n"
+                               "        YmFy\n"
+                               "        Zm9v\n"
+                               "        YmFy\n"
+                               "        Zm9v\n"
+                               "        YmFy");
+
+        assert_se(free_and_strdup(&buf, "aaa") >= 0);
+        len = strlen(buf);
+
+        test_base64_append_one(&buf, &len, "foobarfoobarfoobarfoobar",
+                               "aaa Zm9vYmFy\n"
+                               "    Zm9vYmFy\n"
+                               "    Zm9vYmFy\n"
+                               "    Zm9vYmFy");
+}
+
 static void test_unbase64mem_one(const char *input, const char *output, int ret) {
         _cleanup_free_ void *buffer = NULL;
         size_t size = 0;
index 4b71c5c00b984cb01fbfa5aba9a0ea6e46e4a972..afdbf1e4b9016329846d77c7fcfdf7e6a62c6321 100644 (file)
@@ -38,7 +38,7 @@ TEST(id128) {
         assert_se(!sd_id128_in_set(id, ID128_WALDI));
         assert_se(!sd_id128_in_set(id, ID128_WALDI, ID128_WALDI));
 
-        if (sd_booted() > 0 && access("/etc/machine-id", F_OK) >= 0) {
+        if (sd_booted() > 0 && sd_id128_get_machine(NULL) >= 0) {
                 assert_se(sd_id128_get_machine(&id) == 0);
                 printf("machine: %s\n", sd_id128_to_string(id, t));
 
@@ -86,17 +86,17 @@ TEST(id128) {
 
         /* First, write as UUID */
         assert_se(sd_id128_randomize(&id) >= 0);
-        assert_se(id128_write_fd(fd, ID128_UUID, id, false) >= 0);
+        assert_se(id128_write_fd(fd, ID128_FORMAT_UUID, id) >= 0);
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) == -EUCLEAN);
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) >= 0);
         assert_se(sd_id128_equal(id, id2));
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_ANY, &id2) >= 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, &id2) >= 0);
         assert_se(sd_id128_equal(id, id2));
 
         /* Second, write as plain */
@@ -104,17 +104,17 @@ TEST(id128) {
         assert_se(ftruncate(fd, 0) >= 0);
 
         assert_se(sd_id128_randomize(&id) >= 0);
-        assert_se(id128_write_fd(fd, ID128_PLAIN, id, false) >= 0);
+        assert_se(id128_write_fd(fd, ID128_FORMAT_PLAIN, id) >= 0);
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_UUID, &id2) == -EINVAL);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) == -EUCLEAN);
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) >= 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) >= 0);
         assert_se(sd_id128_equal(id, id2));
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_ANY, &id2) >= 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, &id2) >= 0);
         assert_se(sd_id128_equal(id, id2));
 
         /* Third, write plain without trailing newline */
@@ -125,13 +125,13 @@ TEST(id128) {
         assert_se(write(fd, sd_id128_to_string(id, t), 32) == 32);
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_UUID, &id2) == -EINVAL);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) == -EUCLEAN);
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) >= 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) >= 0);
         assert_se(sd_id128_equal(id, id2));
 
-        /* Third, write UUID without trailing newline */
+        /* Fourth, write UUID without trailing newline */
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
         assert_se(ftruncate(fd, 0) >= 0);
 
@@ -139,13 +139,38 @@ TEST(id128) {
         assert_se(write(fd, sd_id128_to_uuid_string(id, q), 36) == 36);
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) == -EUCLEAN);
 
         assert_se(lseek(fd, 0, SEEK_SET) == 0);
-        assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) >= 0);
         assert_se(sd_id128_equal(id, id2));
 
-        if (sd_booted() > 0 && access("/etc/machine-id", F_OK) >= 0) {
+        /* Fifth, tests for "uninitialized" */
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(ftruncate(fd, 0) >= 0);
+        assert_se(write(fd, "uninitialized", STRLEN("uninitialized")) == STRLEN("uninitialized"));
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -ENOPKG);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(ftruncate(fd, 0) >= 0);
+        assert_se(write(fd, "uninitialized\n", STRLEN("uninitialized\n")) == STRLEN("uninitialized\n"));
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -ENOPKG);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(ftruncate(fd, 0) >= 0);
+        assert_se(write(fd, "uninitialized\nfoo", STRLEN("uninitialized\nfoo")) == STRLEN("uninitialized\nfoo"));
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -EUCLEAN);
+
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(ftruncate(fd, 0) >= 0);
+        assert_se(write(fd, "uninit", STRLEN("uninit")) == STRLEN("uninit"));
+        assert_se(lseek(fd, 0, SEEK_SET) == 0);
+        assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -EUCLEAN);
+
+        if (sd_booted() > 0 && sd_id128_get_machine(NULL) >= 0) {
                 assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0);
                 assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0);
                 assert_se(sd_id128_equal(id, id2));
@@ -170,8 +195,8 @@ TEST(benchmark_sd_id128_get_machine_app_specific) {
         unsigned iterations = slow_tests_enabled() ? 1000000 : 1000;
         usec_t t, q;
 
-        if (access("/etc/machine-id", F_OK) < 0 && errno == ENOENT)
-                return (void) log_tests_skipped("/etc/machine-id does not exist");
+        if (sd_id128_get_machine(NULL) < 0)
+                return (void) log_tests_skipped("/etc/machine-id is not initialized");
 
         log_info("/* %s (%u iterations) */", __func__, iterations);
 
index 711277550747d4bd794dcd848500ad1a669aa6aa..8206eb01a2f46a15d5997fb8df16ea166eb0f72d 100644 (file)
@@ -17,7 +17,7 @@ TEST(install_file) {
         assert_se(a = path_join(p, "foo"));
         assert_se(b = path_join(p, "bar"));
 
-        RUN_WITH_UMASK(0077)
+        WITH_UMASK(0077)
                 assert_se(write_string_file(a, "wups", WRITE_STRING_FILE_CREATE) >= 0);
 
         assert_se(lstat(a, &stat1) >= 0);
index 17ad2017f8c966aab547440c6579876e68408280..7ff9c560dd34c802011b9a516df4f9004041dc2d 100644 (file)
@@ -663,4 +663,67 @@ TEST(json_append) {
         assert_se(json_variant_equal(v, w));
 }
 
+static inline void json_array_append_with_source_one(bool source) {
+        _cleanup_(json_variant_unrefp) JsonVariant *a, *b;
+
+        /* Parse two sources, each with a different name and line/column numbers */
+
+        assert_se(json_parse_with_source(" [41]", source ? "string 1" : NULL, 0,
+                                         &a, NULL, NULL) >= 0);
+        assert_se(json_parse_with_source("\n\n   [42]", source ? "string 2" : NULL, 0,
+                                         &b, NULL, NULL) >= 0);
+
+        assert_se(json_variant_is_array(a));
+        assert_se(json_variant_elements(a) == 1);
+        assert_se(json_variant_is_array(b));
+        assert_se(json_variant_elements(b) == 1);
+
+        /* Verify source information */
+
+        const char *s1, *s2;
+        unsigned line1, col1, line2, col2;
+        assert_se(json_variant_get_source(a, &s1, &line1, &col1) >= 0);
+        assert_se(json_variant_get_source(b, &s2, &line2, &col2) >= 0);
+
+        assert_se(streq_ptr(s1, source ? "string 1" : NULL));
+        assert_se(streq_ptr(s2, source ? "string 2" : NULL));
+        assert_se(line1 == 1);
+        assert_se(col1 == 2);
+        assert_se(line2 == 3);
+        assert_se(col2 == 4);
+
+        /* Append one elem from the second array (and source) to the first. */
+
+        JsonVariant *elem;
+        assert_se(elem = json_variant_by_index(b, 0));
+        assert_se(json_variant_is_integer(elem));
+        assert_se(json_variant_elements(elem) == 0);
+
+        assert_se(json_variant_append_array(&a, elem) >= 0);
+
+        assert_se(json_variant_is_array(a));
+        assert_se(json_variant_elements(a) == 2);
+
+        /* Verify that source information was propagated correctly */
+
+        assert_se(json_variant_get_source(elem, &s1, &line1, &col1) >= 0);
+        assert_se(elem = json_variant_by_index(a, 1));
+        assert_se(json_variant_get_source(elem, &s2, &line2, &col2) >= 0);
+
+        assert_se(streq_ptr(s1, source ? "string 2" : NULL));
+        assert_se(streq_ptr(s2, source ? "string 2" : NULL));
+        assert_se(line1 == 3);
+        assert_se(col1 == 5);
+        assert_se(line2 == 3);
+        assert_se(col2 == 5);
+}
+
+TEST(json_array_append_with_source) {
+        json_array_append_with_source_one(true);
+}
+
+TEST(json_array_append_without_source) {
+        json_array_append_with_source_one(false);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 3cf704134c0b08dcfa76e7f7ffc6a48c37965c7d..997c2b252461c93135fb4cc1fb2d974adc362fcc 100644 (file)
@@ -5,6 +5,8 @@
 #include <stdio.h>
 #include <unistd.h>
 
+#include "sd-id128.h"
+
 #include "all-units.h"
 #include "alloc-util.h"
 #include "capability-util.h"
@@ -518,7 +520,7 @@ TEST(install_printf, .sd_booted = true) {
 
         _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *gid = NULL, *group = NULL, *uid = NULL, *user = NULL;
 
-        if (access("/etc/machine-id", F_OK) >= 0)
+        if (sd_id128_get_machine(NULL) >= 0)
                 assert_se(specifier_machine_id('m', NULL, NULL, NULL, &mid) >= 0 && mid);
         if (sd_booted() > 0)
                 assert_se(specifier_boot_id('b', NULL, NULL, NULL, &bid) >= 0 && bid);
index aa501b650aa0b5ba1f1cc88ef163dafb5092677a..dd96af6894a7f23b24e45bac520e5b70bc89c43a 100644 (file)
@@ -82,7 +82,7 @@ TEST(keymaps) {
 
 #define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
 TEST(dump_special_glyphs) {
-        assert_cc(SPECIAL_GLYPH_SPARKLES + 1 == _SPECIAL_GLYPH_MAX);
+        assert_cc(SPECIAL_GLYPH_WARNING_SIGN + 1 == _SPECIAL_GLYPH_MAX);
 
         log_info("is_locale_utf8: %s", yes_no(is_locale_utf8()));
 
@@ -119,6 +119,7 @@ TEST(dump_special_glyphs) {
         dump_glyph(SPECIAL_GLYPH_RECYCLING);
         dump_glyph(SPECIAL_GLYPH_DOWNLOAD);
         dump_glyph(SPECIAL_GLYPH_SPARKLES);
+        dump_glyph(SPECIAL_GLYPH_WARNING_SIGN);
 }
 
 DEFINE_TEST_MAIN(LOG_INFO);
index 001166d0dcce4c9d5fcf6e703ed0e6ba49034d65..d4f32496b7e71ddecae4b9958a379461a50f16e3 100644 (file)
@@ -531,4 +531,57 @@ TEST(ISPOWEROF2) {
         assert_se(!ISPOWEROF2(u));
 }
 
+TEST(ALIGNED) {
+        assert_se(IS_ALIGNED16(NULL));
+        assert_se(IS_ALIGNED32(NULL));
+        assert_se(IS_ALIGNED64(NULL));
+
+        uint64_t u64;
+        uint32_t u32;
+        uint16_t u16;
+
+        assert_se(IS_ALIGNED16(&u16));
+        assert_se(IS_ALIGNED16(&u32));
+        assert_se(IS_ALIGNED16(&u64));
+        assert_se(IS_ALIGNED32(&u32));
+        assert_se(IS_ALIGNED32(&u64));
+        assert_se(IS_ALIGNED64(&u64));
+
+        _align_(32) uint8_t ua256;
+        _align_(8) uint8_t ua64;
+        _align_(4) uint8_t ua32;
+        _align_(2) uint8_t ua16;
+
+        assert_se(IS_ALIGNED16(&ua256));
+        assert_se(IS_ALIGNED32(&ua256));
+        assert_se(IS_ALIGNED64(&ua256));
+
+        assert_se(IS_ALIGNED16(&ua64));
+        assert_se(IS_ALIGNED32(&ua64));
+        assert_se(IS_ALIGNED64(&ua64));
+
+        assert_se(IS_ALIGNED16(&ua32));
+        assert_se(IS_ALIGNED32(&ua32));
+
+        assert_se(IS_ALIGNED16(&ua16));
+
+#ifdef __x86_64__
+        /* Conditionalized on x86-64, since there we know for sure that all three types are aligned to
+         * their size. Too lazy to figure it out for other archs */
+        void *p = UINT_TO_PTR(1); /* definitely not aligned */
+        assert_se(!IS_ALIGNED16(p));
+        assert_se(!IS_ALIGNED32(p));
+        assert_se(!IS_ALIGNED64(p));
+
+        assert_se(IS_ALIGNED16(ALIGN2_PTR(p)));
+        assert_se(IS_ALIGNED32(ALIGN4_PTR(p)));
+        assert_se(IS_ALIGNED64(ALIGN8_PTR(p)));
+
+        p = UINT_TO_PTR(-1); /* also definitely not aligned */
+        assert_se(!IS_ALIGNED16(p));
+        assert_se(!IS_ALIGNED32(p));
+        assert_se(!IS_ALIGNED64(p));
+#endif
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index 7e06fc419cdc62d0a4946a7b7b4a647c90d08ac2..fddf70584f02b5ea0a825d56df23b059ab51e51a 100644 (file)
@@ -11,6 +11,7 @@
 #include "missing_mount.h"
 #include "mkdir.h"
 #include "mount-util.h"
+#include "mountpoint-util.h"
 #include "namespace-util.h"
 #include "path-util.h"
 #include "process-util.h"
@@ -36,14 +37,14 @@ TEST(mount_option_mangle) {
         assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
         assert_se(opts == NULL);
 
-        assert_se(mount_option_mangle("ro,nosuid,nodev,noexec,mode=755", 0, &f, &opts) == 0);
+        assert_se(mount_option_mangle("ro,nosuid,nodev,noexec,mode=0755", 0, &f, &opts) == 0);
         assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
-        assert_se(streq(opts, "mode=755"));
+        assert_se(streq(opts, "mode=0755"));
         opts = mfree(opts);
 
-        assert_se(mount_option_mangle("rw,nosuid,foo,hogehoge,nodev,mode=755", 0, &f, &opts) == 0);
+        assert_se(mount_option_mangle("rw,nosuid,foo,hogehoge,nodev,mode=0755", 0, &f, &opts) == 0);
         assert_se(f == (MS_NOSUID|MS_NODEV));
-        assert_se(streq(opts, "foo,hogehoge,mode=755"));
+        assert_se(streq(opts, "foo,hogehoge,mode=0755"));
         opts = mfree(opts);
 
         assert_se(mount_option_mangle("rw,nosuid,nodev,noexec,relatime,net_cls,net_prio", MS_RDONLY, &f, &opts) == 0);
@@ -51,19 +52,19 @@ TEST(mount_option_mangle) {
         assert_se(streq(opts, "net_cls,net_prio"));
         opts = mfree(opts);
 
-        assert_se(mount_option_mangle("rw,nosuid,nodev,relatime,size=1630748k,mode=700,uid=1000,gid=1000", MS_RDONLY, &f, &opts) == 0);
+        assert_se(mount_option_mangle("rw,nosuid,nodev,relatime,size=1630748k,mode=0700,uid=1000,gid=1000", MS_RDONLY, &f, &opts) == 0);
         assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
-        assert_se(streq(opts, "size=1630748k,mode=700,uid=1000,gid=1000"));
+        assert_se(streq(opts, "size=1630748k,mode=0700,uid=1000,gid=1000"));
         opts = mfree(opts);
 
-        assert_se(mount_option_mangle("size=1630748k,rw,gid=1000,,,nodev,relatime,,mode=700,nosuid,uid=1000", MS_RDONLY, &f, &opts) == 0);
+        assert_se(mount_option_mangle("size=1630748k,rw,gid=1000,,,nodev,relatime,,mode=0700,nosuid,uid=1000", MS_RDONLY, &f, &opts) == 0);
         assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
-        assert_se(streq(opts, "size=1630748k,gid=1000,mode=700,uid=1000"));
+        assert_se(streq(opts, "size=1630748k,gid=1000,mode=0700,uid=1000"));
         opts = mfree(opts);
 
-        assert_se(mount_option_mangle("rw,exec,size=8143984k,nr_inodes=2035996,mode=755", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, &f, &opts) == 0);
+        assert_se(mount_option_mangle("rw,exec,size=8143984k,nr_inodes=2035996,mode=0755", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, &f, &opts) == 0);
         assert_se(f == (MS_NOSUID|MS_NODEV));
-        assert_se(streq(opts, "size=8143984k,nr_inodes=2035996,mode=755"));
+        assert_se(streq(opts, "size=8143984k,nr_inodes=2035996,mode=0755"));
         opts = mfree(opts);
 
         assert_se(mount_option_mangle("rw,relatime,fmask=0022,,,dmask=0022", MS_RDONLY, &f, &opts) == 0);
@@ -73,9 +74,9 @@ TEST(mount_option_mangle) {
 
         assert_se(mount_option_mangle("rw,relatime,fmask=0022,dmask=0022,\"hogehoge", MS_RDONLY, &f, &opts) < 0);
 
-        assert_se(mount_option_mangle("mode=1777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"", 0, &f, &opts) == 0);
+        assert_se(mount_option_mangle("mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"", 0, &f, &opts) == 0);
         assert_se(f == 0);
-        assert_se(streq(opts, "mode=1777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""));
+        assert_se(streq(opts, "mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""));
         opts = mfree(opts);
 }
 
index df3ae98f5050238f5bf0c58e08d3c9db29d85a5b..bd5bc9cb437029d2571323bf2a9f2979d2ddb020 100644 (file)
@@ -12,7 +12,7 @@ int main(int argc, char *argv[]) {
         u = umask(0111);
 
         n = 0;
-        RUN_WITH_UMASK(0123) {
+        WITH_UMASK(0123) {
                 assert_se(umask(000) == 0123);
                 n++;
         }
@@ -20,7 +20,7 @@ int main(int argc, char *argv[]) {
         assert_se(n == 1);
         assert_se(umask(u) == 0111);
 
-        RUN_WITH_UMASK(0135) {
+        WITH_UMASK(0135) {
                 assert_se(umask(000) == 0135);
                 n++;
         }
@@ -28,7 +28,7 @@ int main(int argc, char *argv[]) {
         assert_se(n == 2);
         assert_se(umask(0111) == u);
 
-        RUN_WITH_UMASK(0315) {
+        WITH_UMASK(0315) {
                 assert_se(umask(000) == 0315);
                 n++;
                 break;
index 98fffc7c87e4fb12f86b1b022d71eb52c823dffa..c6428e844f8ecbbfc7558da6d2e57ed3f95560a7 100644 (file)
@@ -3,6 +3,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "sd-id128.h"
+
 #include "alloc-util.h"
 #include "all-units.h"
 #include "glob-util.h"
@@ -261,7 +263,7 @@ TEST_RET(unit_printf, .sd_booted = true) {
         assert_se(short_hostname);
         assert_se(specifier_pretty_hostname('q', NULL, NULL, NULL, &pretty_hostname) == 0);
         assert_se(pretty_hostname);
-        if (access("/etc/machine-id", F_OK) >= 0) {
+        if (sd_id128_get_machine(NULL) >= 0) {
                 assert_se(specifier_machine_id('m', NULL, NULL, NULL, &machine_id) >= 0);
                 assert_se(machine_id);
         }
index 5b076157aac71ebb7ffc8f4508b83afbc1de0446..f70873ff6eb7e2eb44a0f72e28aff47bd3c3d725 100644 (file)
@@ -119,8 +119,10 @@ static int manager_send_request(Manager *m) {
         m->event_timeout = sd_event_source_unref(m->event_timeout);
 
         r = manager_listen_setup(m);
-        if (r < 0)
-                return log_warning_errno(r, "Failed to set up connection socket: %m");
+        if (r < 0) {
+                log_warning_errno(r, "Failed to set up connection socket: %m");
+                return manager_connect(m);
+        }
 
         /*
          * Set transmit timestamp, remember it; the server will send that back
@@ -752,7 +754,7 @@ static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct ad
                 assert(ai->ai_addrlen >= offsetof(struct sockaddr, sa_data));
 
                 if (!IN_SET(ai->ai_addr->sa_family, AF_INET, AF_INET6)) {
-                        log_warning("Unsuitable address protocol for %s", m->current_server_name->string);
+                        log_debug("Ignoring unsuitable address protocol for %s.", m->current_server_name->string);
                         continue;
                 }
 
@@ -806,11 +808,6 @@ int manager_connect(Manager *m) {
         if (m->current_server_address && m->current_server_address->addresses_next)
                 manager_set_server_address(m, m->current_server_address->addresses_next);
         else {
-                static const struct addrinfo hints = {
-                        .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG,
-                        .ai_socktype = SOCK_DGRAM,
-                };
-
                 /* Hmm, we are through all addresses, let's look for the next host instead */
                 if (m->current_server_name && m->current_server_name->names_next)
                         manager_set_server_name(m, m->current_server_name->names_next);
@@ -878,6 +875,12 @@ int manager_connect(Manager *m) {
 
                 log_debug("Resolving %s...", m->current_server_name->string);
 
+                struct addrinfo hints = {
+                        .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG,
+                        .ai_socktype = SOCK_DGRAM,
+                        .ai_family = socket_ipv6_is_supported() ? AF_UNSPEC : AF_INET,
+                };
+
                 r = resolve_getaddrinfo(m->resolve, &m->resolve_query, m->current_server_name->string, "123", &hints, manager_resolve_handler, NULL, m);
                 if (r < 0)
                         return log_error_errno(r, "Failed to create resolver: %m");
index f156d90073dc27557648ce4e5b6868303614207c..efe0e107b1467cdf381e375624b3281014f630bf 100644 (file)
@@ -1499,7 +1499,7 @@ static int create_file(Item *i, const char *path) {
         if (dir_fd < 0)
                 return dir_fd;
 
-        RUN_WITH_UMASK(0000) {
+        WITH_UMASK(0000) {
                 mac_selinux_create_file_prepare(path, S_IFREG);
                 fd = RET_NERRNO(openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode));
                 mac_selinux_create_file_clear();
@@ -1572,7 +1572,7 @@ static int truncate_file(Item *i, const char *path) {
         if (fd == -ENOENT) {
                 creation = CREATION_NORMAL; /* Didn't work without O_CREATE, try again with */
 
-                RUN_WITH_UMASK(0000) {
+                WITH_UMASK(0000) {
                         mac_selinux_create_file_prepare(path, S_IFREG);
                         fd = RET_NERRNO(openat(dir_fd, bn, O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode));
                         mac_selinux_create_file_clear();
@@ -1716,14 +1716,14 @@ static int create_directory_or_subvolume(
 
                         subvol = false;
                 else {
-                        RUN_WITH_UMASK((~mode) & 0777)
+                        WITH_UMASK((~mode) & 0777)
                                 r = btrfs_subvol_make_fd(pfd, bn);
                 }
         } else
                 r = 0;
 
         if (!subvol || ERRNO_IS_NOT_SUPPORTED(r))
-                RUN_WITH_UMASK(0000)
+                WITH_UMASK(0000)
                         r = mkdirat_label(pfd, bn, mode);
 
         creation = r >= 0 ? CREATION_NORMAL : CREATION_EXISTING;
@@ -1869,7 +1869,7 @@ static int create_device(Item *i, mode_t file_type) {
         if (dfd < 0)
                 return dfd;
 
-        RUN_WITH_UMASK(0000) {
+        WITH_UMASK(0000) {
                 mac_selinux_create_file_prepare(i->path, file_type);
                 r = RET_NERRNO(mknodat(dfd, bn, i->mode | file_type, i->major_minor));
                 mac_selinux_create_file_clear();
@@ -1900,7 +1900,7 @@ static int create_device(Item *i, mode_t file_type) {
                 if (i->append_or_force) {
                         fd = safe_close(fd);
 
-                        RUN_WITH_UMASK(0000) {
+                        WITH_UMASK(0000) {
                                 mac_selinux_create_file_prepare(i->path, file_type);
                                 r = mknodat_atomic(dfd, bn, i->mode | file_type, i->major_minor);
                                 mac_selinux_create_file_clear();
@@ -1971,7 +1971,7 @@ static int create_fifo(Item *i) {
         if (pfd < 0)
                 return pfd;
 
-        RUN_WITH_UMASK(0000) {
+        WITH_UMASK(0000) {
                 mac_selinux_create_file_prepare(i->path, S_IFIFO);
                 r = RET_NERRNO(mkfifoat(pfd, bn, i->mode));
                 mac_selinux_create_file_clear();
@@ -1996,7 +1996,7 @@ static int create_fifo(Item *i) {
                 if (i->append_or_force) {
                         fd = safe_close(fd);
 
-                        RUN_WITH_UMASK(0000) {
+                        WITH_UMASK(0000) {
                                 mac_selinux_create_file_prepare(i->path, S_IFIFO);
                                 r = mkfifoat_atomic(pfd, bn, i->mode);
                                 mac_selinux_create_file_clear();
@@ -2378,7 +2378,7 @@ static int mkdir_parents_rm_if_wrong_type(mode_t child_mode, const char *path) {
                 if (r == -ENOENT)
                         r = rm_if_wrong_type_safe(S_IFDIR, parent_fd, &parent_st, t, AT_SYMLINK_NOFOLLOW);
                 if (r == -ENOENT) {
-                        RUN_WITH_UMASK(0000)
+                        WITH_UMASK(0000)
                                 r = mkdirat_label(parent_fd, t, 0755);
                         if (r < 0) {
                                 _cleanup_free_ char *parent_name = NULL;
@@ -2416,7 +2416,7 @@ static int mkdir_parents_item(Item *i, mode_t child_mode) {
                 if (r < 0 && r != -ENOENT)
                         return r;
         } else
-                RUN_WITH_UMASK(0000)
+                WITH_UMASK(0000)
                         (void) mkdir_parents_label(i->path, 0755);
 
         return 0;
index 6dd9eebd9323fcc4d9e610d642fc2121ed0067c1..e67a02aee6e063e91f38b24990688b062da7ad9c 100644 (file)
@@ -247,5 +247,5 @@ static int builtin_keyboard(sd_device *dev, sd_netlink **rtnl, int argc, char *a
 const UdevBuiltin udev_builtin_keyboard = {
         .name = "keyboard",
         .cmd = builtin_keyboard,
-        .help = "Keyboard scan code to key mapping",
+        .help = "Keyboard scancode mapping and touchpad/pointingstick characteristics",
 };
index a4468cf24f0e45ba2d10cf43b1fabaad750535fd..2f5429fd89d1e9f7ff0043c127a851bdb3079ea8 100644 (file)
@@ -87,6 +87,8 @@ int hwdb_main(int argc, char *argv[], void *userdata) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Either --update or --test must be used.");
 
+        log_notice("udevadm hwdb is deprecated. Use systemd-hwdb instead.");
+
         if (arg_update) {
                 r = hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, true);
                 if (r < 0)
diff --git a/src/ukify/test/example.signing.crt.base64 b/src/ukify/test/example.signing.crt.base64
new file mode 100644 (file)
index 0000000..694d13b
--- /dev/null
@@ -0,0 +1,23 @@
+LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURsVENDQW4yZ0F3SUJBZ0lVTzlqUWhhblhj
+b3ViOERzdXlMMWdZbksrR1lvd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1dURUxNQWtHQTFVRUJoTUNX
+Rmd4RlRBVEJnTlZCQWNNREVSbFptRjFiSFFnUTJsMGVURWNNQm9HQTFVRQpDZ3dUUkdWbVlYVnNk
+Q0JEYjIxd1lXNTVJRXgwWkRFVk1CTUdBMVVFQXd3TWEyVjVJSE5wWjI1cGJtbG5NQ0FYCkRUSXlN
+VEF5T1RFM01qY3dNVm9ZRHpNd01qSXdNekF4TVRjeU56QXhXakJaTVFzd0NRWURWUVFHRXdKWVdE
+RVYKTUJNR0ExVUVCd3dNUkdWbVlYVnNkQ0JEYVhSNU1Sd3dHZ1lEVlFRS0RCTkVaV1poZFd4MElF
+TnZiWEJoYm5rZwpUSFJrTVJVd0V3WURWUVFEREF4clpYa2djMmxuYm1sdWFXY3dnZ0VpTUEwR0NT
+cUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRREtVeHR4Y0d1aGYvdUp1SXRjWEhvdW0v
+RE9RL1RJM3BzUWlaR0ZWRkJzbHBicU5wZDUKa2JDaUFMNmgrY1FYaGRjUmlOT1dBR0wyMFZ1T2Rv
+VTZrYzlkdklGQnFzKzc2NHhvWGY1UGd2SlhvQUxSUGxDZAp4YVdPQzFsOFFIRHpxZ09SdnREMWNI
+WFoveTkvZ1YxVU1GK1FlYm12aUhRN0U4eGw1T2h5MG1TQVZYRDhBTitsCjdpMUR6N0NuTzhrMVph
+alhqYXlpNWV1WEV0TnFSZXNuVktRRElTQ0t2STFueUxySWxHRU1GZmFuUmRLQWthZ3MKalJnTmVh
+T3N3aklHNjV6UzFVdjJTZXcxVFpIaFhtUmd5TzRVT0JySHZlSml2T2hObzU3UlRKd0M2K2lGY0FG
+aApSSnorVmM2QUlSSkI1ZWtJUmdCN3VDNEI5ZmwydXdZKytMODNBZ01CQUFHalV6QlJNQjBHQTFV
+ZERnUVdCQlFqCllIMnpzVFlPQU51MkcweXk1QkxlOHBvbWZUQWZCZ05WSFNNRUdEQVdnQlFqWUgy
+enNUWU9BTnUyRzB5eTVCTGUKOHBvbWZUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0li
+M0RRRUJDd1VBQTRJQkFRQ2dxcmFXaE51dQptUmZPUjVxcURVcC83RkpIL1N6Zk1vaDBHL2lWRkhv
+OUpSS0tqMUZ2Q0VZc1NmeThYTmdaUDI5eS81Z0h4cmcrCjhwZWx6bWJLczdhUTRPK01TcmIzTm11
+V1IzT0M0alBoNENrM09ZbDlhQy9iYlJqSWFvMDJ6K29XQWNZZS9xYTEKK2ZsemZWVEUwMHJ5V1RM
+K0FJdDFEZEVqaG01WXNtYlgvbWtacUV1TjBtSVhhRXhSVE9walczUWRNeVRQaURTdApvanQvQWMv
+R2RUWDd0QkhPTk44Z3djaC91V293aVNORERMUm1wM2VScnlOZ3RPKzBISUd5Qm16ZWNsM0VlVEo2
+CnJzOGRWUFhqR1Z4dlZDb2tqQllrOWdxbkNGZEJCMGx4VXVNZldWdVkyRUgwSjI3aGh4SXNFc3ls
+VTNIR1EyK2MKN1JicVY4VTNSRzA4Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
diff --git a/src/ukify/test/example.signing.key.base64 b/src/ukify/test/example.signing.key.base64
new file mode 100644 (file)
index 0000000..88baedb
--- /dev/null
@@ -0,0 +1,30 @@
+LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZB
+QVNDQktnd2dnU2tBZ0VBQW9JQkFRREtVeHR4Y0d1aGYvdUoKdUl0Y1hIb3VtL0RPUS9USTNwc1Fp
+WkdGVkZCc2xwYnFOcGQ1a2JDaUFMNmgrY1FYaGRjUmlOT1dBR0wyMFZ1Twpkb1U2a2M5ZHZJRkJx
+cys3NjR4b1hmNVBndkpYb0FMUlBsQ2R4YVdPQzFsOFFIRHpxZ09SdnREMWNIWFoveTkvCmdWMVVN
+RitRZWJtdmlIUTdFOHhsNU9oeTBtU0FWWEQ4QU4rbDdpMUR6N0NuTzhrMVphalhqYXlpNWV1WEV0
+TnEKUmVzblZLUURJU0NLdkkxbnlMcklsR0VNRmZhblJkS0FrYWdzalJnTmVhT3N3aklHNjV6UzFV
+djJTZXcxVFpIaApYbVJneU80VU9Cckh2ZUppdk9oTm81N1JUSndDNitpRmNBRmhSSnorVmM2QUlS
+SkI1ZWtJUmdCN3VDNEI5ZmwyCnV3WSsrTDgzQWdNQkFBRUNnZ0VBQkhZQ28rU3JxdHJzaStQU3hz
+MlBNQm5tSEZZcFBvaVIrTEpmMEFYRTVEQUoKMGM0MFZzemNqU1hoRGljNHFLQWQxdGdpZWlzMkEy
+VW9WS0xPV3pVOTBqNUd4MURoMWEzaTRhWTQ1ajNuNUFDMgpMekRsakNVQWVucExsYzdCN3MxdjJM
+WFJXNmdJSVM5Y043NTlkVTYvdktyQ2FsbGkzcTZZRWlNUzhQMHNsQnZFCkZtdEc1elFsOVJjV0gr
+cHBqdzlIMTJSZ3BldUVJVEQ2cE0vd2xwcXZHRlUwcmZjM0NjMHhzaWdNTnh1Z1FJNGgKbnpjWDVs
+OEs0SHdvbmhOTG9TYkh6OU5BK3p3QkpuUlZVSWFaaEVjSThtaEVPWHRaRkpYc01aRnhjS2l3SHFS
+dApqUUVHOHJRa3lPLytXMmR5Z2czV1lNYXE1OWpUWVdIOUsrQmFyeEMzRVFLQmdRRFBNSFMycjgz
+ZUpRTTlreXpkCndDdnlmWGhQVlVtbVJnOGwyWng0aC9tci9mNUdDeW5SdzRzT2JuZGVQd29tZ1Iz
+cFBleFFGWlFFSExoZ1RGY3UKVk5uYXcrTzBFL1VnL01pRGswZDNXU0hVZXZPZnM1cEM2b3hYNjNT
+VENwNkVLT2VEZlpVMW9OeHRsZ0YyRVhjcgpmVlZpSzFKRGk3N2dtaENLcFNGcjBLK3gyUUtCZ1FE
+NS9VUC9hNU52clExdUhnKzR0SzJZSFhSK1lUOFREZG00Ck8xZmh5TU5lOHRYSkd5UUJjTktVTWg2
+M2VyR1MwWlRWdGdkNHlGS3RuOGtLU2U4TmlacUl1aitVUVIyZ3pEQVAKQ2VXcXl2Y2pRNmovU1Yw
+WjVvKzlTNytiOStpWWx5RTg2bGZobHh5Z21aNnptYisxUUNteUtNVUdBNis5VmUvMgo1MHhDMXBB
+L2p3S0JnUUNEOHA4UnpVcDFZK3I1WnVaVzN0RGVJSXZqTWpTeVFNSGE0QWhuTm1tSjREcjBUcDIy
+CmFpci82TmY2WEhsUlpqOHZVSEZUMnpvbG1FalBneTZ1WWZsUCtocmtqeVU0ZWVRVTcxRy9Mek45
+UjBRcCs4Nk4KT1NSaHhhQzdHRE0xaFh0VFlVSUtJa1RmUVgzeXZGTEJqcE0yN3RINEZHSmVWWitk
+UEdiWmE5REltUUtCZ1FENQpHTU5qeExiQnhhY25QYThldG5KdnE1SUR5RFRJY0xtc1dQMkZ6cjNX
+WTVSZzhybGE4aWZ5WVVxNE92cXNPRWZjCjk2ZlVVNUFHejd2TWs4VXZNUmtaK3JRVnJ4aXR2Q2g3
+STdxRkIvOWdWVEFWU080TE8vR29oczBqeGRBd0ZBK2IKbWtyOVQ4ekh2cXNqZlNWSW51bXRTL0Nl
+d0plaHl2cjBoSjg1em9Fbnd3S0JnR1h6UXVDSjJDb3NHVVhEdnlHKwpyRzVBd3pUZGd0bHg4YTBK
+NTg1OWtZbVd0cW5WYUJmbFdrRmNUcHNEaGZ2ZWVDUkswc29VRlNPWkcranpsbWJrCkpRL09aVkZJ
+dG9MSVZCeE9qeWVXNlhUSkJXUzFRSkVHckkwY0tTbXNKcENtUXVPdUxMVnZYczV0U21CVmc5RXQK
+MjZzUkZwcjVWWmsrZlNRa3RhbkM4NGV1Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
diff --git a/src/ukify/test/example.tpm2-pcr-private.pem.base64 b/src/ukify/test/example.tpm2-pcr-private.pem.base64
new file mode 100644 (file)
index 0000000..586b28e
--- /dev/null
@@ -0,0 +1,30 @@
+LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZB
+QVNDQktnd2dnU2tBZ0VBQW9JQkFRQzVuOHFhbzVNZ1BJUVcKc0F5Y2R3dnB1bjdNNHlRSW9FL3I3
+ekFGTG1hZlBXclo3d2JaaUIyTkY1MVdHOEo4bnlDQkI3M0RLcmZaeWs5cwphQXdXVW5RR2t0dGFv
+RXpXRzZSRTM3dXdQOUpVM09YdklTNTBhcy9KSHVHNlJPYmE2V0NOOFp2TTdkZGpvTDFKCkZlYnBS
+SXI1Vi82VStMTFhrUnRNYVczUnZ6T0xYeU1NT2QzOEcxZ0d0VlRHcm90ejVldFgrTUNVU2lOVGFE
+OVUKN1dEZXVsZXVpMlRnK1I3TGRoSXg3ZTQ5cEhRM3d6a1NxeFQ4SGpoU3ZURWpITWVSNjIwaUhF
+ZW9uYzdsMXVnagpzY1pwTktHdk13bXUvU2ptWFp6UkpOdjVOU0txcEVnQll2RnFkS3dUdlc4MWl6
+SUFvN3paMkx6NDJYb25zSWJ2CjNrbGZqTG1mQWdNQkFBRUNnZ0VBQXozYm8yeTAzb3kvLzhkdVNQ
+TTVSWWtvdXJwQ3dGWFFYMzNyV0VQUnJmazgKR3ZjMkp1bGVIcjhwVTc0alhOcklqZ2hORTVIMDZQ
+eEQrOUFyV2Q1eHdVV2lTQWhobnlHWGNrNTM4Q0dGTWs4egpRc1JSRTk1anA0Ny9BU28vMzlYUWhs
+b1FUdmxlV0JLUUM2MHl2YU1oVEM1eHR6ZEtwRUlYK0hNazVGTlMrcDJVCmxtL3AzVE1YWDl1bmc5
+Mk9pTzUzV1VreFpQN2cwTVJHbGJrNzhqc1dkdjFYY0tLRjhuVmU5WC9NR1lTYlVLNy8KM2NYazFR
+WTRUdVZaQlBFSE12RFRpWWwxbmdDd1ZuL2MyY3JQU3hJRFdFWlhEdm90SFUwQkNQZURVckxGa0F5
+cQpDaloza3MzdEh4am42STkraEVNcUJDMzY1MHFjdDNkZ0RVV2loc2MzdVFLQmdRRG1mVTNKc29K
+QWFOdmxCbXgyClhzRDRqbXlXV1F2Z244cVNVNG03a2JKdmprcUJ6VnB0T0ZsYmk2ejZHOXR6ZHNX
+a0dJSjh3T0ZRb1hlM0dKOFIKSlVpeEFXTWZOM1JURGo5VjVXbzZJdE5EbzM1N3dNbVVYOW1qeThF
+YXp0RE1BckdSNGJva0Q5RjY3clhqSGdSMQpaZVcvSDlUWHFUV1l4VHl6UDB3ZDBQeUZ4d0tCZ1FE
+T0swWHVQS0o0WG00WmFCemN0OTdETXdDcFBSVmVvUWU3CmkzQjRJQ3orWFZ4cVM2amFTY2xNeEVm
+Nk5tM2tLNERDR1dwVkpXcm9qNjlMck1KWnQzTlI2VUJ5NzNqUVBSamsKRXk5N3YrR04yVGwwNjFw
+ZUxUM0dRS2RhT2VxWldpdElOcFc1dUxHL1poMGhoRUY5c1lSVTRtUFYwUWpla2kvdgp1bnVmcWx0
+TmFRS0JnQTl6TE1pdFg0L0R0NkcxZVlYQnVqdXZDRlpYcDdVcDRPRklHajVwZU1XRGl6a0NNK0tJ
+CldXMEtndERORnp1NUpXeG5mQyt5bWlmV2V2alovS2Vna1N2VVJQbXR0TzF3VWd5RzhVVHVXcXo1
+QTV4MkFzMGcKVTYxb0ZneWUrbDRDZkRha0k5OFE5R0RDS1kwTTBRMnhnK0g0MTBLUmhCYzJlV2dt
+Z1FxcW5KSzNBb0dCQU1rZgpnOWZXQlBVQndjdzlPYkxFR0tjNkVSSUlTZG1IbytCOE5kcXFJTnAv
+djFEZXdEazZ0QXFVakZiMlZCdTdxSjh4ClpmN3NRcS9ldzdaQ01WS09XUXgyVEc0VFdUdGo3dTFJ
+SGhGTjdiNlFRN0hnaXNiR3diV3VpdFBGSGl3OXYyMXgKK253MFJnb2VscHFFeDlMVG92R2Y3SjdB
+ampONlR4TkJTNnBGNlUzSkFvR0JBT0tnbHlRWDJpVG5oMXd4RG1TVQo4RXhoQVN3S09iNS8yRmx4
+aUhtUHVjNTZpS0tHY0lkV1cxMUdtbzdJakNzSTNvRm9iRkFjKzBXZkMvQTdMNWlmCjNBYVNWcmh0
+cThRRklRaUtyYUQ0YlRtRk9Famg5QVVtUHMrWnd1OE9lSXJBSWtwZDV3YmlhTEJLd0pRbVdtSFAK
+dUNBRTA3cXlSWXJ0c3QvcnVSSG5IdFA1Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
diff --git a/src/ukify/test/example.tpm2-pcr-private2.pem.base64 b/src/ukify/test/example.tpm2-pcr-private2.pem.base64
new file mode 100644 (file)
index 0000000..d21a3d6
--- /dev/null
@@ -0,0 +1,30 @@
+LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZB
+QVNDQktZd2dnU2lBZ0VBQW9JQkFRQzJ2Nk1oZHg3a3VjUHIKbmtFNFIrY3FnV2Y5T3B1c2h2M2o3
+SG50K08wdi84d2l2T1BFNTlLMHYvRWJOOG94TDZEWUNXU0JCRU4vREJ5MgpMUTYwbldSdHBZN2Ju
+bEcrcEtVeTRvSDRNZXZCR2JqZUhrak9LU3dNYVVWNGs4UmVSSjg4cVZ1U1MxSnVORW1NCmd5SERF
+NGFPNG5ndG5UUFZZdzUydVBIcG1rN0E4VFdXN2lLZE5JWWZWOCtuR1pENXIzRWllekRsUUNORG54
+UkcKdm5uSFZ6VFhZR3RwY2xaeWlJclpVekpBNFFPZnRueXB5UDVrQS94NVM1MU9QeGFxWlA3eGtP
+S0NicUUvZmZvMApFTi9rTno0N0ZoUGUxbVBHUkZZWldHZXg0aWFPdHlLdHhnU1FYYkdlNEVoeVR4
+SjJlT3U4QUVoVklTdjh6UU9nClNtbWx2UGQvQWdNQkFBRUNnZ0VBUUFpRERRRlR3bG96QTVhMmpK
+VnBNdlFYNzF0L1c2TUxTRGMrZS90cWhKU1IKUHlUSGZHR3NhMmdMLy9qNjhHUWJiRWRTUDRDeWM4
+eFhMU0E1bEdESDVVR0svbm9KYzQ3MlVZK2JjYzl3SjMrdgpUcWoyNHNIN2JMZmdQMEVybjhwVXIy
+azZMRmNYSVlWUnRobm1sUmQ4NFFrS2loVVlxZTdsRFFWOXdsZ3V1eHpRCnBmVEtDTWk1bXJlYjIx
+OExHS0QrMUxjVmVYZjExamc3Z2JnMllLZ1dOQ2R3VmIyUzJ5V0hTTjBlT3hPd21kWXIKSUVCekpG
+eEc2MFJxSlJ1RzVIam9iemc2cy9ycUo1THFta3JhUWh6bHFPQVZLblpGOHppbG9vcDhXUXBQY3RN
+cwp0cHBjczhtYkFkWHpoSTVjN0U1VVpDM2NJcEd6SE4raDZLK0F3R3ZEeVFLQmdRRDRBOTdQM29v
+dGhoMHZHQmFWCnZWOXhHTm1YbW5TeUg0b29HcmJvaG1JWkkwVlhZdms5dWViSUJjbDZRMUx4WnN3
+UFpRMVh5TUhpTjY1Z0E1emgKai9HZGcrdDlvcU5CZml0TUFrUTl1aWxvaXlKVWhYblk5REMvRitl
+ZksycEpNbHdkci9qWEttRHpkQUZBVDgyWQpWRmJ3MlpLVi9GNEJNMUtCdDBZN0RPTmlad0tCZ1FD
+OG9kZk0waytqL25VSzQ4TEV2MGtGbVNMdWdnTVlkM3hVCmZibmx0cUhFTVpJZU45OFVHK2hBWEdw
+dU1Ya0JPM2Mwcm5ZRDVXZkNBRzFxT1V2ZTZzdHd6N0VuK3hWdlkvcWEKU3ZTaDRzMzhnZlBIeXhR
+aGJvNWRwQTZUT3pwT0MyVi9rVXBVRUdJSmVVVllhQ05uWXNpUjRWUGVWL1lvR1htSwpQV29KbnAw
+REtRS0JnQlk3cXBheDJXczVVWlp1TDJBZkNOWkhwd0hySzdqb0VPZUZkWTRrdGRpUkM5OUlsUlZP
+CmUvekVZQXBnektldFVtK3kzRjVaTmVCRW81SWg0TWRyc3ZvdTRFWno5UFNqRGRpVGYzQ1ZKcThq
+Z2VGWDBkTjgKR0g2WTh2K1cwY0ZjRFZ2djhYdkFaYzZOUUt0Mk8vVUM0b1JXek1nN1JtWVBKcjlR
+SWJDYmVDclRBb0dBTjdZbApJbDFMSUVoYkVTaExzZ2c4N09aWnBzL0hVa2FYOWV4Y0p6aFZkcmlk
+UzBkOUgxZE90Uk9XYTQwNUMrQWdTUEx0CjhDQ2xFR3RINVlPZW9Pdi93Z1hWY05WN2N6YTRJVEhh
+SnFYeDZJNEpEZzB3bU44cU5RWHJPQmphRTRyU0kyY3AKNk1JZDhtWmEwTTJSQjB2cHFRdy8xUDl0
+dUZJdHoySnNHd001cEdFQ2dZQVVnQVV3WENBcEtZVkZFRmxHNlBhYwpvdTBhdzdGNm1aMi9NNUcv
+ek9tMHFDYnNXdGFNU09TdUEvNmlVOXB0NDBaWUFONFUvd2ZxbncyVkVoRnA3dzFNCnpZWmJCRDBx
+ZVlkcDRmc1NuWXFMZmJBVmxQLzB6dmEzdkwwMlJFa25WalBVSnAvaGpKVWhBK21WN252VDZ5VjQK
+cTg4SWVvOEx3Q1c1c2Jtd2lyU3Btdz09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
diff --git a/src/ukify/test/example.tpm2-pcr-public.pem.base64 b/src/ukify/test/example.tpm2-pcr-public.pem.base64
new file mode 100644 (file)
index 0000000..728a0f5
--- /dev/null
@@ -0,0 +1,8 @@
+LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FR
+OEFNSUlCQ2dLQ0FRRUF1Wi9LbXFPVElEeUVGckFNbkhjTAo2YnArek9Na0NLQlA2Kzh3QlM1bW56
+MXEyZThHMllnZGpSZWRWaHZDZko4Z2dRZTl3eXEzMmNwUGJHZ01GbEowCkJwTGJXcUJNMWh1a1JO
+KzdzRC9TVk56bDd5RXVkR3JQeVI3aHVrVG0ydWxnamZHYnpPM1hZNkM5U1JYbTZVU0sKK1ZmK2xQ
+aXkxNUViVEdsdDBiOHppMThqRERuZC9CdFlCclZVeHE2TGMrWHJWL2pBbEVvalUyZy9WTzFnM3Jw
+WApyb3RrNFBrZXkzWVNNZTN1UGFSME44TTVFcXNVL0I0NFVyMHhJeHpIa2V0dEloeEhxSjNPNWRi
+b0k3SEdhVFNoCnJ6TUpydjBvNWwyYzBTVGIrVFVpcXFSSUFXTHhhblNzRTcxdk5Zc3lBS084MmRp
+OCtObDZKN0NHNzk1Slg0eTUKbndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
diff --git a/src/ukify/test/example.tpm2-pcr-public2.pem.base64 b/src/ukify/test/example.tpm2-pcr-public2.pem.base64
new file mode 100644 (file)
index 0000000..44bb3ee
--- /dev/null
@@ -0,0 +1,8 @@
+LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FR
+OEFNSUlCQ2dLQ0FRRUF0citqSVhjZTVMbkQ2NTVCT0VmbgpLb0ZuL1RxYnJJYjk0K3g1N2ZqdEwv
+L01JcnpqeE9mU3RML3hHemZLTVMrZzJBbGtnUVJEZnd3Y3RpME90SjFrCmJhV08yNTVSdnFTbE11
+S0IrREhyd1JtNDNoNUl6aWtzREdsRmVKUEVYa1NmUEtsYmtrdFNialJKaklNaHd4T0cKanVKNExa
+MHoxV01PZHJqeDZacE93UEUxbHU0aW5UU0dIMWZQcHhtUSthOXhJbnN3NVVBalE1OFVScjU1eDFj
+MAoxMkJyYVhKV2NvaUsyVk15UU9FRG43WjhxY2orWkFQOGVVdWRUajhXcW1UKzhaRGlnbTZoUDMz
+Nk5CRGY1RGMrCk94WVQzdFpqeGtSV0dWaG5zZUltanJjaXJjWUVrRjJ4bnVCSWNrOFNkbmpydkFC
+SVZTRXIvTTBEb0VwcHBiejMKZndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
diff --git a/src/ukify/test/meson.build b/src/ukify/test/meson.build
new file mode 100644 (file)
index 0000000..e39178f
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+if want_ukify and want_tests != 'false'
+   test('test-ukify',
+        files('test_ukify.py'),
+        env : test_env)
+endif
diff --git a/src/ukify/test/setup.cfg b/src/ukify/test/setup.cfg
new file mode 100644 (file)
index 0000000..1f655da
--- /dev/null
@@ -0,0 +1,2 @@
+[tool:pytest]
+addopts = --flakes
diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py
new file mode 100755 (executable)
index 0000000..3470140
--- /dev/null
@@ -0,0 +1,392 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1+
+
+# pylint: disable=missing-docstring,redefined-outer-name,invalid-name
+# pylint: disable=unused-import,import-outside-toplevel,useless-else-on-loop
+# pylint: disable=consider-using-with,wrong-import-position,unspecified-encoding
+
+import base64
+import json
+import os
+import pathlib
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+
+try:
+    import pytest
+except ImportError:
+    sys.exit(77)
+
+try:
+    # pyflakes: noqa
+    import pefile  # noqa
+except ImportError:
+    sys.exit(77)
+
+# We import ukify.py, which is a template file. But only __version__ is
+# substituted, which we don't care about here. Having the .py suffix makes it
+# easier to import the file.
+sys.path.append(os.path.dirname(__file__) + '/..')
+import ukify
+
+
+def test_guess_efi_arch():
+    arch = ukify.guess_efi_arch()
+    assert arch in ukify.EFI_ARCHES
+
+def test_shell_join():
+    assert ukify.shell_join(['a', 'b', ' ']) == "a b ' '"
+
+def test_round_up():
+    assert ukify.round_up(0) == 0
+    assert ukify.round_up(4095) == 4096
+    assert ukify.round_up(4096) == 4096
+    assert ukify.round_up(4097) == 8192
+
+def test_parse_args_minimal():
+    opts = ukify.parse_args('arg1 arg2'.split())
+    assert opts.linux == pathlib.Path('arg1')
+    assert opts.initrd == [pathlib.Path('arg2')]
+    assert opts.os_release in (pathlib.Path('/etc/os-release'),
+                               pathlib.Path('/usr/lib/os-release'))
+
+def test_parse_args_many():
+    opts = ukify.parse_args(
+        ['/ARG1', '///ARG2', '/ARG3 WITH SPACE',
+         '--cmdline=a b c',
+         '--os-release=K1=V1\nK2=V2',
+         '--devicetree=DDDDTTTT',
+         '--splash=splash',
+         '--pcrpkey=PATH',
+         '--uname=1.2.3',
+         '--stub=STUBPATH',
+         '--pcr-private-key=PKEY1',
+         '--pcr-public-key=PKEY2',
+         '--pcr-banks=SHA1,SHA256',
+         '--signing-engine=ENGINE',
+         '--secureboot-private-key=SBKEY',
+         '--secureboot-certificate=SBCERT',
+         '--sign-kernel',
+         '--no-sign-kernel',
+         '--tools=TOOLZ///',
+         '--output=OUTPUT',
+         '--measure',
+         '--no-measure',
+         ])
+    assert opts.linux == pathlib.Path('/ARG1')
+    assert opts.initrd == [pathlib.Path('/ARG2'), pathlib.Path('/ARG3 WITH SPACE')]
+    assert opts.os_release == 'K1=V1\nK2=V2'
+    assert opts.devicetree == pathlib.Path('DDDDTTTT')
+    assert opts.splash == pathlib.Path('splash')
+    assert opts.pcrpkey == pathlib.Path('PATH')
+    assert opts.uname == '1.2.3'
+    assert opts.stub == pathlib.Path('STUBPATH')
+    assert opts.pcr_private_keys == [pathlib.Path('PKEY1')]
+    assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
+    assert opts.pcr_banks == ['SHA1', 'SHA256']
+    assert opts.signing_engine == 'ENGINE'
+    assert opts.sb_key == 'SBKEY'
+    assert opts.sb_cert == 'SBCERT'
+    assert opts.sign_kernel is False
+    assert opts.tools == pathlib.Path('TOOLZ/')
+    assert opts.output == pathlib.Path('OUTPUT')
+    assert opts.measure is False
+
+def test_parse_sections():
+    opts = ukify.parse_args(
+        ['/ARG1', '/ARG2',
+         '--section=test:TESTTESTTEST',
+         '--section=test2:@FILE',
+         ])
+
+    assert opts.linux == pathlib.Path('/ARG1')
+    assert opts.initrd == [pathlib.Path('/ARG2')]
+    assert len(opts.sections) == 2
+
+    assert opts.sections[0].name == 'test'
+    assert isinstance(opts.sections[0].content, pathlib.Path)
+    assert opts.sections[0].tmpfile
+    assert opts.sections[0].offset is None
+    assert opts.sections[0].measure is False
+
+    assert opts.sections[1].name == 'test2'
+    assert opts.sections[1].content == pathlib.Path('FILE')
+    assert opts.sections[1].tmpfile is None
+    assert opts.sections[1].offset is None
+    assert opts.sections[1].measure is False
+
+def test_help(capsys):
+    with pytest.raises(SystemExit):
+        ukify.parse_args(['--help'])
+    out = capsys.readouterr()
+    assert '--section' in out.out
+    assert not out.err
+
+def test_help_error(capsys):
+    with pytest.raises(SystemExit):
+        ukify.parse_args(['a', 'b', '--no-such-option'])
+    out = capsys.readouterr()
+    assert not out.out
+    assert '--no-such-option' in out.err
+    assert len(out.err.splitlines()) == 1
+
+@pytest.fixture(scope='session')
+def kernel_initrd():
+    try:
+        text = subprocess.check_output(['bootctl', 'list', '--json=short'],
+                                       text=True)
+    except subprocess.CalledProcessError:
+        return None
+
+    items = json.loads(text)
+
+    for item in items:
+        try:
+            linux = f"{item['root']}{item['linux']}"
+            initrd = f"{item['root']}{item['initrd'][0]}"
+        except (KeyError, IndexError):
+            pass
+        return [linux, initrd]
+    else:
+        return None
+
+def test_check_splash():
+    try:
+        # pyflakes: noqa
+        import PIL  # noqa
+    except ImportError:
+        pytest.skip('PIL not available')
+
+    with pytest.raises(OSError):
+        ukify.check_splash(os.devnull)
+
+def test_basic_operation(kernel_initrd, tmpdir):
+    if kernel_initrd is None:
+        pytest.skip('linux+initrd not found')
+
+    output = f'{tmpdir}/basic.efi'
+    opts = ukify.parse_args(kernel_initrd + [f'--output={output}'])
+    try:
+        ukify.check_inputs(opts)
+    except OSError as e:
+        pytest.skip(str(e))
+
+    ukify.make_uki(opts)
+
+    # let's check that objdump likes the resulting file
+    subprocess.check_output(['objdump', '-h', output])
+
+def test_sections(kernel_initrd, tmpdir):
+    if kernel_initrd is None:
+        pytest.skip('linux+initrd not found')
+
+    output = f'{tmpdir}/basic.efi'
+    opts = ukify.parse_args([
+        *kernel_initrd,
+        f'--output={output}',
+        '--uname=1.2.3',
+        '--cmdline=ARG1 ARG2 ARG3',
+        '--os-release=K1=V1\nK2=V2\n',
+        '--section=.test:CONTENTZ',
+    ])
+
+    try:
+        ukify.check_inputs(opts)
+    except OSError as e:
+        pytest.skip(str(e))
+
+    ukify.make_uki(opts)
+
+    # let's check that objdump likes the resulting file
+    dump = subprocess.check_output(['objdump', '-h', output], text=True)
+
+    for sect in 'text osrel cmdline linux initrd uname test'.split():
+        assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
+
+
+def unbase64(filename):
+    tmp = tempfile.NamedTemporaryFile()
+    base64.decode(filename.open('rb'), tmp)
+    tmp.flush()
+    return tmp
+
+
+def test_uname_scraping(kernel_initrd):
+    if kernel_initrd is None:
+        pytest.skip('linux+initrd not found')
+
+    uname = ukify.Uname.scrape(kernel_initrd[0])
+    assert re.match(r'\d+\.\d+\.\d+', uname)
+
+
+def test_efi_signing(kernel_initrd, tmpdir):
+    if kernel_initrd is None:
+        pytest.skip('linux+initrd not found')
+    if not shutil.which('sbsign'):
+        pytest.skip('sbsign not found')
+
+    ourdir = pathlib.Path(__file__).parent
+    cert = unbase64(ourdir / 'example.signing.crt.base64')
+    key = unbase64(ourdir / 'example.signing.key.base64')
+
+    output = f'{tmpdir}/signed.efi'
+    opts = ukify.parse_args([
+        *kernel_initrd,
+        f'--output={output}',
+        '--uname=1.2.3',
+        '--cmdline=ARG1 ARG2 ARG3',
+        f'--secureboot-certificate={cert.name}',
+        f'--secureboot-private-key={key.name}',
+    ])
+
+    try:
+        ukify.check_inputs(opts)
+    except OSError as e:
+        pytest.skip(str(e))
+
+    ukify.make_uki(opts)
+
+    if shutil.which('sbverify'):
+        # let's check that sbverify likes the resulting file
+        dump = subprocess.check_output([
+            'sbverify',
+            '--cert', cert.name,
+            output,
+        ], text=True)
+
+        assert 'Signature verification OK' in dump
+
+def test_pcr_signing(kernel_initrd, tmpdir):
+    if kernel_initrd is None:
+        pytest.skip('linux+initrd not found')
+    if os.getuid() != 0:
+        pytest.skip('must be root to access tpm2')
+    if subprocess.call(['systemd-creds', 'has-tpm2', '-q']) != 0:
+        pytest.skip('tpm2 is not available')
+
+    ourdir = pathlib.Path(__file__).parent
+    pub = unbase64(ourdir / 'example.tpm2-pcr-public.pem.base64')
+    priv = unbase64(ourdir / 'example.tpm2-pcr-private.pem.base64')
+
+    output = f'{tmpdir}/signed.efi'
+    opts = ukify.parse_args([
+        *kernel_initrd,
+        f'--output={output}',
+        '--uname=1.2.3',
+        '--cmdline=ARG1 ARG2 ARG3',
+        '--os-release=ID=foobar\n',
+        '--pcr-banks=sha1',   # use sha1 as that is most likely to be supported
+        f'--pcrpkey={pub.name}',
+        f'--pcr-public-key={pub.name}',
+        f'--pcr-private-key={priv.name}',
+    ])
+
+    try:
+        ukify.check_inputs(opts)
+    except OSError as e:
+        pytest.skip(str(e))
+
+    ukify.make_uki(opts)
+
+    # let's check that objdump likes the resulting file
+    dump = subprocess.check_output(['objdump', '-h', output], text=True)
+
+    for sect in 'text osrel cmdline linux initrd uname pcrsig'.split():
+        assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
+
+    # objcopy fails when called without an output argument (EPERM).
+    # It also fails when called with /dev/null (file truncated).
+    # It also fails when called with /dev/zero (because it reads the
+    # output file, infinitely in this case.)
+    # So let's just call it with a dummy output argument.
+    subprocess.check_call([
+        'objcopy',
+        *(f'--dump-section=.{n}={tmpdir}/out.{n}' for n in (
+            'pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline')),
+        output,
+        tmpdir / 'dummy',
+    ],
+        text=True)
+
+    assert open(tmpdir / 'out.pcrpkey').read() == open(pub.name).read()
+    assert open(tmpdir / 'out.osrel').read() == 'ID=foobar\n'
+    assert open(tmpdir / 'out.uname').read() == '1.2.3'
+    assert open(tmpdir / 'out.cmdline').read() == 'ARG1 ARG2 ARG3'
+    sig = open(tmpdir / 'out.pcrsig').read()
+    sig = json.loads(sig)
+    assert list(sig.keys()) == ['sha1']
+    assert len(sig['sha1']) == 4   # four items for four phases
+
+def test_pcr_signing2(kernel_initrd, tmpdir):
+    if kernel_initrd is None:
+        pytest.skip('linux+initrd not found')
+    if os.getuid() != 0:
+        pytest.skip('must be root to access tpm2')
+    if subprocess.call(['systemd-creds', 'has-tpm2', '-q']) != 0:
+        pytest.skip('tpm2 is not available')
+
+    ourdir = pathlib.Path(__file__).parent
+    pub = unbase64(ourdir / 'example.tpm2-pcr-public.pem.base64')
+    priv = unbase64(ourdir / 'example.tpm2-pcr-private.pem.base64')
+    pub2 = unbase64(ourdir / 'example.tpm2-pcr-public2.pem.base64')
+    priv2 = unbase64(ourdir / 'example.tpm2-pcr-private2.pem.base64')
+
+    # simulate a microcode file
+    with open(f'{tmpdir}/microcode', 'wb') as microcode:
+        microcode.write(b'1234567890')
+
+    output = f'{tmpdir}/signed.efi'
+    opts = ukify.parse_args([
+        kernel_initrd[0], microcode.name, kernel_initrd[1],
+        f'--output={output}',
+        '--uname=1.2.3',
+        '--cmdline=ARG1 ARG2 ARG3',
+        '--os-release=ID=foobar\n',
+        '--pcr-banks=sha1',   # use sha1 as that is most likely to be supported
+        f'--pcrpkey={pub2.name}',
+        f'--pcr-public-key={pub.name}',
+        f'--pcr-private-key={priv.name}',
+        '--phases=enter-initrd enter-initrd:leave-initrd',
+        f'--pcr-public-key={pub2.name}',
+        f'--pcr-private-key={priv2.name}',
+        '--phases=sysinit ready shutdown final',  # yes, those phase paths are not reachable
+    ])
+
+    try:
+        ukify.check_inputs(opts)
+    except OSError as e:
+        pytest.skip(str(e))
+
+    ukify.make_uki(opts)
+
+    # let's check that objdump likes the resulting file
+    dump = subprocess.check_output(['objdump', '-h', output], text=True)
+
+    for sect in 'text osrel cmdline linux initrd uname pcrsig'.split():
+        assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
+
+    subprocess.check_call([
+        'objcopy',
+        *(f'--dump-section=.{n}={tmpdir}/out.{n}' for n in (
+            'pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline', 'initrd')),
+        output,
+        tmpdir / 'dummy',
+    ],
+        text=True)
+
+    assert open(tmpdir / 'out.pcrpkey').read() == open(pub2.name).read()
+    assert open(tmpdir / 'out.osrel').read() == 'ID=foobar\n'
+    assert open(tmpdir / 'out.uname').read() == '1.2.3'
+    assert open(tmpdir / 'out.cmdline').read() == 'ARG1 ARG2 ARG3'
+    assert open(tmpdir / 'out.initrd', 'rb').read(10) == b'1234567890'
+
+    sig = open(tmpdir / 'out.pcrsig').read()
+    sig = json.loads(sig)
+    assert list(sig.keys()) == ['sha1']
+    assert len(sig['sha1']) == 6   # six items for six phases paths
+
+if __name__ == '__main__':
+    pytest.main([__file__, '-v'])
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
new file mode 100755 (executable)
index 0000000..e9e5d13
--- /dev/null
@@ -0,0 +1,727 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1+
+
+# pylint: disable=missing-docstring,invalid-name,import-outside-toplevel
+# pylint: disable=consider-using-with,unspecified-encoding,line-too-long
+# pylint: disable=too-many-locals,too-many-statements,too-many-return-statements
+# pylint: disable=too-many-branches
+
+import argparse
+import collections
+import dataclasses
+import fnmatch
+import itertools
+import json
+import os
+import pathlib
+import re
+import shlex
+import subprocess
+import tempfile
+import typing
+
+
+__version__ = '{{GIT_VERSION}}'
+
+EFI_ARCH_MAP = {
+        # host_arch glob : [efi_arch, 32_bit_efi_arch if mixed mode is supported]
+        'x86_64'       : ['x64', 'ia32'],
+        'i[3456]86'    : ['ia32'],
+        'aarch64'      : ['aa64'],
+        'arm[45678]*l' : ['arm'],
+        'riscv64'      : ['riscv64'],
+}
+EFI_ARCHES: list[str] = sum(EFI_ARCH_MAP.values(), [])
+
+def guess_efi_arch():
+    arch = os.uname().machine
+
+    for glob, mapping in EFI_ARCH_MAP.items():
+        if fnmatch.fnmatch(arch, glob):
+            efi_arch, *fallback = mapping
+            break
+    else:
+        raise ValueError(f'Unsupported architecture {arch}')
+
+    # This makes sense only on some architectures, but it also probably doesn't
+    # hurt on others, so let's just apply the check everywhere.
+    if fallback:
+        fw_platform_size = pathlib.Path('/sys/firmware/efi/fw_platform_size')
+        try:
+            size = fw_platform_size.read_text().strip()
+        except FileNotFoundError:
+            pass
+        else:
+            if int(size) == 32:
+                efi_arch = fallback[0]
+
+    print(f'Host arch {arch!r}, EFI arch {efi_arch!r}')
+    return efi_arch
+
+
+def shell_join(cmd):
+    # TODO: drop in favour of shlex.join once shlex.join supports pathlib.Path.
+    return ' '.join(shlex.quote(str(x)) for x in cmd)
+
+
+def pe_executable_size(filename):
+    import pefile
+
+    pe = pefile.PE(filename)
+    section = pe.sections[-1]
+    return section.VirtualAddress + section.Misc_VirtualSize
+
+
+def round_up(x, blocksize=4096):
+    return (x + blocksize - 1) // blocksize * blocksize
+
+
+def maybe_decompress(filename):
+    """Decompress file if compressed. Return contents."""
+    f = open(filename, 'rb')
+    start = f.read(4)
+    f.seek(0)
+
+    if start.startswith(b'\x7fELF'):
+        # not compressed
+        return f.read()
+
+    if start.startswith(b'\x1f\x8b'):
+        import gzip
+        return gzip.open(f).read()
+
+    if start.startswith(b'\x28\xb5\x2f\xfd'):
+        import zstd
+        return zstd.uncompress(f.read())
+
+    if start.startswith(b'\x02\x21\x4c\x18'):
+        import lz4.frame
+        return lz4.frame.decompress(f.read())
+
+    if start.startswith(b'\x04\x22\x4d\x18'):
+        print('Newer lz4 stream format detected! This may not boot!')
+        import lz4.frame
+        return lz4.frame.decompress(f.read())
+
+    if start.startswith(b'\x89LZO'):
+        # python3-lzo is not packaged for Fedora
+        raise NotImplementedError('lzo decompression not implemented')
+
+    if start.startswith(b'BZh'):
+        import bz2
+        return bz2.open(f).read()
+
+    if start.startswith(b'\x5d\x00\x00'):
+        import lzma
+        return lzma.open(f).read()
+
+    raise NotImplementedError(f'unknown file format (starts with {start})')
+
+
+class Uname:
+    # This class is here purely as a namespace for the functions
+
+    VERSION_PATTERN = r'(?P<version>[a-z0-9._-]+) \([^ )]+\) (?:#.*)'
+
+    NOTES_PATTERN = r'^\s+Linux\s+0x[0-9a-f]+\s+OPEN\n\s+description data: (?P<version>[0-9a-f ]+)\s*$'
+
+    # Linux version 6.0.8-300.fc37.ppc64le (mockbuild@buildvm-ppc64le-03.iad2.fedoraproject.org)
+    # (gcc (GCC) 12.2.1 20220819 (Red Hat 12.2.1-2), GNU ld version 2.38-24.fc37)
+    # #1 SMP Fri Nov 11 14:39:11 UTC 2022
+    TEXT_PATTERN = rb'Linux version (?P<version>\d\.\S+) \('
+
+    @classmethod
+    def scrape_x86(cls, filename, opts=None):
+        # Based on https://gitlab.archlinux.org/archlinux/mkinitcpio/mkinitcpio/-/blob/master/functions#L136
+        # and https://www.kernel.org/doc/html/latest/x86/boot.html#the-real-mode-kernel-header
+        with open(filename, 'rb') as f:
+            f.seek(0x202)
+            magic = f.read(4)
+            if magic != b'HdrS':
+                raise ValueError('Real-Mode Kernel Header magic not found')
+            f.seek(0x20E)
+            offset = f.read(1)[0] + f.read(1)[0]*256  # Pointer to kernel version string
+            f.seek(0x200 + offset)
+            text = f.read(128)
+        text = text.split(b'\0', maxsplit=1)[0]
+        text = text.decode()
+
+        if not (m := re.match(cls.VERSION_PATTERN, text)):
+            raise ValueError(f'Cannot parse version-host-release uname string: {text!r}')
+        return m.group('version')
+
+    @classmethod
+    def scrape_elf(cls, filename, opts=None):
+        readelf = find_tool('readelf', opts=opts)
+
+        cmd = [
+            readelf,
+            '--notes',
+            filename,
+        ]
+
+        print('+', shell_join(cmd))
+        notes = subprocess.check_output(cmd, text=True)
+
+        if not (m := re.search(cls.NOTES_PATTERN, notes, re.MULTILINE)):
+            raise ValueError('Cannot find Linux version note')
+
+        text = ''.join(chr(int(c, 16)) for c in m.group('version').split())
+        return text.rstrip('\0')
+
+    @classmethod
+    def scrape_generic(cls, filename, opts=None):
+        # import libarchive
+        # libarchive-c fails with
+        # ArchiveError: Unrecognized archive format (errno=84, retcode=-30, archive_p=94705420454656)
+
+        # Based on https://gitlab.archlinux.org/archlinux/mkinitcpio/mkinitcpio/-/blob/master/functions#L209
+
+        text = maybe_decompress(filename)
+        if not (m := re.search(cls.TEXT_PATTERN, text)):
+            raise ValueError(f'Cannot find {cls.TEXT_PATTERN!r} in {filename}')
+
+        return m.group('version').decode()
+
+    @classmethod
+    def scrape(cls, filename, opts=None):
+        for func in (cls.scrape_x86, cls.scrape_elf, cls.scrape_generic):
+            try:
+                version = func(filename, opts=opts)
+                print(f'Found uname version: {version}')
+                return version
+            except ValueError as e:
+                print(str(e))
+        return None
+
+
+@dataclasses.dataclass
+class Section:
+    name: str
+    content: pathlib.Path
+    tmpfile: typing.IO | None = None
+    flags: list[str] | None = dataclasses.field(default=None)
+    offset: int | None = None
+    measure: bool = False
+
+    @classmethod
+    def create(cls, name, contents, flags=None, measure=False):
+        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)
+            tmp.flush()
+            contents = pathlib.Path(tmp.name)
+        else:
+            tmp = None
+
+        return cls(name, contents, tmpfile=tmp, flags=flags, measure=measure)
+
+    @classmethod
+    def parse_arg(cls, s):
+        try:
+            name, contents, *rest = s.split(':')
+        except ValueError as e:
+            raise ValueError(f'Cannot parse section spec (name or contents missing): {s!r}') from e
+        if rest:
+            raise ValueError(f'Cannot parse section spec (extraneous parameters): {s!r}')
+
+        if contents.startswith('@'):
+            contents = pathlib.Path(contents[1:])
+
+        return cls.create(name, contents)
+
+    def size(self):
+        return self.content.stat().st_size
+
+    def check_name(self):
+        # PE section names with more than 8 characters are legal, but our stub does
+        # not support them.
+        if not self.name.isascii() or not self.name.isprintable():
+            raise ValueError(f'Bad section name: {self.name!r}')
+        if len(self.name) > 8:
+            raise ValueError(f'Section name too long: {self.name!r}')
+
+
+@dataclasses.dataclass
+class UKI:
+    executable: list[pathlib.Path|str]
+    sections: list[Section] = dataclasses.field(default_factory=list, init=False)
+    offset: int | None = dataclasses.field(default=None, init=False)
+
+    def __post_init__(self):
+        self.offset = round_up(pe_executable_size(self.executable))
+
+    def add_section(self, section):
+        assert self.offset
+        assert section.offset is None
+
+        if section.name in [s.name for s in self.sections]:
+            raise ValueError(f'Duplicate section {section.name}')
+
+        section.offset = self.offset
+        self.offset += round_up(section.size())
+        self.sections += [section]
+
+
+def parse_banks(s):
+    banks = re.split(r',|\s+', s)
+    # TODO: do some sanity checking here
+    return banks
+
+
+KNOWN_PHASES = (
+    'enter-initrd',
+    'leave-initrd',
+    'sysinit',
+    'ready',
+    'shutdown',
+    'final',
+)
+
+def parse_phase_paths(s):
+    # Split on commas or whitespace here. Commas might be hard to parse visually.
+    paths = re.split(r',|\s+', s)
+
+    for path in paths:
+        for phase in path.split(':'):
+            if phase not in KNOWN_PHASES:
+                raise argparse.ArgumentTypeError(f'Unknown boot phase {phase!r} ({path=})')
+
+    return paths
+
+
+def check_splash(filename):
+    if filename is None:
+        return
+
+    # import is delayed, to avoid import when the splash image is not used
+    try:
+        from PIL import Image
+    except ImportError:
+        return
+
+    img = Image.open(filename, formats=['BMP'])
+    print(f'Splash image {filename} is {img.width}×{img.height} pixels')
+
+
+def check_inputs(opts):
+    for name, value in vars(opts).items():
+        if name in {'output', 'tools'}:
+            continue
+
+        if not isinstance(value, pathlib.Path):
+            continue
+
+        # Open file to check that we can read it, or generate an exception
+        value.open().close()
+
+    check_splash(opts.splash)
+
+
+def find_tool(name, fallback=None, opts=None):
+    if opts and opts.tools:
+        tool = opts.tools / name
+        if tool.exists():
+            return tool
+
+    return fallback or name
+
+
+def combine_signatures(pcrsigs):
+    combined = collections.defaultdict(list)
+    for pcrsig in pcrsigs:
+        for bank, sigs in pcrsig.items():
+            for sig in sigs:
+                if sig not in combined[bank]:
+                    combined[bank] += [sig]
+    return json.dumps(combined)
+
+
+def call_systemd_measure(uki, linux, opts):
+    measure_tool = find_tool('systemd-measure',
+                             '/usr/lib/systemd/systemd-measure',
+                             opts=opts)
+
+    banks = opts.pcr_banks or ()
+
+    # PCR measurement
+
+    if opts.measure:
+        pp_groups = opts.phase_path_groups or []
+
+        cmd = [
+            measure_tool,
+            'calculate',
+            f'--linux={linux}',
+            *(f"--{s.name.removeprefix('.')}={s.content}"
+              for s in uki.sections
+              if s.measure),
+            *(f'--bank={bank}'
+              for bank in banks),
+            # For measurement, the keys are not relevant, so we can lump all the phase paths
+            # into one call to systemd-measure calculate.
+            *(f'--phase={phase_path}'
+              for phase_path in itertools.chain.from_iterable(pp_groups)),
+        ]
+
+        print('+', shell_join(cmd))
+        subprocess.check_call(cmd)
+
+    # PCR signing
+
+    if opts.pcr_private_keys:
+        n_priv = len(opts.pcr_private_keys or ())
+        pp_groups = opts.phase_path_groups or [None] * n_priv
+        pub_keys = opts.pcr_public_keys or [None] * n_priv
+
+        pcrsigs = []
+
+        cmd = [
+            measure_tool,
+            'sign',
+            f'--linux={linux}',
+            *(f"--{s.name.removeprefix('.')}={s.content}"
+              for s in uki.sections
+              if s.measure),
+            *(f'--bank={bank}'
+              for bank in banks),
+        ]
+
+        for priv_key, pub_key, group in zip(opts.pcr_private_keys,
+                                            pub_keys,
+                                            pp_groups):
+            extra = [f'--private-key={priv_key}']
+            if pub_key:
+                extra += [f'--public-key={pub_key}']
+            extra += [f'--phase={phase_path}' for phase_path in group or ()]
+
+            print('+', shell_join(cmd + extra))
+            pcrsig = subprocess.check_output(cmd + extra, text=True)
+            pcrsig = json.loads(pcrsig)
+            pcrsigs += [pcrsig]
+
+        combined = combine_signatures(pcrsigs)
+        uki.add_section(Section.create('.pcrsig', combined))
+
+
+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
+
+
+def make_uki(opts):
+    # kernel payload signing
+
+    sbsign_tool = find_tool('sbsign', opts=opts)
+    sbsign_invocation = [
+        sbsign_tool,
+        '--key', opts.sb_key,
+        '--cert', opts.sb_cert,
+    ]
+
+    if opts.signing_engine is not None:
+        sbsign_invocation += ['--engine', opts.signing_engine]
+
+    sign_kernel = opts.sign_kernel
+    if sign_kernel is None and opts.sb_key:
+        # figure out if we should sign the kernel
+        sbverify_tool = find_tool('sbverify', opts=opts)
+
+        cmd = [
+            sbverify_tool,
+            '--list',
+            opts.linux,
+        ]
+
+        print('+', shell_join(cmd))
+        info = subprocess.check_output(cmd, text=True)
+
+        # sbverify has wonderful API
+        if 'No signature table present' in info:
+            sign_kernel = True
+
+    if sign_kernel:
+        linux_signed = tempfile.NamedTemporaryFile(prefix='linux-signed')
+        linux = linux_signed.name
+
+        cmd = [
+            *sbsign_invocation,
+            opts.linux,
+            '--output', linux,
+        ]
+
+        print('+', shell_join(cmd))
+        subprocess.check_call(cmd)
+    else:
+        linux = opts.linux
+
+    if opts.uname is None:
+        print('Kernel version not specified, starting autodetection 😖.')
+        opts.uname = Uname.scrape(opts.linux, opts=opts)
+
+    uki = UKI(opts.stub)
+    initrd = join_initrds(opts.initrd)
+
+    # TODO: derive public key from from opts.pcr_private_keys?
+    pcrpkey = opts.pcrpkey
+    if pcrpkey is None:
+        if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
+            pcrpkey = opts.pcr_public_keys[0]
+
+    sections = [
+        # name,      content,         measure?
+        ('.osrel',   opts.os_release, True ),
+        ('.cmdline', opts.cmdline,    True ),
+        ('.dtb',     opts.devicetree, True ),
+        ('.splash',  opts.splash,     True ),
+        ('.pcrpkey', pcrpkey,         True ),
+        ('.initrd',  initrd,          True ),
+        ('.uname',   opts.uname,      False),
+
+        # linux shall be last to leave breathing room for decompression.
+        # We'll add it later.
+    ]
+
+    for name, content, measure in sections:
+        if content:
+            uki.add_section(Section.create(name, content, measure=measure))
+
+    # systemd-measure doesn't know about those extra sections
+    for section in opts.sections:
+        uki.add_section(section)
+
+    # PCR measurement and signing
+
+    call_systemd_measure(uki, linux, opts=opts)
+
+    # UKI creation
+
+    uki.add_section(
+        Section.create('.linux', linux, measure=True,
+                       flags=['code', 'readonly']))
+
+    if opts.sb_key:
+        unsigned = tempfile.NamedTemporaryFile(prefix='uki')
+        output = unsigned.name
+    else:
+        output = opts.output
+
+    objcopy_tool = find_tool('objcopy', opts=opts)
+
+    cmd = [
+        objcopy_tool,
+        opts.stub,
+        *itertools.chain.from_iterable(
+            ('--add-section',        f'{s.name}={s.content}',
+             '--change-section-vma', f'{s.name}=0x{s.offset:x}')
+            for s in uki.sections),
+        *itertools.chain.from_iterable(
+            ('--set-section-flags',  f"{s.name}={','.join(s.flags)}")
+            for s in uki.sections
+            if s.flags is not None),
+        output,
+    ]
+    print('+', shell_join(cmd))
+    subprocess.check_call(cmd)
+
+    # UKI signing
+
+    if opts.sb_key:
+        cmd = [
+            *sbsign_invocation,
+            unsigned.name,
+            '--output', opts.output,
+        ]
+        print('+', shell_join(cmd))
+        subprocess.check_call(cmd)
+
+        # We end up with no executable bits, let's reapply them
+        os.umask(umask := os.umask(0))
+        os.chmod(opts.output, 0o777 & ~umask)
+
+    print(f"Wrote {'signed' if opts.sb_key else 'unsigned'} {opts.output}")
+
+
+def parse_args(args=None):
+    p = argparse.ArgumentParser(
+        description='Build and sign Unified Kernel Images',
+        allow_abbrev=False,
+        usage='''\
+usage: ukify [options…] linux initrd…
+       ukify -h | --help
+''')
+
+    # Suppress printing of usage synopsis on errors
+    p.error = lambda message: p.exit(2, f'{p.prog}: error: {message}\n')
+
+    p.add_argument('linux',
+                   type=pathlib.Path,
+                   help='vmlinuz file [.linux section]')
+    p.add_argument('initrd',
+                   type=pathlib.Path,
+                   nargs='*',
+                   help='initrd files [.initrd section]')
+
+    p.add_argument('--cmdline',
+                   metavar='TEXT|@PATH',
+                   help='kernel command line [.cmdline section]')
+
+    p.add_argument('--os-release',
+                   metavar='TEXT|@PATH',
+                   help='path to os-release file [.osrel section]')
+
+    p.add_argument('--devicetree',
+                   metavar='PATH',
+                   type=pathlib.Path,
+                   help='Device Tree file [.dtb section]')
+    p.add_argument('--splash',
+                   metavar='BMP',
+                   type=pathlib.Path,
+                   help='splash image bitmap file [.splash section]')
+    p.add_argument('--pcrpkey',
+                   metavar='KEY',
+                   type=pathlib.Path,
+                   help='embedded public key to seal secrets to [.pcrpkey section]')
+    p.add_argument('--uname',
+                   metavar='VERSION',
+                   help='"uname -r" information [.uname section]')
+
+    p.add_argument('--efi-arch',
+                   metavar='ARCH',
+                   choices=('ia32', 'x64', 'arm', 'aa64', 'riscv64'),
+                   help='target EFI architecture')
+
+    p.add_argument('--stub',
+                   type=pathlib.Path,
+                   help='path the the sd-stub file [.text,.data,… sections]')
+
+    p.add_argument('--section',
+                   dest='sections',
+                   metavar='NAME:TEXT|@PATH',
+                   type=Section.parse_arg,
+                   action='append',
+                   default=[],
+                   help='additional section as name and contents [NAME section]')
+
+    p.add_argument('--pcr-private-key',
+                   dest='pcr_private_keys',
+                   metavar='PATH',
+                   type=pathlib.Path,
+                   action='append',
+                   help='private part of the keypair for signing PCR signatures')
+    p.add_argument('--pcr-public-key',
+                   dest='pcr_public_keys',
+                   metavar='PATH',
+                   type=pathlib.Path,
+                   action='append',
+                   help='public part of the keypair for signing PCR signatures')
+    p.add_argument('--phases',
+                   dest='phase_path_groups',
+                   metavar='PHASE-PATH…',
+                   type=parse_phase_paths,
+                   action='append',
+                   help='phase-paths to create signatures for')
+
+    p.add_argument('--pcr-banks',
+                   metavar='BANK…',
+                   type=parse_banks)
+
+    p.add_argument('--signing-engine',
+                   metavar='ENGINE',
+                   help='OpenSSL engine to use for signing')
+    p.add_argument('--secureboot-private-key',
+                   dest='sb_key',
+                   help='path to key file or engine-specific designation for SB signing')
+    p.add_argument('--secureboot-certificate',
+                   dest='sb_cert',
+                   help='path to certificate file or engine-specific designation for SB signing')
+
+    p.add_argument('--sign-kernel',
+                   action=argparse.BooleanOptionalAction,
+                   help='Sign the embedded kernel')
+
+    p.add_argument('--tools',
+                   type=pathlib.Path,
+                   help='a directory with systemd-measure and other tools')
+
+    p.add_argument('--output', '-o',
+                   type=pathlib.Path,
+                   help='output file path')
+
+    p.add_argument('--measure',
+                   action=argparse.BooleanOptionalAction,
+                   help='print systemd-measure output for the UKI')
+
+    p.add_argument('--version',
+                   action='version',
+                   version=f'ukify {__version__}')
+
+    opts = p.parse_args(args)
+
+    if opts.cmdline and opts.cmdline.startswith('@'):
+        opts.cmdline = pathlib.Path(opts.cmdline[1:])
+
+    if opts.os_release is not None and opts.os_release.startswith('@'):
+        opts.os_release = pathlib.Path(opts.os_release[1:])
+    elif opts.os_release is None:
+        p = pathlib.Path('/etc/os-release')
+        if not p.exists():
+            p = pathlib.Path('/usr/lib/os-release')
+        opts.os_release = p
+
+    if opts.efi_arch is None:
+        opts.efi_arch = guess_efi_arch()
+
+    if opts.stub is None:
+        opts.stub = f'/usr/lib/systemd/boot/efi/linux{opts.efi_arch}.efi.stub'
+
+    if opts.signing_engine is None:
+        opts.sb_key = pathlib.Path(opts.sb_key) if opts.sb_key else None
+        opts.sb_cert = pathlib.Path(opts.sb_cert) if opts.sb_cert else None
+
+    if bool(opts.sb_key) ^ bool(opts.sb_cert):
+        raise ValueError('--secureboot-private-key= and --secureboot-certificate= must be specified together')
+
+    if opts.sign_kernel and not opts.sb_key:
+        raise ValueError('--sign-kernel requires --secureboot-private-key= and --secureboot-certificate= to be specified')
+
+    n_pcr_pub = None if opts.pcr_public_keys is None else len(opts.pcr_public_keys)
+    n_pcr_priv = None if opts.pcr_private_keys is None else len(opts.pcr_private_keys)
+    n_phase_path_groups = None if opts.phase_path_groups is None else len(opts.phase_path_groups)
+    if n_pcr_pub is not None and n_pcr_pub != n_pcr_priv:
+        raise ValueError('--pcr-public-key= specifications must match --pcr-private-key=')
+    if n_phase_path_groups is not None and n_phase_path_groups != n_pcr_priv:
+        raise ValueError('--phases= specifications must match --pcr-private-key=')
+
+    if opts.output is None:
+        suffix = '.efi' if opts.sb_key else '.unsigned.efi'
+        opts.output = opts.linux.name + suffix
+
+    for section in opts.sections:
+        section.check_name()
+
+    return opts
+
+
+def main():
+    opts = parse_args()
+    check_inputs(opts)
+    make_uki(opts)
+
+
+if __name__ == '__main__':
+    main()
index ff2607f8fe24724b1d115c8e96f5a007f429a433..81d3f6407c9d5f61e2d960a8840f1a8eb8e9ad5e 100644 (file)
@@ -277,7 +277,7 @@ int manager_startup(Manager *m) {
 
                 (void) sockaddr_un_unlink(&sockaddr.un);
 
-                RUN_WITH_UMASK(0000)
+                WITH_UMASK(0000)
                         if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
                                 return log_error_errno(errno, "Failed to bind socket: %m");
 
index e29d4df6925646de329022bb9fe1b023eb09f08d..bf0b1decb421f05a7256473476c60e0f4c4b1ccf 100644 (file)
@@ -29,7 +29,7 @@ static int make_volatile(const char *path) {
         if (r < 0)
                 return log_error_errno(r, "Couldn't generate volatile sysroot directory: %m");
 
-        r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS);
+        r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=0755" TMPFS_LIMITS_ROOTFS);
         if (r < 0)
                 goto finish_rmdir;
 
@@ -80,7 +80,7 @@ static int make_overlay(const char *path) {
         if (r < 0)
                 return log_error_errno(r, "Couldn't create overlay sysroot directory: %m");
 
-        r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS);
+        r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=0755" TMPFS_LIMITS_ROOTFS);
         if (r < 0)
                 goto finish;
 
index 0d513cf85bf02c1d890865905ed444bacb1b6f62..5f04d2017a1c14866859bb1d5593a20630ef80d8 100755 (executable)
@@ -13,8 +13,10 @@ test_append_files() {
     if ! get_bool "${TEST_NO_QEMU:=}"; then
         install_dmevent
         instmods dm_verity =md
+        instmods erofs
         generate_module_dependencies
         image_install -o /sbin/mksquashfs
+        image_install -o /bin/mkfs.erofs
     fi
 
     inst_binary mcopy
diff --git a/test/fuzz/fuzz-bootspec/oss-fuzz-53578 b/test/fuzz/fuzz-bootspec/oss-fuzz-53578
new file mode 100644 (file)
index 0000000..8804abd
--- /dev/null
@@ -0,0 +1 @@
+{"config":"default @saved","loader":[""]}
\ No newline at end of file
diff --git a/test/fuzz/fuzz-etc-hosts/oss-fuzz-47708 b/test/fuzz/fuzz-etc-hosts/oss-fuzz-47708
new file mode 100644 (file)
index 0000000..c0f59de
Binary files /dev/null and b/test/fuzz/fuzz-etc-hosts/oss-fuzz-47708 differ
diff --git a/test/fuzz/fuzz-resource-record/crash-type-bitmap-to-json b/test/fuzz/fuzz-resource-record/crash-type-bitmap-to-json
new file mode 100644 (file)
index 0000000..a7d5b43
Binary files /dev/null and b/test/fuzz/fuzz-resource-record/crash-type-bitmap-to-json differ
diff --git a/test/fuzz/fuzz-resource-record/oss-fuzz-54059 b/test/fuzz/fuzz-resource-record/oss-fuzz-54059
new file mode 100644 (file)
index 0000000..13778bf
Binary files /dev/null and b/test/fuzz/fuzz-resource-record/oss-fuzz-54059 differ
diff --git a/test/fuzz/fuzz-resource-record/oss-fuzz-54065 b/test/fuzz/fuzz-resource-record/oss-fuzz-54065
new file mode 100644 (file)
index 0000000..9439452
Binary files /dev/null and b/test/fuzz/fuzz-resource-record/oss-fuzz-54065 differ
diff --git a/test/fuzz/fuzz-resource-record/oss-fuzz-54080 b/test/fuzz/fuzz-resource-record/oss-fuzz-54080
new file mode 100644 (file)
index 0000000..2577e65
Binary files /dev/null and b/test/fuzz/fuzz-resource-record/oss-fuzz-54080 differ
diff --git a/test/fuzz/fuzz-resource-record/oss-fuzz-54090 b/test/fuzz/fuzz-resource-record/oss-fuzz-54090
new file mode 100644 (file)
index 0000000..994d908
Binary files /dev/null and b/test/fuzz/fuzz-resource-record/oss-fuzz-54090 differ
diff --git a/test/fuzz/fuzz-resource-record/ub-zero-length-rdata b/test/fuzz/fuzz-resource-record/ub-zero-length-rdata
new file mode 100644 (file)
index 0000000..b5301ee
Binary files /dev/null and b/test/fuzz/fuzz-resource-record/ub-zero-length-rdata differ
index f2080b5a9a1ce7c3d02fe7451f18e2785d832b4f..93f0e679fe3afba0ad186631193c3004e549d9f6 100644 (file)
@@ -89,6 +89,7 @@ if install_tests
                      install_dir : testdata_dir)
 endif
 
+test_bootctl_json_sh = find_program('test-bootctl-json.sh')
 test_fstab_generator_sh = find_program('test-fstab-generator.sh')
 test_network_generator_conversion_sh = find_program('test-network-generator-conversion.sh')
 test_systemctl_enable_sh = find_program('test-systemctl-enable.sh')
diff --git a/test/test-bootctl-json.sh b/test/test-bootctl-json.sh
new file mode 100755 (executable)
index 0000000..7a660a8
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+set -o pipefail
+
+bootctl="${1:?}"
+
+"$bootctl" --no-pager list >/dev/null || {
+    echo "$bootctl list failed, skipping tests" 1>&2
+    exit 77
+}
+
+set -x
+
+"$bootctl" list --json=pretty | python3 -m json.tool >/dev/null
+"$bootctl" list --json=short | python3 -m json.tool >/dev/null
+
+command -v jq >/dev/null || {
+    echo "jq is not available, skipping jq tests" 1>&2
+    exit 0
+}
+
+"$bootctl" list --json=pretty | jq . >/dev/null
+"$bootctl" list --json=short | jq . >/dev/null
index 28331c241221e395bb99104a5a8c071a68c6c613..9aa9c37a9e532ad9608aa4845465d75186b2f11a 100644 (file)
@@ -158,6 +158,7 @@ BASICTOOLS=(
     cat
     chmod
     chown
+    chroot
     cmp
     cryptsetup
     cut
@@ -1344,7 +1345,7 @@ create_empty_image() {
     fi
 
     # Partition sizes are in MiBs
-    local root_size=500
+    local root_size=1000
     local data_size=50
     if ! get_bool "$NO_BUILD"; then
         if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then
index e4e92d5a37915cb762b11bf91cf3cb0c10293621..7e17cac10b79c49bcc64c9412ac092c09b2e64d0 100755 (executable)
@@ -3871,7 +3871,7 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
 
         output = check_output('ip -d link show bond199')
         print(output)
-        self.assertRegex(output, 'active_slave dummy98')
+        self.assertIn('active_slave dummy98', output)
 
     def test_bond_primary_slave(self):
         copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
@@ -3880,7 +3880,20 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
 
         output = check_output('ip -d link show bond199')
         print(output)
-        self.assertRegex(output, 'primary dummy98')
+        self.assertIn('primary dummy98', output)
+
+        # for issue #25627
+        mkdir_p(os.path.join(network_unit_dir, '23-bond199.network.d'))
+        for mac in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
+            with open(os.path.join(network_unit_dir, '23-bond199.network.d/mac.conf'), mode='w', encoding='utf-8') as f:
+                f.write(f'[Link]\nMACAddress={mac}\n')
+
+            networkctl_reload()
+            self.wait_online(['dummy98:enslaved', 'bond199:degraded'])
+
+            output = check_output('ip -d link show bond199')
+            print(output)
+            self.assertIn(f'link/ether {mac}', output)
 
     def test_bond_operstate(self):
         copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
index 3099b5746fbd11a1f4a9c3cbfd1b7bd83af5ffec..ca84db6a032feb083d71039e5c59f7eab00d52e6 100755 (executable)
@@ -2444,7 +2444,7 @@ sub udev_setup {
         rmdir($udev_tmpfs);
         mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n";
 
-        if (system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs)) {
+        if (system("mount", "-o", "rw,mode=0755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs)) {
                 warn "unable to mount tmpfs";
                 return 0;
         }
index 7c7a12b1ae5a683c07b6063bfaba2e97969651fc..70fdc5956e56f4d574dded1e47affdcb4a95810f 100755 (executable)
@@ -9,6 +9,9 @@ at_exit() {
     fi
 }
 
+# shellcheck source=test/units/assert.sh
+. "$(dirname "$0")"/assert.sh
+
 trap at_exit EXIT
 
 # Create a simple unit file for testing
@@ -79,6 +82,16 @@ systemctl list-jobs --before
 systemctl list-jobs --after --before
 systemctl list-jobs "*"
 
+# is-* verbs
+# Should return 4 for a missing unit file
+assert_rc 4 systemctl --quiet is-active not-found.service
+assert_rc 4 systemctl --quiet is-failed not-found.service
+assert_rc 4 systemctl --quiet is-enabled not-found.service
+# is-active: return 3 when the unit exists but inactive
+assert_rc 3 systemctl --quiet is-active "$UNIT_NAME"
+# is-enabled: return 1 when the unit exists but disabled
+assert_rc 1 systemctl --quiet is-enabled "$UNIT_NAME"
+
 # Basic service management
 systemctl start --show-transaction "$UNIT_NAME"
 systemctl status -n 5 "$UNIT_NAME"
index a5c1eaba268674cf9fcc14ef21a14e93c4b5106c..088e5677277dcde6d7539210dd12ca6598368b50 100755 (executable)
@@ -237,6 +237,12 @@ grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
 grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
 systemd-dissect --umount "${image_dir}/mount"
 
+systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" --in-memory "${image_dir}/mount"
+grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
+grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
+grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
+systemd-dissect --umount "${image_dir}/mount"
+
 # add explicit -p MountAPIVFS=yes once to test the parser
 systemd-run -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
 
@@ -409,6 +415,15 @@ systemd-sysext unmerge
 rmdir /etc/extensions/app-nodistro
 rm /var/lib/extensions/app-nodistro.raw
 
+mkdir -p /run/machines /run/portables /run/extensions
+touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+systemd-dissect --discover --json=short > /tmp/discover.json
+grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
+grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
+grep -q -F '{"name":"c","type":"raw","class":"extension","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
+rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
 echo OK >/testok
 
 exit 0
index 01eec207459489838c422ce2dba49cc47dc48d21..2b0fe7dfb20508009ba24af511b1cb8083d38951 100755 (executable)
@@ -161,7 +161,7 @@ $imgs/zzz2 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5
                                   --dry-run=no \
                                   --seed="$seed" \
                                   --empty=force \
-                                  --skip-partitions=home,root \
+                                  --defer-partitions=home,root \
                                   "$imgs/zzz"
 
     output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
@@ -901,7 +901,7 @@ test_minimize() {
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
 
-    for format in ext4 vfat; do
+    for format in ext4 vfat erofs; do
         if ! command -v "mkfs.$format" >/dev/null; then
             continue
         fi
index 83db0531072ca6f58aa8c1fbacea9a682e045877..ab6b2216fe1ad24dcdb27e3844de8f62ea33f8d0 100755 (executable)
@@ -85,6 +85,25 @@ wait_on_state_or_fail "testservice-abort-restart-59.service" "failed" "30"
 
 systemd-analyze log-level info
 
+# Test that rate-limiting daemon-reload works
+mkdir -p /run/systemd/system.conf.d/
+cat >/run/systemd/system.conf.d/50-test-59-reload.conf <<EOF
+[Manager]
+ReloadLimitIntervalSec=9
+ReloadLimitBurst=3
+EOF
+
+# Pick up the new config
+systemctl daemon-reload
+
+# The timeout will hit (and the test will fail) if the reloads are not rate-limited
+timeout 15 bash -c 'while systemctl daemon-reload --no-block; do true; done'
+
+# Rate limit should reset after 9s
+sleep 10
+
+systemctl daemon-reload
+
 echo OK >/testok
 
 exit 0
index 8e4653312baf0fee5c8977f06c4527201e3c58db..c4406f381bf581659d7e557e85f8fa734982f269 100755 (executable)
@@ -192,7 +192,7 @@ testcase_nvme_subsystem() {
 testcase_virtio_scsi_identically_named_partitions() {
     local num
 
-    if [[ -n "${ASAN_OPTIONS:-}" ]] || [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
         num=$((4 * 4))
     else
         num=$((16 * 8))
@@ -306,7 +306,7 @@ testcase_simultaneous_events() {
     local -a devices symlinks
     local -A running
 
-    if [[ -n "${ASAN_OPTIONS:-}" ]] || [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
         num_part=2
         iterations=10
         timeout=240
@@ -401,7 +401,7 @@ testcase_lvm_basic() {
         /dev/disk/by-id/ata-foobar_deadbeeflvm{0..3}
     )
 
-    if [[ -n "${ASAN_OPTIONS:-}" ]] || [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
         timeout=180
     else
         timeout=30
@@ -454,7 +454,7 @@ testcase_lvm_basic() {
     helper_check_device_units
 
     # Same as above, but now with more "stress"
-    if [[ -n "${ASAN_OPTIONS:-}" ]] || [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
         iterations=10
     else
         iterations=50
@@ -479,7 +479,7 @@ testcase_lvm_basic() {
     helper_check_device_units
 
     # Create & remove LVs in a loop, i.e. with more "stress"
-    if [[ -n "${ASAN_OPTIONS:-}" ]]; then
+    if [[ -v ASAN_OPTIONS ]]; then
         iterations=8
         partitions=16
     elif [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
index 1f34308b44cdc41a1398c9fddfbca929ef22f0bd..ebe1f57b52f181e2005fc336af7b4b2f59f74056 100755 (executable)
@@ -139,6 +139,16 @@ systemd-analyze cat-config systemd/system.conf systemd/journald.conf >/dev/null
 systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null
 systemd-analyze cat-config foo/bar
 
+if [[ ! -v ASAN_OPTIONS ]]; then
+    # check that systemd-analyze cat-config paths work in a chroot
+    mkdir -p /tmp/root
+    mount --bind / /tmp/root
+    systemd-analyze cat-config systemd/system-preset >/tmp/out1
+    chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2
+    diff /tmp/out{1,2}
+fi
+
+# verify
 mkdir -p /tmp/img/usr/lib/systemd/system/
 mkdir -p /tmp/img/opt/
 
index b1cf7e83c4b5b5d37fcd3428ff3a1bac49e4a2dc..89cd2a3f827ad9b3193a9580a6e9f8cfbbc54c99 100755 (executable)
@@ -102,8 +102,17 @@ if [ -e /usr/lib/systemd/systemd-measure ] && \
     openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "/tmp/pcrsign-private.pem"
     openssl rsa -pubout -in "/tmp/pcrsign-private.pem" -out "/tmp/pcrsign-public.pem"
 
+    MEASURE_BANKS=("--bank=sha256")
+    # Check if SHA1 signatures are supported
+    #
+    # Some distros have started phasing out SHA1, so make sure the SHA1
+    # signatures are supported before trying to use them.
+    if echo hello | openssl dgst -sign /tmp/pcrsign-private.pem -sha1 >/dev/null; then
+        MEASURE_BANKS+=("--bank=sha1")
+    fi
+
     # Sign current PCR state with it
-    /usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig"
+    /usr/lib/systemd/systemd-measure sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig"
     dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64
     systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem"
     systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata
@@ -113,7 +122,7 @@ if [ -e /usr/lib/systemd/systemd-measure ] && \
     systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" > /dev/null && { echo 'unexpected success'; exit 1; }
 
     # Sign new PCR state, decrypting should work now.
-    /usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig2"
+    /usr/lib/systemd/systemd-measure sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig2"
     systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata
 
     # Now, do the same, but with a cryptsetup binding
@@ -135,7 +144,7 @@ if [ -e /usr/lib/systemd/systemd-measure ] && \
     SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 /usr/lib/systemd/systemd-cryptsetup attach test-volume2 $img - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 && { echo 'unexpected success'; exit 1; }
 
     # But once we sign the current PCRs, we should be able to unlock again
-    /usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig3"
+    /usr/lib/systemd/systemd-measure sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig3"
     SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 /usr/lib/systemd/systemd-cryptsetup attach test-volume2 $img - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
     /usr/lib/systemd/systemd-cryptsetup detach test-volume2
     SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 /usr/lib/systemd/systemd-cryptsetup attach test-volume2 $img - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
index 0c68e0636f92b692ab54eaf3effeff299f3355ab..d2062c7b059514844f0ee55a4ed6c83061b9b2e6 100755 (executable)
@@ -212,6 +212,11 @@ resolvectl log-level debug
 
 # Start monitoring queries
 systemd-run -u resmontest.service -p Type=notify resolvectl monitor
+# Wait for the monitoring service to become active
+for _ in {0..9}; do
+    [[ "$(systemctl show -P ActiveState resmontest.service)" == "active" ]] && break
+    sleep .5
+done
 
 # We need to manually propagate the DS records of onlinesign.test. to the parent
 # zone, since they're generated online
index a702c0d9d7736725370ad82925e7d879c9820e68..b04412e037f2ed92b5a33fd03af6158aaba4f411 100644 (file)
@@ -18,7 +18,7 @@ After=proc-sys-fs-binfmt_misc.automount
 After=proc-sys-fs-binfmt_misc.mount
 After=local-fs.target
 Before=sysinit.target shutdown.target
-ConditionPathIsReadWrite=/proc/sys/
+ConditionPathIsMountPoint=/proc/sys/fs/binfmt_misc
 ConditionDirectoryNotEmpty=|/lib/binfmt.d
 ConditionDirectoryNotEmpty=|/usr/lib/binfmt.d
 ConditionDirectoryNotEmpty=|/usr/local/lib/binfmt.d