]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #28917 from yuwata/network-address-pool
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 24 Aug 2023 13:58:31 +0000 (15:58 +0200)
committerGitHub <noreply@github.com>
Thu, 24 Aug 2023 13:58:31 +0000 (15:58 +0200)
undefined

116 files changed:
.github/workflows/build_test.sh
.github/workflows/unit_tests.sh
TODO
catalog/meson.build
hwdb.d/meson.build
man/meson.build
man/networkd.conf.xml
man/repart.d.xml
man/rules/meson.build
man/systemd-boot.xml
man/systemd-bsod.service.xml [moved from man/systemd-bsod.xml with 59% similarity]
man/systemd.network.xml
meson.build
meson_options.txt
mkosi.conf.d/10-fedora-rawhide.conf [deleted file]
mkosi.presets/base/mkosi.build
src/basic/alloc-util.h
src/basic/cgroup-util.c
src/basic/efivars.c
src/basic/extract-word.c
src/basic/locale-util.c
src/basic/rlimit-util.c
src/basic/string-util.c
src/basic/string-util.h
src/basic/strv.c
src/basic/utf8.c
src/boot/bootctl-set-efivar.c
src/boot/efi/boot.c
src/boot/efi/meson.build
src/boot/efi/test-bcd.c
src/boot/efi/util.c
src/boot/efi/util.h
src/boot/measure.c
src/core/credential.c [new file with mode: 0644]
src/core/credential.h [new file with mode: 0644]
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment.c
src/core/meson.build
src/core/namespace.c
src/core/namespace.h
src/core/socket.c
src/core/unit.c
src/fuzz/fuzz-json.c
src/home/homectl.c
src/home/homed-home.c
src/home/user-record-sign.c
src/home/user-record-util.c
src/hostname/hostnamed.c
src/journal/bsod.c
src/journal/meson.build
src/libsystemd-network/dhcp-identifier.c
src/libsystemd-network/dhcp-identifier.h
src/libsystemd-network/dhcp6-lease-internal.h
src/libsystemd-network/radv-internal.h
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-radv.c
src/libsystemd-network/test-dhcp6-client.c
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-json.c
src/network/networkd-ndisc.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.h
src/network/networkd-radv.c
src/network/networkd-radv.h
src/network/networkd-route.c
src/network/test-networkd-conf.c
src/nspawn/nspawn-network.c
src/partition/repart.c
src/resolve/resolved-dns-cache.c
src/resolve/resolved-manager.c
src/resolve/resolved-varlink.c
src/shared/bootspec.c
src/shared/bus-polkit.c
src/shared/dissect-image.c
src/shared/elf-util.c
src/shared/gpt.c
src/shared/gpt.h
src/shared/json.c
src/shared/json.h
src/shared/meson.build
src/shared/mount-util.c
src/shared/mount-util.h
src/shared/userdb-dropin.c
src/shared/verbs.c
src/sysext/sysext.c
src/systemd/sd-dhcp-client.h
src/systemd/sd-dhcp6-client.h
src/systemd/sd-radv.h
src/test/test-execute.c
src/test/test-json.c
src/test/test-mount-util.c
src/test/test-namespace.c
src/test/test-ns.c
src/test/test-string-util.c
src/test/test-utf8.c
test/fuzz/meson.build
test/test-execute/exec-load-credential-with-mount-namespace.service [new file with mode: 0644]
test/test-execute/exec-load-credential-with-seccomp.service [new file with mode: 0644]
test/test-execute/exec-set-credential-with-mount-namespace.service [new file with mode: 0644]
test/test-execute/exec-set-credential-with-seccomp.service [new file with mode: 0644]
test/test-network/conf/25-dhcp-client-ipv6-only-custom-client-identifier.network
test/test-network/conf/25-route-congctl.network
test/test-network/systemd-networkd-tests.py
test/units/testsuite-07.mount-invalid-chars.sh
test/units/testsuite-13.nspawn.sh
test/units/testsuite-58.sh
tools/oss-fuzz.sh
units/meson.build
units/systemd-bsod.service.in

index 5ffff1952172b85bb0cbead4f1f179f2ea3c40f4..a512e5101d298b6f2036e23352998a358dd898d0 100755 (executable)
@@ -11,7 +11,7 @@ ARGS=(
     "--optimization=0"
     "--optimization=s"
     "--optimization=3 -Db_lto=true -Ddns-over-tls=false"
-    "--optimization=3 -Db_lto=false -Dtpm2=false -Dlibfido2=false -Dp11kit=false"
+    "--optimization=3 -Db_lto=false -Dtpm2=disabled -Dlibfido2=disabled -Dp11kit=disabled"
     "--optimization=3 -Ddns-over-tls=openssl"
     "--optimization=3 -Dfexecve=true -Dstandalone-binaries=true -Dstatic-libsystemd=true -Dstatic-libudev=true"
     "-Db_ndebug=true"
index e9a398c701fe2dca293aa8407c845403d809eff7..0985817b72e5cb047f56a22877f45854e6e40111 100755 (executable)
@@ -60,7 +60,7 @@ for phase in "${PHASES[@]}"; do
                 export CXX=clang++
                 if [[ "$phase" == RUN_CLANG ]]; then
                     # The docs build is slow and is not affected by compiler/flags, so do it just once
-                    MESON_ARGS+=(-Dman=true)
+                    MESON_ARGS+=(-Dman=enabled)
                 else
                     MESON_ARGS+=(-Dmode=release --optimization=2)
                 fi
@@ -87,7 +87,7 @@ for phase in "${PHASES[@]}"; do
                 MESON_ARGS+=(-Db_lundef=false -Dfuzz-tests=true)
 
                 if [[ "$phase" == "RUN_CLANG_ASAN_UBSAN_NO_DEPS" ]]; then
-                    MESON_ARGS+=(-Dskip-deps=true)
+                    MESON_ARGS+=(--auto-features=disabled)
                 fi
             fi
             MESON_ARGS+=(--fatal-meson-warnings)
diff --git a/TODO b/TODO
index 2ba0666d4aa4a6208ae311be3928c131de6f10c9..eda525dc443dc8626386c312cff0292bf361b98e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -949,8 +949,6 @@ Features:
   appropriate qemu cmdline. That way qemu payloads could talk sd_notify()
   directly to host service manager.
 
-* sd-boot: add menu item for shutdown? or hotkey?
-
 * sd-device has an API to create an sd_device object from a device id, but has
   no api to query the device id
 
index 146daba1249e6eaff15ab1863676c138c2fe949d..3c62749cf982be69cc6a4ab96e24a95af119cd1a 100644 (file)
@@ -34,5 +34,5 @@ foreach file : in_files
                 install_dir : catalogdir)
 endforeach
 
-meson.add_install_script('sh', '-c',
+meson.add_install_script(sh, '-c',
                          'test -n "$DESTDIR" || @0@/journalctl --update-catalog'.format(bindir))
index 64a7e7d5065226f184d37b527b0305fa53a07e75..32e6505bc61e21d543d7c85e8d79a9afc200bd6a 100644 (file)
@@ -53,7 +53,7 @@ if conf.get('ENABLE_HWDB') == 1
         if install_sysconfdir
                 install_emptydir(sysconfdir / 'udev/hwdb.d')
 
-                meson.add_install_script('sh', '-c',
+                meson.add_install_script(sh, '-c',
                                          'test -n "$DESTDIR" || @0@/systemd-hwdb update'.format(bindir))
         endif
 
index f980fd223063174e8319fe12134958557a983e73..8709007e12b8bfbbb224365cd6f0e561760483bf 100644 (file)
@@ -6,9 +6,9 @@ subdir('rules')
 want_man = get_option('man')
 want_html = get_option('html')
 xsltproc = find_program('xsltproc',
-                        required : want_man == 'true' or want_html == 'true')
-want_man = want_man != 'false' and xsltproc.found()
-want_html = want_html != 'false' and xsltproc.found()
+                        required : want_man.enabled() or want_html.enabled())
+want_man = want_man.allowed() and xsltproc.found()
+want_html = want_html.allowed() and xsltproc.found()
 
 xsltproc_flags = [
         '--nonet',
index 9fa925e85c87d7ff96260fcbabd918205f46b2ee..f25083cc69ccd389cccd719cfe728041743d98cd 100644 (file)
         <ulink url="https://tools.ietf.org/html/rfc3315#section-9">RFC 3315</ulink>
         for a description of all the options.</para>
 
-        <para>The following values are understood:
+        <para>This takes an integer in the range 0…65535, or one of the following string values:
         <variablelist>
           <varlistentry>
             <term><option>vendor</option></term>
             </para></listitem>
           </varlistentry>
 
-          <varlistentry>
-            <term><option>custom</option></term>
-            <listitem><para>If <literal>DUIDType=custom</literal>, then the <literal>DUIDRawData=</literal> value will
-            used be as custom identifier. If <literal>DUIDType=custom</literal> is specified then the
-            <literal>DUIDRawData=</literal> key is mandatory. Note it applies only on DHCPv6 clients.</para></listitem>
-          </varlistentry>
-
           <varlistentry>
             <term><option>uuid</option></term>
             <listitem><para>If <literal>DUIDType=uuid</literal>, and <varname>DUIDRawData=</varname> is not set,
index 2fe723673eaf99a5bb101b90fc204eff056042bc..ac72d0e8456c6e7973d5c97b0ee23933edeebbfd 100644 (file)
@@ -83,8 +83,9 @@
         <constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
         <constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
         <constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).
+        </para>
 
-        The supported identifiers are:</para>
+        <para>The supported identifiers are:</para>
 
         <table>
           <title>GPT partition type identifiers</title>
     <example>
       <title>Grow the root partition to the full disk size at first boot</title>
 
-      <para>With the following file the root partition is automatically grown to the full disk if possible during boot.</para>
+      <para>With the following file the root partition is automatically grown to the full disk if possible
+      during boot.</para>
 
       <para><programlisting># /usr/lib/repart.d/50-root.conf
 [Partition]
@@ -831,10 +833,11 @@ SizeMaxBytes=64M
     </example>
 
      <example>
-      <title>Create a data and verity partition from a OS tree</title>
+      <title>Create a data partition and corresponding verity partitions from a OS tree</title>
 
-      <para>Assuming we have an OS tree at /var/tmp/os-tree that we want to package in a root partition
-      together with a matching verity partition, we can do so as follows:</para>
+      <para>Assuming we have an OS tree at <filename index='false'>/var/tmp/os-tree</filename> that we want
+      to package in a root partition together with matching verity partitions, we can do so as follows:
+      </para>
 
       <para><programlisting># 50-root.conf
 [Partition]
@@ -842,6 +845,7 @@ Type=root
 CopyFiles=/var/tmp/os-tree
 Verity=data
 VerityMatchKey=root
+Minimize=guess
 </programlisting></para>
 
       <para><programlisting># 60-root-verity.conf
@@ -852,6 +856,14 @@ VerityMatchKey=root
 # Explicitly set the hash and data block size to 4K
 VerityDataBlockSizeBytes=4096
 VerityHashBlockSizeBytes=4096
+Minimize=best
+</programlisting></para>
+
+<para><programlisting># 70-root-verity-sig.conf
+[Partition]
+Type=root-verity-sig
+Verity=signature
+VerityMatchKey=root
 </programlisting></para>
     </example>
 
index f03cde876d74d34bb11122b7048a34f2523fd854..eaaf4adb1bd1cdde8cd7e467d7937b9e6095a919 100644 (file)
@@ -902,7 +902,7 @@ manpages = [
   ''],
  ['systemd-boot-random-seed.service', '8', [], 'ENABLE_BOOTLOADER'],
  ['systemd-boot', '7', ['sd-boot'], 'ENABLE_BOOTLOADER'],
- ['systemd-bsod', '8', [], 'HAVE_QRENCODE'],
+ ['systemd-bsod.service', '8', ['systemd-bsod'], 'HAVE_QRENCODE'],
  ['systemd-cat', '1', [], ''],
  ['systemd-cgls', '1', [], ''],
  ['systemd-cgtop', '1', [], ''],
@@ -1083,6 +1083,7 @@ manpages = [
   '8',
   ['systemd-tmpfiles-clean.service',
    'systemd-tmpfiles-clean.timer',
+   'systemd-tmpfiles-setup-dev-early.service',
    'systemd-tmpfiles-setup-dev.service',
    'systemd-tmpfiles-setup.service'],
   ''],
index 30908e398a3c5975f8a2c025768a6a8786a06206..56044281d44cae32b743c1377843e8bb26d14862 100644 (file)
         may also be reached with <keycap>F2</keycap>, <keycap>F10</keycap>, <keycap>Del</keycap> and
         <keycap>Esc</keycap>.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><keycombo><keycap>Shift</keycap><keycap>o</keycap></keycombo></term>
+        <listitem><para>Power off the system.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><keycombo><keycap>Shift</keycap><keycap>b</keycap></keycombo></term>
+        <listitem><para>Reboot the system.</para></listitem>
+      </varlistentry>
     </variablelist>
 
     <para>The following keys may be pressed during bootup or in the boot menu to directly boot a specific
similarity index 59%
rename from man/systemd-bsod.xml
rename to man/systemd-bsod.service.xml
index 86efb29db0809da215309831bc69becf6dca75af..8530468d1dc2f279ed2a48873827fba6ddde8abb 100644 (file)
@@ -3,7 +3,7 @@
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
 
-<refentry id="systemd-bsod" conditional='HAVE_QRENCODE' xmlns:xi="http://www.w3.org/2001/XInclude">
+<refentry id="systemd-bsod.service" conditional='HAVE_QRENCODE' xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>systemd-bsod</title>
   </refentryinfo>
 
   <refmeta>
-    <refentrytitle>systemd-bsod</refentrytitle>
+    <refentrytitle>systemd-bsod.service</refentrytitle>
     <manvolnum>8</manvolnum>
   </refmeta>
 
   <refnamediv>
+    <refname>systemd-bsod.service</refname>
     <refname>systemd-bsod</refname>
-    <refpurpose>Displays boot-time emergency log message full-screen.</refpurpose>
+    <refpurpose>Displays boot-time emergency log message in full screen.</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
+    <para><filename>systemd-bsod.service</filename></para>
     <cmdsynopsis>
       <command>systemd-bsod</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
   <refsect1>
     <title>Description</title>
 
-    <para><command>systemd-bsod</command> is used to display a blue screen which contains a message relating to
-        a boot failure, including a QR code which users can scan with their phones to get helpful information
-        about the cause of their boot failure.</para>
+    <para><filename>systemd-bsod.service</filename> is used to display a blue screen which contains a message
+    relating to a boot failure, including a QR code which can be scanned to get helpful information about the
+    failure.</para>
   </refsect1>
 
   <refsect1>
     <title>Options</title>
 
-    <para>The following options are understood:</para>
+    <para>The following options are understood by <command>systemd-bsod</command>:</para>
 
     <variablelist>
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
 
-     <varlistentry>
+      <varlistentry>
         <term><option>-c</option></term>
         <term><option>--continuous</option></term>
 
-        <listitem><para> Used to make systemd-bsod wait continuously for changes in the
-         journal's status if doesn't find any emergency messages on initial attempt.</para></listitem>
-     </varlistentry>
+        <listitem><para>When specified, <command>systemd-bsod</command> waits continuously for changes in the
+        journal if it doesn't find any emergency messages on the initial attempt.</para></listitem>
+      </varlistentry>
     </variablelist>
 
   </refsect1>
index da4282e5020c8697d90b73a922e54c63cc5785a3..93830b5935b1f323b6a2fbf1458ef7486cdf3608 100644 (file)
@@ -1620,7 +1620,8 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
       <varlistentry>
         <term><varname>HopLimit=</varname></term>
         <listitem>
-          <para> Configures per route hop limit. See also <varname>IPv6HopLimit=</varname>.</para>
+          <para>Configures per route hop limit. Takes an integer in the range 1…255. See also
+          <varname>IPv6HopLimit=</varname>.</para>
         </listitem>
       </varlistentry>
 
@@ -1727,11 +1728,11 @@ allow my_server_t localnet_peer_t:peer recv;</programlisting>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>TCPRetransmissionTimeOutSec=</varname></term>
+        <term><varname>TCPRetransmissionTimeoutSec=</varname></term>
         <listitem>
-          <para>Specifies the TCP Retransmission Time Out for the route. Takes time values in seconds.
-            This value specifies the timeout of an alive TCP connection, when RTO retransmissions remain unacknowledged.
-            When unset, the kernel's default will be used.</para>
+          <para>Specifies the TCP Retransmission Timeout (RTO) for the route. Takes time values in seconds.
+          This value specifies the timeout of an alive TCP connection, when retransmissions remain
+          unacknowledged. When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
 
@@ -3075,6 +3076,15 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RetransmitSec=</varname></term>
+
+        <listitem><para>Takes a timespan. Configures the retransmit time, used by clients to retransmit Neighbor
+        Solicitation messages on address resolution and the Neighbor Unreachability Detection algorithm.
+        An integer the default unit of seconds, in the range 0…4294967295 msec. Defaults to 0.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>RouterPreference=</varname></term>
 
@@ -3088,6 +3098,14 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
         for details. Defaults to <literal>medium</literal>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>HopLimit=</varname></term>
+        <listitem>
+          <para>Configures hop limit. Takes an integer in the range 0…255. See also
+          <varname>IPv6HopLimit=</varname>.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>UplinkInterface=</varname></term>
         <listitem><para>Specifies the name or the index of the uplink interface, or one of the special
index 8f7da1ec6e3412912c37b135ee3f71da1e9e4aaa..7d08c0f852b020a0f286b4b6b9075d20c13b28a7 100644 (file)
@@ -33,13 +33,13 @@ conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path)
 
 conf.set10('BUILD_MODE_DEVELOPER', get_option('mode') == 'developer',
            description : 'tailor build to development or release builds')
-verification = get_option('log-message-verification')
-if verification == 'auto'
-        verification = conf.get('BUILD_MODE_DEVELOPER') == 1
+feature = get_option('log-message-verification')
+if feature.auto()
+        have = conf.get('BUILD_MODE_DEVELOPER') == 1
 else
-        verification = verification == 'true'
+        have = feature.enabled()
 endif
-conf.set10('LOG_MESSAGE_VERIFICATION', verification)
+conf.set10('LOG_MESSAGE_VERIFICATION', have)
 
 want_ossfuzz = get_option('oss-fuzz')
 want_libfuzzer = get_option('llvm-fuzz')
@@ -47,7 +47,6 @@ if want_ossfuzz and want_libfuzzer
         error('only one of oss-fuzz or llvm-fuzz can be specified')
 endif
 
-skip_deps = want_ossfuzz or get_option('skip-deps')
 fuzzer_build = want_ossfuzz or want_libfuzzer
 
 # If we're building *not* for actual fuzzing, allow input samples of any size
@@ -1027,23 +1026,14 @@ foreach ident : [
         conf.set10('HAVE_' + ident[0].to_upper(), have)
 endforeach
 
-want_bpf_framework = get_option('bpf-framework')
+bpf_framework = get_option('bpf-framework')
 bpf_compiler = get_option('bpf-compiler')
-bpf_framework_required = want_bpf_framework == 'true'
-
-libbpf_version_requirement = '>= 0.1.0'
-if bpf_compiler == 'gcc'
-        libbpf_version_requirement = '>= 1.0.0'
-endif
-libbpf = dependency('libbpf', required : bpf_framework_required, version : libbpf_version_requirement)
+libbpf = dependency('libbpf',
+                    required : bpf_framework,
+                    version : bpf_compiler == 'gcc' ? '>= 1.0.0' : '>= 0.1.0')
 conf.set10('HAVE_LIBBPF', libbpf.found())
 
-bpftool_strip_version_requirement = '>= 5.13.0'
-if bpf_compiler == 'gcc'
-        bpftool_strip_version_requirement = '>= 7.0.0'
-endif
-
-if want_bpf_framework == 'false' or not libbpf.found() or skip_deps
+if not libbpf.found()
         conf.set10('BPF_FRAMEWORK', false)
 else
         clang_found = false
@@ -1056,7 +1046,9 @@ else
                 # Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu
                 # (like clang-10/llvm-strip-10)
                 if meson.is_cross_build() or cc.get_id() != 'clang' or cc.cmd_array()[0].contains('afl-clang') or cc.cmd_array()[0].contains('hfuzz-clang')
-                        r = find_program('clang', required : bpf_framework_required, version : '>= 10.0.0')
+                        r = find_program('clang',
+                                         required : bpf_framework,
+                                         version : '>= 10.0.0')
                         clang_found = r.found()
                         if clang_found
                                 clang = r.full_path()
@@ -1082,11 +1074,10 @@ else
                 # Debian installs this in /usr/sbin/ which is not in $PATH.
                 # We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
                 # We use 'bpftool gen object' subcommand for bpftool strip, it was added by d80b2fcbe0a023619e0fc73112f2a02c2662f6ab (v5.13).
-                bpftool_strip_required = bpf_framework_required and bpf_compiler == 'gcc'
                 bpftool = find_program('bpftool',
                                        '/usr/sbin/bpftool',
-                                       required : bpftool_strip_required,
-                                       version : bpftool_strip_version_requirement)
+                                       required : bpf_framework.enabled() and bpf_compiler == 'gcc',
+                                       version : bpf_compiler == 'gcc' ? '>= 7.0.0' : '>= 5.13.0')
 
                 if bpftool.found()
                         bpftool_strip = true
@@ -1095,7 +1086,7 @@ else
                         # We require the 'bpftool gen skeleton' subcommand, it was added by 985ead416df39d6fe8e89580cc1db6aa273e0175 (v5.6).
                         bpftool = find_program('bpftool',
                                                '/usr/sbin/bpftool',
-                                               required : bpf_framework_required,
+                                               required : bpf_framework,
                                                version : '>= 5.6.0')
                 endif
 
@@ -1107,7 +1098,9 @@ else
                         else
                                 llvm_strip_bin = 'llvm-strip'
                         endif
-                        llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required, version : '>= 10.0.0')
+                        llvm_strip = find_program(llvm_strip_bin,
+                                                  required : bpf_framework,
+                                                  version : '>= 10.0.0')
                         deps_found = llvm_strip.found()
                 endif
         endif
@@ -1119,80 +1112,40 @@ endif
 libmount = dependency('mount',
                       version : fuzzer_build ? '>= 0' : '>= 2.30')
 
-want_libfdisk = get_option('fdisk')
-if want_libfdisk != 'false' and not skip_deps
-        libfdisk = dependency('fdisk',
-                              version : '>= 2.32',
-                              required : want_libfdisk == 'true')
-        have = libfdisk.found()
-else
-        have = false
-        libfdisk = []
-endif
-conf.set10('HAVE_LIBFDISK', have)
-
-want_passwdqc = get_option('passwdqc')
-want_pwquality = get_option('pwquality')
-if want_passwdqc == 'true' and want_pwquality == 'true'
-        error('passwdqc and pwquality cannot be requested simultaneously')
-endif
-
-if want_pwquality != 'false' and want_passwdqc != 'true' and not skip_deps
-        libpwquality = dependency('pwquality',
-                                  version : '>= 1.4.1',
-                                  required : want_pwquality == 'true')
-        have = libpwquality.found()
-else
-        have = false
-        libpwquality = []
+libfdisk = dependency('fdisk',
+                      version : '>= 2.32',
+                      disabler : true,
+                      required : get_option('fdisk'))
+conf.set10('HAVE_LIBFDISK', libfdisk.found())
+
+# This prefers pwquality if both are enabled or auto.
+feature = get_option('pwquality').disable_auto_if(get_option('passwdqc').enabled())
+libpwquality = dependency('pwquality',
+                          version : '>= 1.4.1',
+                          required : feature)
+have = libpwquality.found()
+if not have
+        # libpwquality is used for both features for simplicity
+        libpwquality = dependency('passwdqc',
+                                  required : get_option('passwdqc'))
 endif
 conf.set10('HAVE_PWQUALITY', have)
+conf.set10('HAVE_PASSWDQC', not have and libpwquality.found())
 
-if not have and want_passwdqc != 'false' and not skip_deps
-        libpasswdqc = dependency('passwdqc',
-                                 required : want_passwdqc == 'true')
-        have = libpasswdqc.found()
-else
-        have = false
-        libpasswdqc = []
-endif
-conf.set10('HAVE_PASSWDQC', have)
+libseccomp = dependency('libseccomp',
+                        version : '>= 2.3.1',
+                        required : get_option('seccomp'))
+conf.set10('HAVE_SECCOMP', libseccomp.found())
 
-want_seccomp = get_option('seccomp')
-if want_seccomp != 'false' and not skip_deps
-        libseccomp = dependency('libseccomp',
-                                version : '>= 2.3.1',
-                                required : want_seccomp == 'true')
-        have = libseccomp.found()
-else
-        have = false
-        libseccomp = []
-endif
-conf.set10('HAVE_SECCOMP', have)
-
-want_selinux = get_option('selinux')
-if want_selinux != 'false' and not skip_deps
-        libselinux = dependency('libselinux',
-                                version : '>= 2.1.9',
-                                required : want_selinux == 'true')
-        have = libselinux.found()
-else
-        have = false
-        libselinux = []
-endif
-conf.set10('HAVE_SELINUX', have)
+libselinux = dependency('libselinux',
+                        version : '>= 2.1.9',
+                        required : get_option('selinux'))
+conf.set10('HAVE_SELINUX', libselinux.found())
 
-want_apparmor = get_option('apparmor')
-if want_apparmor != 'false' and not skip_deps
-        libapparmor = dependency('libapparmor',
-                                 version : '>= 2.13',
-                                 required : want_apparmor == 'true')
-        have = libapparmor.found()
-else
-        have = false
-        libapparmor = []
-endif
-conf.set10('HAVE_APPARMOR', have)
+libapparmor = dependency('libapparmor',
+                         version : '>= 2.13',
+                         required : get_option('apparmor'))
+conf.set10('HAVE_APPARMOR', libapparmor.found())
 
 have = get_option('smack') and get_option('smack-run-label') != ''
 conf.set10('HAVE_SMACK_RUN_LABEL', have)
@@ -1205,141 +1158,89 @@ if have
         conf.set_quoted('SMACK_DEFAULT_PROCESS_LABEL', get_option('smack-default-process-label'))
 endif
 
-want_polkit = get_option('polkit')
-install_polkit = false
-install_polkit_pkla = false
-if want_polkit != 'false' and not skip_deps
-        install_polkit = true
-
-        libpolkit = dependency('polkit-gobject-1',
-                               required : false)
-        if libpolkit.found() and libpolkit.version().version_compare('< 0.106')
-                message('Old polkit detected, will install pkla files')
-                install_polkit_pkla = true
-        endif
+feature = get_option('polkit')
+libpolkit = dependency('polkit-gobject-1',
+                       required : feature.disabled() ? feature : false)
+install_polkit = feature.allowed()
+install_polkit_pkla = libpolkit.found() and libpolkit.version().version_compare('< 0.106')
+if install_polkit_pkla
+        message('Old polkit detected, will install pkla files')
 endif
 conf.set10('ENABLE_POLKIT', install_polkit)
 
-want_acl = get_option('acl')
-if want_acl != 'false' and not skip_deps
-        libacl = dependency('libacl', required : want_acl == 'true')
-        have = libacl.found()
-else
-        have = false
-        libacl = []
-endif
-conf.set10('HAVE_ACL', have)
-
-want_audit = get_option('audit')
-if want_audit != 'false' and not skip_deps
-        libaudit = dependency('audit', required : want_audit == 'true')
-        have = libaudit.found()
-else
-        have = false
-        libaudit = []
-endif
-conf.set10('HAVE_AUDIT', have)
-
-want_blkid = get_option('blkid')
-if want_blkid != 'false' and not skip_deps
-        libblkid = dependency('blkid', required : want_blkid == 'true')
-        have = libblkid.found()
-
-        conf.set10('HAVE_BLKID_PROBE_SET_HINT',
-                   have and cc.has_function('blkid_probe_set_hint', dependencies : libblkid))
-else
-        have = false
-        libblkid = []
-endif
-conf.set10('HAVE_BLKID', have)
-
-want_kmod = get_option('kmod')
-if want_kmod != 'false' and not skip_deps
-        libkmod = dependency('libkmod',
-                             version : '>= 15',
-                             required : want_kmod == 'true')
-        have = libkmod.found()
-else
-        have = false
-        libkmod = []
-endif
-conf.set10('HAVE_KMOD', have)
-
-want_xenctrl = get_option('xenctrl')
-if want_xenctrl != 'false' and not skip_deps
-        libxenctrl = dependency('xencontrol',
-                                version : '>= 4.9',
-                                required : want_xenctrl == 'true')
-        have = libxenctrl.found()
-else
-        have = false
-        libxenctrl = []
-endif
-conf.set10('HAVE_XENCTRL', have)
-
-want_pam = get_option('pam')
-if want_pam != 'false' and not skip_deps
-        libpam = dependency('pam', required : false)
-        if not libpam.found()
-                # Debian older than bookworm and Ubuntu older than 22.10 do not provide the .pc file.
-                libpam = cc.find_library('pam', required : want_pam == 'true')
+libacl = dependency('libacl',
+                    required : get_option('acl'))
+conf.set10('HAVE_ACL', libacl.found())
+
+libaudit = dependency('audit',
+                      required : get_option('audit'))
+conf.set10('HAVE_AUDIT', libaudit.found())
+
+libblkid = dependency('blkid',
+                      required : get_option('blkid'))
+conf.set10('HAVE_BLKID', libblkid.found())
+conf.set10('HAVE_BLKID_PROBE_SET_HINT',
+           libblkid.found() and cc.has_function('blkid_probe_set_hint', dependencies : libblkid))
+
+libkmod = dependency('libkmod',
+                     version : '>= 15',
+                     required : get_option('kmod'))
+conf.set10('HAVE_KMOD', libkmod.found())
+
+libxenctrl = dependency('xencontrol',
+                        version : '>= 4.9',
+                        required : get_option('xenctrl'))
+conf.set10('HAVE_XENCTRL', libxenctrl.found())
+
+feature = get_option('pam')
+libpam = dependency('pam',
+                    required : feature.disabled() ? feature : false)
+if not libpam.found()
+        # Debian older than bookworm and Ubuntu older than 22.10 do not provide the .pc file.
+        libpam = cc.find_library('pam', required : feature)
+endif
+libpam_misc = dependency('pam_misc',
+                         required : feature.disabled() ? feature : false)
+if not libpam_misc.found()
+        libpam_misc = cc.find_library('pam_misc', required : feature)
+endif
+conf.set10('HAVE_PAM', libpam.found() and libpam_misc.found())
+
+libmicrohttpd = dependency('libmicrohttpd',
+                           version : '>= 0.9.33',
+                           required : get_option('microhttpd'))
+conf.set10('HAVE_MICROHTTPD', libmicrohttpd.found())
+
+libcryptsetup = get_option('libcryptsetup')
+libcryptsetup_plugins = get_option('libcryptsetup-plugins')
+if libcryptsetup_plugins.enabled()
+        if libcryptsetup.disabled()
+                error('libcryptsetup-plugins can not be requested without libcryptsetup')
         endif
-        libpam_misc = dependency('pam_misc', required : false)
-        if not libpam_misc.found()
-                libpam_misc = cc.find_library('pam_misc', required : want_pam == 'true')
-        endif
-        have = libpam.found() and libpam_misc.found()
-else
-        have = false
-        libpam = []
-        libpam_misc = []
-endif
-conf.set10('HAVE_PAM', have)
-
-want_microhttpd = get_option('microhttpd')
-if want_microhttpd != 'false' and not skip_deps
-        libmicrohttpd = dependency('libmicrohttpd',
-                                   version : '>= 0.9.33',
-                                   required : want_microhttpd == 'true')
-        have = libmicrohttpd.found()
-else
-        have = false
-        libmicrohttpd = []
-endif
-conf.set10('HAVE_MICROHTTPD', have)
-
-want_libcryptsetup = get_option('libcryptsetup')
-want_libcryptsetup_plugins = get_option('libcryptsetup-plugins')
-
-if want_libcryptsetup_plugins == 'true' and want_libcryptsetup == 'false'
-        error('libcryptsetup-plugins can not be requested without libcryptsetup')
-endif
-
-if want_libcryptsetup != 'false' and not skip_deps
-        libcryptsetup = dependency('libcryptsetup',
-                                   version : want_libcryptsetup_plugins == 'true' ? '>= 2.4.0' : '>= 2.0.1',
-                                   required : want_libcryptsetup == 'true' or want_libcryptsetup_plugins == 'true')
-        have = libcryptsetup.found()
-
-        foreach ident : ['crypt_set_metadata_size',
-                         'crypt_activate_by_signed_key',
-                         'crypt_token_max',
-                         'crypt_reencrypt_init_by_passphrase',
-                         'crypt_reencrypt',
-                         'crypt_set_data_offset']
-                have_ident = have and cc.has_function(
-                        ident,
-                        prefix : '#include <libcryptsetup.h>',
-                        dependencies : libcryptsetup)
-                conf.set10('HAVE_' + ident.to_upper(), have_ident)
-        endforeach
-else
-        have = false
-        libcryptsetup = []
-endif
+        libcryptsetup = libcryptsetup_plugins
+endif
+
+libcryptsetup = dependency('libcryptsetup',
+                           version : libcryptsetup_plugins.enabled() ? '>= 2.4.0' : '>= 2.0.1',
+                           required : libcryptsetup)
+
+have = libcryptsetup.found()
+foreach ident : ['crypt_set_metadata_size',
+                 'crypt_activate_by_signed_key',
+                 'crypt_token_max',
+                 'crypt_reencrypt_init_by_passphrase',
+                 'crypt_reencrypt',
+                 'crypt_set_data_offset']
+        have_ident = have and cc.has_function(
+                ident,
+                prefix : '#include <libcryptsetup.h>',
+                dependencies : libcryptsetup)
+        conf.set10('HAVE_' + ident.to_upper(), have_ident)
+endforeach
 conf.set10('HAVE_LIBCRYPTSETUP', have)
 
-if want_libcryptsetup_plugins != 'false' and not skip_deps
+# TODO: Use has_function(required : libcryptsetup_plugins) with meson >= 1.3.0
+if libcryptsetup_plugins.allowed()
         have = (cc.has_function(
                         'crypt_activate_by_token_pin',
                         prefix : '#include <libcryptsetup.h>',
@@ -1353,79 +1254,44 @@ else
 endif
 conf.set10('HAVE_LIBCRYPTSETUP_PLUGINS', have)
 
-want_libcurl = get_option('libcurl')
-if want_libcurl != 'false' and not skip_deps
-        libcurl = dependency('libcurl',
-                             version : '>= 7.32.0',
-                             required : want_libcurl == 'true')
-        have = libcurl.found()
-else
-        have = false
-        libcurl = []
-endif
-conf.set10('HAVE_LIBCURL', have)
+libcurl = dependency('libcurl',
+                     version : '>= 7.32.0',
+                     required : get_option('libcurl'))
+conf.set10('HAVE_LIBCURL', libcurl.found())
 conf.set10('CURL_NO_OLDIES', conf.get('BUILD_MODE_DEVELOPER') == 1)
 
-want_libidn = get_option('libidn')
-want_libidn2 = get_option('libidn2')
-if want_libidn == 'true' and want_libidn2 == 'true'
-        error('libidn and libidn2 cannot be requested simultaneously')
-endif
-
-if want_libidn2 != 'false' and want_libidn != 'true' and not skip_deps
-        libidn = dependency('libidn2',
-                            required : want_libidn2 == 'true')
-        have = libidn.found()
-else
-        have = false
-        libidn = []
-endif
-conf.set10('HAVE_LIBIDN2', have)
-if not have and want_libidn != 'false' and not skip_deps
+feature = get_option('libidn2').disable_auto_if(get_option('libidn').enabled())
+libidn = dependency('libidn2',
+                    required : feature)
+have = libidn.found()
+if not have
         # libidn is used for both libidn and libidn2 objects
         libidn = dependency('libidn',
-                            required : want_libidn == 'true')
-        have = libidn.found()
-else
-        have = false
+                            required : get_option('libidn'))
 endif
-conf.set10('HAVE_LIBIDN', have)
+conf.set10('HAVE_LIBIDN', not have and libidn.found())
+conf.set10('HAVE_LIBIDN2', have)
 
-want_libiptc = get_option('libiptc')
-if want_libiptc != 'false' and not skip_deps
-        libiptc = dependency('libiptc',
-                             required : want_libiptc == 'true')
-        have = libiptc.found()
-else
-        have = false
-        libiptc = []
-endif
-conf.set10('HAVE_LIBIPTC', have)
+libiptc = dependency('libiptc',
+                     required : get_option('libiptc'))
+conf.set10('HAVE_LIBIPTC', libiptc.found())
 
-want_qrencode = get_option('qrencode')
-if want_qrencode != 'false' and not skip_deps
-        libqrencode = dependency('libqrencode',
-                                 version : '>= 3',
-                                 required : want_qrencode == 'true')
-        have = libqrencode.found()
-else
-        have = false
-        libqrencode = []
-endif
-conf.set10('HAVE_QRENCODE', have)
+libqrencode = dependency('libqrencode',
+                         version : '>= 3',
+                         required : get_option('qrencode'))
+conf.set10('HAVE_QRENCODE', libqrencode.found())
 
-want_gcrypt = get_option('gcrypt')
-if want_gcrypt != 'false' and not skip_deps
-        libgcrypt = dependency('libgcrypt', required : want_gcrypt == 'true')
-        libgpg_error = dependency('gpg-error', required : false)
-        if not libgpg_error.found()
-                # CentOS 8 does not provide the .pc file.
-                libgpg_error = cc.find_library('gpg-error', required : want_gcrypt == 'true')
-        endif
-        have = libgcrypt.found() and libgpg_error.found()
-else
-        have = false
+feature = get_option('gcrypt')
+libgcrypt = dependency('libgcrypt',
+                       required : feature)
+libgpg_error = dependency('gpg-error',
+                          required : feature.disabled() ? feature : false)
+if not libgpg_error.found()
+        # CentOS 8 does not provide the .pc file.
+        libgpg_error = cc.find_library('gpg-error', required : feature)
 endif
+
+have = libgcrypt.found() and libgpg_error.found()
 if not have
         # link to neither of the libs if one is not found
         libgcrypt = []
@@ -1433,231 +1299,115 @@ if not have
 endif
 conf.set10('HAVE_GCRYPT', have)
 
-want_gnutls = get_option('gnutls')
-if want_gnutls != 'false' and not skip_deps
-        libgnutls = dependency('gnutls',
-                               version : '>= 3.1.4',
-                               required : want_gnutls == 'true')
-        have = libgnutls.found()
-else
-        have = false
-        libgnutls = []
-endif
-conf.set10('HAVE_GNUTLS', have)
-
-want_openssl = get_option('openssl')
-if want_openssl != 'false' and not skip_deps
-        libopenssl = dependency('openssl',
-                                version : '>= 1.1.0',
-                                required : want_openssl == 'true')
-        have = libopenssl.found()
-else
-        have = false
-        libopenssl = []
-endif
-conf.set10('HAVE_OPENSSL', have)
-
-want_p11kit = get_option('p11kit')
-if want_p11kit != 'false' and not skip_deps
-        libp11kit = dependency('p11-kit-1',
-                               version : '>= 0.23.3',
-                               required : want_p11kit == 'true')
-        have = libp11kit.found()
-        libp11kit_cflags = libp11kit.partial_dependency(includes: true, compile_args: true)
-else
-        have = false
-        libp11kit_cflags = []
-        libp11kit = []
-endif
-conf.set10('HAVE_P11KIT', have)
-
-want_libfido2 = get_option('libfido2')
-if want_libfido2 != 'false' and not skip_deps
-        if conf.get('HAVE_OPENSSL') == 1
-                libfido2 = dependency('libfido2',
-                                      required : want_libfido2 == 'true')
-                have = libfido2.found()
-        elif want_libfido2 == 'true'
-                error('libfido2=true requires openssl')
-        else
-                have = false
-                libfido2 = []
-        endif
-else
-        have = false
-        libfido2 = []
-endif
-conf.set10('HAVE_LIBFIDO2', have)
-
-want_tpm2 = get_option('tpm2')
-if want_tpm2 != 'false' and not skip_deps
-        tpm2 = dependency('tss2-esys tss2-rc tss2-mu tss2-tcti-device',
-                          required : want_tpm2 == 'true')
-        have = tpm2.found()
-        have_esys3 = tpm2.version().version_compare('>= 3.0.0')
-else
-        have = false
-        have_esys3 = false
-        tpm2 = []
-endif
-conf.set10('HAVE_TPM2', have)
-conf.set10('HAVE_TSS2_ESYS3', have_esys3)
-
-want_elfutils = get_option('elfutils')
-if want_elfutils != 'false' and not skip_deps
-        libdw = dependency('libdw',
-                           required : want_elfutils == 'true')
-        have = libdw.found()
-
-        # New in elfutils 0.177
-        conf.set10('HAVE_DWELF_ELF_E_MACHINE_STRING',
-                   have and cc.has_function('dwelf_elf_e_machine_string', dependencies : libdw))
-else
-        have = false
-        libdw = []
-endif
-conf.set10('HAVE_ELFUTILS', have)
-
-want_zlib = get_option('zlib')
-if want_zlib != 'false' and not skip_deps
-        libz = dependency('zlib',
-                          required : want_zlib == 'true')
-        have = libz.found()
-else
-        have = false
-        libz = []
-endif
-conf.set10('HAVE_ZLIB', have)
-
-want_bzip2 = get_option('bzip2')
-if want_bzip2 != 'false' and not skip_deps
-        libbzip2 = dependency('bzip2', required : false)
-        if not libbzip2.found()
-                # Debian and Ubuntu do not provide the .pc file.
-                libbzip2 = cc.find_library('bz2', required : want_bzip2 == 'true')
-        endif
-        have = libbzip2.found()
-else
-        have = false
-        libbzip2 = []
-endif
-conf.set10('HAVE_BZIP2', have)
-
-want_xz = get_option('xz')
-if want_xz != 'false' and not skip_deps
-        libxz = dependency('liblzma',
-                           required : want_xz == 'true')
-        have_xz = libxz.found()
-else
-        have_xz = false
-        libxz = []
-endif
-conf.set10('HAVE_XZ', have_xz)
-
-want_lz4 = get_option('lz4')
-if want_lz4 != 'false' and not skip_deps
-        liblz4 = dependency('liblz4',
-                            version : '>= 1.3.0',
-                            required : want_lz4 == 'true')
-        have_lz4 = liblz4.found()
-else
-        have_lz4 = false
-        liblz4 = []
-endif
-conf.set10('HAVE_LZ4', have_lz4)
-
-want_zstd = get_option('zstd')
-if want_zstd != 'false' and not skip_deps
-        libzstd = dependency('libzstd',
-                             required : want_zstd == 'true',
-                             version : '>= 1.4.0')
-        have_zstd = libzstd.found()
-else
-        have_zstd = false
-        libzstd = []
-endif
-conf.set10('HAVE_ZSTD', have_zstd)
-
-conf.set10('HAVE_COMPRESSION', have_xz or have_lz4 or have_zstd)
+libgnutls = dependency('gnutls',
+                       version : '>= 3.1.4',
+                       required : get_option('gnutls'))
+conf.set10('HAVE_GNUTLS', libgnutls.found())
+
+libopenssl = dependency('openssl',
+                        version : '>= 1.1.0',
+                        required : get_option('openssl'))
+conf.set10('HAVE_OPENSSL', libopenssl.found())
+
+libp11kit = dependency('p11-kit-1',
+                       version : '>= 0.23.3',
+                       required : get_option('p11kit'))
+conf.set10('HAVE_P11KIT', libp11kit.found())
+libp11kit_cflags = libp11kit.partial_dependency(includes: true, compile_args: true)
+
+feature = get_option('libfido2').require(
+        conf.get('HAVE_OPENSSL') == 1,
+        error_message : 'openssl required')
+libfido2 = dependency('libfido2',
+                      required : feature)
+conf.set10('HAVE_LIBFIDO2', libfido2.found())
+
+tpm2 = dependency('tss2-esys tss2-rc tss2-mu tss2-tcti-device',
+                  required : get_option('tpm2'))
+conf.set10('HAVE_TPM2', tpm2.found())
+conf.set10('HAVE_TSS2_ESYS3', tpm2.found() and tpm2.version().version_compare('>= 3.0.0'))
+
+libdw = dependency('libdw',
+                   required : get_option('elfutils'))
+conf.set10('HAVE_ELFUTILS', libdw.found())
+# New in elfutils 0.177
+conf.set10('HAVE_DWELF_ELF_E_MACHINE_STRING',
+           libdw.found() and cc.has_function('dwelf_elf_e_machine_string', dependencies : libdw))
+
+libz = dependency('zlib',
+                  required : get_option('zlib'))
+conf.set10('HAVE_ZLIB', libz.found())
+
+feature = get_option('bzip2')
+libbzip2 = dependency('bzip2',
+                      required : feature.disabled() ? feature : false)
+if not libbzip2.found()
+        # Debian and Ubuntu do not provide the .pc file.
+        libbzip2 = cc.find_library('bz2', required : feature)
+endif
+conf.set10('HAVE_BZIP2', libbzip2.found())
+
+libxz = dependency('liblzma',
+                   required : get_option('xz'))
+conf.set10('HAVE_XZ', libxz.found())
+
+liblz4 = dependency('liblz4',
+                    version : '>= 1.3.0',
+                    required : get_option('lz4'))
+conf.set10('HAVE_LZ4', liblz4.found())
+
+libzstd = dependency('libzstd',
+                     version : '>= 1.4.0',
+                     required : get_option('zstd'))
+conf.set10('HAVE_ZSTD', libzstd.found())
+
+conf.set10('HAVE_COMPRESSION', libxz.found() or liblz4.found() or libzstd.found())
 
 compression = get_option('default-compression')
 if compression == 'auto'
-        if have_zstd
+        if libzstd.found()
                 compression = 'zstd'
-        elif have_lz4
+        elif liblz4.found()
                 compression = 'lz4'
-        elif have_xz
+        elif libxz.found()
                 compression = 'xz'
         else
                 compression = 'none'
         endif
-elif compression == 'zstd' and not have_zstd
+elif compression == 'zstd' and not libzstd.found()
         error('default-compression=zstd requires zstd')
-elif compression == 'lz4' and not have_lz4
+elif compression == 'lz4' and not liblz4.found()
         error('default-compression=lz4 requires lz4')
-elif compression == 'xz' and not have_xz
+elif compression == 'xz' and not libxz.found()
         error('default-compression=xz requires xz')
 endif
 conf.set('DEFAULT_COMPRESSION', 'COMPRESSION_@0@'.format(compression.to_upper()))
 
-want_xkbcommon = get_option('xkbcommon')
-if want_xkbcommon != 'false' and not skip_deps
-        libxkbcommon = dependency('xkbcommon',
-                                  version : '>= 0.3.0',
-                                  required : want_xkbcommon == 'true')
-        have = libxkbcommon.found()
-else
-        have = false
-        libxkbcommon = []
-endif
-conf.set10('HAVE_XKBCOMMON', have)
+libxkbcommon = dependency('xkbcommon',
+                          version : '>= 0.3.0',
+                          required : get_option('xkbcommon'))
+conf.set10('HAVE_XKBCOMMON', libxkbcommon.found())
 
-want_pcre2 = get_option('pcre2')
-if want_pcre2 != 'false' and not skip_deps
-        libpcre2 = dependency('libpcre2-8',
-                              required : want_pcre2 == 'true')
-        have = libpcre2.found()
-else
-        have = false
-        libpcre2 = []
-endif
-conf.set10('HAVE_PCRE2', have)
-
-want_glib = get_option('glib')
-if want_glib != 'false' and not skip_deps
-        libglib =    dependency('glib-2.0',
-                                version : '>= 2.22.0',
-                                required : want_glib == 'true')
-        libgobject = dependency('gobject-2.0',
-                                version : '>= 2.22.0',
-                                required : want_glib == 'true')
-        libgio =     dependency('gio-2.0',
-                                required : want_glib == 'true')
-        have = libglib.found() and libgobject.found() and libgio.found()
-else
-        have = false
-        libglib = []
-        libgobject = []
-        libgio = []
-endif
-conf.set10('HAVE_GLIB', have)
-
-want_dbus = get_option('dbus')
-if want_dbus != 'false' and not skip_deps
-        libdbus = dependency('dbus-1',
-                             version : '>= 1.3.2',
-                             required : want_dbus == 'true')
-        have = libdbus.found()
-else
-        have = false
-        libdbus = []
-endif
-conf.set10('HAVE_DBUS', have)
+libpcre2 = dependency('libpcre2-8',
+                      required : get_option('pcre2'))
+conf.set10('HAVE_PCRE2', libpcre2.found())
 
-dbusdatadir = datadir / 'dbus-1'
-if conf.get('HAVE_DBUS') == 1
-        dbusdatadir = libdbus.get_variable(pkgconfig: 'datadir', default_value: datadir) / 'dbus-1'
-endif
+libglib =    dependency('glib-2.0',
+                        version : '>= 2.22.0',
+                        required : get_option('glib'))
+libgobject = dependency('gobject-2.0',
+                        version : '>= 2.22.0',
+                        required : get_option('glib'))
+libgio =     dependency('gio-2.0',
+                        required : get_option('glib'))
+conf.set10('HAVE_GLIB', libglib.found() and libgobject.found() and libgio.found())
+
+libdbus = dependency('dbus-1',
+                     version : '>= 1.3.2',
+                     required : get_option('dbus'))
+conf.set10('HAVE_DBUS', libdbus.found())
+
+dbusdatadir = libdbus.get_variable(pkgconfig: 'datadir', default_value: datadir) / 'dbus-1'
 
 dbuspolicydir = get_option('dbuspolicydir')
 if dbuspolicydir == ''
@@ -1666,18 +1416,12 @@ endif
 
 dbussessionservicedir = get_option('dbussessionservicedir')
 if dbussessionservicedir == ''
-        dbussessionservicedir = dbusdatadir / 'services'
-        if conf.get('HAVE_DBUS') == 1
-                dbussessionservicedir = libdbus.get_variable(pkgconfig: 'session_bus_services_dir', default_value: dbussessionservicedir)
-        endif
+        dbussessionservicedir = libdbus.get_variable(pkgconfig: 'session_bus_services_dir', default_value: dbusdatadir / 'services')
 endif
 
 dbussystemservicedir = get_option('dbussystemservicedir')
 if dbussystemservicedir == ''
-        dbussystemservicedir = dbusdatadir / 'system-services'
-        if conf.get('HAVE_DBUS') == 1
-                dbussystemservicedir = libdbus.get_variable(pkgconfig: 'system_bus_services_dir', default_value: dbussystemservicedir)
-        endif
+        dbussystemservicedir = libdbus.get_variable(pkgconfig: 'system_bus_services_dir', default_value: dbusdatadir / 'system-services')
 endif
 
 dbus_interfaces_dir = get_option('dbus-interfaces-dir')
@@ -1686,10 +1430,7 @@ if dbus_interfaces_dir == '' or dbus_interfaces_dir == 'yes'
                 dbus_interfaces_dir = 'no'
                 warning('Exporting D-Bus interface XML files is disabled during cross build. Pass path or "yes" to force enable.')
         else
-                dbus_interfaces_dir = dbusdatadir / 'interfaces'
-                if conf.get('HAVE_DBUS') == 1
-                        dbus_interfaces_dir = libdbus.get_variable(pkgconfig: 'interfaces_dir', default_value: dbus_interfaces_dir)
-                endif
+                dbus_interfaces_dir = libdbus.get_variable(pkgconfig: 'interfaces_dir', default_value: dbusdatadir / 'interfaces')
         endif
 endif
 
@@ -1742,9 +1483,6 @@ conf.set10('DNS_OVER_TLS_USE_GNUTLS', have_gnutls)
 conf.set10('DNS_OVER_TLS_USE_OPENSSL', have_openssl)
 
 default_dns_over_tls = get_option('default-dns-over-tls')
-if skip_deps
-        default_dns_over_tls = 'no'
-endif
 if default_dns_over_tls != 'no' and conf.get('ENABLE_DNS_OVER_TLS') == 0
         message('default-dns-over-tls cannot be enabled or set to opportunistic when DNS-over-TLS support is disabled. Setting default-dns-over-tls to no.')
         default_dns_over_tls = 'no'
@@ -1763,21 +1501,12 @@ conf.set('DEFAULT_LLMNR_MODE',
          'RESOLVE_SUPPORT_' + default_llmnr.to_upper())
 conf.set_quoted('DEFAULT_LLMNR_MODE_STR', default_llmnr)
 
-want_repart = get_option('repart')
-if want_repart != 'false'
-        have = conf.get('HAVE_LIBFDISK') == 1
-        if want_repart == 'true' and not have
-                error('repart support was requested, but dependencies are not available')
-        endif
-else
-        have = false
-endif
+have = get_option('repart').require(
+        conf.get('HAVE_LIBFDISK') == 1,
+        error_message : 'fdisk required').allowed()
 conf.set10('ENABLE_REPART', have)
 
 default_dnssec = get_option('default-dnssec')
-if skip_deps
-        default_dnssec = 'no'
-endif
 if default_dnssec != 'no' and conf.get('HAVE_OPENSSL_OR_GCRYPT') == 0
         message('default-dnssec cannot be set to yes or allow-downgrade openssl and gcrypt are disabled. Setting default-dnssec to no.')
         default_dnssec = 'no'
@@ -1786,63 +1515,41 @@ conf.set('DEFAULT_DNSSEC_MODE',
          'DNSSEC_' + default_dnssec.underscorify().to_upper())
 conf.set_quoted('DEFAULT_DNSSEC_MODE_STR', default_dnssec)
 
-want_sysupdate = get_option('sysupdate')
-if want_sysupdate != 'false'
-        have = (conf.get('HAVE_OPENSSL') == 1 and
-                conf.get('HAVE_LIBFDISK') == 1)
-        if want_sysupdate == 'true' and not have
-                error('sysupdate support was requested, but dependencies are not available')
-        endif
-else
-        have = false
-endif
+have = get_option('sysupdate').require(
+        conf.get('HAVE_OPENSSL') == 1 and
+        conf.get('HAVE_LIBFDISK') == 1,
+        error_message : 'fdisk and openssl required').allowed()
 conf.set10('ENABLE_SYSUPDATE', have)
 
-want_importd = get_option('importd')
-if want_importd != 'false'
-        have = (conf.get('HAVE_LIBCURL') == 1 and
-                conf.get('HAVE_OPENSSL_OR_GCRYPT') == 1 and
-                conf.get('HAVE_ZLIB') == 1 and
-                conf.get('HAVE_XZ') == 1)
-        if want_importd == 'true' and not have
-                error('importd support was requested, but dependencies are not available')
-        endif
-else
-        have = false
-endif
+have = get_option('importd').require(
+        conf.get('HAVE_LIBCURL') == 1 and
+        conf.get('HAVE_OPENSSL_OR_GCRYPT') == 1 and
+        conf.get('HAVE_ZLIB') == 1 and
+        conf.get('HAVE_XZ') == 1,
+        error_message : 'curl, openssl/grypt, zlib and xz required').allowed()
 conf.set10('ENABLE_IMPORTD', have)
 
-want_homed = get_option('homed')
-if want_homed != 'false'
-        have = (conf.get('HAVE_OPENSSL') == 1 and
-                conf.get('HAVE_LIBFDISK') == 1 and
-                conf.get('HAVE_LIBCRYPTSETUP') == 1)
-        if want_homed == 'true' and not have
-                error('homed support was requested, but dependencies are not available')
-        endif
-else
-        have = false
-endif
+have = get_option('homed').require(
+        conf.get('HAVE_OPENSSL') == 1 and
+        conf.get('HAVE_LIBFDISK') == 1 and
+        conf.get('HAVE_LIBCRYPTSETUP') == 1,
+        error_message : 'openssl, fdisk and libcryptsetup required').allowed()
 conf.set10('ENABLE_HOMED', have)
 
 have = have and conf.get('HAVE_PAM') == 1
 conf.set10('ENABLE_PAM_HOME', have)
 
-want_remote = get_option('remote')
-if want_remote != 'false'
-        have_deps = [conf.get('HAVE_MICROHTTPD') == 1,
-                     conf.get('HAVE_LIBCURL') == 1]
-        # sd-j-remote requires Âµhttpd, and sd-j-upload requires libcurl, so
-        # it's possible to build one without the other. Complain only if
-        # support was explicitly requested. The auxiliary files like sysusers
-        # config should be installed when any of the programs are built.
-        if want_remote == 'true' and not (have_deps[0] and have_deps[1])
-                error('remote support was requested, but dependencies are not available')
-        endif
-        have = have_deps[0] or have_deps[1]
-else
-        have = false
-endif
+feature = get_option('remote')
+have_deps = [conf.get('HAVE_MICROHTTPD') == 1,
+             conf.get('HAVE_LIBCURL') == 1]
+# sd-j-remote requires Âµhttpd, and sd-j-upload requires libcurl, so
+# it's possible to build one without the other. Complain only if
+# support was explicitly requested. The auxiliary files like sysusers
+# config should be installed when any of the programs are built.
+if feature.enabled() and not (have_deps[0] and have_deps[1])
+        error('remote support was requested, but dependencies are not available')
+endif
+have = feature.allowed() and (have_deps[0] or have_deps[1])
 conf.set10('ENABLE_REMOTE', have)
 
 foreach term : ['analyze',
@@ -1897,9 +1604,9 @@ enable_sysusers = conf.get('ENABLE_SYSUSERS') == 1
 foreach tuple : [['nss-mymachines', 'machined'],
                  ['nss-resolve',    'resolve']]
         want = get_option(tuple[0])
-        if want != 'false'
+        if want.allowed()
                 have = get_option(tuple[1])
-                if want == 'true' and not have
+                if want.enabled() and not have
                         error('@0@ is requested but @1@ is disabled'.format(tuple[0], tuple[1]))
                 endif
         else
@@ -2055,18 +1762,19 @@ efi_arch = {
         'x86'         : 'ia32',
 }.get(host_machine.cpu_family(), '')
 
-if get_option('bootloader') != 'false' and efi_arch != ''
-        conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch)
-elif get_option('bootloader') == 'false' and efi_arch != ''
-        # Ensure that if the option is explicitly set to false, then no EFI code is built, including tests
-        efi_arch = ''
-elif get_option('bootloader') == 'true' and efi_arch == ''
-        error('EFI not supported for this arch.')
-endif
+pyelftools = pymod.find_installation('python3',
+                                     required : get_option('bootloader'),
+                                     modules : ['elftools'])
+
+have = get_option('bootloader').require(
+        pyelftools.found() and get_option('efi') and efi_arch != '',
+        error_message : 'unsupported EFI arch or EFI support is disabled').allowed()
+conf.set10('ENABLE_BOOTLOADER', have)
+conf.set_quoted('EFI_MACHINE_TYPE_NAME', have ? efi_arch : '')
 
 efi_arch_alt = ''
 efi_cpu_family_alt = ''
-if efi_arch == 'x64' and cc.links('''
+if have and efi_arch == 'x64' and cc.links('''
                 #include <limits.h>
                 int main(int argc, char *argv[]) {
                         return __builtin_popcount(argc - CHAR_MAX);
@@ -2075,26 +1783,9 @@ if efi_arch == 'x64' and cc.links('''
         efi_cpu_family_alt = 'x86'
 endif
 
-have_pyelftools = pymod.find_installation('python3', required : false, modules : ['elftools']).found()
-if get_option('bootloader') == 'true' and not have_pyelftools
-        error('EFI bootloader support requires pyelftools.')
-endif
-
-conf.set10(
-        'ENABLE_BOOTLOADER',
-        get_option('efi') and
-        get_option('bootloader') in ['auto', 'true'] and
-        efi_arch != '' and
-        have_pyelftools,
-)
-
-if get_option('ukify') == 'auto'
-    want_ukify = python_39  and conf.get('ENABLE_BOOTLOADER') == 1
-elif get_option('ukify') == 'true' and (not python_39 or conf.get('ENABLE_BOOTLOADER') != 1)
-    error('ukify requires Python >= 3.9 and -Dbootloader=true')
-else
-    want_ukify = get_option('ukify') == 'true'
-endif
+want_ukify = get_option('ukify').require(
+        python_39 and conf.get('ENABLE_BOOTLOADER') == 1,
+        error_message : 'Python >= 3.9 and -Dbootloader=true required').allowed()
 conf.set10('ENABLE_UKIFY', want_ukify)
 
 ############################################################
@@ -2707,7 +2398,7 @@ foreach dict : modules
         if is_nss
                 # We cannot use shared_module because it does not support version suffix.
                 # Unfortunately shared_library insists on creating the symlink…
-                meson.add_install_script('sh', '-c', 'rm $DESTDIR@0@/lib@1@.so'.format(libdir, name),
+                meson.add_install_script(sh, '-c', 'rm $DESTDIR@0@/lib@1@.so'.format(libdir, name),
                                          install_tag : 'nss')
         endif
 
index 8d845f0441eb19a6a52d7638ebc303b61528b905..b8116bcedde12ec8e24a83982ecf12176e869f79 100644 (file)
@@ -102,9 +102,9 @@ option('environment-d', type : 'boolean',
        description : 'support for environment.d')
 option('binfmt', type : 'boolean',
        description : 'support for custom binary formats')
-option('repart', type : 'combo', choices : ['auto', 'true', 'false'],
+option('repart', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'install the systemd-repart tool')
-option('sysupdate', type : 'combo', choices : ['auto', 'true', 'false'],
+option('sysupdate', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'install the systemd-sysupdate tool')
 option('coredump', type : 'boolean',
        description : 'install the coredump handler')
@@ -126,7 +126,7 @@ option('sysext', type : 'boolean',
        description : 'install the systemd-sysext stack')
 option('userdb', type : 'boolean',
        description : 'install the systemd-userdbd stack')
-option('homed', type : 'combo', choices : ['auto', 'true', 'false'],
+option('homed', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'install the systemd-homed stack')
 option('networkd', type : 'boolean',
        description : 'install the systemd-networkd stack')
@@ -134,15 +134,15 @@ option('timedated', type : 'boolean',
        description : 'install the systemd-timedated daemon')
 option('timesyncd', type : 'boolean',
        description : 'install the systemd-timesyncd daemon')
-option('remote', type : 'combo', choices : ['auto', 'true', 'false'],
+option('remote', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'support for "journal over the network"')
 option('create-log-dirs', type : 'boolean',
        description : 'create /var/log/journal{,/remote}')
 option('nss-myhostname', type : 'boolean',
        description : 'install nss-myhostname module')
-option('nss-mymachines', type : 'combo', choices : ['auto', 'true', 'false'],
+option('nss-mymachines', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'install nss-mymachines module')
-option('nss-resolve', type : 'combo', choices : ['auto', 'true', 'false'],
+option('nss-resolve', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'install nss-resolve module')
 option('nss-systemd', type : 'boolean',
        description : 'install nss-systemd module')
@@ -160,7 +160,7 @@ option('sysusers', type : 'boolean',
        description : 'support for the sysusers configuration')
 option('tmpfiles', type : 'boolean',
        description : 'support for tmpfiles.d')
-option('importd', type : 'combo', choices : ['auto', 'true', 'false'],
+option('importd', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'install the systemd-importd daemon')
 option('hwdb', type : 'boolean',
        description : 'support for the hardware database')
@@ -168,11 +168,11 @@ option('rfkill', type : 'boolean',
        description : 'support for the rfkill tools')
 option('xdg-autostart', type : 'boolean',
        description : 'install the xdg-autostart-generator and unit')
-option('man', type : 'combo', choices : ['auto', 'true', 'false'],
-       value : 'false',
+option('man', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
+       value : 'disabled',
        description : 'build and install man pages')
-option('html', type : 'combo', choices : ['auto', 'true', 'false'],
-       value : 'false',
+option('html', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
+       value : 'disabled',
        description : 'build and install html pages')
 option('translations', type : 'boolean', value : true,
        description : 'build and install translations')
@@ -350,11 +350,11 @@ option('www-target', type : 'string',
        description : 'the address and dir to upload docs too',
        value : 'www.freedesktop.org:/srv/www.freedesktop.org/www/software/systemd')
 
-option('seccomp', type : 'combo', choices : ['auto', 'true', 'false'],
+option('seccomp', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'SECCOMP support')
-option('selinux', type : 'combo', choices : ['auto', 'true', 'false'],
+option('selinux', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'SELinux support')
-option('apparmor', type : 'combo', choices : ['auto', 'true', 'false'],
+option('apparmor', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'AppArmor support')
 option('smack', type : 'boolean',
        description : 'SMACK support')
@@ -362,85 +362,85 @@ option('smack-run-label', type : 'string',
        description : 'run systemd --system itself with a specific SMACK label')
 option('smack-default-process-label', type : 'string',
        description : 'default SMACK label for executed processes')
-option('polkit', type : 'combo', choices : ['auto', 'true', 'false'],
+option('polkit', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'polkit support')
 option('ima', type : 'boolean',
        description : 'IMA support')
 
-option('acl', type : 'combo', choices : ['auto', 'true', 'false'],
+option('acl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libacl support')
-option('audit', type : 'combo', choices : ['auto', 'true', 'false'],
+option('audit', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libaudit support')
-option('blkid', type : 'combo', choices : ['auto', 'true', 'false'],
+option('blkid', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libblkid support')
-option('fdisk', type : 'combo', choices : ['auto', 'true', 'false'],
+option('fdisk', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libfdisk support')
-option('kmod', type : 'combo', choices : ['auto', 'true', 'false'],
+option('kmod', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'support for loadable modules')
-option('xenctrl', type : 'combo', choices : ['auto', 'true', 'false'],
+option('xenctrl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'support for Xen kexec')
-option('pam', type : 'combo', choices : ['auto', 'true', 'false'],
+option('pam', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'PAM support')
-option('passwdqc', type : 'combo', choices : ['auto', 'true', 'false'],
+option('passwdqc', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libpasswdqc support')
-option('pwquality', type : 'combo', choices : ['auto', 'true', 'false'],
+option('pwquality', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libpwquality support')
-option('microhttpd', type : 'combo', choices : ['auto', 'true', 'false'],
+option('microhttpd', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libµhttpd support')
-option('libcryptsetup', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libcryptsetup', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libcryptsetup support')
-option('libcryptsetup-plugins', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libcryptsetup-plugins', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libcryptsetup LUKS2 external token handlers support (plugins)')
-option('libcurl', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libcurl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libcurl support')
 option('idn', type : 'boolean',
        description : 'use IDN when printing hostnames')
-option('libidn2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libidn2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libidn2 support')
-option('libidn', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libidn', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libidn support')
-option('libiptc', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libiptc', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libiptc support')
-option('qrencode', type : 'combo', choices : ['auto', 'true', 'false'],
+option('qrencode', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libqrencode support')
-option('gcrypt', type : 'combo', choices : ['auto', 'true', 'false'],
+option('gcrypt', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'gcrypt support')
-option('gnutls', type : 'combo', choices : ['auto', 'true', 'false'],
+option('gnutls', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'gnutls support')
-option('openssl', type : 'combo', choices : ['auto', 'true', 'false'],
+option('openssl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'openssl support')
 option('cryptolib', type : 'combo', choices : ['auto', 'openssl', 'gcrypt'],
        description : 'whether to use openssl or gcrypt where both are supported')
-option('p11kit', type : 'combo', choices : ['auto', 'true', 'false'],
+option('p11kit', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'p11kit support')
-option('libfido2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libfido2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'FIDO2 support')
-option('tpm2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('tpm2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'TPM2 support')
-option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'],
+option('elfutils', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'elfutils support')
-option('zlib', type : 'combo', choices : ['auto', 'true', 'false'],
+option('zlib', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'zlib compression support')
-option('bzip2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('bzip2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'bzip2 compression support')
-option('xz', type : 'combo', choices : ['auto', 'true', 'false'],
+option('xz', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'xz compression support')
-option('lz4', type : 'combo', choices : ['auto', 'true', 'false'],
+option('lz4', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'lz4 compression support')
-option('zstd', type : 'combo', choices : ['auto', 'true', 'false'],
+option('zstd', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'zstd compression support')
 option('default-compression', type : 'combo', choices : ['auto', 'zstd', 'lz4', 'xz'], value: 'auto',
        description : 'default compression algorithm')
-option('xkbcommon', type : 'combo', choices : ['auto', 'true', 'false'],
+option('xkbcommon', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'xkbcommon keymap support')
-option('pcre2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('pcre2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'regexp matching support using pcre2')
-option('glib', type : 'combo', choices : ['auto', 'true', 'false'],
+option('glib', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libglib support (for tests only)')
-option('dbus', type : 'combo', choices : ['auto', 'true', 'false'],
+option('dbus', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'libdbus support (for tests only)')
 
-option('bootloader', type : 'combo', choices : ['auto', 'true', 'false'],
+option('bootloader', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'sd-boot/stub and userspace tools')
 option('sbat-distro', type : 'string', value : 'auto',
        description : 'SBAT distribution ID, e.g. fedora, or auto for autodetection')
@@ -476,7 +476,7 @@ option('fuzz-tests', type : 'boolean', value : false,
        description : 'run the fuzzer regression tests by default (with sanitizers)')
 option('install-tests', type : 'boolean', value : false,
        description : 'install test executables')
-option('log-message-verification', type : 'combo', choices : ['auto', 'true', 'false'],
+option('log-message-verification', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'do fake printf() calls to verify format strings')
 
 option('ok-color', type : 'combo',
@@ -497,15 +497,12 @@ 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'],
+option('ukify', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
        description : 'install ukify')
 option('analyze', type: 'boolean', value: true,
        description : 'install systemd-analyze')
 
 option('bpf-compiler', type : 'combo', choices : ['clang', 'gcc'],
     description: 'compiler used to build BPF programs')
-option('bpf-framework', type : 'combo', choices : ['auto', 'true', 'false'],
+option('bpf-framework', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
     description: 'build BPF programs from source code in restricted C')
-
-option('skip-deps', type : 'boolean', value : false,
-       description : 'skip optional dependencies')
diff --git a/mkosi.conf.d/10-fedora-rawhide.conf b/mkosi.conf.d/10-fedora-rawhide.conf
deleted file mode 100644 (file)
index df283fd..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Match]
-Distribution=fedora
-Release=rawhide
-
-[Distribution]
-RepositoryKeyCheck=no
index d75f1425a582651df8c403aca6bf830d0a44e2a8..c5f31c5be64297bfc375aba042eb66ceefd06483 100755 (executable)
@@ -51,9 +51,9 @@ if [ ! -f "$BUILDDIR"/build.ninja ]; then
     sysvinit_path=$(realpath /etc/init.d)
 
     if [ "$ID" = "centos" ] && [ "$VERSION" = "8" ]; then
-        UKIFY=false
+        UKIFY="disabled"
     else
-        UKIFY=true
+        UKIFY="enabled"
     fi
 
     # On Debian 'loadkeys us' fails
@@ -65,7 +65,7 @@ if [ ! -f "$BUILDDIR"/build.ninja ]; then
 
     CONFIGURE_OPTS=(
         -D sysvinit-path="$sysvinit_path"
-        -D man=false
+        -D man=disabled
         -D translations=false
         -D version-tag="${VERSION_TAG}"
         -D mode=developer
@@ -83,8 +83,8 @@ if [ ! -f "$BUILDDIR"/build.ninja ]; then
         -D tpm=true
         -D environment-d=true
         -D binfmt=true
-        -D repart=true
-        -D sysupdate=true
+        -D repart=enabled
+        -D sysupdate=enabled
         -D coredump=true
         -D pstore=true
         -D oomd=true
@@ -95,14 +95,14 @@ if [ ! -f "$BUILDDIR"/build.ninja ]; then
         -D portabled=true
         -D sysext=true
         -D userdb=true
-        -D homed=true
+        -D homed=enabled
         -D networkd=true
         -D timedated=true
         -D timesyncd=true
-        -D remote=true
+        -D remote=enabled
         -D nss-myhostname=true
-        -D nss-mymachines=true
-        -D nss-resolve=true
+        -D nss-mymachines=enabled
+        -D nss-resolve=enabled
         -D nss-systemd=true
         -D firstboot=true
         -D randomseed=true
@@ -111,44 +111,44 @@ if [ ! -f "$BUILDDIR"/build.ninja ]; then
         -D quotacheck=true
         -D sysusers=true
         -D tmpfiles=true
-        -D importd=true
+        -D importd=enabled
         -D hwdb=true
         -D rfkill=true
         -D xdg-autostart=true
         -D translations=true
-        -D polkit=true
-        -D acl=true
-        -D audit=true
-        -D blkid=true
-        -D fdisk=true
-        -D kmod=true
-        -D pam=true
-        -D pwquality=true
-        -D microhttpd=true
-        -D libcryptsetup=true
-        -D libcurl=true
+        -D polkit=enabled
+        -D acl=enabled
+        -D audit=enabled
+        -D blkid=enabled
+        -D fdisk=enabled
+        -D kmod=enabled
+        -D pam=enabled
+        -D pwquality=enabled
+        -D microhttpd=enabled
+        -D libcryptsetup=enabled
+        -D libcurl=enabled
         -D idn=true
-        -D libidn2=true
-        -D qrencode=true
-        -D gcrypt=true
-        -D gnutls=true
-        -D openssl=true
+        -D libidn2=enabled
+        -D qrencode=enabled
+        -D gcrypt=enabled
+        -D gnutls=enabled
+        -D openssl=enabled
         -D cryptolib=openssl
-        -D p11kit=true
-        -D libfido2=true
-        -D tpm2=true
-        -D elfutils=true
-        -D zstd=true
-        -D xkbcommon=true
-        -D pcre2=true
-        -D glib=true
-        -D dbus=true
-        -D bootloader=true
+        -D p11kit=enabled
+        -D libfido2=enabled
+        -D tpm2=enabled
+        -D elfutils=enabled
+        -D zstd=enabled
+        -D xkbcommon=enabled
+        -D pcre2=enabled
+        -D glib=enabled
+        -D dbus=enabled
+        -D bootloader=enabled
         -D kernel-install=true
         -D analyze=true
-        -D bpf-framework=true
+        -D bpf-framework=enabled
         -D ukify="$UKIFY"
-        -D seccomp=true
+        -D seccomp=enabled
         -D selinux=auto
         -D apparmor=auto
         -D smack=true
index 9abe8620c86066bb9d82ae73e8bfa0e2040cc98d..e909d5de101d58d21ac7586311a3c5bd3931e806 100644 (file)
@@ -242,4 +242,17 @@ static inline size_t malloc_sizeof_safe(void **xp) {
                 (char*) memdupa_suffix0(_t, strnlen(_t, (n)));          \
         })
 
+/* Free every element of the array. */
+static inline void free_many(void **p, size_t n) {
+        assert(p || n == 0);
+
+        FOREACH_ARRAY(i, p, n)
+                *i = mfree(*i);
+}
+
+/* Typesafe wrapper for char** rather than void**. Unfortunately C won't implicitly cast this. */
+static inline void free_many_charp(char **c, size_t n) {
+        free_many((void**) c, n);
+}
+
 #include "memory-util.h"
index 21e2255daed511a040a5189cb493e699332fef19..b2b0ddd1907b3272f4772a7db41e7c4e2a53114e 100644 (file)
@@ -1915,9 +1915,7 @@ int cg_get_keyed_attribute_full(
         r = -ENXIO;
 
 fail:
-        for (i = 0; i < n; i++)
-                free(v[i]);
-
+        free_many_charp(v, n);
         return r;
 
 done:
index 07a2a54f3bc859bf07b8f8a489a8b18846a368a7..cb239bdf18c61fede5c53d72bd1ce10cce0af68b 100644 (file)
@@ -265,7 +265,7 @@ finish:
 int efi_set_variable_string(const char *variable, const char *value) {
         _cleanup_free_ char16_t *u16 = NULL;
 
-        u16 = utf8_to_utf16(value, strlen(value));
+        u16 = utf8_to_utf16(value, SIZE_MAX);
         if (!u16)
                 return -ENOMEM;
 
index 9f9bb0c7910cd44629cdf239ea7106ce1b29f2ee..160f771b228f631ca40847470156bc445b35a1c1 100644 (file)
@@ -272,11 +272,7 @@ int extract_many_words(const char **p, const char *separators, unsigned flags, .
 
                 r = extract_first_word(p, &l[c], separators, flags);
                 if (r < 0) {
-                        int j;
-
-                        for (j = 0; j < c; j++)
-                                free(l[j]);
-
+                        free_many_charp(l, c);
                         return r;
                 }
 
index d94fbcff4b97d2fef939dcf666970272d886834e..d43f91fd226b96bc633dd6f5fb453cd27156a60b 100644 (file)
@@ -340,11 +340,7 @@ out:
 }
 
 void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
-        if (!l)
-                return;
-
-        for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++)
-                l[i] = mfree(l[i]);
+        free_many_charp(l, _VARIABLE_LC_MAX);
 }
 
 void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]) {
index 91424cd3cced0e121c7d20db22d7cd573839b3ea..1cf64d9cdd3126d192c976a67f77cf01eb962804 100644 (file)
@@ -359,13 +359,7 @@ int rlimit_from_string_harder(const char *s) {
 }
 
 void rlimit_free_all(struct rlimit **rl) {
-        int i;
-
-        if (!rl)
-                return;
-
-        for (i = 0; i < _RLIMIT_MAX; i++)
-                rl[i] = mfree(rl[i]);
+        free_many((void**) rl, _RLIMIT_MAX);
 }
 
 int rlimit_nofile_bump(int limit) {
index 7cddec712b0ca4cc36930dbd8f8c724c6f7b6039..854cf963acb1edcacd0a01d8de8ca8f9660cbcac 100644 (file)
@@ -1446,3 +1446,67 @@ bool version_is_valid_versionspec(const char *s) {
 
         return true;
 }
+
+ssize_t strlevenshtein(const char *x, const char *y) {
+        _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
+        size_t xl, yl;
+
+        /* This is inspired from the Linux kernel's Levenshtein implementation */
+
+        if (streq_ptr(x, y))
+                return 0;
+
+        xl = strlen_ptr(x);
+        if (xl > SSIZE_MAX)
+                return -E2BIG;
+
+        yl = strlen_ptr(y);
+        if (yl > SSIZE_MAX)
+                return -E2BIG;
+
+        if (isempty(x))
+                return yl;
+        if (isempty(y))
+                return xl;
+
+        t0 = new0(size_t, yl + 1);
+        if (!t0)
+                return -ENOMEM;
+        t1 = new0(size_t, yl + 1);
+        if (!t1)
+                return -ENOMEM;
+        t2 = new0(size_t, yl + 1);
+        if (!t2)
+                return -ENOMEM;
+
+        for (size_t i = 0; i <= yl; i++)
+                t1[i] = i;
+
+        for (size_t i = 0; i < xl; i++) {
+                t2[0] = i + 1;
+
+                for (size_t j = 0; j < yl; j++) {
+                        /* Substitution */
+                        t2[j+1] = t1[j] + (x[i] != y[j]);
+
+                        /* Swap */
+                        if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
+                                t2[j+1] = t0[j-1] + 1;
+
+                        /* Deletion */
+                        if (t2[j+1] > t1[j+1] + 1)
+                                t2[j+1] = t1[j+1] + 1;
+
+                        /* Insertion */
+                        if (t2[j+1] > t2[j] + 1)
+                                t2[j+1] = t2[j] + 1;
+                }
+
+                size_t *dummy = t0;
+                t0 = t1;
+                t1 = t2;
+                t2 = dummy;
+        }
+
+        return t1[yl];
+}
index f473946864e748054d2a2abf7b9869e24e67661c..73d586d4c54c849687bb4f0cff489b68b5c30135 100644 (file)
@@ -284,3 +284,5 @@ char *startswith_strv(const char *string, char **strv);
 bool version_is_valid(const char *s);
 
 bool version_is_valid_versionspec(const char *s);
+
+ssize_t strlevenshtein(const char *x, const char *y);
index deadb9909e99b7643a0934043200b7c48cbe596e..37c8f553eb3f53cbc56eb840936f7505816eed59 100644 (file)
@@ -212,9 +212,7 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
         return (int) i;
 
 rollback:
-        for (size_t j = 0; j < i; j++)
-                free(t[p + j]);
-
+        free_many_charp(t + p, i);
         t[p] = NULL;
         return -ENOMEM;
 }
index 9d9e76904ee103518e47f4a79abd331d7c4ef2f0..36e1e0f1558cd8300acc42eea98240c70b7eab98 100644 (file)
@@ -389,11 +389,23 @@ char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
         const uint8_t *f;
         char *r, *t;
 
+        if (length == 0)
+                return new0(char, 1);
+
         assert(s);
 
+        if (length == SIZE_MAX) {
+                length = char16_strlen(s);
+
+                if (length > SIZE_MAX/2)
+                        return NULL; /* overflow */
+
+                length *= 2;
+        }
+
         /* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may
          * take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */
-        if (length * 2 < length)
+        if (length > (SIZE_MAX - 1) / 2)
                 return NULL; /* overflow */
 
         r = new(char, length * 2 + 1);
@@ -463,8 +475,17 @@ char16_t *utf8_to_utf16(const char *s, size_t length) {
         char16_t *n, *p;
         int r;
 
+        if (length == 0)
+                return new0(char16_t, 1);
+
         assert(s);
 
+        if (length == SIZE_MAX)
+                length = strlen(s);
+
+        if (length > SIZE_MAX - 1)
+                return NULL; /* overflow */
+
         n = new(char16_t, length + 1);
         if (!n)
                 return NULL;
index cbf92caaafd15fa10866249236e573ae7ab53d0a..9feb0e3d2ea7d7a1209f5c72bf841c38efb9feee 100644 (file)
@@ -34,7 +34,7 @@ static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_t
 
         xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
 
-        encoded = utf8_to_utf16(utf8, strlen(utf8));
+        encoded = utf8_to_utf16(utf8, SIZE_MAX);
         if (!encoded)
                 return log_oom();
 
@@ -69,7 +69,7 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
         } else if (arg1[0] == '@' && !streq(arg1, "@saved"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
         else {
-                encoded = utf8_to_utf16(arg1, strlen(arg1));
+                encoded = utf8_to_utf16(arg1, SIZE_MAX);
                 if (!encoded)
                         return log_oom();
 
index 6c2c488c742692af36a56334f675ac6510100cc8..3704ee684697e9c683e0c4bdc4ca696df74e1668 100644 (file)
@@ -881,7 +881,7 @@ static bool menu_run(
                 case KEYPRESS(0, 0, 'H'):
                 case KEYPRESS(0, 0, '?'):
                         /* This must stay below 80 characters! Q/v/Ctrl+l/f deliberately not advertised. */
-                        status = xstrdup16(u"(d)efault (t/T)timeout (e)dit (r/R)resolution (p)rint (h)elp");
+                        status = xstrdup16(u"(d)efault (t/T)imeout (e)dit (r/R)esolution (p)rint (O)ff re(B)oot (h)elp");
                         break;
 
                 case KEYPRESS(0, 0, 'Q'):
@@ -918,17 +918,21 @@ static bool menu_run(
                 case KEYPRESS(0, 0, 'e'):
                 case KEYPRESS(0, 0, 'E'):
                         /* only the options of configured entries can be edited */
-                        if (!config->editor || !IN_SET(config->entries[idx_highlight]->type,
-                            LOADER_EFI, LOADER_LINUX, LOADER_UNIFIED_LINUX))
+                        if (!config->editor ||
+                            !IN_SET(config->entries[idx_highlight]->type, LOADER_EFI, LOADER_LINUX, LOADER_UNIFIED_LINUX)) {
+                                status = xstrdup16(u"Entry does not support editing the command line.");
                                 break;
+                        }
 
                         /* Unified kernels that are signed as a whole will not accept command line options
                          * when secure boot is enabled unless there is none embedded in the image. Do not try
                          * to pretend we can edit it to only have it be ignored. */
                         if (config->entries[idx_highlight]->type == LOADER_UNIFIED_LINUX &&
                             secure_boot_enabled() &&
-                            config->entries[idx_highlight]->options)
+                            config->entries[idx_highlight]->options) {
+                                status = xstrdup16(u"Entry not editable in SecureBoot mode.");
                                 break;
+                        }
 
                         /* The edit line may end up on the last line of the screen. And even though we're
                          * not telling the firmware to advance the line, it still does in this one case,
@@ -959,6 +963,7 @@ static bool menu_run(
 
                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
+                case 'L': /* only uppercase, do not conflict with lower-case 'l' which picks first Linux entry */
                         clear = true;
                         break;
 
@@ -1004,6 +1009,14 @@ static bool menu_run(
                                 status = xstrdup16(u"Reboot into firmware interface not supported.");
                         break;
 
+                case KEYPRESS(0, 0, 'O'): /* Only uppercase, so that it can't be hit so easily fat-fingered, but still works safely over serial */
+                        RT->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
+                        break;
+
+                case KEYPRESS(0, 0, 'B'): /* ditto */
+                        RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
+                        break;
+
                 default:
                         /* jump with a hotkey directly to a matching entry */
                         idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
index 05997744df5faa5441b2d84cc64ef4f32f726919..e35ab91402a1773ce6d06b2781247d14435b4b0c 100644 (file)
@@ -3,49 +3,48 @@
 efi_config_h_dir = meson.current_build_dir()
 efi_addon = ''
 
-if efi_arch != ''
-        libefitest = static_library(
-                'efitest',
-                files(
-                        'bcd.c',
-                        'efi-string.c',
-                ),
-                build_by_default : false,
-                include_directories : [
-                        basic_includes,
-                        include_directories('.'),
-                ],
-                dependencies : userspace)
-
-        efitest_base = {
-                'link_with' : [
-                        libefitest,
-                        libshared,
-                ],
-        }
-        efi_test_template = test_template + efitest_base
-        efi_fuzz_template = fuzz_template + efitest_base
-
-        executables += [
-                efi_test_template + {
-                        'sources' : files('test-bcd.c'),
-                        'dependencies' : libzstd,
-                        'conditions' : ['HAVE_ZSTD'],
-                },
-                efi_test_template + {
-                        'sources' : files('test-efi-string.c'),
-                },
-                efi_fuzz_template + {
-                        'sources' : files('fuzz-bcd.c'),
-                },
-                efi_fuzz_template + {
-                        'sources' : files('fuzz-efi-string.c'),
-                },
-                efi_fuzz_template + {
-                        'sources' : files('fuzz-efi-printf.c'),
-                },
-        ]
-endif
+libefitest = static_library(
+        'efitest',
+        files(
+                'bcd.c',
+                'efi-string.c',
+        ),
+        build_by_default : false,
+        include_directories : [
+                basic_includes,
+                include_directories('.'),
+        ],
+        dependencies : userspace)
+
+efitest_base = {
+        'link_with' : [
+                libefitest,
+                libshared,
+        ],
+}
+efi_test_template = test_template + efitest_base
+efi_fuzz_template = fuzz_template + efitest_base
+
+executables += [
+        efi_test_template + {
+                'sources' : files('test-bcd.c'),
+                'dependencies' : libzstd,
+                'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_ZSTD'],
+        },
+        efi_test_template + {
+                'sources' : files('test-efi-string.c'),
+                'conditions' : ['ENABLE_BOOTLOADER'],
+        },
+        efi_fuzz_template + {
+                'sources' : files('fuzz-bcd.c'),
+        },
+        efi_fuzz_template + {
+                'sources' : files('fuzz-efi-string.c'),
+        },
+        efi_fuzz_template + {
+                'sources' : files('fuzz-efi-printf.c'),
+        },
+]
 
 if conf.get('ENABLE_BOOTLOADER') != 1
         subdir_done()
index 0ee29477040827453f09d76abc6fea558476d894..3f93ca05c58cb18d6cc8d986adc9ba89db6cf0c2 100644 (file)
@@ -152,7 +152,7 @@ TEST(argv_bcds) {
 
                 char16_t *title = get_bcd_title(bcd, len);
                 if (title) {
-                        _cleanup_free_ char *title_utf8 = utf16_to_utf8(title, char16_strlen(title) * 2);
+                        _cleanup_free_ char *title_utf8 = utf16_to_utf8(title, SIZE_MAX);
                         log_info("%s: \"%s\"", saved_argv[i], title_utf8);
                 } else
                         log_info("%s: Bad BCD", saved_argv[i]);
index fdd5abac2902c8af071f3e9eb45155f9cf8cdd31..79fa525175c472f553b0559a69228962acd8d4e6 100644 (file)
@@ -265,26 +265,36 @@ char16_t *xstr8_to_path(const char *str8) {
         return path;
 }
 
-void mangle_stub_cmdline(char16_t *cmdline) {
-        char16_t *p = cmdline;
+static bool shall_be_whitespace(char16_t c) {
+        return c <= 0x20U || c == 0x7FU; /* All control characters + space */
+}
+
+char16_t* mangle_stub_cmdline(char16_t *cmdline) {
+        char16_t *p, *q, *e;
 
         if (!cmdline)
-                return;
+                return cmdline;
 
-        for (; *cmdline != '\0'; cmdline++)
-                /* Convert ASCII control characters to spaces. */
-                if (*cmdline <= 0x1F)
-                        *cmdline = ' ';
+        p = q = cmdline;
 
-        /* chomp the trailing whitespaces */
-        while (cmdline != p) {
-                --cmdline;
+        /* Skip initial whitespace */
+        while (shall_be_whitespace(*p))
+                p++;
 
-                if (*cmdline != ' ')
-                        break;
+        /* Turn inner control characters into proper spaces */
+        for (e = p; *p != 0; p++) {
+                if (shall_be_whitespace(*p)) {
+                        *(q++) = ' ';
+                        continue;
+                }
 
-                *cmdline = '\0';
+                *(q++) = *p;
+                e = q; /* remember last non-whitespace char */
         }
+
+        /* Chop off trailing whitespace */
+        *e = 0;
+        return cmdline;
 }
 
 EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf) {
index 8cb4ed0c1cde69c89f121e56af1841b8e0add257..d190db9f3fcc8e87a57bb5670596f6bc69ec80dd 100644 (file)
@@ -98,7 +98,7 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, b
 
 void convert_efi_path(char16_t *path);
 char16_t *xstr8_to_path(const char *stra);
-void mangle_stub_cmdline(char16_t *cmdline);
+char16_t *mangle_stub_cmdline(char16_t *cmdline);
 
 EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf);
 EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t size, char **content, size_t *content_size);
index c08386d9fc2d2a09e36aa43fb808affa441b765c..fbeb4473f77c8bb850617644909930abf48cf7db 100644 (file)
@@ -693,21 +693,16 @@ static int verb_calculate(int argc, char *argv[], void *userdata) {
 
                                 printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, pcr_states[i].bank, hd);
                         } else {
-                                _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL, *array = NULL;
+                                _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
 
                                 array = json_variant_ref(json_variant_by_key(w, pcr_states[i].bank));
 
-                                r = json_build(&bv,
-                                               JSON_BUILD_OBJECT(
-                                                               JSON_BUILD_PAIR_CONDITION(!isempty(*phase), "phase", JSON_BUILD_STRING(*phase)),
-                                                               JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE)),
-                                                               JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states[i].value, pcr_states[i].value_size))
-                                               )
-                                );
-                                if (r < 0)
-                                        return log_error_errno(r, "Failed to build JSON object: %m");
-
-                                r = json_variant_append_array(&array, bv);
+                                r = json_variant_append_arrayb(
+                                                &array,
+                                                JSON_BUILD_OBJECT(
+                                                                JSON_BUILD_PAIR_CONDITION(!isempty(*phase), "phase", JSON_BUILD_STRING(*phase)),
+                                                                JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE)),
+                                                                JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states[i].value, pcr_states[i].value_size))));
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to append JSON object to array: %m");
 
diff --git a/src/core/credential.c b/src/core/credential.c
new file mode 100644 (file)
index 0000000..d9a8800
--- /dev/null
@@ -0,0 +1,1067 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/mount.h>
+
+#include "acl-util.h"
+#include "credential.h"
+#include "creds-util.h"
+#include "execute.h"
+#include "fileio.h"
+#include "glob-util.h"
+#include "io-util.h"
+#include "label-util.h"
+#include "missing_syscall.h"
+#include "mkdir-label.h"
+#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "recurse-dir.h"
+#include "rm-rf.h"
+#include "socket-util.h"
+#include "tmpfile-util.h"
+
+ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc) {
+        if (!sc)
+                return NULL;
+
+        free(sc->id);
+        free(sc->data);
+        return mfree(sc);
+}
+
+ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc) {
+        if (!lc)
+                return NULL;
+
+        free(lc->id);
+        free(lc->path);
+        return mfree(lc);
+}
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        exec_set_credential_hash_ops,
+        char, string_hash_func, string_compare_func,
+        ExecSetCredential, exec_set_credential_free);
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        exec_load_credential_hash_ops,
+        char, string_hash_func, string_compare_func,
+        ExecLoadCredential, exec_load_credential_free);
+
+bool exec_context_has_credentials(const ExecContext *c) {
+        assert(c);
+
+        return !hashmap_isempty(c->set_credentials) ||
+                !hashmap_isempty(c->load_credentials) ||
+                !set_isempty(c->import_credentials);
+}
+
+bool exec_context_has_encrypted_credentials(ExecContext *c) {
+        ExecLoadCredential *load_cred;
+        ExecSetCredential *set_cred;
+
+        assert(c);
+
+        HASHMAP_FOREACH(load_cred, c->load_credentials)
+                if (load_cred->encrypted)
+                        return true;
+
+        HASHMAP_FOREACH(set_cred, c->set_credentials)
+                if (set_cred->encrypted)
+                        return true;
+
+        return false;
+}
+
+static int get_credential_directory(
+                const char *runtime_prefix,
+                const char *unit,
+                char **ret) {
+
+        char *p;
+
+        assert(ret);
+
+        if (!runtime_prefix || !unit) {
+                *ret = NULL;
+                return 0;
+        }
+
+        p = path_join(runtime_prefix, "credentials", unit);
+        if (!p)
+                return -ENOMEM;
+
+        *ret = p;
+        return 1;
+}
+
+int unit_add_default_credential_dependencies(Unit *u, const ExecContext *c) {
+        _cleanup_free_ char *p = NULL, *m = NULL;
+        int r;
+
+        assert(u);
+        assert(c);
+
+        if (!exec_context_has_credentials(c))
+                return 0;
+
+        /* Let's make sure the credentials directory of this service is unmounted *after* the service itself
+         * shuts down. This only matters if mount namespacing is not used for the service, and hence the
+         * credentials mount appears on the host. */
+
+        r = get_credential_directory(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], u->id, &p);
+        if (r <= 0)
+                return r;
+
+        r = unit_name_from_path(p, ".mount", &m);
+        if (r < 0)
+                return r;
+
+        return unit_add_dependency_by_name(u, UNIT_AFTER, m, /* add_reference= */ true, UNIT_DEPENDENCY_FILE);
+}
+
+int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_prefix, const char *unit) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        assert(c);
+
+        r = get_credential_directory(runtime_prefix, unit, &p);
+        if (r <= 0)
+                return r;
+
+        /* This is either a tmpfs/ramfs of its own, or a plain directory. Either way, let's first try to
+         * unmount it, and afterwards remove the mount point */
+        (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW);
+        (void) rm_rf(p, REMOVE_ROOT|REMOVE_CHMOD);
+
+        return 0;
+}
+
+static int write_credential(
+                int dfd,
+                const char *id,
+                const void *data,
+                size_t size,
+                uid_t uid,
+                gid_t gid,
+                bool ownership_ok) {
+
+        _cleanup_(unlink_and_freep) char *tmp = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        int r;
+
+        r = tempfn_random_child("", "cred", &tmp);
+        if (r < 0)
+                return r;
+
+        fd = openat(dfd, tmp, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL|O_NOFOLLOW|O_NOCTTY, 0600);
+        if (fd < 0) {
+                tmp = mfree(tmp);
+                return -errno;
+        }
+
+        r = loop_write(fd, data, size, /* do_poll = */ false);
+        if (r < 0)
+                return r;
+
+        if (fchmod(fd, 0400) < 0) /* Take away "w" bit */
+                return -errno;
+
+        if (uid_is_valid(uid) && uid != getuid()) {
+                r = fd_add_uid_acl_permission(fd, uid, ACL_READ);
+                if (r < 0) {
+                        if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
+                                return r;
+
+                        if (!ownership_ok) /* Ideally we use ACLs, since we can neatly express what we want
+                                            * to express: that the user gets read access and nothing
+                                            * else. But if the backing fs can't support that (e.g. ramfs)
+                                            * then we can use file ownership instead. But that's only safe if
+                                            * we can then re-mount the whole thing read-only, so that the
+                                            * user can no longer chmod() the file to gain write access. */
+                                return r;
+
+                        if (fchown(fd, uid, gid) < 0)
+                                return -errno;
+                }
+        }
+
+        if (renameat(dfd, tmp, dfd, id) < 0)
+                return -errno;
+
+        tmp = mfree(tmp);
+        return 0;
+}
+
+typedef enum CredentialSearchPath {
+        CREDENTIAL_SEARCH_PATH_TRUSTED,
+        CREDENTIAL_SEARCH_PATH_ENCRYPTED,
+        CREDENTIAL_SEARCH_PATH_ALL,
+        _CREDENTIAL_SEARCH_PATH_MAX,
+        _CREDENTIAL_SEARCH_PATH_INVALID = -EINVAL,
+} CredentialSearchPath;
+
+static char **credential_search_path(const ExecParameters *params, CredentialSearchPath path) {
+
+        _cleanup_strv_free_ char **l = NULL;
+
+        assert(params);
+        assert(path >= 0 && path < _CREDENTIAL_SEARCH_PATH_MAX);
+
+        /* Assemble a search path to find credentials in. For non-encrypted credentials, We'll look in
+         * /etc/credstore/ (and similar directories in /usr/lib/ + /run/). If we're looking for encrypted
+         * credentials, we'll look in /etc/credstore.encrypted/ (and similar dirs). */
+
+        if (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) {
+                if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0)
+                        return NULL;
+
+                if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 0)
+                        return NULL;
+        }
+
+        if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) {
+                if (params->received_credentials_directory)
+                        if (strv_extend(&l, params->received_credentials_directory) < 0)
+                                return NULL;
+
+                if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
+                        return NULL;
+        }
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *t = strv_join(l, ":");
+
+                log_debug("Credential search path is: %s", strempty(t));
+        }
+
+        return TAKE_PTR(l);
+}
+
+static int maybe_decrypt_and_write_credential(
+                int dir_fd,
+                const char *id,
+                bool encrypted,
+                uid_t uid,
+                gid_t gid,
+                bool ownership_ok,
+                const char *data,
+                size_t size,
+                uint64_t *left) {
+
+        _cleanup_free_ void *plaintext = NULL;
+        size_t add;
+        int r;
+
+        if (encrypted) {
+                size_t plaintext_size = 0;
+
+                r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size,
+                                                &plaintext, &plaintext_size);
+                if (r < 0)
+                        return r;
+
+                data = plaintext;
+                size = plaintext_size;
+        }
+
+        add = strlen(id) + size;
+        if (add > *left)
+                return -E2BIG;
+
+        r = write_credential(dir_fd, id, data, size, uid, gid, ownership_ok);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to write credential '%s': %m", id);
+
+        *left -= add;
+        return 0;
+}
+
+static int load_credential_glob(
+                const char *path,
+                bool encrypted,
+                char **search_path,
+                ReadFullFileFlags flags,
+                int write_dfd,
+                uid_t uid,
+                gid_t gid,
+                bool ownership_ok,
+                uint64_t *left) {
+
+        int r;
+
+        STRV_FOREACH(d, search_path) {
+                _cleanup_globfree_ glob_t pglob = {};
+                _cleanup_free_ char *j = NULL;
+
+                j = path_join(*d, path);
+                if (!j)
+                        return -ENOMEM;
+
+                r = safe_glob(j, 0, &pglob);
+                if (r == -ENOENT)
+                        continue;
+                if (r < 0)
+                        return r;
+
+                for (size_t n = 0; n < pglob.gl_pathc; n++) {
+                        _cleanup_free_ char *fn = NULL;
+                        _cleanup_(erase_and_freep) char *data = NULL;
+                        size_t size;
+
+                        /* path is absolute, hence pass AT_FDCWD as nop dir fd here */
+                        r = read_full_file_full(
+                                AT_FDCWD,
+                                pglob.gl_pathv[n],
+                                UINT64_MAX,
+                                encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
+                                flags,
+                                NULL,
+                                &data, &size);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to read credential '%s': %m",
+                                                        pglob.gl_pathv[n]);
+
+                        r = path_extract_filename(pglob.gl_pathv[n], &fn);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to extract filename from '%s': %m",
+                                                        pglob.gl_pathv[n]);
+
+                        r = maybe_decrypt_and_write_credential(
+                                write_dfd,
+                                fn,
+                                encrypted,
+                                uid,
+                                gid,
+                                ownership_ok,
+                                data, size,
+                                left);
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
+static int load_credential(
+                const ExecContext *context,
+                const ExecParameters *params,
+                const char *id,
+                const char *path,
+                bool encrypted,
+                const char *unit,
+                int read_dfd,
+                int write_dfd,
+                uid_t uid,
+                gid_t gid,
+                bool ownership_ok,
+                uint64_t *left) {
+
+        ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER;
+        _cleanup_strv_free_ char **search_path = NULL;
+        _cleanup_(erase_and_freep) char *data = NULL;
+        _cleanup_free_ char *bindname = NULL;
+        const char *source = NULL;
+        bool missing_ok = true;
+        size_t size, maxsz;
+        int r;
+
+        assert(context);
+        assert(params);
+        assert(id);
+        assert(path);
+        assert(unit);
+        assert(read_dfd >= 0 || read_dfd == AT_FDCWD);
+        assert(write_dfd >= 0);
+        assert(left);
+
+        if (read_dfd >= 0) {
+                /* If a directory fd is specified, then read the file directly from that dir. In this case we
+                 * won't do AF_UNIX stuff (we simply don't want to recursively iterate down a tree of AF_UNIX
+                 * IPC sockets). It's OK if a file vanishes here in the time we enumerate it and intend to
+                 * open it. */
+
+                if (!filename_is_valid(path)) /* safety check */
+                        return -EINVAL;
+
+                missing_ok = true;
+                source = path;
+
+        } else if (path_is_absolute(path)) {
+                /* If this is an absolute path, read the data directly from it, and support AF_UNIX
+                 * sockets */
+
+                if (!path_is_valid(path)) /* safety check */
+                        return -EINVAL;
+
+                flags |= READ_FULL_FILE_CONNECT_SOCKET;
+
+                /* Pass some minimal info about the unit and the credential name we are looking to acquire
+                 * via the source socket address in case we read off an AF_UNIX socket. */
+                if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, id) < 0)
+                        return -ENOMEM;
+
+                missing_ok = false;
+                source = path;
+
+        } else if (credential_name_valid(path)) {
+                /* If this is a relative path, take it as credential name relative to the credentials
+                 * directory we received ourselves. We don't support the AF_UNIX stuff in this mode, since we
+                 * are operating on a credential store, i.e. this is guaranteed to be regular files. */
+
+                search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ALL);
+                if (!search_path)
+                        return -ENOMEM;
+
+                missing_ok = true;
+        } else
+                source = NULL;
+
+        if (encrypted)
+                flags |= READ_FULL_FILE_UNBASE64;
+
+        maxsz = encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX;
+
+        if (search_path) {
+                STRV_FOREACH(d, search_path) {
+                        _cleanup_free_ char *j = NULL;
+
+                        j = path_join(*d, path);
+                        if (!j)
+                                return -ENOMEM;
+
+                        r = read_full_file_full(
+                                        AT_FDCWD, j, /* path is absolute, hence pass AT_FDCWD as nop dir fd here */
+                                        UINT64_MAX,
+                                        maxsz,
+                                        flags,
+                                        NULL,
+                                        &data, &size);
+                        if (r != -ENOENT)
+                                break;
+                }
+        } else if (source)
+                r = read_full_file_full(
+                                read_dfd, source,
+                                UINT64_MAX,
+                                maxsz,
+                                flags,
+                                bindname,
+                                &data, &size);
+        else
+                r = -ENOENT;
+
+        if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, id))) {
+                /* Make a missing inherited credential non-fatal, let's just continue. After all apps
+                 * will get clear errors if we don't pass such a missing credential on as they
+                 * themselves will get ENOENT when trying to read them, which should not be much
+                 * worse than when we handle the error here and make it fatal.
+                 *
+                 * Also, if the source file doesn't exist, but a fallback is set via SetCredentials=
+                 * we are fine, too. */
+                log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", path);
+                return 0;
+        }
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read credential '%s': %m", path);
+
+        return maybe_decrypt_and_write_credential(write_dfd, id, encrypted, uid, gid, ownership_ok, data, size, left);
+}
+
+struct load_cred_args {
+        const ExecContext *context;
+        const ExecParameters *params;
+        bool encrypted;
+        const char *unit;
+        int dfd;
+        uid_t uid;
+        gid_t gid;
+        bool ownership_ok;
+        uint64_t *left;
+};
+
+static int load_cred_recurse_dir_cb(
+                RecurseDirEvent event,
+                const char *path,
+                int dir_fd,
+                int inode_fd,
+                const struct dirent *de,
+                const struct statx *sx,
+                void *userdata) {
+
+        struct load_cred_args *args = ASSERT_PTR(userdata);
+        _cleanup_free_ char *sub_id = NULL;
+        int r;
+
+        if (event != RECURSE_DIR_ENTRY)
+                return RECURSE_DIR_CONTINUE;
+
+        if (!IN_SET(de->d_type, DT_REG, DT_SOCK))
+                return RECURSE_DIR_CONTINUE;
+
+        sub_id = strreplace(path, "/", "_");
+        if (!sub_id)
+                return -ENOMEM;
+
+        if (!credential_name_valid(sub_id))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Credential would get ID %s, which is not valid, refusing", sub_id);
+
+        if (faccessat(args->dfd, sub_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) {
+                log_debug("Skipping credential with duplicated ID %s at %s", sub_id, path);
+                return RECURSE_DIR_CONTINUE;
+        }
+        if (errno != ENOENT)
+                return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sub_id);
+
+        r = load_credential(
+                        args->context,
+                        args->params,
+                        sub_id,
+                        de->d_name,
+                        args->encrypted,
+                        args->unit,
+                        dir_fd,
+                        args->dfd,
+                        args->uid,
+                        args->gid,
+                        args->ownership_ok,
+                        args->left);
+        if (r < 0)
+                return r;
+
+        return RECURSE_DIR_CONTINUE;
+}
+
+static int acquire_credentials(
+                const ExecContext *context,
+                const ExecParameters *params,
+                const char *unit,
+                const char *p,
+                uid_t uid,
+                gid_t gid,
+                bool ownership_ok) {
+
+        uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
+        _cleanup_close_ int dfd = -EBADF;
+        const char *ic;
+        ExecLoadCredential *lc;
+        ExecSetCredential *sc;
+        int r;
+
+        assert(context);
+        assert(p);
+
+        dfd = open(p, O_DIRECTORY|O_CLOEXEC);
+        if (dfd < 0)
+                return -errno;
+
+        r = fd_acl_make_writable(dfd); /* Add the "w" bit, if we are reusing an already set up credentials dir where it was unset */
+        if (r < 0)
+                return r;
+
+        /* First, load credentials off disk (or acquire via AF_UNIX socket) */
+        HASHMAP_FOREACH(lc, context->load_credentials) {
+                _cleanup_close_ int sub_fd = -EBADF;
+
+                /* If this is an absolute path, then try to open it as a directory. If that works, then we'll
+                 * recurse into it. If it is an absolute path but it isn't a directory, then we'll open it as
+                 * a regular file. Finally, if it's a relative path we will use it as a credential name to
+                 * propagate a credential passed to us from further up. */
+
+                if (path_is_absolute(lc->path)) {
+                        sub_fd = open(lc->path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+                        if (sub_fd < 0 && !IN_SET(errno,
+                                                  ENOTDIR,  /* Not a directory */
+                                                  ENOENT))  /* Doesn't exist? */
+                                return log_debug_errno(errno, "Failed to open '%s': %m", lc->path);
+                }
+
+                if (sub_fd < 0)
+                        /* Regular file (incl. a credential passed in from higher up) */
+                        r = load_credential(
+                                        context,
+                                        params,
+                                        lc->id,
+                                        lc->path,
+                                        lc->encrypted,
+                                        unit,
+                                        AT_FDCWD,
+                                        dfd,
+                                        uid,
+                                        gid,
+                                        ownership_ok,
+                                        &left);
+                else
+                        /* Directory */
+                        r = recurse_dir(
+                                        sub_fd,
+                                        /* path= */ lc->id, /* recurse_dir() will suffix the subdir paths from here to the top-level id */
+                                        /* statx_mask= */ 0,
+                                        /* n_depth_max= */ UINT_MAX,
+                                        RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE,
+                                        load_cred_recurse_dir_cb,
+                                        &(struct load_cred_args) {
+                                                .context = context,
+                                                .params = params,
+                                                .encrypted = lc->encrypted,
+                                                .unit = unit,
+                                                .dfd = dfd,
+                                                .uid = uid,
+                                                .gid = gid,
+                                                .ownership_ok = ownership_ok,
+                                                .left = &left,
+                                        });
+                if (r < 0)
+                        return r;
+        }
+
+        /* Next, look for system credentials and credentials in the credentials store. Note that these do not
+         * override any credentials found earlier. */
+        SET_FOREACH(ic, context->import_credentials) {
+                _cleanup_free_ char **search_path = NULL;
+
+                search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED);
+                if (!search_path)
+                        return -ENOMEM;
+
+                r = load_credential_glob(
+                                ic,
+                                /* encrypted = */ false,
+                                search_path,
+                                READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER,
+                                dfd,
+                                uid,
+                                gid,
+                                ownership_ok,
+                                &left);
+                if (r < 0)
+                        return r;
+
+                search_path = strv_free(search_path);
+                search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED);
+                if (!search_path)
+                        return -ENOMEM;
+
+                r = load_credential_glob(
+                                ic,
+                                /* encrypted = */ true,
+                                search_path,
+                                READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER|READ_FULL_FILE_UNBASE64,
+                                dfd,
+                                uid,
+                                gid,
+                                ownership_ok,
+                                &left);
+                if (r < 0)
+                        return r;
+        }
+
+        /* Finally, we add in literally specified credentials. If the credentials already exist, we'll not
+         * add them, so that they can act as a "default" if the same credential is specified multiple times. */
+        HASHMAP_FOREACH(sc, context->set_credentials) {
+                _cleanup_(erase_and_freep) void *plaintext = NULL;
+                const char *data;
+                size_t size, add;
+
+                /* Note that we check ahead of time here instead of relying on O_EXCL|O_CREAT later to return
+                 * EEXIST if the credential already exists. That's because the TPM2-based decryption is kinda
+                 * slow and involved, hence it's nice to be able to skip that if the credential already
+                 * exists anyway. */
+                if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
+                        continue;
+                if (errno != ENOENT)
+                        return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id);
+
+                if (sc->encrypted) {
+                        r = decrypt_credential_and_warn(sc->id, now(CLOCK_REALTIME), NULL, NULL, sc->data, sc->size, &plaintext, &size);
+                        if (r < 0)
+                                return r;
+
+                        data = plaintext;
+                } else {
+                        data = sc->data;
+                        size = sc->size;
+                }
+
+                add = strlen(sc->id) + size;
+                if (add > left)
+                        return -E2BIG;
+
+                r = write_credential(dfd, sc->id, data, size, uid, gid, ownership_ok);
+                if (r < 0)
+                        return r;
+
+                left -= add;
+        }
+
+        r = fd_acl_make_read_only(dfd); /* Now take away the "w" bit */
+        if (r < 0)
+                return r;
+
+        /* After we created all keys with the right perms, also make sure the credential store as a whole is
+         * accessible */
+
+        if (uid_is_valid(uid) && uid != getuid()) {
+                r = fd_add_uid_acl_permission(dfd, uid, ACL_READ | ACL_EXECUTE);
+                if (r < 0) {
+                        if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
+                                return r;
+
+                        if (!ownership_ok)
+                                return r;
+
+                        if (fchown(dfd, uid, gid) < 0)
+                                return -errno;
+                }
+        }
+
+        return 0;
+}
+
+static int setup_credentials_internal(
+                const ExecContext *context,
+                const ExecParameters *params,
+                const char *unit,
+                const char *final,        /* This is where the credential store shall eventually end up at */
+                const char *workspace,    /* This is where we can prepare it before moving it to the final place */
+                bool reuse_workspace,     /* Whether to reuse any existing workspace mount if it already is a mount */
+                bool must_mount,          /* Whether to require that we mount something, it's not OK to use the plain directory fall back */
+                uid_t uid,
+                gid_t gid,
+                int *ret_mount_fd) {
+
+        int r, workspace_mounted; /* negative if we don't know yet whether we have/can mount something; true
+                                   * if we mounted something; false if we definitely can't mount anything */
+        bool final_mounted;
+        const char *where;
+
+        assert(context);
+        assert(final);
+        assert(workspace);
+
+        if (reuse_workspace) {
+                r = path_is_mount_point(workspace, NULL, 0);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        workspace_mounted = true; /* If this is already a mount, and we are supposed to reuse
+                                                   * it, let's keep this in mind */
+                else
+                        workspace_mounted = -1; /* We need to figure out if we can mount something to the workspace */
+        } else
+                workspace_mounted = -1; /* ditto */
+
+        r = path_is_mount_point(final, NULL, 0);
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                /* If the final place already has something mounted, we use that. If the workspace also has
+                 * something mounted we assume it's actually the same mount (but with MS_RDONLY
+                 * different). */
+                final_mounted = true;
+
+                if (workspace_mounted < 0) {
+                        /* If the final place is mounted, but the workspace isn't, then let's bind mount
+                         * the final version to the workspace, and make it writable, so that we can make
+                         * changes */
+
+                        r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
+                        if (r < 0)
+                                return r;
+
+                        r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
+                        if (r < 0)
+                                return r;
+
+                        workspace_mounted = true;
+                }
+        } else
+                final_mounted = false;
+
+        if (workspace_mounted < 0) {
+                /* Nothing is mounted on the workspace yet, let's try to mount something now */
+
+                r = mount_credentials_fs(workspace, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
+                if (r < 0) {
+                        /* If that didn't work, try to make a bind mount from the final to the workspace, so
+                         * that we can make it writable there. */
+                        r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
+                        if (r < 0) {
+                                if (!ERRNO_IS_PRIVILEGE(r))
+                                        /* Propagate anything that isn't a permission problem. */
+                                        return r;
+
+                                if (must_mount)
+                                        /* If it's not OK to use the plain directory fallback, propagate all
+                                         * errors too. */
+                                        return r;
+
+                                /* If we lack privileges to bind mount stuff, then let's gracefully proceed
+                                 * for compat with container envs, and just use the final dir as is. */
+
+                                workspace_mounted = false;
+                        } else {
+                                /* Make the new bind mount writable (i.e. drop MS_RDONLY) */
+                                r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
+                                if (r < 0)
+                                        return r;
+
+                                workspace_mounted = true;
+                        }
+                } else
+                        workspace_mounted = true;
+        }
+
+        assert(!must_mount || workspace_mounted > 0);
+        where = workspace_mounted ? workspace : final;
+
+        (void) label_fix_full(AT_FDCWD, where, final, 0);
+
+        r = acquire_credentials(context, params, unit, where, uid, gid, workspace_mounted);
+        if (r < 0)
+                return r;
+
+        if (workspace_mounted) {
+                bool install;
+
+                /* Determine if we should actually install the prepared mount in the final location by bind
+                 * mounting it there. We do so only if the mount is not established there already, and if the
+                 * mount is actually non-empty (i.e. carries at least one credential). Not that in the best
+                 * case we are doing all this in a mount namespace, thus no one else will see that we
+                 * allocated a file system we are getting rid of again here. */
+                if (final_mounted)
+                        install = false; /* already installed */
+                else {
+                        r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false);
+                        if (r < 0)
+                                return r;
+
+                        install = r == 0; /* install only if non-empty */
+                }
+
+                if (install) {
+                        /* Make workspace read-only now, so that any bind mount we make from it defaults to
+                         * read-only too */
+                        r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL);
+                        if (r < 0)
+                                return r;
+
+                        if (ret_mount_fd) {
+                                _cleanup_close_ int mount_fd = -EBADF;
+
+                                r = mount_fd = RET_NERRNO(open_tree(AT_FDCWD, workspace, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC));
+                                if (r >= 0) {
+                                        /* The workspace is already cloned in the above, and not necessary
+                                         * anymore. Even though the workspace is unmounted when the short-lived
+                                         * child process exits, let's explicitly unmount it here for safety. */
+                                        r = umount_verbose(LOG_DEBUG, workspace, MNT_DETACH|UMOUNT_NOFOLLOW);
+                                        if (r < 0)
+                                                return r;
+
+                                        *ret_mount_fd = TAKE_FD(mount_fd);
+                                        return 0;
+                                }
+
+                                /* Old kernel? Unprivileged? */
+                                if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
+                                        return r;
+                        }
+
+                        /* And mount it to the final place, read-only */
+                        r = mount_nofollow_verbose(LOG_DEBUG, workspace, final, NULL, MS_MOVE, NULL);
+                } else
+                        /* Otherwise get rid of it */
+                        r = umount_verbose(LOG_DEBUG, workspace, MNT_DETACH|UMOUNT_NOFOLLOW);
+                if (r < 0)
+                        return r;
+        } else {
+                _cleanup_free_ char *parent = NULL;
+
+                /* If we do not have our own mount put used the plain directory fallback, then we need to
+                 * open access to the top-level credential directory and the per-service directory now */
+
+                r = path_extract_directory(final, &parent);
+                if (r < 0)
+                        return r;
+                if (chmod(parent, 0755) < 0)
+                        return -errno;
+        }
+
+        if (ret_mount_fd)
+                *ret_mount_fd = -EBADF;
+        return 0;
+}
+
+int setup_credentials(
+                const ExecContext *context,
+                const ExecParameters *params,
+                const char *unit,
+                uid_t uid,
+                gid_t gid,
+                char **ret_path,
+                int *ret_mount_fd) {
+
+        _cleanup_close_pair_ int socket_pair[2] = PIPE_EBADF;
+        _cleanup_free_ char *p = NULL, *q = NULL;
+        _cleanup_close_ int mount_fd = -EBADF;
+        int r;
+
+        assert(context);
+        assert(params);
+        assert(ret_path);
+        assert(ret_mount_fd);
+
+        if (!exec_context_has_credentials(context)) {
+                *ret_path = NULL;
+                *ret_mount_fd = -EBADF;
+                return 0;
+        }
+
+        if (!params->prefix[EXEC_DIRECTORY_RUNTIME])
+                return -EINVAL;
+
+        /* This where we'll place stuff when we are done; this main credentials directory is world-readable,
+         * and the subdir we mount over with a read-only file system readable by the service's user */
+        q = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "credentials");
+        if (!q)
+                return -ENOMEM;
+
+        r = mkdir_label(q, 0755); /* top-level dir: world readable/searchable */
+        if (r < 0 && r != -EEXIST)
+                return r;
+
+        p = path_join(q, unit);
+        if (!p)
+                return -ENOMEM;
+
+        r = mkdir_label(p, 0700); /* per-unit dir: private to user */
+        if (r < 0 && r != -EEXIST)
+                return r;
+
+        if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, socket_pair) < 0)
+                return -errno;
+
+        r = safe_fork_full("(sd-mkdcreds)",
+                           NULL, &socket_pair[1], 1,
+                           FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_REOPEN_LOG, NULL);
+        if (r < 0) {
+                _cleanup_free_ char *t = NULL, *u = NULL;
+
+                /* If this is not a privilege or support issue then propagate the error */
+                if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
+                        return r;
+
+                /* Temporary workspace, that remains inaccessible all the time. We prepare stuff there before moving
+                 * it into place, so that users can't access half-initialized credential stores. */
+                t = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/temporary-credentials");
+                if (!t)
+                        return -ENOMEM;
+
+                /* We can't set up a mount namespace. In that case operate on a fixed, inaccessible per-unit
+                 * directory outside of /run/credentials/ first, and then move it over to /run/credentials/
+                 * after it is fully set up */
+                u = path_join(t, unit);
+                if (!u)
+                        return -ENOMEM;
+
+                FOREACH_STRING(i, t, u) {
+                        r = mkdir_label(i, 0700);
+                        if (r < 0 && r != -EEXIST)
+                                return r;
+                }
+
+                r = setup_credentials_internal(
+                                context,
+                                params,
+                                unit,
+                                p,       /* final mount point */
+                                u,       /* temporary workspace to overmount */
+                                true,    /* reuse the workspace if it is already a mount */
+                                false,   /* it's OK to fall back to a plain directory if we can't mount anything */
+                                uid,
+                                gid,
+                                NULL);
+
+                (void) rmdir(u); /* remove the workspace again if we can. */
+
+                if (r < 0)
+                        return r;
+
+        } else if (r == 0) { /* child */
+
+                /* We managed to set up a mount namespace, and are now in a child. That's great. In this case
+                 * we can use the same directory for all cases, after turning off propagation. Question
+                 * though is: where do we turn off propagation exactly, and where do we place the workspace
+                 * directory? We need some place that is guaranteed to be a mount point in the host, and
+                 * which is guaranteed to have a subdir we can mount over. /run/ is not suitable for this,
+                 * since we ultimately want to move the resulting file system there, i.e. we need propagation
+                 * for /run/ eventually. We could use our own /run/systemd/bind mount on itself, but that
+                 * would be visible in the host mount table all the time, which we want to avoid. Hence, what
+                 * we do here instead we use /dev/ and /dev/shm/ for our purposes. We know for sure that
+                 * /dev/ is a mount point and we now for sure that /dev/shm/ exists. Hence we can turn off
+                 * propagation on the former, and then overmount the latter.
+                 *
+                 * Yes it's nasty playing games with /dev/ and /dev/shm/ like this, since it does not exist
+                 * for this purpose, but there are few other candidates that work equally well for us, and
+                 * given that we do this in a privately namespaced short-lived single-threaded process that
+                 * no one else sees this should be OK to do. */
+
+                _cleanup_close_ int fd = -EBADF;
+
+                /* Turn off propagation from our namespace to host */
+                r = mount_nofollow_verbose(LOG_DEBUG, NULL, "/dev", NULL, MS_SLAVE|MS_REC, NULL);
+                if (r < 0)
+                        goto child_fail;
+
+                r = setup_credentials_internal(
+                                context,
+                                params,
+                                unit,
+                                p,           /* final mount point */
+                                "/dev/shm",  /* temporary workspace to overmount */
+                                false,       /* do not reuse /dev/shm if it is already a mount, under no circumstances */
+                                true,        /* insist that something is mounted, do not allow fallback to plain directory */
+                                uid,
+                                gid,
+                                &fd);
+                if (r < 0)
+                        goto child_fail;
+
+                r = send_one_fd_iov(socket_pair[1], fd,
+                                    &IOVEC_MAKE((int[]) { fd >= 0 }, sizeof(int)), 1,
+                                    MSG_DONTWAIT);
+                if (r < 0)
+                        goto child_fail;
+
+                _exit(EXIT_SUCCESS);
+
+        child_fail:
+                _exit(EXIT_FAILURE);
+
+        } else { /* parent */
+
+                int ret;
+                struct iovec iov = IOVEC_MAKE(&ret, sizeof(int));
+
+                r = receive_one_fd_iov(socket_pair[0], &iov, 1, MSG_DONTWAIT, &mount_fd);
+                if (r < 0)
+                        return r;
+                if (ret > 0 && mount_fd < 0)
+                        return -EIO;
+        }
+
+        /* If the credentials dir is empty and not a mount point, then there's no point in having it. Let's
+         * try to remove it. This matters in particular if we created the dir as mount point but then didn't
+         * actually end up mounting anything on it. In that case we'd rather have ENOENT than EACCESS being
+         * seen by users when trying access this inode. */
+        (void) rmdir(p);
+
+        *ret_path = TAKE_PTR(p);
+        *ret_mount_fd = TAKE_FD(mount_fd);
+        return 0;
+}
diff --git a/src/core/credential.h b/src/core/credential.h
new file mode 100644 (file)
index 0000000..4b936b3
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "hash-funcs.h"
+
+typedef struct ExecContext ExecContext;
+typedef struct ExecParameters ExecParameters;
+typedef struct Unit Unit;
+
+/* A credential configured with LoadCredential= */
+typedef struct ExecLoadCredential {
+        char *id, *path;
+        bool encrypted;
+} ExecLoadCredential;
+
+/* A credential configured with SetCredential= */
+typedef struct ExecSetCredential {
+        char *id;
+        bool encrypted;
+        void *data;
+        size_t size;
+} ExecSetCredential;
+
+ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSetCredential*, exec_set_credential_free);
+
+ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecLoadCredential*, exec_load_credential_free);
+
+extern const struct hash_ops exec_set_credential_hash_ops;
+extern const struct hash_ops exec_load_credential_hash_ops;
+
+bool exec_context_has_encrypted_credentials(ExecContext *c);
+bool exec_context_has_credentials(const ExecContext *c);
+
+int unit_add_default_credential_dependencies(Unit *u, const ExecContext *c);
+
+int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_root, const char *unit);
+int setup_credentials(
+                const ExecContext *context,
+                const ExecParameters *params,
+                const char *unit,
+                uid_t uid,
+                gid_t gid,
+                char **ret_path,
+                int *ret_mount_fd);
index 395051e57f791350a65ee1096dbcd0729358d6d6..2a32063ddde25e92d2fd6db253f5745f801ed621 100644 (file)
@@ -9,6 +9,7 @@
 #include "cap-list.h"
 #include "capability-util.h"
 #include "cpu-set-util.h"
+#include "credential.h"
 #include "creds-util.h"
 #include "dbus-execute.h"
 #include "dbus-util.h"
index 5ffb088aee1efd98d6bbb6940de5eecbcd61bd8c..eb452a45ec21dbe019d5aeebbb3d3e1cee5cd4f9 100644 (file)
@@ -32,7 +32,6 @@
 
 #include "sd-messages.h"
 
-#include "acl-util.h"
 #include "af-list.h"
 #include "alloc-util.h"
 #if HAVE_APPARMOR
@@ -51,7 +50,7 @@
 #include "chown-recursive.h"
 #include "constants.h"
 #include "cpu-set-util.h"
-#include "creds-util.h"
+#include "credential.h"
 #include "data-fd-util.h"
 #include "env-file.h"
 #include "env-util.h"
 #include "execute.h"
 #include "exit-status.h"
 #include "fd-util.h"
-#include "fileio.h"
 #include "format-util.h"
 #include "glob-util.h"
 #include "hexdecoct.h"
 #include "io-util.h"
 #include "ioprio-util.h"
-#include "label-util.h"
 #include "lock-util.h"
 #include "log.h"
 #include "macro.h"
 #include "missing_fs.h"
 #include "missing_ioprio.h"
 #include "missing_prctl.h"
+#include "missing_syscall.h"
 #include "mkdir-label.h"
-#include "mount-util.h"
-#include "mountpoint-util.h"
 #include "namespace.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
 #include "process-util.h"
 #include "psi-util.h"
-#include "random-util.h"
-#include "recurse-dir.h"
 #include "rlimit-util.h"
 #include "rm-rf.h"
 #include "seccomp-util.h"
@@ -1509,15 +1503,6 @@ static bool context_has_no_new_privileges(const ExecContext *c) {
                 context_has_syscall_logs(c);
 }
 
-bool exec_context_has_credentials(const ExecContext *context) {
-
-        assert(context);
-
-        return !hashmap_isempty(context->set_credentials) ||
-                !hashmap_isempty(context->load_credentials) ||
-                !set_isempty(context->import_credentials);
-}
-
 #if HAVE_SECCOMP
 
 static bool skip_seccomp_unavailable(const Unit* u, const char* msg) {
@@ -1881,6 +1866,7 @@ static int build_environment(
                 dev_t journal_stream_dev,
                 ino_t journal_stream_ino,
                 const char *memory_pressure_path,
+                const char *creds_path,
                 char ***ret) {
 
         _cleanup_strv_free_ char **our_env = NULL;
@@ -2058,8 +2044,8 @@ static int build_environment(
                 our_env[n_env++] = x;
         }
 
-        if (exec_context_has_credentials(c) && p->prefix[EXEC_DIRECTORY_RUNTIME]) {
-                x = strjoin("CREDENTIALS_DIRECTORY=", p->prefix[EXEC_DIRECTORY_RUNTIME], "/credentials/", u->id);
+        if (creds_path) {
+                x = strjoin("CREDENTIALS_DIRECTORY=", creds_path);
                 if (!x)
                         return -ENOMEM;
 
@@ -2734,866 +2720,6 @@ fail:
         return r;
 }
 
-static int write_credential(
-                int dfd,
-                const char *id,
-                const void *data,
-                size_t size,
-                uid_t uid,
-                gid_t gid,
-                bool ownership_ok) {
-
-        _cleanup_(unlink_and_freep) char *tmp = NULL;
-        _cleanup_close_ int fd = -EBADF;
-        int r;
-
-        r = tempfn_random_child("", "cred", &tmp);
-        if (r < 0)
-                return r;
-
-        fd = openat(dfd, tmp, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL|O_NOFOLLOW|O_NOCTTY, 0600);
-        if (fd < 0) {
-                tmp = mfree(tmp);
-                return -errno;
-        }
-
-        r = loop_write(fd, data, size, /* do_poll = */ false);
-        if (r < 0)
-                return r;
-
-        if (fchmod(fd, 0400) < 0) /* Take away "w" bit */
-                return -errno;
-
-        if (uid_is_valid(uid) && uid != getuid()) {
-                r = fd_add_uid_acl_permission(fd, uid, ACL_READ);
-                if (r < 0) {
-                        if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
-                                return r;
-
-                        if (!ownership_ok) /* Ideally we use ACLs, since we can neatly express what we want
-                                            * to express: that the user gets read access and nothing
-                                            * else. But if the backing fs can't support that (e.g. ramfs)
-                                            * then we can use file ownership instead. But that's only safe if
-                                            * we can then re-mount the whole thing read-only, so that the
-                                            * user can no longer chmod() the file to gain write access. */
-                                return r;
-
-                        if (fchown(fd, uid, gid) < 0)
-                                return -errno;
-                }
-        }
-
-        if (renameat(dfd, tmp, dfd, id) < 0)
-                return -errno;
-
-        tmp = mfree(tmp);
-        return 0;
-}
-
-typedef enum CredentialSearchPath {
-        CREDENTIAL_SEARCH_PATH_TRUSTED,
-        CREDENTIAL_SEARCH_PATH_ENCRYPTED,
-        CREDENTIAL_SEARCH_PATH_ALL,
-        _CREDENTIAL_SEARCH_PATH_MAX,
-        _CREDENTIAL_SEARCH_PATH_INVALID = -EINVAL,
-} CredentialSearchPath;
-
-static char **credential_search_path(const ExecParameters *params, CredentialSearchPath path) {
-
-        _cleanup_strv_free_ char **l = NULL;
-
-        assert(params);
-        assert(path >= 0 && path < _CREDENTIAL_SEARCH_PATH_MAX);
-
-        /* Assemble a search path to find credentials in. For non-encrypted credentials, We'll look in
-         * /etc/credstore/ (and similar directories in /usr/lib/ + /run/). If we're looking for encrypted
-         * credentials, we'll look in /etc/credstore.encrypted/ (and similar dirs). */
-
-        if (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) {
-                if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0)
-                        return NULL;
-
-                if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 0)
-                        return NULL;
-        }
-
-        if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) {
-                if (params->received_credentials_directory)
-                        if (strv_extend(&l, params->received_credentials_directory) < 0)
-                                return NULL;
-
-                if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
-                        return NULL;
-        }
-
-        if (DEBUG_LOGGING) {
-                _cleanup_free_ char *t = strv_join(l, ":");
-
-                log_debug("Credential search path is: %s", strempty(t));
-        }
-
-        return TAKE_PTR(l);
-}
-
-static int maybe_decrypt_and_write_credential(
-                int dir_fd,
-                const char *id,
-                bool encrypted,
-                uid_t uid,
-                gid_t gid,
-                bool ownership_ok,
-                const char *data,
-                size_t size,
-                uint64_t *left) {
-
-        _cleanup_free_ void *plaintext = NULL;
-        size_t add;
-        int r;
-
-        if (encrypted) {
-                size_t plaintext_size = 0;
-
-                r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size,
-                                                &plaintext, &plaintext_size);
-                if (r < 0)
-                        return r;
-
-                data = plaintext;
-                size = plaintext_size;
-        }
-
-        add = strlen(id) + size;
-        if (add > *left)
-                return -E2BIG;
-
-        r = write_credential(dir_fd, id, data, size, uid, gid, ownership_ok);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to write credential '%s': %m", id);
-
-        *left -= add;
-        return 0;
-}
-
-static int load_credential_glob(
-                const char *path,
-                bool encrypted,
-                char **search_path,
-                ReadFullFileFlags flags,
-                int write_dfd,
-                uid_t uid,
-                gid_t gid,
-                bool ownership_ok,
-                uint64_t *left) {
-
-        int r;
-
-        STRV_FOREACH(d, search_path) {
-                _cleanup_globfree_ glob_t pglob = {};
-                _cleanup_free_ char *j = NULL;
-
-                j = path_join(*d, path);
-                if (!j)
-                        return -ENOMEM;
-
-                r = safe_glob(j, 0, &pglob);
-                if (r == -ENOENT)
-                        continue;
-                if (r < 0)
-                        return r;
-
-                for (size_t n = 0; n < pglob.gl_pathc; n++) {
-                        _cleanup_free_ char *fn = NULL;
-                        _cleanup_(erase_and_freep) char *data = NULL;
-                        size_t size;
-
-                        /* path is absolute, hence pass AT_FDCWD as nop dir fd here */
-                        r = read_full_file_full(
-                                AT_FDCWD,
-                                pglob.gl_pathv[n],
-                                UINT64_MAX,
-                                encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
-                                flags,
-                                NULL,
-                                &data, &size);
-                        if (r < 0)
-                                return log_debug_errno(r, "Failed to read credential '%s': %m",
-                                                        pglob.gl_pathv[n]);
-
-                        r = path_extract_filename(pglob.gl_pathv[n], &fn);
-                        if (r < 0)
-                                return log_debug_errno(r, "Failed to extract filename from '%s': %m",
-                                                        pglob.gl_pathv[n]);
-
-                        r = maybe_decrypt_and_write_credential(
-                                write_dfd,
-                                fn,
-                                encrypted,
-                                uid,
-                                gid,
-                                ownership_ok,
-                                data, size,
-                                left);
-                        if (r == -EEXIST)
-                                continue;
-                        if (r < 0)
-                                return r;
-                }
-        }
-
-        return 0;
-}
-
-static int load_credential(
-                const ExecContext *context,
-                const ExecParameters *params,
-                const char *id,
-                const char *path,
-                bool encrypted,
-                const char *unit,
-                int read_dfd,
-                int write_dfd,
-                uid_t uid,
-                gid_t gid,
-                bool ownership_ok,
-                uint64_t *left) {
-
-        ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER;
-        _cleanup_strv_free_ char **search_path = NULL;
-        _cleanup_(erase_and_freep) char *data = NULL;
-        _cleanup_free_ char *bindname = NULL;
-        const char *source = NULL;
-        bool missing_ok = true;
-        size_t size, maxsz;
-        int r;
-
-        assert(context);
-        assert(params);
-        assert(id);
-        assert(path);
-        assert(unit);
-        assert(read_dfd >= 0 || read_dfd == AT_FDCWD);
-        assert(write_dfd >= 0);
-        assert(left);
-
-        if (read_dfd >= 0) {
-                /* If a directory fd is specified, then read the file directly from that dir. In this case we
-                 * won't do AF_UNIX stuff (we simply don't want to recursively iterate down a tree of AF_UNIX
-                 * IPC sockets). It's OK if a file vanishes here in the time we enumerate it and intend to
-                 * open it. */
-
-                if (!filename_is_valid(path)) /* safety check */
-                        return -EINVAL;
-
-                missing_ok = true;
-                source = path;
-
-        } else if (path_is_absolute(path)) {
-                /* If this is an absolute path, read the data directly from it, and support AF_UNIX
-                 * sockets */
-
-                if (!path_is_valid(path)) /* safety check */
-                        return -EINVAL;
-
-                flags |= READ_FULL_FILE_CONNECT_SOCKET;
-
-                /* Pass some minimal info about the unit and the credential name we are looking to acquire
-                 * via the source socket address in case we read off an AF_UNIX socket. */
-                if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, id) < 0)
-                        return -ENOMEM;
-
-                missing_ok = false;
-                source = path;
-
-        } else if (credential_name_valid(path)) {
-                /* If this is a relative path, take it as credential name relative to the credentials
-                 * directory we received ourselves. We don't support the AF_UNIX stuff in this mode, since we
-                 * are operating on a credential store, i.e. this is guaranteed to be regular files. */
-
-                search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ALL);
-                if (!search_path)
-                        return -ENOMEM;
-
-                missing_ok = true;
-        } else
-                source = NULL;
-
-        if (encrypted)
-                flags |= READ_FULL_FILE_UNBASE64;
-
-        maxsz = encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX;
-
-        if (search_path) {
-                STRV_FOREACH(d, search_path) {
-                        _cleanup_free_ char *j = NULL;
-
-                        j = path_join(*d, path);
-                        if (!j)
-                                return -ENOMEM;
-
-                        r = read_full_file_full(
-                                        AT_FDCWD, j, /* path is absolute, hence pass AT_FDCWD as nop dir fd here */
-                                        UINT64_MAX,
-                                        maxsz,
-                                        flags,
-                                        NULL,
-                                        &data, &size);
-                        if (r != -ENOENT)
-                                break;
-                }
-        } else if (source)
-                r = read_full_file_full(
-                                read_dfd, source,
-                                UINT64_MAX,
-                                maxsz,
-                                flags,
-                                bindname,
-                                &data, &size);
-        else
-                r = -ENOENT;
-
-        if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, id))) {
-                /* Make a missing inherited credential non-fatal, let's just continue. After all apps
-                 * will get clear errors if we don't pass such a missing credential on as they
-                 * themselves will get ENOENT when trying to read them, which should not be much
-                 * worse than when we handle the error here and make it fatal.
-                 *
-                 * Also, if the source file doesn't exist, but a fallback is set via SetCredentials=
-                 * we are fine, too. */
-                log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", path);
-                return 0;
-        }
-        if (r < 0)
-                return log_debug_errno(r, "Failed to read credential '%s': %m", path);
-
-        return maybe_decrypt_and_write_credential(write_dfd, id, encrypted, uid, gid, ownership_ok, data, size, left);
-}
-
-struct load_cred_args {
-        const ExecContext *context;
-        const ExecParameters *params;
-        bool encrypted;
-        const char *unit;
-        int dfd;
-        uid_t uid;
-        gid_t gid;
-        bool ownership_ok;
-        uint64_t *left;
-};
-
-static int load_cred_recurse_dir_cb(
-                RecurseDirEvent event,
-                const char *path,
-                int dir_fd,
-                int inode_fd,
-                const struct dirent *de,
-                const struct statx *sx,
-                void *userdata) {
-
-        struct load_cred_args *args = ASSERT_PTR(userdata);
-        _cleanup_free_ char *sub_id = NULL;
-        int r;
-
-        if (event != RECURSE_DIR_ENTRY)
-                return RECURSE_DIR_CONTINUE;
-
-        if (!IN_SET(de->d_type, DT_REG, DT_SOCK))
-                return RECURSE_DIR_CONTINUE;
-
-        sub_id = strreplace(path, "/", "_");
-        if (!sub_id)
-                return -ENOMEM;
-
-        if (!credential_name_valid(sub_id))
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Credential would get ID %s, which is not valid, refusing", sub_id);
-
-        if (faccessat(args->dfd, sub_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) {
-                log_debug("Skipping credential with duplicated ID %s at %s", sub_id, path);
-                return RECURSE_DIR_CONTINUE;
-        }
-        if (errno != ENOENT)
-                return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sub_id);
-
-        r = load_credential(
-                        args->context,
-                        args->params,
-                        sub_id,
-                        de->d_name,
-                        args->encrypted,
-                        args->unit,
-                        dir_fd,
-                        args->dfd,
-                        args->uid,
-                        args->gid,
-                        args->ownership_ok,
-                        args->left);
-        if (r < 0)
-                return r;
-
-        return RECURSE_DIR_CONTINUE;
-}
-
-static int acquire_credentials(
-                const ExecContext *context,
-                const ExecParameters *params,
-                const char *unit,
-                const char *p,
-                uid_t uid,
-                gid_t gid,
-                bool ownership_ok) {
-
-        uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
-        _cleanup_close_ int dfd = -EBADF;
-        const char *ic;
-        ExecLoadCredential *lc;
-        ExecSetCredential *sc;
-        int r;
-
-        assert(context);
-        assert(p);
-
-        dfd = open(p, O_DIRECTORY|O_CLOEXEC);
-        if (dfd < 0)
-                return -errno;
-
-        r = fd_acl_make_writable(dfd); /* Add the "w" bit, if we are reusing an already set up credentials dir where it was unset */
-        if (r < 0)
-                return r;
-
-        /* First, load credentials off disk (or acquire via AF_UNIX socket) */
-        HASHMAP_FOREACH(lc, context->load_credentials) {
-                _cleanup_close_ int sub_fd = -EBADF;
-
-                /* If this is an absolute path, then try to open it as a directory. If that works, then we'll
-                 * recurse into it. If it is an absolute path but it isn't a directory, then we'll open it as
-                 * a regular file. Finally, if it's a relative path we will use it as a credential name to
-                 * propagate a credential passed to us from further up. */
-
-                if (path_is_absolute(lc->path)) {
-                        sub_fd = open(lc->path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
-                        if (sub_fd < 0 && !IN_SET(errno,
-                                                  ENOTDIR,  /* Not a directory */
-                                                  ENOENT))  /* Doesn't exist? */
-                                return log_debug_errno(errno, "Failed to open '%s': %m", lc->path);
-                }
-
-                if (sub_fd < 0)
-                        /* Regular file (incl. a credential passed in from higher up) */
-                        r = load_credential(
-                                        context,
-                                        params,
-                                        lc->id,
-                                        lc->path,
-                                        lc->encrypted,
-                                        unit,
-                                        AT_FDCWD,
-                                        dfd,
-                                        uid,
-                                        gid,
-                                        ownership_ok,
-                                        &left);
-                else
-                        /* Directory */
-                        r = recurse_dir(
-                                        sub_fd,
-                                        /* path= */ lc->id, /* recurse_dir() will suffix the subdir paths from here to the top-level id */
-                                        /* statx_mask= */ 0,
-                                        /* n_depth_max= */ UINT_MAX,
-                                        RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE,
-                                        load_cred_recurse_dir_cb,
-                                        &(struct load_cred_args) {
-                                                .context = context,
-                                                .params = params,
-                                                .encrypted = lc->encrypted,
-                                                .unit = unit,
-                                                .dfd = dfd,
-                                                .uid = uid,
-                                                .gid = gid,
-                                                .ownership_ok = ownership_ok,
-                                                .left = &left,
-                                        });
-                if (r < 0)
-                        return r;
-        }
-
-        /* Next, look for system credentials and credentials in the credentials store. Note that these do not
-         * override any credentials found earlier. */
-        SET_FOREACH(ic, context->import_credentials) {
-                _cleanup_free_ char **search_path = NULL;
-
-                search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED);
-                if (!search_path)
-                        return -ENOMEM;
-
-                r = load_credential_glob(
-                                ic,
-                                /* encrypted = */ false,
-                                search_path,
-                                READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER,
-                                dfd,
-                                uid,
-                                gid,
-                                ownership_ok,
-                                &left);
-                if (r < 0)
-                        return r;
-
-                search_path = strv_free(search_path);
-                search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED);
-                if (!search_path)
-                        return -ENOMEM;
-
-                r = load_credential_glob(
-                                ic,
-                                /* encrypted = */ true,
-                                search_path,
-                                READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER|READ_FULL_FILE_UNBASE64,
-                                dfd,
-                                uid,
-                                gid,
-                                ownership_ok,
-                                &left);
-                if (r < 0)
-                        return r;
-        }
-
-        /* Finally, we add in literally specified credentials. If the credentials already exist, we'll not
-         * add them, so that they can act as a "default" if the same credential is specified multiple times. */
-        HASHMAP_FOREACH(sc, context->set_credentials) {
-                _cleanup_(erase_and_freep) void *plaintext = NULL;
-                const char *data;
-                size_t size, add;
-
-                /* Note that we check ahead of time here instead of relying on O_EXCL|O_CREAT later to return
-                 * EEXIST if the credential already exists. That's because the TPM2-based decryption is kinda
-                 * slow and involved, hence it's nice to be able to skip that if the credential already
-                 * exists anyway. */
-                if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
-                        continue;
-                if (errno != ENOENT)
-                        return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id);
-
-                if (sc->encrypted) {
-                        r = decrypt_credential_and_warn(sc->id, now(CLOCK_REALTIME), NULL, NULL, sc->data, sc->size, &plaintext, &size);
-                        if (r < 0)
-                                return r;
-
-                        data = plaintext;
-                } else {
-                        data = sc->data;
-                        size = sc->size;
-                }
-
-                add = strlen(sc->id) + size;
-                if (add > left)
-                        return -E2BIG;
-
-                r = write_credential(dfd, sc->id, data, size, uid, gid, ownership_ok);
-                if (r < 0)
-                        return r;
-
-                left -= add;
-        }
-
-        r = fd_acl_make_read_only(dfd); /* Now take away the "w" bit */
-        if (r < 0)
-                return r;
-
-        /* After we created all keys with the right perms, also make sure the credential store as a whole is
-         * accessible */
-
-        if (uid_is_valid(uid) && uid != getuid()) {
-                r = fd_add_uid_acl_permission(dfd, uid, ACL_READ | ACL_EXECUTE);
-                if (r < 0) {
-                        if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
-                                return r;
-
-                        if (!ownership_ok)
-                                return r;
-
-                        if (fchown(dfd, uid, gid) < 0)
-                                return -errno;
-                }
-        }
-
-        return 0;
-}
-
-static int setup_credentials_internal(
-                const ExecContext *context,
-                const ExecParameters *params,
-                const char *unit,
-                const char *final,        /* This is where the credential store shall eventually end up at */
-                const char *workspace,    /* This is where we can prepare it before moving it to the final place */
-                bool reuse_workspace,     /* Whether to reuse any existing workspace mount if it already is a mount */
-                bool must_mount,          /* Whether to require that we mount something, it's not OK to use the plain directory fall back */
-                uid_t uid,
-                gid_t gid) {
-
-        int r, workspace_mounted; /* negative if we don't know yet whether we have/can mount something; true
-                                   * if we mounted something; false if we definitely can't mount anything */
-        bool final_mounted;
-        const char *where;
-
-        assert(context);
-        assert(final);
-        assert(workspace);
-
-        if (reuse_workspace) {
-                r = path_is_mount_point(workspace, NULL, 0);
-                if (r < 0)
-                        return r;
-                if (r > 0)
-                        workspace_mounted = true; /* If this is already a mount, and we are supposed to reuse it, let's keep this in mind */
-                else
-                        workspace_mounted = -1; /* We need to figure out if we can mount something to the workspace */
-        } else
-                workspace_mounted = -1; /* ditto */
-
-        r = path_is_mount_point(final, NULL, 0);
-        if (r < 0)
-                return r;
-        if (r > 0) {
-                /* If the final place already has something mounted, we use that. If the workspace also has
-                 * something mounted we assume it's actually the same mount (but with MS_RDONLY
-                 * different). */
-                final_mounted = true;
-
-                if (workspace_mounted < 0) {
-                        /* If the final place is mounted, but the workspace isn't, then let's bind mount
-                         * the final version to the workspace, and make it writable, so that we can make
-                         * changes */
-
-                        r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
-                        if (r < 0)
-                                return r;
-
-                        r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
-                        if (r < 0)
-                                return r;
-
-                        workspace_mounted = true;
-                }
-        } else
-                final_mounted = false;
-
-        if (workspace_mounted < 0) {
-                /* Nothing is mounted on the workspace yet, let's try to mount something now */
-
-                r = mount_credentials_fs(workspace, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
-                if (r < 0) {
-                        /* If that didn't work, try to make a bind mount from the final to the workspace, so that we can make it writable there. */
-                        r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
-                        if (r < 0) {
-                                if (!ERRNO_IS_PRIVILEGE(r)) /* Propagate anything that isn't a permission problem */
-                                        return r;
-
-                                if (must_mount) /* If we it's not OK to use the plain directory
-                                                 * fallback, propagate all errors too */
-                                        return r;
-
-                                /* If we lack privileges to bind mount stuff, then let's gracefully
-                                 * proceed for compat with container envs, and just use the final dir
-                                 * as is. */
-
-                                workspace_mounted = false;
-                        } else {
-                                /* Make the new bind mount writable (i.e. drop MS_RDONLY) */
-                                r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
-                                if (r < 0)
-                                        return r;
-
-                                workspace_mounted = true;
-                        }
-                } else
-                        workspace_mounted = true;
-        }
-
-        assert(!must_mount || workspace_mounted > 0);
-        where = workspace_mounted ? workspace : final;
-
-        (void) label_fix_full(AT_FDCWD, where, final, 0);
-
-        r = acquire_credentials(context, params, unit, where, uid, gid, workspace_mounted);
-        if (r < 0)
-                return r;
-
-        if (workspace_mounted) {
-                bool install;
-
-                /* Determine if we should actually install the prepared mount in the final location by bind
-                 * mounting it there. We do so only if the mount is not established there already, and if the
-                 * mount is actually non-empty (i.e. carries at least one credential). Not that in the best
-                 * case we are doing all this in a mount namespace, thus no one else will see that we
-                 * allocated a file system we are getting rid of again here. */
-                if (final_mounted)
-                        install = false; /* already installed */
-                else {
-                        r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false);
-                        if (r < 0)
-                                return r;
-
-                        install = r == 0; /* install only if non-empty */
-                }
-
-                if (install) {
-                        /* Make workspace read-only now, so that any bind mount we make from it defaults to read-only too */
-                        r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL);
-                        if (r < 0)
-                                return r;
-
-                        /* And mount it to the final place, read-only */
-                        r = mount_nofollow_verbose(LOG_DEBUG, workspace, final, NULL, MS_MOVE, NULL);
-                } else
-                        /* Otherwise get rid of it */
-                        r = umount_verbose(LOG_DEBUG, workspace, MNT_DETACH|UMOUNT_NOFOLLOW);
-                if (r < 0)
-                        return r;
-        } else {
-                _cleanup_free_ char *parent = NULL;
-
-                /* If we do not have our own mount put used the plain directory fallback, then we need to
-                 * open access to the top-level credential directory and the per-service directory now */
-
-                r = path_extract_directory(final, &parent);
-                if (r < 0)
-                        return r;
-                if (chmod(parent, 0755) < 0)
-                        return -errno;
-        }
-
-        return 0;
-}
-
-static int setup_credentials(
-                const ExecContext *context,
-                const ExecParameters *params,
-                const char *unit,
-                uid_t uid,
-                gid_t gid) {
-
-        _cleanup_free_ char *p = NULL, *q = NULL;
-        int r;
-
-        assert(context);
-        assert(params);
-
-        if (!exec_context_has_credentials(context))
-                return 0;
-
-        if (!params->prefix[EXEC_DIRECTORY_RUNTIME])
-                return -EINVAL;
-
-        /* This where we'll place stuff when we are done; this main credentials directory is world-readable,
-         * and the subdir we mount over with a read-only file system readable by the service's user */
-        q = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "credentials");
-        if (!q)
-                return -ENOMEM;
-
-        r = mkdir_label(q, 0755); /* top-level dir: world readable/searchable */
-        if (r < 0 && r != -EEXIST)
-                return r;
-
-        p = path_join(q, unit);
-        if (!p)
-                return -ENOMEM;
-
-        r = mkdir_label(p, 0700); /* per-unit dir: private to user */
-        if (r < 0 && r != -EEXIST)
-                return r;
-
-        r = safe_fork("(sd-mkdcreds)", FORK_DEATHSIG|FORK_WAIT|FORK_NEW_MOUNTNS, NULL);
-        if (r < 0) {
-                _cleanup_free_ char *t = NULL, *u = NULL;
-
-                /* If this is not a privilege or support issue then propagate the error */
-                if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
-                        return r;
-
-                /* Temporary workspace, that remains inaccessible all the time. We prepare stuff there before moving
-                 * it into place, so that users can't access half-initialized credential stores. */
-                t = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/temporary-credentials");
-                if (!t)
-                        return -ENOMEM;
-
-                /* We can't set up a mount namespace. In that case operate on a fixed, inaccessible per-unit
-                 * directory outside of /run/credentials/ first, and then move it over to /run/credentials/
-                 * after it is fully set up */
-                u = path_join(t, unit);
-                if (!u)
-                        return -ENOMEM;
-
-                FOREACH_STRING(i, t, u) {
-                        r = mkdir_label(i, 0700);
-                        if (r < 0 && r != -EEXIST)
-                                return r;
-                }
-
-                r = setup_credentials_internal(
-                                context,
-                                params,
-                                unit,
-                                p,       /* final mount point */
-                                u,       /* temporary workspace to overmount */
-                                true,    /* reuse the workspace if it is already a mount */
-                                false,   /* it's OK to fall back to a plain directory if we can't mount anything */
-                                uid,
-                                gid);
-
-                (void) rmdir(u); /* remove the workspace again if we can. */
-
-                if (r < 0)
-                        return r;
-
-        } else if (r == 0) {
-
-                /* We managed to set up a mount namespace, and are now in a child. That's great. In this case
-                 * we can use the same directory for all cases, after turning off propagation. Question
-                 * though is: where do we turn off propagation exactly, and where do we place the workspace
-                 * directory? We need some place that is guaranteed to be a mount point in the host, and
-                 * which is guaranteed to have a subdir we can mount over. /run/ is not suitable for this,
-                 * since we ultimately want to move the resulting file system there, i.e. we need propagation
-                 * for /run/ eventually. We could use our own /run/systemd/bind mount on itself, but that
-                 * would be visible in the host mount table all the time, which we want to avoid. Hence, what
-                 * we do here instead we use /dev/ and /dev/shm/ for our purposes. We know for sure that
-                 * /dev/ is a mount point and we now for sure that /dev/shm/ exists. Hence we can turn off
-                 * propagation on the former, and then overmount the latter.
-                 *
-                 * Yes it's nasty playing games with /dev/ and /dev/shm/ like this, since it does not exist
-                 * for this purpose, but there are few other candidates that work equally well for us, and
-                 * given that the we do this in a privately namespaced short-lived single-threaded process
-                 * that no one else sees this should be OK to do. */
-
-                r = mount_nofollow_verbose(LOG_DEBUG, NULL, "/dev", NULL, MS_SLAVE|MS_REC, NULL); /* Turn off propagation from our namespace to host */
-                if (r < 0)
-                        goto child_fail;
-
-                r = setup_credentials_internal(
-                                context,
-                                params,
-                                unit,
-                                p,           /* final mount point */
-                                "/dev/shm",  /* temporary workspace to overmount */
-                                false,       /* do not reuse /dev/shm if it is already a mount, under no circumstances */
-                                true,        /* insist that something is mounted, do not allow fallback to plain directory */
-                                uid,
-                                gid);
-                if (r < 0)
-                        goto child_fail;
-
-                _exit(EXIT_SUCCESS);
-
-        child_fail:
-                _exit(EXIT_FAILURE);
-        }
-
-        /* If the credentials dir is empty and not a mount point, then there's no point in having it. Let's
-         * try to remove it. This matters in particular if we created the dir as mount point but then didn't
-         * actually end up mounting anything on it. In that case we'd rather have ENOENT than EACCESS being
-         * seen by users when trying access this inode. */
-        (void) rmdir(p);
-        return 0;
-}
-
 #if ENABLE_SMACK
 static int setup_smack(
                 const Manager *manager,
@@ -3987,12 +3113,14 @@ static int apply_mount_namespace(
                 const ExecParameters *params,
                 ExecRuntime *runtime,
                 const char *memory_pressure_path,
+                const char *creds_path,
+                int creds_fd,
                 char **error_path) {
 
         _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
         _cleanup_strv_free_ char **empty_directories = NULL, **symlinks = NULL,
                         **read_write_paths_cleanup = NULL;
-        _cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL,
+        _cleanup_free_ char *incoming_dir = NULL, *propagate_dir = NULL,
                         *extension_dir = NULL, *host_os_release_stage = NULL;
         const char *root_dir = NULL, *root_image = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL;
         char **read_write_paths;
@@ -4094,14 +3222,6 @@ static int apply_mount_namespace(
         if (context->mount_propagation_flag == MS_SHARED)
                 log_unit_debug(u, "shared mount propagation hidden by other fs namespacing unit settings: ignoring");
 
-        if (exec_context_has_credentials(context) &&
-            params->prefix[EXEC_DIRECTORY_RUNTIME] &&
-            FLAGS_SET(params->flags, EXEC_WRITE_CREDENTIALS)) {
-                creds_path = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "credentials", u->id);
-                if (!creds_path)
-                        return -ENOMEM;
-        }
-
         if (params->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
                 propagate_dir = path_join("/run/systemd/propagate/", u->id);
                 if (!propagate_dir)
@@ -4170,6 +3290,7 @@ static int apply_mount_namespace(
                         tmp_dir,
                         var_tmp_dir,
                         creds_path,
+                        creds_fd,
                         context->log_namespace,
                         context->mount_propagation_flag,
                         &verity,
@@ -4823,7 +3944,7 @@ static int exec_child(
         int r, ngids = 0, exec_fd;
         _cleanup_free_ gid_t *supplementary_gids = NULL;
         const char *username = NULL, *groupname = NULL;
-        _cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL;
+        _cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL, *creds_path = NULL;
         const char *home = NULL, *shell = NULL;
         char **final_argv = NULL;
         dev_t journal_stream_dev = 0;
@@ -4854,6 +3975,7 @@ static int exec_child(
         int ngids_after_pam = 0;
         _cleanup_free_ int *fds = NULL;
         _cleanup_strv_free_ char **fdnames = NULL;
+        _cleanup_close_ int creds_fd = -EBADF;
 
         assert(unit);
         assert(command);
@@ -5304,7 +4426,7 @@ static int exec_child(
         }
 
         if (FLAGS_SET(params->flags, EXEC_WRITE_CREDENTIALS)) {
-                r = setup_credentials(context, params, unit->id, uid, gid);
+                r = setup_credentials(context, params, unit->id, uid, gid, &creds_path, &creds_fd);
                 if (r < 0) {
                         *exit_status = EXIT_CREDENTIALS;
                         return log_unit_error_errno(unit, r, "Failed to set up credentials: %m");
@@ -5324,6 +4446,7 @@ static int exec_child(
                         journal_stream_dev,
                         journal_stream_ino,
                         memory_pressure_path,
+                        creds_path,
                         &our_env);
         if (r < 0) {
                 *exit_status = EXIT_MEMORY;
@@ -5517,7 +4640,7 @@ static int exec_child(
         if (needs_mount_namespace) {
                 _cleanup_free_ char *error_path = NULL;
 
-                r = apply_mount_namespace(unit, command->flags, context, params, runtime, memory_pressure_path, &error_path);
+                r = apply_mount_namespace(unit, command->flags, context, params, runtime, memory_pressure_path, creds_path, creds_fd, &error_path);
                 if (r < 0) {
                         *exit_status = EXIT_NAMESPACE;
                         return log_unit_error_errno(unit, r, "Failed to set up mount namespacing%s%s: %m",
@@ -5525,6 +4648,19 @@ static int exec_child(
                 }
         }
 
+        if (creds_fd >= 0) {
+                assert(creds_path);
+
+                /* When a mount namespace is not requested, then the target directory may not exist yet.
+                 * Here, we ignore the failure, as if it fails, the subsequent move_mount() will fail. */
+                (void) mkdir_p_label(creds_path, 0755);
+
+                if (move_mount(creds_fd, "", AT_FDCWD, creds_path, MOVE_MOUNT_F_EMPTY_PATH) < 0) {
+                        *exit_status = EXIT_CREDENTIALS;
+                        return log_unit_error_errno(unit, errno, "Failed to mount credentials directory on %s: %m", creds_path);
+                }
+        }
+
         if (needs_sandboxing) {
                 r = apply_protect_hostname(unit, context, exit_status);
                 if (r < 0)
@@ -6268,26 +5404,6 @@ int exec_context_destroy_runtime_directory(const ExecContext *c, const char *run
         return 0;
 }
 
-int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_prefix, const char *unit) {
-        _cleanup_free_ char *p = NULL;
-
-        assert(c);
-
-        if (!runtime_prefix || !unit)
-                return 0;
-
-        p = path_join(runtime_prefix, "credentials", unit);
-        if (!p)
-                return -ENOMEM;
-
-        /* This is either a tmpfs/ramfs of its own, or a plain directory. Either way, let's first try to
-         * unmount it, and afterwards remove the mount point */
-        (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW);
-        (void) rm_rf(p, REMOVE_ROOT|REMOVE_CHMOD);
-
-        return 0;
-}
-
 int exec_context_destroy_mount_ns_dir(Unit *u) {
         _cleanup_free_ char *p = NULL;
 
@@ -7216,23 +6332,6 @@ int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
         return 0;
 }
 
-bool exec_context_has_encrypted_credentials(ExecContext *c) {
-        ExecLoadCredential *load_cred;
-        ExecSetCredential *set_cred;
-
-        assert(c);
-
-        HASHMAP_FOREACH(load_cred, c->load_credentials)
-                if (load_cred->encrypted)
-                        return true;
-
-        HASHMAP_FOREACH(set_cred, c->set_credentials)
-                if (set_cred->encrypted)
-                        return true;
-
-        return false;
-}
-
 void exec_status_start(ExecStatus *s, pid_t pid) {
         assert(s);
 
@@ -7941,24 +7040,6 @@ void exec_params_clear(ExecParameters *p) {
         p->exec_fd = safe_close(p->exec_fd);
 }
 
-ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc) {
-        if (!sc)
-                return NULL;
-
-        free(sc->id);
-        free(sc->data);
-        return mfree(sc);
-}
-
-ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc) {
-        if (!lc)
-                return NULL;
-
-        free(lc->id);
-        free(lc->path);
-        return mfree(lc);
-}
-
 void exec_directory_done(ExecDirectory *d) {
         if (!d)
                 return;
@@ -8068,9 +7149,6 @@ ExecCleanMask exec_clean_mask_from_string(const char *s) {
         return 1U << t;
 }
 
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_set_credential_hash_ops, char, string_hash_func, string_compare_func, ExecSetCredential, exec_set_credential_free);
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_load_credential_hash_ops, char, string_hash_func, string_compare_func, ExecLoadCredential, exec_load_credential_free);
-
 static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
         [EXEC_INPUT_NULL] = "null",
         [EXEC_INPUT_TTY] = "tty",
index f784c1a5652c1196d8059cb3a94891cf6ab678ec..33fe77bf7c448033f8d69d70361e04425074655b 100644 (file)
@@ -176,20 +176,6 @@ typedef enum ExecCleanMask {
         _EXEC_CLEAN_MASK_INVALID = -EINVAL,
 } ExecCleanMask;
 
-/* A credential configured with LoadCredential= */
-typedef struct ExecLoadCredential {
-        char *id, *path;
-        bool encrypted;
-} ExecLoadCredential;
-
-/* A credential configured with SetCredential= */
-typedef struct ExecSetCredential {
-        char *id;
-        bool encrypted;
-        void *data;
-        size_t size;
-} ExecSetCredential;
-
 /* Encodes configuration parameters applied to invoked commands. Does not carry runtime data, but only configuration
  * changes sourced from unit files and suchlike. ExecContext objects are usually embedded into Unit objects, and do not
  * change after being loaded. */
@@ -482,15 +468,12 @@ void exec_context_done(ExecContext *c);
 void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix);
 
 int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_root);
-int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_root, const char *unit);
 int exec_context_destroy_mount_ns_dir(Unit *u);
 
 const char* exec_context_fdname(const ExecContext *c, int fd_index);
 
 bool exec_context_may_touch_console(const ExecContext *c);
 bool exec_context_maintains_privileges(const ExecContext *c);
-bool exec_context_has_encrypted_credentials(ExecContext *c);
-bool exec_context_has_credentials(const ExecContext *context);
 
 int exec_context_get_effective_ioprio(const ExecContext *c);
 bool exec_context_get_effective_mount_apivfs(const ExecContext *c);
@@ -526,21 +509,12 @@ void exec_params_clear(ExecParameters *p);
 
 bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c);
 
-ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc);
-DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSetCredential*, exec_set_credential_free);
-
-ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc);
-DEFINE_TRIVIAL_CLEANUP_FUNC(ExecLoadCredential*, exec_load_credential_free);
-
 void exec_directory_done(ExecDirectory *d);
 int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink);
 void exec_directory_sort(ExecDirectory *d);
 
 ExecCleanMask exec_clean_mask_from_string(const char *s);
 
-extern const struct hash_ops exec_set_credential_hash_ops;
-extern const struct hash_ops exec_load_credential_hash_ops;
-
 const char* exec_output_to_string(ExecOutput i) _const_;
 ExecOutput exec_output_from_string(const char *s) _pure_;
 
index 9d1fd0f7a71224391e216473fc060d27d84ca65c..3c931568a0166cd4191cf2930923716ab007a125 100644 (file)
@@ -28,6 +28,7 @@
 #include "conf-parser.h"
 #include "core-varlink.h"
 #include "cpu-set-util.h"
+#include "credential.h"
 #include "creds-util.h"
 #include "env-util.h"
 #include "errno-list.h"
index 9f1d84e3f953382ea8e833b3cc3381622c6d8cba..045ad31a5140f7c839c123a1b453c42621f0f454 100644 (file)
@@ -11,6 +11,7 @@ libcore_sources = files(
         'bpf-socket-bind.c',
         'cgroup.c',
         'core-varlink.c',
+        'credential.c',
         'dbus-automount.c',
         'dbus-cgroup.c',
         'dbus-device.c',
index 51b5aad9c906d592703ea6248ac95a9e177733cb..2197287fd08afa3824269b02018ce6e27ec6447b 100644 (file)
@@ -74,7 +74,8 @@ typedef enum MountMode {
         EXTENSION_DIRECTORIES, /* Bind-mounted outside the root directory, and used by subsequent mounts */
         EXTENSION_IMAGES, /* Mounted outside the root directory, and used by subsequent mounts */
         MQUEUEFS,
-        READWRITE_IMPLICIT, /* Should have the lowest priority. */
+        READWRITE_IMPLICIT, /* Should have the 2nd lowest priority. */
+        MKDIR,              /* Should have the lowest priority. */
         _MOUNT_MODE_MAX,
 } MountMode;
 
@@ -208,25 +209,30 @@ static const MountEntry protect_system_strict_table[] = {
 };
 
 static const char * const mount_mode_table[_MOUNT_MODE_MAX] = {
-        [INACCESSIBLE]         = "inaccessible",
-        [OVERLAY_MOUNT]        = "overlay",
-        [BIND_MOUNT]           = "bind",
-        [BIND_MOUNT_RECURSIVE] = "rbind",
-        [PRIVATE_TMP]          = "private-tmp",
-        [PRIVATE_DEV]          = "private-dev",
-        [BIND_DEV]             = "bind-dev",
-        [EMPTY_DIR]            = "empty",
-        [PRIVATE_SYSFS]        = "private-sysfs",
-        [BIND_SYSFS]           = "bind-sysfs",
-        [PROCFS]               = "procfs",
-        [READONLY]             = "read-only",
-        [READWRITE]            = "read-write",
-        [TMPFS]                = "tmpfs",
-        [MOUNT_IMAGES]         = "mount-images",
-        [READWRITE_IMPLICIT]   = "rw-implicit",
-        [EXEC]                 = "exec",
-        [NOEXEC]               = "noexec",
-        [MQUEUEFS]             = "mqueuefs",
+        [INACCESSIBLE]          = "inaccessible",
+        [OVERLAY_MOUNT]         = "overlay",
+        [MOUNT_IMAGES]          = "mount-images",
+        [BIND_MOUNT]            = "bind",
+        [BIND_MOUNT_RECURSIVE]  = "rbind",
+        [PRIVATE_TMP]           = "private-tmp",
+        [PRIVATE_TMP_READONLY]  = "private-tmp-read-only",
+        [PRIVATE_DEV]           = "private-dev",
+        [BIND_DEV]              = "bind-dev",
+        [EMPTY_DIR]             = "empty",
+        [PRIVATE_SYSFS]         = "private-sysfs",
+        [BIND_SYSFS]            = "bind-sysfs",
+        [PROCFS]                = "procfs",
+        [READONLY]              = "read-only",
+        [READWRITE]             = "read-write",
+        [NOEXEC]                = "noexec",
+        [EXEC]                  = "exec",
+        [TMPFS]                 = "tmpfs",
+        [RUN]                   = "run",
+        [EXTENSION_DIRECTORIES] = "extension-directories",
+        [EXTENSION_IMAGES]      = "extension-images",
+        [MQUEUEFS]              = "mqueuefs",
+        [READWRITE_IMPLICIT]    = "read-write-implicit",
+        [MKDIR]                 = "mkdir",
 };
 
 /* Helper struct for naming simplicity and reusability */
@@ -1045,34 +1051,7 @@ static int mount_bind_dev(const MountEntry *m) {
         if (r > 0) /* make this a NOP if /dev is already a mount point */
                 return 0;
 
-        r = mount_nofollow_verbose(LOG_DEBUG, "/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int mount_private_sysfs(const MountEntry *m) {
-        const char *p = mount_entry_path(ASSERT_PTR(m));
-        int r;
-
-        (void) mkdir_p_label(p, 0755);
-
-        r = remount_sysfs(p);
-        if (r < 0 && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
-                /* Running with an unprivileged user (PrivateUsers=yes), or the kernel seems old. Falling
-                 * back to bind mount the host's version so that we get all child mounts of it, too. */
-
-                log_debug_errno(r, "Failed to remount sysfs on %s, falling back to bind mount: %m", p);
-
-                (void) umount_recursive(p, 0);
-
-                r = mount_nofollow_verbose(LOG_DEBUG, "/sys", p, NULL, MS_BIND|MS_REC, NULL);
-        }
-        if (r < 0)
-                return log_debug_errno(r, "Failed to remount sysfs on %s: %m", p);
-
-        return 1;
+        return mount_nofollow_verbose(LOG_DEBUG, "/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
 }
 
 static int mount_bind_sysfs(const MountEntry *m) {
@@ -1089,11 +1068,34 @@ static int mount_bind_sysfs(const MountEntry *m) {
                 return 0;
 
         /* Bind mount the host's version so that we get all child mounts of it, too. */
-        r = mount_nofollow_verbose(LOG_DEBUG, "/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
-        if (r < 0)
+        return mount_nofollow_verbose(LOG_DEBUG, "/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
+}
+
+static int mount_private_sysfs(const MountEntry *m) {
+        const char *entry_path = mount_entry_path(ASSERT_PTR(m));
+        int r, n;
+
+        (void) mkdir_p_label(entry_path, 0755);
+
+        n = umount_recursive(entry_path, 0);
+
+        r = mount_nofollow_verbose(LOG_DEBUG, "sysfs", entry_path, "sysfs", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
+        if (ERRNO_IS_NEG_PRIVILEGE(r)) {
+                /* When we do not have enough privileges to mount sysfs, fall back to use existing /sys. */
+
+                if (n > 0)
+                        /* /sys or some of sub-mounts are umounted in the above. Refuse incomplete tree.
+                         * Propagate the original error code returned by mount() in the above. */
+                        return r;
+
+                return mount_bind_sysfs(m);
+
+        } else if (r < 0)
                 return r;
 
-        return 1;
+        /* We mounted a new instance now. Let's bind mount the children over now. */
+        (void) bind_mount_submounts("/sys", entry_path);
+        return 0;
 }
 
 static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) {
@@ -1152,34 +1154,32 @@ static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) {
                  * means we really don't want to use it, since it would affect our host's /proc
                  * mount. Hence let's gracefully fallback to a classic, unrestricted version. */
                 r = mount_nofollow_verbose(LOG_DEBUG, "proc", entry_path, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
-        if (r == -EPERM) {
-                /* When we do not have enough privileges to mount /proc, fallback to use existing /proc. */
+        if (ERRNO_IS_NEG_PRIVILEGE(r)) {
+                /* When we do not have enough privileges to mount /proc, fall back to use existing /proc. */
 
                 if (n > 0)
                         /* /proc or some of sub-mounts are umounted in the above. Refuse incomplete tree.
                          * Propagate the original error code returned by mount() in the above. */
-                        return -EPERM;
+                        return r;
 
                 r = path_is_mount_point(entry_path, NULL, 0);
                 if (r < 0)
                         return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m");
-                if (r == 0) {
-                        /* We lack permissions to mount a new instance of /proc, and it is not already
-                         * mounted. But we can access the host's, so as a final fallback bind-mount it to
-                         * the destination, as most likely we are inside a user manager in an unprivileged
-                         * user namespace. */
-                        r = mount_nofollow_verbose(LOG_DEBUG, "/proc", entry_path, NULL, MS_BIND|MS_REC, NULL);
-                        if (r < 0)
-                                return -EPERM;
-                }
+                if (r > 0)
+                        return 0;
+
+                /* We lack permissions to mount a new instance of /proc, and it is not already mounted. But
+                 * we can access the host's, so as a final fallback bind-mount it to the destination, as most
+                 * likely we are inside a user manager in an unprivileged user namespace. */
+                return mount_nofollow_verbose(LOG_DEBUG, "/proc", entry_path, NULL, MS_BIND|MS_REC, NULL);
+
         } else if (r < 0)
                 return r;
-        else
-                /* We mounted a new instance now. Let's bind mount the children over now. This matters for
-                 * nspawn where a bunch of files are overmounted, in particular the boot id */
-                (void) bind_mount_submounts("/proc", entry_path);
 
-        return 1;
+        /* We mounted a new instance now. Let's bind mount the children over now. This matters for nspawn
+         * where a bunch of files are overmounted, in particular the boot id */
+        (void) bind_mount_submounts("/proc", entry_path);
+        return 0;
 }
 
 static int mount_tmpfs(const MountEntry *m) {
@@ -1205,7 +1205,7 @@ static int mount_tmpfs(const MountEntry *m) {
         if (r < 0)
                 return log_debug_errno(r, "Failed to fix label of '%s' as '%s': %m", entry_path, inner_path);
 
-        return 1;
+        return 0;
 }
 
 static int mount_run(const MountEntry *m) {
@@ -1303,7 +1303,7 @@ static int mount_image(
         if (r < 0)
                 return log_debug_errno(r, "Failed to mount image %s on %s: %m", mount_entry_source(m), mount_entry_path(m));
 
-        return 1;
+        return 0;
 }
 
 static int mount_overlay(const MountEntry *m) {
@@ -1319,10 +1319,8 @@ static int mount_overlay(const MountEntry *m) {
         r = mount_nofollow_verbose(LOG_DEBUG, "overlay", mount_entry_path(m), "overlay", MS_RDONLY, options);
         if (r == -ENOENT && m->ignore)
                 return 0;
-        if (r < 0)
-                return r;
 
-        return 1;
+        return r;
 }
 
 static int follow_symlink(
@@ -1551,6 +1549,12 @@ static int apply_one_mount(
         case OVERLAY_MOUNT:
                 return mount_overlay(m);
 
+        case MKDIR:
+                r = mkdir_p_label(mount_entry_path(m), 0755);
+                if (r < 0)
+                        return r;
+                return 1;
+
         default:
                 assert_not_reached();
         }
@@ -2017,6 +2021,7 @@ int setup_namespace(
                 const char* tmp_dir,
                 const char* var_tmp_dir,
                 const char *creds_path,
+                int creds_fd,
                 const char *log_namespace,
                 unsigned long mount_propagation_flag,
                 VeritySettings *verity,
@@ -2339,13 +2344,22 @@ int setup_namespace(
                                 .flags = MS_NODEV|MS_STRICTATIME|MS_NOSUID|MS_NOEXEC,
                         };
 
-                        *(m++) = (MountEntry) {
-                                .path_const = creds_path,
-                                .mode = BIND_MOUNT,
-                                .read_only = true,
-                                .source_const = creds_path,
-                                .ignore = true,
-                        };
+                        /* If we have mount fd for credentials directory, then it will be mounted after
+                         * namespace is set up. So, here we only create the mount point. */
+
+                        if (creds_fd < 0)
+                                *(m++) = (MountEntry) {
+                                        .path_const = creds_path,
+                                        .mode = BIND_MOUNT,
+                                        .read_only = true,
+                                        .source_const = creds_path,
+                                        .ignore = true,
+                                };
+                        else
+                                *(m++) = (MountEntry) {
+                                        .path_const = creds_path,
+                                        .mode = MKDIR,
+                                };
                 } else {
                         /* If our service has no credentials store configured, then make the whole
                          * credentials tree inaccessible wholesale. */
index b6132154c5132ec0c98d83b7b3c3e82e4b1f1ba8..b8615cbd98696c10ef1020a8b412cd5915a5c3da 100644 (file)
@@ -122,6 +122,7 @@ int setup_namespace(
                 const char *tmp_dir,
                 const char *var_tmp_dir,
                 const char *creds_path,
+                int creds_fd,
                 const char *log_namespace,
                 unsigned long mount_propagation_flag,
                 VeritySettings *verity,
index 8b40f6b97637d7538dbe37441ca2144eeb88fbac..88e45c7385fc9eac4fe45b608b75030282f23b30 100644 (file)
@@ -1350,9 +1350,7 @@ fail:
         p->n_auxiliary_fds = 0;
 
 clear:
-        for (size_t i = 0; i < n; ++i)
-                free(ent[i]);
-
+        free_many((void**) ent, n);
         return r;
 }
 
index 0451a235099657dd0683f9ddf5366eae68e24115..f1b27d30d3a1da0ec515b8149b1e089107218cb7 100644 (file)
@@ -20,6 +20,7 @@
 #include "cgroup-util.h"
 #include "chase.h"
 #include "core-varlink.h"
+#include "credential.h"
 #include "dbus-unit.h"
 #include "dbus.h"
 #include "dropin.h"
@@ -207,7 +208,7 @@ static void unit_init(Unit *u) {
                         /* User manager might have its umask redefined by PAM or UMask=. In this
                          * case let the units it manages inherit this value by default. They can
                          * still tune this value through their own unit file */
-                        (void) get_process_umask(getpid_cached(), &ec->umask);
+                        (void) get_process_umask(0, &ec->umask);
                 }
         }
 
@@ -1375,31 +1376,16 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
                 r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, varlink_socket_unit, true, UNIT_DEPENDENCY_FILE);
                 if (r < 0)
                         return r;
-        } else
+        } else {
                 r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
-        if (r < 0)
-                return r;
-
-        if (exec_context_has_credentials(c) && u->manager->prefix[EXEC_DIRECTORY_RUNTIME]) {
-                _cleanup_free_ char *p = NULL, *m = NULL;
-
-                /* Let's make sure the credentials directory of this service is unmounted *after* the service
-                 * itself shuts down. This only matters if mount namespacing is not used for the service, and
-                 * hence the credentials mount appears on the host. */
-
-                p = path_join(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], "credentials", u->id);
-                if (!p)
-                        return -ENOMEM;
-
-                r = unit_name_from_path(p, ".mount", &m);
-                if (r < 0)
-                        return r;
-
-                r = unit_add_dependency_by_name(u, UNIT_AFTER, m, /* add_reference= */ true, UNIT_DEPENDENCY_FILE);
                 if (r < 0)
                         return r;
         }
 
+        r = unit_add_default_credential_dependencies(u, c);
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
@@ -2560,8 +2546,7 @@ static int unit_log_resources(Unit *u) {
         r = 0;
 
 finish:
-        for (size_t i = 0; i < n_message_parts; i++)
-                free(message_parts[i]);
+        free_many_charp(message_parts, n_message_parts);
 
         for (size_t i = 0; i < n_iovec; i++)
                 free(iovec[i].iov_base);
index b2cca785015ad03a5d5e984e52649a4101a06efd..c83ec833281a60b28ee2a8e7b81f777182667577 100644 (file)
@@ -107,7 +107,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         log_debug_errno(r, "json_variant_filter: %d/%m", r);
 
         /* I assume we can merge v with itself… */
-        r = json_variant_merge(&v, v);
+        r = json_variant_merge_object(&v, v);
         log_debug_errno(r, "json_variant_merge: %d/%m", r);
 
         r = json_variant_append_array(&v, v);
index 55323b44e10c121eff61b355c9ca0784d734c35a..56b3de4a95dffb0d0356147411f63e1e30b8f8fb 100644 (file)
@@ -903,7 +903,7 @@ static int apply_identity_changes(JsonVariant **_v) {
         if (r < 0)
                 return log_error_errno(r, "Failed to filter identity: %m");
 
-        r = json_variant_merge(&v, arg_identity_extra);
+        r = json_variant_merge_object(&v, arg_identity_extra);
         if (r < 0)
                 return log_error_errno(r, "Failed to merge identities: %m");
 
@@ -948,7 +948,7 @@ static int apply_identity_changes(JsonVariant **_v) {
                                 if (!json_variant_equal(u, mmid))
                                         continue;
 
-                                r = json_variant_merge(&add, z);
+                                r = json_variant_merge_object(&add, z);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to merge perMachine entry: %m");
 
@@ -959,7 +959,7 @@ static int apply_identity_changes(JsonVariant **_v) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to filter perMachine: %m");
 
-                        r = json_variant_merge(&add, arg_identity_extra_this_machine);
+                        r = json_variant_merge_object(&add, arg_identity_extra_this_machine);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to merge in perMachine fields: %m");
 
@@ -972,7 +972,7 @@ static int apply_identity_changes(JsonVariant **_v) {
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to filter resource limits: %m");
 
-                                r = json_variant_merge(&rlv, arg_identity_extra_rlimits);
+                                r = json_variant_merge_object(&rlv, arg_identity_extra_rlimits);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to set resource limits: %m");
 
@@ -1033,7 +1033,7 @@ static int apply_identity_changes(JsonVariant **_v) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to filter identity (privileged part): %m");
 
-                r = json_variant_merge(&privileged, arg_identity_extra_privileged);
+                r = json_variant_merge_object(&privileged, arg_identity_extra_privileged);
                 if (r < 0)
                         return log_error_errno(r, "Failed to merge identities (privileged part): %m");
 
@@ -2708,7 +2708,7 @@ static int parse_argv(int argc, char *argv[]) {
                 }
 
                 case ARG_RLIMIT: {
-                        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *jcur = NULL, *jmax = NULL;
+                        _cleanup_(json_variant_unrefp) JsonVariant *jcur = NULL, *jmax = NULL;
                         _cleanup_free_ char *field = NULL, *t = NULL;
                         const char *eq;
                         struct rlimit rl;
@@ -2764,18 +2764,15 @@ static int parse_argv(int argc, char *argv[]) {
                         if (r < 0)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate maximum integer: %m");
 
-                        r = json_build(&v,
-                                       JSON_BUILD_OBJECT(
-                                                       JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur)),
-                                                       JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax))));
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to build resource limit: %m");
-
                         t = strjoin("RLIMIT_", rlimit_to_string(l));
                         if (!t)
                                 return log_oom();
 
-                        r = json_variant_set_field(&arg_identity_extra_rlimits, t, v);
+                        r = json_variant_set_fieldb(
+                                        &arg_identity_extra_rlimits, t,
+                                        JSON_BUILD_OBJECT(
+                                                        JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur)),
+                                                        JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax))));
                         if (r < 0)
                                 return log_error_errno(r, "Failed to set %s field: %m", rlimit_to_string(l));
 
index 46d6fb589c16ddecd8a5dac207a0fd1cac0f9682..206f6480acac0a830c9a22a8d9427f1c216c4df3 100644 (file)
@@ -2562,7 +2562,7 @@ int home_augment_status(
         if (r < 0)
                 return r;
 
-        r = json_variant_merge(&m, status);
+        r = json_variant_merge_object(&m, status);
         if (r < 0)
                 return r;
 
index cabeb71f2ec9124d7eb2ff4cbc6ddde43f1abc24..cf72eaa55675c401ec9f54d25bf4d81b133a518d 100644 (file)
@@ -31,7 +31,7 @@ static int user_record_signable_json(UserRecord *ur, char **ret) {
 
 int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) {
         _cleanup_(memstream_done) MemStream m = {};
-        _cleanup_(json_variant_unrefp) JsonVariant *encoded = NULL, *v = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(user_record_unrefp) UserRecord *signed_ur = NULL;
         _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL;
         _cleanup_free_ char *text = NULL, *key = NULL;
@@ -77,15 +77,14 @@ int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) {
         if (r < 0)
                 return r;
 
-        r = json_build(&encoded, JSON_BUILD_ARRAY(
-                                       JSON_BUILD_OBJECT(JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(signature, signature_size)),
-                                                         JSON_BUILD_PAIR("key", JSON_BUILD_STRING(key)))));
-        if (r < 0)
-                return r;
-
         v = json_variant_ref(ur->json);
 
-        r = json_variant_set_field(&v, "signature", encoded);
+        r = json_variant_set_fieldb(
+                        &v,
+                        "signature",
+                        JSON_BUILD_ARRAY(
+                                        JSON_BUILD_OBJECT(JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(signature, signature_size)),
+                                                          JSON_BUILD_PAIR("key", JSON_BUILD_STRING(key)))));
         if (r < 0)
                 return r;
 
index 58f8ecc6f043752d89cf5bd7bd1a87f68de88a3e..089cbb1a1d0ee47645f8fac6f563c47863d48e61 100644 (file)
@@ -353,7 +353,7 @@ int user_record_add_binding(
                 /* Merge the new entry with an old one, if that exists */
                 be = json_variant_ref(json_variant_by_key(binding, SD_ID128_TO_STRING(mid)));
                 if (be) {
-                        r = json_variant_merge(&be, new_binding_entry);
+                        r = json_variant_merge_object(&be, new_binding_entry);
                         if (r < 0)
                                 return r;
 
index 9ef45f8e75a6c3cf1fc884312df421df59a661bb..85904aabe988c5ec1773f91dd8c40d038c4992ec 100644 (file)
@@ -1318,7 +1318,7 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
                         false,
                         UID_INVALID,
                         &c->polkit_registry,
-                        NULL);
+                        error);
         if (r == 0)
                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
 
index 76907d2e5cb4685edfda72722c4866cb9926bd28..4e7090cda93fd5fe7db536e645466a3cba837315 100644 (file)
@@ -39,7 +39,7 @@ static int help(void) {
                "   -h --help            Show this help\n"
                "      --version         Show package version\n"
                "   -c --continuous      Make systemd-bsod wait continuously\n"
-               "for changes in the journal\n"
+               "                        for changes in the journal\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -77,9 +77,9 @@ static int acquire_first_emergency_log_message(char **ret) {
 
         r = sd_journal_seek_head(j);
         if (r < 0)
-                return log_error_errno(r, "Failed to seek to start of jornal: %m");
+                return log_error_errno(r, "Failed to seek to start of journal: %m");
 
-        for(;;) {
+        for (;;) {
                 r = sd_journal_next(j);
                 if (r < 0)
                         return log_error_errno(r, "Failed to read next journal entry: %m");
@@ -92,11 +92,9 @@ static int acquire_first_emergency_log_message(char **ret) {
                         return 0;
                 }
 
-                r = sd_journal_wait(j, (uint64_t) -1);
+                r = sd_journal_wait(j, UINT64_MAX);
                 if (r < 0)
                         return log_error_errno(r, "Failed to wait for changes: %m");
-
-                continue;
         }
 
         r = sd_journal_get_data(j, "MESSAGE", &d, &l);
@@ -223,9 +221,9 @@ static int parse_argv(int argc, char * argv[]) {
         };
 
         static const struct option options[] = {
-                { "help",    no_argument, NULL, 'h'         },
-                { "version", no_argument, NULL, ARG_VERSION },
-                { "continuous",    no_argument, NULL, 'c'   },
+                { "help",       no_argument, NULL, 'h'         },
+                { "version",    no_argument, NULL, ARG_VERSION },
+                { "continuous", no_argument, NULL, 'c'         },
                 {}
         };
 
@@ -254,6 +252,7 @@ static int parse_argv(int argc, char * argv[]) {
                 default:
                         assert_not_reached();
                 }
+
         if (optind < argc)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "%s takes no argument.",
index 32d73cca828dbfb06bfb1a719927ec58c27b90d7..402522827f46563e247df9b6bfe9b9cd35a0fff5 100644 (file)
@@ -74,6 +74,7 @@ executables += [
         },
         libexec_template + {
                 'name' : 'systemd-bsod',
+                'public' : true,
                 'conditions' : ['HAVE_QRENCODE'],
                 'sources' : files('bsod.c'),
                 'link_with' : libshared,
@@ -187,12 +188,12 @@ if get_option('create-log-dirs')
                          install_mode : [ 'rwxr-xr-x', 'root', 'root' ])
         if get_option('adm-group')
                 meson.add_install_script(
-                        'sh', '-c',
+                        sh, '-c',
                         'setfacl -nm g:adm:rx,d:g:adm:rx $DESTDIR/var/log/journal || :')
         endif
         if get_option('wheel-group')
                 meson.add_install_script(
-                        'sh', '-c',
+                        sh, '-c',
                         'setfacl -nm g:wheel:rx,d:g:wheel:rx $DESTDIR/var/log/journal || :')
         endif
 endif
index 83fb6ce3408d4d79eeae58764f79cbf8302f1f17..c905704ab5e85a4446360affab180da004d17390 100644 (file)
 #define USEC_2000       ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
 
 static const char * const duid_type_table[_DUID_TYPE_MAX] = {
-        [DUID_TYPE_LLT]    = "DUID-LLT",
-        [DUID_TYPE_EN]     = "DUID-EN/Vendor",
-        [DUID_TYPE_LL]     = "DUID-LL",
-        [DUID_TYPE_UUID]   = "UUID",
-        [DUID_TYPE_CUSTOM] = "Custom",
+        [DUID_TYPE_LLT]  = "DUID-LLT",
+        [DUID_TYPE_EN]   = "DUID-EN/Vendor",
+        [DUID_TYPE_LL]   = "DUID-LL",
+        [DUID_TYPE_UUID] = "UUID",
 };
 
 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
 
-int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) {
-        struct duid d;
-
-        assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
-        if (duid_len > MAX_DUID_LEN)
-                return -EINVAL;
-
-        if (!strict)
-                /* Strict validation is not requested. We only ensure that the
-                 * DUID is not too long. */
-                return 0;
-
-        switch (duid_type) {
-        case DUID_TYPE_LLT:
-                if (duid_len <= sizeof(d.llt))
-                        return -EINVAL;
-                break;
-        case DUID_TYPE_EN:
-                if (duid_len != sizeof(d.en))
-                        return -EINVAL;
-                break;
-        case DUID_TYPE_LL:
-                if (duid_len <= sizeof(d.ll))
-                        return -EINVAL;
-                break;
-        case DUID_TYPE_UUID:
-                if (duid_len != sizeof(d.uuid))
-                        return -EINVAL;
-                break;
-        default:
-                /* accept unknown type in order to be forward compatible */
-                break;
-        }
-        return 0;
-}
-
-static int dhcp_identifier_set_duid_llt(
+int dhcp_identifier_set_duid_llt(
                 const struct hw_addr_data *hw_addr,
                 uint16_t arp_type,
                 usec_t t,
@@ -98,7 +61,7 @@ static int dhcp_identifier_set_duid_llt(
         return 0;
 }
 
-static int dhcp_identifier_set_duid_ll(
+int dhcp_identifier_set_duid_ll(
                 const struct hw_addr_data *hw_addr,
                 uint16_t arp_type,
                 struct duid *ret_duid,
@@ -151,9 +114,9 @@ int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *r
         /* a bit of snake-oil perhaps, but no need to expose the machine-id
          * directly; duid->en.id might not be aligned, so we need to copy */
         hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
-        memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id));
+        memcpy(ret_duid->en.id, &hash, sizeof(hash));
 
-        *ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id);
+        *ret_len = offsetof(struct duid, en.id) + sizeof(hash);
 
         if (test_mode)
                 assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
@@ -161,7 +124,7 @@ int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *r
         return 0;
 }
 
-static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
+int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
         sd_id128_t machine_id;
         int r;
 
@@ -180,27 +143,28 @@ static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len)
         return 0;
 }
 
-int dhcp_identifier_set_duid(
+int dhcp_identifier_set_duid_raw(
                 DUIDType duid_type,
-                const struct hw_addr_data *hw_addr,
-                uint16_t arp_type,
-                usec_t llt_time,
-                bool test_mode,
+                const uint8_t *buf,
+                size_t buf_len,
                 struct duid *ret_duid,
                 size_t *ret_len) {
 
-        switch (duid_type) {
-        case DUID_TYPE_LLT:
-                return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len);
-        case DUID_TYPE_EN:
-                return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
-        case DUID_TYPE_LL:
-                return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len);
-        case DUID_TYPE_UUID:
-                return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
-        default:
+        assert(buf || buf_len == 0);
+        assert(ret_duid);
+        assert(ret_len);
+
+        if (duid_type < 0 || duid_type > UINT16_MAX)
+                return -EINVAL;
+
+        if (buf_len > MAX_DUID_DATA_LEN)
                 return -EINVAL;
-        }
+
+        unaligned_write_be16(&ret_duid->type, duid_type);
+        memcpy_safe(ret_duid->raw.data, buf, buf_len);
+
+        *ret_len = offsetof(struct duid, raw.data) + buf_len;
+        return 0;
 }
 
 int dhcp_identifier_set_iaid(
index 1ac34761573ccdf8d6efd53a53fba588608cbda0..7b36f92e7fa33ec24f335a8416ba1472e61a892f 100644 (file)
@@ -17,15 +17,16 @@ typedef enum DUIDType {
         DUID_TYPE_EN        = 2,
         DUID_TYPE_LL        = 3,
         DUID_TYPE_UUID      = 4,
-        DUID_TYPE_CUSTOM    = 5,
         _DUID_TYPE_MAX,
         _DUID_TYPE_INVALID  = -EINVAL,
+        _DUID_TYPE_FORCE_U16 = UINT16_MAX,
 } DUIDType;
 
 /* RFC 3315 section 9.1:
  *      A DUID can be no more than 128 octets long (not including the type code).
  */
 #define MAX_DUID_LEN 128
+#define MAX_DUID_DATA_LEN (MAX_DUID_LEN - sizeof(be16_t))
 
 /* https://tools.ietf.org/html/rfc3315#section-9.1 */
 struct duid {
@@ -35,36 +36,46 @@ struct duid {
                         /* DUID_TYPE_LLT */
                         be16_t htype;
                         be32_t time;
-                        uint8_t haddr[0];
+                        uint8_t haddr[];
                 } _packed_ llt;
                 struct {
                         /* DUID_TYPE_EN */
                         be32_t pen;
-                        uint8_t id[8];
+                        uint8_t id[];
                 } _packed_ en;
                 struct {
                         /* DUID_TYPE_LL */
                         be16_t htype;
-                        uint8_t haddr[0];
+                        uint8_t haddr[];
                 } _packed_ ll;
                 struct {
                         /* DUID_TYPE_UUID */
                         sd_id128_t uuid;
                 } _packed_ uuid;
                 struct {
-                        uint8_t data[MAX_DUID_LEN];
+                        uint8_t data[MAX_DUID_DATA_LEN];
                 } _packed_ raw;
         };
 } _packed_;
 
 int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict);
-int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid(
-                DUIDType duid_type,
+int dhcp_identifier_set_duid_llt(
+                const struct hw_addr_data *hw_addr,
+                uint16_t arp_type,
+                usec_t t,
+                struct duid *ret_duid,
+                size_t *ret_len);
+int dhcp_identifier_set_duid_ll(
                 const struct hw_addr_data *hw_addr,
                 uint16_t arp_type,
-                usec_t llt_time,
-                bool test_mode,
+                struct duid *ret_duid,
+                size_t *ret_len);
+int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
+int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len);
+int dhcp_identifier_set_duid_raw(
+                DUIDType duid_type,
+                const uint8_t *buf,
+                size_t buf_len,
                 struct duid *ret_duid,
                 size_t *ret_len);
 int dhcp_identifier_set_iaid(
index f4c12ca7c4a598d0e400802c2b9540622c4a2287..470fc798709f0cace42fa755afefbfe809e7e09c 100644 (file)
@@ -10,6 +10,7 @@
 #include "sd-dhcp6-lease.h"
 
 #include "dhcp6-option.h"
+#include "dhcp6-protocol.h"
 #include "macro.h"
 #include "time-util.h"
 
index 1a268757ad29eee7248b5d68265435eff1538e38..4bb32f21283d90093598c5803c6bb910cce57f0c 100644 (file)
@@ -87,6 +87,7 @@ struct sd_radv {
         uint8_t hop_limit;
         uint8_t flags;
         uint32_t mtu;
+        uint32_t retransmit_msec;
         usec_t lifetime_usec; /* timespan */
 
         int fd;
index 272201abfdcf37b1c260eb375539345094be7fba..8f330c8f145628adab3c050b7fb86351f641c4de 100644 (file)
@@ -402,27 +402,15 @@ int sd_dhcp_client_set_client_id(
  * without further modification. Otherwise, if duid_type is supported, DUID
  * is set based on that type. Otherwise, an error is returned.
  */
-static int dhcp_client_set_iaid_duid_internal(
+static int dhcp_client_set_iaid(
                 sd_dhcp_client *client,
                 bool iaid_set,
-                uint32_t iaid,
-                DUIDType duid_type,
-                const void *duid,
-                size_t duid_len,
-                usec_t llt_time) {
+                uint32_t iaid) {
 
-        size_t len;
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
-        assert_return(duid_len == 0 || duid, -EINVAL);
-
-        if (duid) {
-                r = dhcp_validate_duid_len(duid_type, duid_len, true);
-                if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to validate length of DUID: %m");
-        }
 
         zero(client->client_id);
         client->client_id.type = 255;
@@ -437,46 +425,132 @@ static int dhcp_client_set_iaid_duid_internal(
                         return log_dhcp_client_errno(client, r, "Failed to set IAID: %m");
         }
 
-        if (duid) {
-                client->client_id.ns.duid.type = htobe16(duid_type);
-                memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
-                len = sizeof(client->client_id.ns.duid.type) + duid_len;
+        return 0;
+}
 
-        } else {
-                r = dhcp_identifier_set_duid(duid_type, &client->hw_addr,
-                                             client->arp_type, llt_time, client->test_mode,
-                                             &client->client_id.ns.duid, &len);
-                if (r == -EOPNOTSUPP)
-                        return log_dhcp_client_errno(client, r,
-                                                     "Failed to set %s. MAC address is not set or "
-                                                     "interface type is not supported.",
-                                                     duid_type_to_string(duid_type));
-                if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to set %s: %m",
-                                                     duid_type_to_string(duid_type));
-        }
+int sd_dhcp_client_set_iaid_duid_llt(
+                sd_dhcp_client *client,
+                bool iaid_set,
+                uint32_t iaid,
+                usec_t llt_time) {
+
+        size_t len;
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        if (r < 0)
+                return r;
+
+        r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->client_id.ns.duid, &len);
+        if (r < 0)
+                return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
 
         client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
 
         return 0;
 }
 
-int sd_dhcp_client_set_iaid_duid(
+int sd_dhcp_client_set_iaid_duid_ll(
                 sd_dhcp_client *client,
                 bool iaid_set,
-                uint32_t iaid,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len) {
-        return dhcp_client_set_iaid_duid_internal(client, iaid_set, iaid, duid_type, duid, duid_len, 0);
+                uint32_t iaid) {
+
+        size_t len;
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        if (r < 0)
+                return r;
+
+        r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->client_id.ns.duid, &len);
+        if (r < 0)
+                return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
+
+        client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
+
+        return 0;
 }
 
-int sd_dhcp_client_set_iaid_duid_llt(
+int sd_dhcp_client_set_iaid_duid_en(
+                sd_dhcp_client *client,
+                bool iaid_set,
+                uint32_t iaid) {
+
+        size_t len;
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        if (r < 0)
+                return r;
+
+        r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &len);
+        if (r < 0)
+                return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
+
+        client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_iaid_duid_uuid(
+                sd_dhcp_client *client,
+                bool iaid_set,
+                uint32_t iaid) {
+
+        size_t len;
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        if (r < 0)
+                return r;
+
+        r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
+        if (r < 0)
+                return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
+
+        client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
+
+        return 0;
+}
+
+int sd_dhcp_client_set_iaid_duid_raw(
                 sd_dhcp_client *client,
                 bool iaid_set,
                 uint32_t iaid,
-                usec_t llt_time) {
-        return dhcp_client_set_iaid_duid_internal(client, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
+                uint16_t duid_type,
+                const uint8_t *duid,
+                size_t duid_len) {
+
+        size_t len;
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+        assert_return(duid || duid_len == 0, -EINVAL);
+
+        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        if (r < 0)
+                return r;
+
+        r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->client_id.ns.duid, &len);
+        if (r < 0)
+                return log_dhcp_client_errno(client, r, "Failed to set DUID: %m");
+
+        client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
+
+        return 0;
 }
 
 void dhcp_client_set_test_mode(sd_dhcp_client *client, bool test_mode) {
@@ -798,21 +872,9 @@ static int client_message_init(
 
         /* If no client identifier exists, construct an RFC 4361-compliant one */
         if (client->client_id_len == 0) {
-                size_t duid_len;
-
-                client->client_id.type = 255;
-
-                r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr,
-                                             /* legacy_unstable_byteorder = */ true,
-                                             &client->client_id.ns.iaid);
-                if (r < 0)
-                        return r;
-
-                r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len);
+                r = sd_dhcp_client_set_iaid_duid_en(client, /* iaid_set = */ false, /* iaid = */ 0);
                 if (r < 0)
                         return r;
-
-                client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
         }
 
         /* Some DHCP servers will refuse to issue an DHCP lease if the Client
index 791314e9cc7fbb4eff9525703b62d1da11fb45e2..d09908b5a2e6a3edab68d812558ee1ae3b5feacd 100644 (file)
@@ -1159,8 +1159,7 @@ static char **private_options_free(char **options) {
         if (!options)
                 return NULL;
 
-        for (unsigned i = 0; i < SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1; i++)
-                free(options[i]);
+        free_many_charp(options, SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1);
 
         return mfree(options);
 }
index e8d99718b301583eef66062be2bbccb35aa6b571..8957e1cf4bfe57d2fe86a31efffd431e6a799fbf 100644 (file)
@@ -190,64 +190,70 @@ static int client_ensure_duid(sd_dhcp6_client *client) {
  * without further modification. Otherwise, if duid_type is supported, DUID
  * is set based on that type. Otherwise, an error is returned.
  */
-static int dhcp6_client_set_duid_internal(
-                sd_dhcp6_client *client,
-                DUIDType duid_type,
-                const void *duid,
-                size_t duid_len,
-                usec_t llt_time) {
+int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) {
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
-        assert_return(duid_len == 0 || duid, -EINVAL);
 
-        if (duid) {
-                r = dhcp_validate_duid_len(duid_type, duid_len, true);
-                if (r < 0) {
-                        r = dhcp_validate_duid_len(duid_type, duid_len, false);
-                        if (r < 0)
-                                return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
+        r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->duid, &client->duid_len);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
 
-                        log_dhcp6_client(client, "Using DUID of type %i of incorrect length, proceeding.", duid_type);
-                }
+        return 0;
+}
 
-                if (duid_type == DUID_TYPE_CUSTOM) {
-                        memcpy(&client->duid, duid, duid_len);
-                        client->duid_len = duid_len;
-                } else {
-                        client->duid.type = htobe16(duid_type);
-                        memcpy(&client->duid.raw.data, duid, duid_len);
-                        client->duid_len = sizeof(client->duid.type) + duid_len;
-                }
-        } else {
-                r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
-                                             client->test_mode, &client->duid, &client->duid_len);
-                if (r == -EOPNOTSUPP)
-                        return log_dhcp6_client_errno(client, r,
-                                                      "Failed to set %s. MAC address is not set or "
-                                                      "interface type is not supported.",
-                                                      duid_type_to_string(duid_type));
-                if (r < 0)
-                        return log_dhcp6_client_errno(client, r, "Failed to set %s: %m",
-                                                      duid_type_to_string(duid_type));
-        }
+int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+        r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->duid, &client->duid_len);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
 
         return 0;
 }
 
-int sd_dhcp6_client_set_duid(
-                sd_dhcp6_client *client,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len) {
-        return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
+int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+        r = dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
+
+        return 0;
 }
 
-int sd_dhcp6_client_set_duid_llt(
-                sd_dhcp6_client *client,
-                usec_t llt_time) {
-        return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
+int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+        r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+        assert_return(duid || duid_len == 0, -EINVAL);
+
+        r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->duid, &client->duid_len);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
+
+        return 0;
 }
 
 int sd_dhcp6_client_duid_as_string(
index 839c9a9ee6fc92df48eb503c79d3afcb4e2ec96a..5d4dfdef0282b187c51bd8decb2b9d322639a69d 100644 (file)
@@ -179,6 +179,7 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
 
         adv.nd_ra_type = ND_ROUTER_ADVERT;
         adv.nd_ra_curhoplimit = ra->hop_limit;
+        adv.nd_ra_retransmit = htobe32(ra->retransmit_msec);
         adv.nd_ra_flags_reserved = ra->flags;
         assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC);
         adv.nd_ra_router_lifetime = htobe16(DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC));
@@ -494,6 +495,17 @@ int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
         return 0;
 }
 
+int sd_radv_set_retransmit(sd_radv *ra, uint32_t retransmit_msec) {
+        assert_return(ra, -EINVAL);
+
+        if (ra->state != RADV_STATE_IDLE)
+                return -EBUSY;
+
+        ra->retransmit_msec = retransmit_msec;
+
+        return 0;
+}
+
 int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
         assert_return(ra, -EINVAL);
 
index 6b1842f1f8932fefb9e3ef70a0b29e33ba340db0..ccf9208d4332f56560db9673ab675a7e07b50636 100644 (file)
@@ -428,7 +428,7 @@ TEST(client_parse_message_issue_22099) {
 
         assert_se(sd_dhcp6_client_new(&client) >= 0);
         assert_se(sd_dhcp6_client_set_iaid(client, 0xcc59117b) >= 0);
-        assert_se(sd_dhcp6_client_set_duid(client, 2, duid, sizeof(duid)) >= 0);
+        assert_se(sd_dhcp6_client_set_duid_raw(client, 2, duid, sizeof(duid)) >= 0);
 
         assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0);
 }
@@ -472,7 +472,7 @@ TEST(client_parse_message_issue_24002) {
 
         assert_se(sd_dhcp6_client_new(&client) >= 0);
         assert_se(sd_dhcp6_client_set_iaid(client, 0xaabbccdd) >= 0);
-        assert_se(sd_dhcp6_client_set_duid(client, 2, duid, sizeof(duid)) >= 0);
+        assert_se(sd_dhcp6_client_set_duid_raw(client, 2, duid, sizeof(duid)) >= 0);
 
         assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0);
 }
index baf80cb6442b19ce132077a0ae261333ce84bf62..5b5b251e61ed259c5622dffd778e4a411e0d05e0 100644 (file)
@@ -1106,11 +1106,10 @@ static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);
 
 static const char* const duid_type_table[_DUID_TYPE_MAX] = {
-        [DUID_TYPE_LLT]    = "link-layer-time",
-        [DUID_TYPE_EN]     = "vendor",
-        [DUID_TYPE_LL]     = "link-layer",
-        [DUID_TYPE_UUID]   = "uuid",
-        [DUID_TYPE_CUSTOM] = "custom",
+        [DUID_TYPE_LLT]  = "link-layer-time",
+        [DUID_TYPE_EN]   = "vendor",
+        [DUID_TYPE_LL]   = "link-layer",
+        [DUID_TYPE_UUID] = "uuid",
 };
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType);
 
@@ -1155,9 +1154,17 @@ int config_parse_duid_type(
 
         type = duid_type_from_string(type_string);
         if (type < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, type,
-                           "Failed to parse DUID type '%s', ignoring.", type_string);
-                return 0;
+                uint16_t t;
+
+                r = safe_atou16(type_string, &t);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse DUID type '%s', ignoring.", type_string);
+                        return 0;
+                }
+
+                type = t;
+                assert(type == t); /* Check if type can store uint16_t. */
         }
 
         if (!isempty(p)) {
@@ -1244,7 +1251,7 @@ int config_parse_duid_rawdata(
                 void *data,
                 void *userdata) {
 
-        uint8_t raw_data[MAX_DUID_LEN];
+        uint8_t raw_data[MAX_DUID_DATA_LEN];
         unsigned count = 0;
         bool force = ltype;
         DUID *duid = ASSERT_PTR(data);
@@ -1272,7 +1279,7 @@ int config_parse_duid_rawdata(
                 if (r == 0)
                         break;
 
-                if (count >= MAX_DUID_LEN) {
+                if (count >= MAX_DUID_DATA_LEN) {
                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
                         return 0;
                 }
index 07367d4d24bfdffa241c5f6a47ff64e8ee27e835..ea942af721d5b9f40eb7d511aeaf34cb5a9ebdfe 100644 (file)
@@ -48,7 +48,7 @@ typedef struct DUID {
         DUIDType type;
 
         uint8_t raw_data_len;
-        uint8_t raw_data[MAX_DUID_LEN];
+        uint8_t raw_data[MAX_DUID_DATA_LEN];
         usec_t llt_time;
         bool set;
 } DUID;
index eeb3585f2936910c26411fad68335c748f9659ba..9dcd37e11c6ab264d438717844342d0ee67f873b 100644 (file)
@@ -1326,20 +1326,42 @@ static int dhcp4_set_client_identifier(Link *link) {
                 /* If configured, apply user specified DUID and IAID */
                 const DUID *duid = link_get_dhcp4_duid(link);
 
-                if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
-                        r = sd_dhcp_client_set_iaid_duid_llt(link->dhcp_client,
+                if (duid->raw_data_len == 0)
+                        switch (duid->type) {
+                        case DUID_TYPE_LLT:
+                                r = sd_dhcp_client_set_iaid_duid_llt(link->dhcp_client,
+                                                                     link->network->dhcp_iaid_set,
+                                                                     link->network->dhcp_iaid,
+                                                                     duid->llt_time);
+                                break;
+                        case DUID_TYPE_LL:
+                                r = sd_dhcp_client_set_iaid_duid_ll(link->dhcp_client,
+                                                                    link->network->dhcp_iaid_set,
+                                                                    link->network->dhcp_iaid);
+                                break;
+                        case DUID_TYPE_EN:
+                                r = sd_dhcp_client_set_iaid_duid_en(link->dhcp_client,
+                                                                    link->network->dhcp_iaid_set,
+                                                                    link->network->dhcp_iaid);
+                                break;
+                        case DUID_TYPE_UUID:
+                                r = sd_dhcp_client_set_iaid_duid_uuid(link->dhcp_client,
+                                                                      link->network->dhcp_iaid_set,
+                                                                      link->network->dhcp_iaid);
+                                break;
+                        default:
+                                r = sd_dhcp_client_set_iaid_duid_raw(link->dhcp_client,
+                                                                     link->network->dhcp_iaid_set,
+                                                                     link->network->dhcp_iaid,
+                                                                     duid->type, NULL, 0);
+                        }
+                else
+                        r = sd_dhcp_client_set_iaid_duid_raw(link->dhcp_client,
                                                              link->network->dhcp_iaid_set,
                                                              link->network->dhcp_iaid,
-                                                             duid->llt_time);
-                else
-                        r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
-                                                         link->network->dhcp_iaid_set,
-                                                         link->network->dhcp_iaid,
-                                                         duid->type,
-                                                         duid->raw_data_len > 0 ? duid->raw_data : NULL,
-                                                         duid->raw_data_len);
+                                                             duid->type, duid->raw_data, duid->raw_data_len);
                 if (r < 0)
-                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set IAID+DUID: %m");
+                        return r;
                 break;
         }
         case DHCP_CLIENT_ID_MAC: {
index e0f0850898872b239051c3e1c0c79e434ab6065d..95b13ca93c6191db82b419cfa8c46781441a588f 100644 (file)
@@ -556,17 +556,25 @@ static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) {
 
         duid = link_get_dhcp6_duid(link);
 
-        if (duid->type == DUID_TYPE_CUSTOM && duid->raw_data_len == 0)
-                return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
-                                            "DHCPv6 CLIENT: Missing DUID Raw Data as duid type set to 'custom': %m");
-
-        if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
-                r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
+        if (duid->raw_data_len == 0)
+                switch (duid->type) {
+                case DUID_TYPE_LLT:
+                        r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
+                        break;
+                case DUID_TYPE_LL:
+                        r = sd_dhcp6_client_set_duid_ll(client);
+                        break;
+                case DUID_TYPE_EN:
+                        r = sd_dhcp6_client_set_duid_en(client);
+                        break;
+                case DUID_TYPE_UUID:
+                        r = sd_dhcp6_client_set_duid_uuid(client);
+                        break;
+                default:
+                        r = sd_dhcp6_client_set_duid_raw(client, duid->type, NULL, 0);
+                }
         else
-                r = sd_dhcp6_client_set_duid(client,
-                                             duid->type,
-                                             duid->raw_data_len > 0 ? duid->raw_data : NULL,
-                                             duid->raw_data_len);
+                r = sd_dhcp6_client_set_duid_raw(client, duid->type, duid->raw_data, duid->raw_data_len);
         if (r < 0)
                 return r;
 
index bb83a359678c460a06a80de254ae2f86a9b75e5a..a9e33ac627ec44b2069cd2d1994bf67bb3245be3 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/nexthop.h>
 
 #include "dhcp-server-internal.h"
+#include "dhcp6-lease-internal.h"
 #include "dns-domain.h"
 #include "ip-protocol-list.h"
 #include "netif-util.h"
@@ -25,7 +26,7 @@ static int json_append_one(JsonVariant **v, const char *name, JsonVariant *w) {
         assert(v);
         assert(name);
 
-        return json_append(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT_NON_NULL(name, w)));
+        return json_variant_merge_objectb(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT_NON_NULL(name, w)));
 }
 
 static int address_build_json(Address *address, JsonVariant **ret) {
@@ -139,15 +140,11 @@ static int nexthop_group_build_json(NextHop *nexthop, JsonVariant **ret) {
         assert(ret);
 
         HASHMAP_FOREACH(g, nexthop->group) {
-                _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
-
-                r = json_build(&e, JSON_BUILD_OBJECT(
-                                        JSON_BUILD_PAIR_UNSIGNED("ID", g->id),
-                                        JSON_BUILD_PAIR_UNSIGNED("Weight", g->weight+1)));
-                if (r < 0)
-                        return r;
-
-                r = json_variant_append_array(&array, e);
+                r = json_variant_append_arrayb(
+                                &array,
+                                JSON_BUILD_OBJECT(
+                                                JSON_BUILD_PAIR_UNSIGNED("ID", g->id),
+                                                JSON_BUILD_PAIR_UNSIGNED("Weight", g->weight+1)));
                 if (r < 0)
                         return r;
         }
@@ -382,7 +379,8 @@ static int network_append_json(Network *network, JsonVariant **v) {
         if (!network)
                 return 0;
 
-        return json_append(v, JSON_BUILD_OBJECT(
+        return json_variant_merge_objectb(
+                        v, JSON_BUILD_OBJECT(
                                 JSON_BUILD_PAIR_STRING("NetworkFile", network->filename),
                                 JSON_BUILD_PAIR_STRV("NetworkFileDropins", network->dropins),
                                 JSON_BUILD_PAIR_BOOLEAN("RequiredForOnline", network->required_for_online),
@@ -421,7 +419,9 @@ static int device_append_json(sd_device *device, JsonVariant **v) {
         if (sd_device_get_property_value(device, "ID_MODEL_FROM_DATABASE", &model) < 0)
                 (void) sd_device_get_property_value(device, "ID_MODEL", &model);
 
-        return json_append(v, JSON_BUILD_OBJECT(
+        return json_variant_merge_objectb(
+                        v,
+                        JSON_BUILD_OBJECT(
                                 JSON_BUILD_PAIR_STRING_NON_EMPTY("LinkFile", link),
                                 JSON_BUILD_PAIR_STRV_NON_EMPTY("LinkFileDropins", link_dropins),
                                 JSON_BUILD_PAIR_STRING_NON_EMPTY("Path", path),
@@ -430,9 +430,6 @@ static int device_append_json(sd_device *device, JsonVariant **v) {
 }
 
 static int dns_append_json_one(Link *link, const struct in_addr_full *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) {
-        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-        int r;
-
         assert(link);
         assert(a);
         assert(array);
@@ -440,18 +437,16 @@ static int dns_append_json_one(Link *link, const struct in_addr_full *a, Network
         if (a->ifindex != 0 && a->ifindex != link->ifindex)
                 return 0;
 
-        r = json_build(&v, JSON_BUILD_OBJECT(
-                                JSON_BUILD_PAIR_INTEGER("Family", a->family),
-                                JSON_BUILD_PAIR_IN_ADDR("Address", &a->address, a->family),
-                                JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Port", a->port),
-                                JSON_BUILD_PAIR_CONDITION(a->ifindex != 0, "InterfaceIndex", JSON_BUILD_INTEGER(a->ifindex)),
-                                JSON_BUILD_PAIR_STRING_NON_EMPTY("ServerName", a->server_name),
-                                JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
-                                JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, a->family)));
-        if (r < 0)
-                return r;
-
-        return json_variant_append_array(array, v);
+        return json_variant_append_arrayb(
+                        array,
+                        JSON_BUILD_OBJECT(
+                                        JSON_BUILD_PAIR_INTEGER("Family", a->family),
+                                        JSON_BUILD_PAIR_IN_ADDR("Address", &a->address, a->family),
+                                        JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Port", a->port),
+                                        JSON_BUILD_PAIR_CONDITION(a->ifindex != 0, "InterfaceIndex", JSON_BUILD_INTEGER(a->ifindex)),
+                                        JSON_BUILD_PAIR_STRING_NON_EMPTY("ServerName", a->server_name),
+                                        JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
+                                        JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, a->family)));
 }
 
 static int dns_append_json(Link *link, JsonVariant **v) {
@@ -923,7 +918,7 @@ static int captive_portal_append_json(Link *link, JsonVariant **v) {
         if (r <= 0)
                 return r;
 
-        return json_append(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("CaptivePortal", captive_portal)));
+        return json_variant_merge_objectb(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("CaptivePortal", captive_portal)));
 }
 
 static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) {
@@ -938,23 +933,19 @@ static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) {
                 return 0;
 
         HASHMAP_FOREACH(lease, link->dhcp_server->bound_leases_by_client_id) {
-                _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
                 struct in_addr address = { .s_addr = lease->address };
 
-                r = json_build(&e,
-                               JSON_BUILD_OBJECT(
-                                               JSON_BUILD_PAIR_BYTE_ARRAY(
-                                                               "ClientId",
-                                                               lease->client_id.data,
-                                                               lease->client_id.length),
-                                               JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address),
-                                               JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname),
-                                               JSON_BUILD_PAIR_FINITE_USEC(
-                                                               "ExpirationUSec", lease->expiration)));
-                if (r < 0)
-                        return r;
-
-                r = json_variant_append_array(&array, e);
+                r = json_variant_append_arrayb(
+                                &array,
+                                JSON_BUILD_OBJECT(
+                                                JSON_BUILD_PAIR_BYTE_ARRAY(
+                                                                "ClientId",
+                                                                lease->client_id.data,
+                                                                lease->client_id.length),
+                                                JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address),
+                                                JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname),
+                                                JSON_BUILD_PAIR_FINITE_USEC(
+                                                                "ExpirationUSec", lease->expiration)));
                 if (r < 0)
                         return r;
         }
@@ -1023,6 +1014,42 @@ static int dhcp_server_append_json(Link *link, JsonVariant **v) {
         return json_append_one(v, "DHCPServer", w);
 }
 
+static int dhcp6_client_lease_append_json(Link *link, JsonVariant **v) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        int r;
+
+        assert(link);
+        assert(v);
+
+        if (!link->dhcp6_lease)
+                return 0;
+
+        r = json_build(&w, JSON_BUILD_OBJECT(
+                                JSON_BUILD_PAIR_FINITE_USEC("T1", link->dhcp6_lease->lifetime_t1),
+                                JSON_BUILD_PAIR_FINITE_USEC("T2", link->dhcp6_lease->lifetime_t2)));
+        if (r < 0)
+                return r;
+
+        return json_append_one(v, "Lease", w);
+}
+
+static int dhcp6_client_append_json(Link *link, JsonVariant **v) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        int r;
+
+        assert(link);
+        assert(v);
+
+        if (!link->dhcp6_client)
+                return 0;
+
+        r = dhcp6_client_lease_append_json(link, &w);
+        if (r < 0)
+                return r;
+
+        return json_append_one(v, "DHCPv6Client", w);
+}
+
 int link_build_json(Link *link, JsonVariant **ret) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_free_ char *type = NULL, *flags = NULL;
@@ -1138,6 +1165,10 @@ int link_build_json(Link *link, JsonVariant **ret) {
         if (r < 0)
                 return r;
 
+        r = dhcp6_client_append_json(link, &v);
+        if (r < 0)
+                return r;
+
         *ret = TAKE_PTR(v);
         return 0;
 }
index 7570d37662f67c2e1f31bb9fda79bdbdc0b1ce0e..c6ca7f95e2d3664f5956908423c1c9d4432d6fe2 100644 (file)
@@ -448,10 +448,10 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
 
 static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         _cleanup_(route_freep) Route *route = NULL;
+        unsigned prefixlen, preference;
         usec_t timestamp_usec;
         uint32_t lifetime_sec;
         struct in6_addr prefix;
-        unsigned prefixlen;
         int r;
 
         assert(link);
@@ -477,6 +477,11 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
 
+        /* Prefix Information option does not have preference, hence we use the 'main' preference here */
+        r = sd_ndisc_router_get_preference(rt, &preference);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
+
         r = route_new(&route);
         if (r < 0)
                 return log_oom();
@@ -484,6 +489,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         route->family = AF_INET6;
         route->dst.in6 = prefix;
         route->dst_prefixlen = prefixlen;
+        route->pref = preference;
         route->lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
 
         r = ndisc_request_route(TAKE_PTR(route), link, rt);
@@ -972,7 +978,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
         }
 }
 
-static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
+static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec, const struct in6_addr *router) {
         bool updated = false;
         NDiscDNSSL *dnssl;
         NDiscRDNSS *rdnss;
@@ -996,6 +1002,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (route->lifetime_usec >= timestamp_usec)
                         continue; /* the route is still valid */
 
+                if (router && !in6_addr_equal(&route->provider.in6, router))
+                        continue;
+
                 k = route_remove_and_drop(route);
                 if (k < 0)
                         r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC route, ignoring: %m");
@@ -1008,6 +1017,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (address->lifetime_valid_usec >= timestamp_usec)
                         continue; /* the address is still valid */
 
+                if (router && !in6_addr_equal(&address->provider.in6, router))
+                        continue;
+
                 k = address_remove_and_drop(address);
                 if (k < 0)
                         r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC address, ignoring: %m");
@@ -1017,6 +1029,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (rdnss->lifetime_usec >= timestamp_usec)
                         continue; /* the DNS server is still valid */
 
+                if (router && !in6_addr_equal(&rdnss->router, router))
+                        continue;
+
                 free(set_remove(link->ndisc_rdnss, rdnss));
                 updated = true;
         }
@@ -1025,6 +1040,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (dnssl->lifetime_usec >= timestamp_usec)
                         continue; /* the DNS domain is still valid */
 
+                if (router && !in6_addr_equal(&dnssl->router, router))
+                        continue;
+
                 free(set_remove(link->ndisc_dnssl, dnssl));
                 updated = true;
         }
@@ -1033,6 +1051,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (cp->lifetime_usec >= timestamp_usec)
                         continue; /* the captive portal is still valid */
 
+                if (router && !in6_addr_equal(&cp->router, router))
+                        continue;
+
                 ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp));
                 updated = true;
         }
@@ -1053,7 +1074,7 @@ static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
 
         assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
 
-        (void) ndisc_drop_outdated(link, now_usec);
+        (void) ndisc_drop_outdated(link, now_usec, NULL);
         (void) ndisc_setup_expire(link);
         return 0;
 }
@@ -1154,6 +1175,7 @@ static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) {
 }
 
 static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
+        uint16_t router_lifetime_sec;
         struct in6_addr router;
         usec_t timestamp_usec;
         int r;
@@ -1186,12 +1208,28 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
                 log_link_debug(link, "Received RA without timestamp, ignoring.");
                 return 0;
         }
+
+        r = ndisc_drop_outdated(link, timestamp_usec, NULL);
         if (r < 0)
                 return r;
 
-        r = ndisc_drop_outdated(link, timestamp_usec);
+        r = sd_ndisc_router_get_lifetime(rt, &router_lifetime_sec);
         if (r < 0)
-                return r;
+                return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
+
+         /* https://datatracker.ietf.org/doc/html/rfc4861
+          * Router Lifetime: A Lifetime of 0 indicates that the router is not a default router
+          * and SHOULD NOT appear on the default router list.
+          */
+        if (router_lifetime_sec == 0) {
+                log_link_debug(link, "Received RA with lifetime = 0, dropping configurations.");
+
+                r = ndisc_drop_outdated(link, USEC_INFINITY, &router);
+                if (r < 0)
+                        log_link_warning_errno(link, r, "Failed to process RA with zero lifetime, ignoring: %m");
+
+                return 0;
+        }
 
         r = ndisc_start_dhcp6_client(link, rt);
         if (r < 0)
index 69db429fcdd0c6b4147383e31138e0407c983868..40e78c91b3046fe0b05ab5fbf32bc8162c478903 100644 (file)
@@ -196,7 +196,7 @@ Route.GatewayOnlink,                         config_parse_route_boolean,
 Route.IPv6Preference,                        config_parse_ipv6_route_preference,                       0,                             0
 Route.Protocol,                              config_parse_route_protocol,                              0,                             0
 Route.Type,                                  config_parse_route_type,                                  0,                             0
-Route.TCPRetransmissionTimeOutSec,           config_parse_route_tcp_rto,                               0,                             0
+Route.TCPRetransmissionTimeoutSec,           config_parse_route_tcp_rto,                               0,                             0
 Route.HopLimit,                              config_parse_route_hop_limit,                             0,                             0
 Route.InitialCongestionWindow,               config_parse_route_tcp_window,                            0,                             0
 Route.InitialAdvertisedReceiveWindow,        config_parse_route_tcp_window,                            0,                             0
@@ -368,9 +368,11 @@ DHCPPrefixDelegation.Token,                  config_parse_address_generation_typ
 DHCPPrefixDelegation.RouteMetric,            config_parse_uint32,                                      0,                             offsetof(Network, dhcp_pd_route_metric)
 DHCPPrefixDelegation.NetLabel,               config_parse_string,                                      CONFIG_PARSE_STRING_SAFE,      offsetof(Network, dhcp_pd_netlabel)
 IPv6SendRA.RouterLifetimeSec,                config_parse_router_lifetime,                             0,                             offsetof(Network, router_lifetime_usec)
+IPv6SendRA.RetransmitSec,                    config_parse_router_retransmit,                           0,                             offsetof(Network, router_retransmit_usec)
 IPv6SendRA.Managed,                          config_parse_bool,                                        0,                             offsetof(Network, router_managed)
 IPv6SendRA.OtherInformation,                 config_parse_bool,                                        0,                             offsetof(Network, router_other_information)
 IPv6SendRA.RouterPreference,                 config_parse_router_preference,                           0,                             0
+IPv6SendRA.HopLimit,                         config_parse_uint8,                                       0,                             offsetof(Network, router_hop_limit)
 IPv6SendRA.EmitDNS,                          config_parse_bool,                                        0,                             offsetof(Network, router_emit_dns)
 IPv6SendRA.DNS,                              config_parse_radv_dns,                                    0,                             0
 IPv6SendRA.EmitDomains,                      config_parse_bool,                                        0,                             offsetof(Network, router_emit_domains)
index f3b3271811e6726095f0867c62b37e3fe75018f6..cb8626f008e687601200fb4ed450ae4bfb359f60 100644 (file)
@@ -227,6 +227,8 @@ struct Network {
         RADVPrefixDelegation router_prefix_delegation;
         usec_t router_lifetime_usec;
         uint8_t router_preference;
+        usec_t router_retransmit_usec;
+        uint8_t router_hop_limit;
         bool router_managed;
         bool router_other_information;
         bool router_emit_dns;
index 0f67ff09732e12717937fb8fdf5db3233a95c159..1bf6fc99d97dd537ceeeacdc823d908507d06699 100644 (file)
@@ -480,12 +480,22 @@ static int radv_configure(Link *link) {
         if (r < 0)
                 return r;
 
+        r = sd_radv_set_hop_limit(link->radv, link->network->router_hop_limit);
+        if (r < 0)
+                return r;
+
         if (link->network->router_lifetime_usec > 0) {
                 r = sd_radv_set_preference(link->radv, link->network->router_preference);
                 if (r < 0)
                         return r;
         }
 
+        if (link->network->router_retransmit_usec > 0) {
+                r = sd_radv_set_retransmit(link->radv, DIV_ROUND_UP(link->network->router_retransmit_usec, USEC_PER_MSEC));
+                if (r < 0)
+                        return r;
+        }
+
         HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
                 r = radv_set_prefix(link, p);
                 if (r < 0 && r != -EEXIST)
@@ -1305,6 +1315,49 @@ int config_parse_router_lifetime(
         return 0;
 }
 
+int config_parse_router_retransmit(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        usec_t usec, *router_retransmit_usec = ASSERT_PTR(data);
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *router_retransmit_usec = 0;
+                return 0;
+        }
+
+        r = parse_sec(rvalue, &usec);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (usec != USEC_INFINITY &&
+            DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid %s= must be in the range 0...%"PRIu32"Sec, ignoring: %s", lvalue, UINT32_MAX, rvalue);
+                return 0;
+        }
+
+        *router_retransmit_usec = usec;
+        return 0;
+}
+
 int config_parse_router_preference(
                 const char *unit,
                 const char *filename,
index ebebb3942b81d9ec9c83f64008ba69b7c6e612b1..e1c22d054f2691b3d06e21b4dac4ec2480ffe354 100644 (file)
@@ -74,6 +74,7 @@ RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
 
 CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_delegation);
 CONFIG_PARSER_PROTOTYPE(config_parse_router_lifetime);
+CONFIG_PARSER_PROTOTYPE(config_parse_router_retransmit);
 CONFIG_PARSER_PROTOTYPE(config_parse_router_preference);
 CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
 CONFIG_PARSER_PROTOTYPE(config_parse_prefix_boolean);
index 7af697c16d25ec8c7ca38014b83a9bbb7d895a0d..587bca2b26695b4f5e0a484472e518c7b674b4f8 100644 (file)
@@ -1248,7 +1248,7 @@ static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link
         }
 
         if (route->tcp_rto_usec > 0) {
-                r = sd_netlink_message_append_u32(m, RTAX_RTO_MIN, route->tcp_rto_usec / USEC_PER_MSEC);
+                r = sd_netlink_message_append_u32(m, RTAX_RTO_MIN, DIV_ROUND_UP(route->tcp_rto_usec, USEC_PER_MSEC));
                 if (r < 0)
                         return r;
         }
@@ -2573,17 +2573,17 @@ int config_parse_route_hop_limit(
         r = safe_atou32(rvalue, &k);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Could not parse route hop limit %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
+                           "Could not parse per route hop limit, ignoring assignment: %s", rvalue);
                 return 0;
         }
         if (k > 255) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Specified route hop limit %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
+                           "Specified per route hop limit \"%s\" is too large, ignoring assignment: %m", rvalue);
                 return 0;
         }
         if (k == 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Invalid route hop limit %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
+                           "Invalid per route hop limit \"%s\", ignoring assignment: %m", rvalue);
                 return 0;
         }
 
@@ -2854,14 +2854,14 @@ int config_parse_route_tcp_rto(
         r = parse_sec(rvalue, &usec);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse route TCP retransmission time out (RTO) sec '%s', ignoring: %m", rvalue);
+                           "Failed to parse route TCP retransmission timeout (RTO), ignoring assignment: %s", rvalue);
                 return 0;
         }
 
-        if (usec != USEC_INFINITY &&
-            DIV_ROUND_UP(usec, USEC_PER_SEC) > UINT32_MAX) {
+        if (IN_SET(usec, 0, USEC_INFINITY) ||
+            DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Route TCP retransmission time out (RTO) = must be in the range 0...%"PRIu32"ms, ignoring: %s", UINT32_MAX, rvalue);
+                           "Route TCP retransmission timeout (RTO) must be in the range 0…%"PRIu32"ms, ignoring assignment: %s", UINT32_MAX, rvalue);
                 return 0;
         }
 
index f6f068706a81926f86e6c5ce50cc8d8574090692..22dd991fb0e23115d940f7fcbbf4ea69fb2e4313 100644 (file)
@@ -83,9 +83,26 @@ static void test_config_parse_ether_addrs_one(const char *rvalue, const struct e
         assert_se(set_size(s) == 0);
 }
 
-#define BYTES_0_128 "0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:3e:3f:40:41:42:43:44:45:46:47:48:49:4a:4b:4c:4d:4e:4f:50:51:52:53:54:55:56:57:58:59:5a:5b:5c:5d:5e:5f:60:61:62:63:64:65:66:67:68:69:6a:6b:6c:6d:6e:6f:70:71:72:73:74:75:76:77:78:79:7a:7b:7c:7d:7e:7f:80"
-
-#define BYTES_1_128 {0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80}
+#define BYTES_0_126 \
+        "00:01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:"              \
+        "10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:"              \
+        "20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:2f:"              \
+        "30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:3e:3f:"              \
+        "40:41:42:43:44:45:46:47:48:49:4a:4b:4c:4d:4e:4f:"              \
+        "50:51:52:53:54:55:56:57:58:59:5a:5b:5c:5d:5e:5f:"              \
+        "60:61:62:63:64:65:66:67:68:69:6a:6b:6c:6d:6e:6f:"              \
+        "70:71:72:73:74:75:76:77:78:79:7a:7b:7c:7d:7e"
+
+#define BYTES_1_126 {                                                                    \
+        0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,      \
+        0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, \
+        0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, \
+        0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, \
+        0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, \
+        0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, \
+        0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, \
+        0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e       \
+}
 
 TEST(config_parse_duid_rawdata) {
         test_config_parse_duid_rawdata_one("", 0, &(DUID){});
@@ -99,8 +116,8 @@ TEST(config_parse_duid_rawdata) {
                                            &(DUID){0, 8, {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}});
         test_config_parse_duid_rawdata_one("11::", 0, &(DUID){0, 1, {0x11}});  /* FIXME: should this be an error? */
         test_config_parse_duid_rawdata_one("abcdef", 0, &(DUID){});
-        test_config_parse_duid_rawdata_one(BYTES_0_128, 0, &(DUID){});
-        test_config_parse_duid_rawdata_one(&BYTES_0_128[2], 0, &(DUID){0, 128, BYTES_1_128});
+        test_config_parse_duid_rawdata_one(BYTES_0_126, 0, &(DUID){});
+        test_config_parse_duid_rawdata_one(&BYTES_0_126[3], 0, &(DUID){0, 126, BYTES_1_126});
 }
 
 TEST(config_parse_ether_addr) {
index 89b50026db34e491bf1d0361e2af249261735dc9..b83e472245c9308f4a2a099a024c217c2ed035d9 100644 (file)
@@ -774,20 +774,22 @@ static int network_iface_pair_parse(const char* iftype, char ***l, const char *p
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "%s, interface name not valid: %s", iftype, a);
 
-                if (isempty(interface)) {
-                        if (ifprefix)
-                                b = strjoin(ifprefix, a);
-                        else
-                                b = strdup(a);
-                } else
+                /* Here, we only check the validity of the specified second name. If it is not specified,
+                 * the copied or prefixed name should be already valid, except for its length. If it is too
+                 * long, then it will be shortened later. */
+                if (!isempty(interface)) {
+                        if (!ifname_valid(interface))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "%s, interface name not valid: %s", iftype, interface);
+
                         b = strdup(interface);
+                } else if (ifprefix)
+                        b = strjoin(ifprefix, a);
+                else
+                        b = strdup(a);
                 if (!b)
                         return log_oom();
 
-                if (!ifname_valid(b))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "%s, interface name not valid: %s", iftype, b);
-
                 r = strv_consume_pair(l, TAKE_PTR(a), TAKE_PTR(b));
                 if (r < 0)
                         return log_oom();
index 198e698c794efdd59fcb894a176c0cb500562d8c..aed17cc922426d1b46ad9e164cb6b6ac4152d510 100644 (file)
@@ -2716,7 +2716,7 @@ 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, split_path_col = 15;
+        const size_t roothash_col = 14, dropin_files_col = 15, split_path_col = 16;
         bool has_roothash = false, has_dropin_files = false, has_split_path = false;
 
         if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) {
@@ -2727,6 +2727,7 @@ static int context_dump_partitions(Context *context) {
         t = table_new("type",
                       "label",
                       "uuid",
+                      "partno",
                       "file",
                       "node",
                       "offset",
@@ -2746,12 +2747,12 @@ static int context_dump_partitions(Context *context) {
         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) 9, (size_t) 12, 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) 5, (size_t) 6, (size_t) 7, (size_t) 8, (size_t) 10,
+                                                    (size_t) 11, (size_t) 13, roothash_col, dropin_files_col,
                                                     split_path_col);
         }
 
@@ -2805,6 +2806,7 @@ static int context_dump_partitions(Context *context) {
                                 TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type.uuid, uuid_buffer),
                                 TABLE_STRING, empty_to_null(label) ?: "-", TABLE_SET_COLOR, empty_to_null(label) ? NULL : ansi_grey(),
                                 TABLE_UUID, p->new_uuid_is_set ? p->new_uuid : p->current_uuid,
+                                TABLE_UINT64, p->partno,
                                 TABLE_PATH_BASENAME, p->definition_path, TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
                                 TABLE_STRING, partname ?: "-", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
                                 TABLE_UINT64, p->offset,
@@ -2842,6 +2844,7 @@ static int context_dump_partitions(Context *context) {
                                 TABLE_EMPTY,
                                 TABLE_EMPTY,
                                 TABLE_EMPTY,
+                                TABLE_EMPTY,
                                 TABLE_STRING, a,
                                 TABLE_EMPTY,
                                 TABLE_EMPTY,
index 3805150d9811b8504f780d3d8e128b08e156e5eb..7a1a1620afff8ed45077dd58ecfcf393c80b3210 100644 (file)
@@ -1407,7 +1407,7 @@ int dns_cache_dump_to_json(DnsCache *cache, JsonVariant **ret) {
                         _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
 
                         LIST_FOREACH(by_key, j, i) {
-                                _cleanup_(json_variant_unrefp) JsonVariant *rj = NULL, *item = NULL;
+                                _cleanup_(json_variant_unrefp) JsonVariant *rj = NULL;
 
                                 assert(j->rr);
 
@@ -1419,13 +1419,11 @@ int dns_cache_dump_to_json(DnsCache *cache, JsonVariant **ret) {
                                 if (r < 0)
                                         return r;
 
-                                r = json_build(&item, JSON_BUILD_OBJECT(
-                                                               JSON_BUILD_PAIR_VARIANT("rr", rj),
-                                                               JSON_BUILD_PAIR_BASE64("raw", j->rr->wire_format, j->rr->wire_format_size)));
-                                if (r < 0)
-                                        return r;
-
-                                r = json_variant_append_array(&l, item);
+                                r = json_variant_append_arrayb(
+                                                &l,
+                                                JSON_BUILD_OBJECT(
+                                                                JSON_BUILD_PAIR_VARIANT("rr", rj),
+                                                                JSON_BUILD_PAIR_BASE64("raw", j->rr->wire_format, j->rr->wire_format_size)));
                                 if (r < 0)
                                         return r;
                         }
index 43e7b95e1796f28479a772a9c71ebb02835d9a63..ed3800ed8aaa5a84abe5443fb3f48d6c7e68ed36 100644 (file)
@@ -1134,7 +1134,7 @@ int manager_monitor_send(
                 return log_error_errno(r, "Failed to convert question to JSON: %m");
 
         DNS_ANSWER_FOREACH_ITEM(rri, answer) {
-                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
+                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
 
                 r = dns_resource_record_to_json(rri->rr, &v);
                 if (r < 0)
@@ -1144,14 +1144,12 @@ int manager_monitor_send(
                 if (r < 0)
                         return log_error_errno(r, "Failed to generate RR wire format: %m");
 
-                r = json_build(&w, JSON_BUILD_OBJECT(
-                                               JSON_BUILD_PAIR_CONDITION(v, "rr", JSON_BUILD_VARIANT(v)),
-                                               JSON_BUILD_PAIR("raw", JSON_BUILD_BASE64(rri->rr->wire_format, rri->rr->wire_format_size)),
-                                               JSON_BUILD_PAIR_CONDITION(rri->ifindex > 0, "ifindex", JSON_BUILD_INTEGER(rri->ifindex))));
-                if (r < 0)
-                        return log_error_errno(r, "Failed to make answer RR object: %m");
-
-                r = json_variant_append_array(&janswer, w);
+                r = json_variant_append_arrayb(
+                                &janswer,
+                                JSON_BUILD_OBJECT(
+                                                JSON_BUILD_PAIR_CONDITION(v, "rr", JSON_BUILD_VARIANT(v)),
+                                                JSON_BUILD_PAIR("raw", JSON_BUILD_BASE64(rri->rr->wire_format, rri->rr->wire_format_size)),
+                                                JSON_BUILD_PAIR_CONDITION(rri->ifindex > 0, "ifindex", JSON_BUILD_INTEGER(rri->ifindex))));
                 if (r < 0)
                         return log_debug_errno(r, "Failed to append notification entry to array: %m");
         }
index b27cf904357ab6070236b112a2b06db38b863462..1ce1a558763ae715c0c042d6a943476f10061d9f 100644 (file)
@@ -427,7 +427,6 @@ static void vl_method_resolve_address_complete(DnsQuery *query) {
         question = dns_query_question_for_protocol(q, q->answer_protocol);
 
         DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
-                _cleanup_(json_variant_unrefp) JsonVariant *entry = NULL;
                 _cleanup_free_ char *normalized = NULL;
 
                 r = dns_question_matches_rr(question, rr, NULL);
@@ -440,14 +439,11 @@ static void vl_method_resolve_address_complete(DnsQuery *query) {
                 if (r < 0)
                         goto finish;
 
-                r = json_build(&entry,
-                               JSON_BUILD_OBJECT(
-                                               JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)),
-                                               JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized))));
-                if (r < 0)
-                        goto finish;
-
-                r = json_variant_append_array(&array, entry);
+                r = json_variant_append_arrayb(
+                                &array,
+                                JSON_BUILD_OBJECT(
+                                                JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)),
+                                                JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized))));
                 if (r < 0)
                         goto finish;
         }
index 6459f5d2f870aeb565c56f667c11042ac9bdb0e8..8d16b36be156dab35432a7c6ba0ef25906cb7514 100644 (file)
@@ -1428,7 +1428,8 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
                                         return log_oom();
                         }
 
-                        r = json_append(&v, JSON_BUILD_OBJECT(
+                        r = json_variant_merge_objectb(
+                                        &v, JSON_BUILD_OBJECT(
                                                        JSON_BUILD_PAIR("type", JSON_BUILD_STRING(boot_entry_type_json_to_string(e->type))),
                                                        JSON_BUILD_PAIR_CONDITION(e->id, "id", JSON_BUILD_STRING(e->id)),
                                                        JSON_BUILD_PAIR_CONDITION(e->path, "path", JSON_BUILD_STRING(e->path)),
@@ -1451,7 +1452,8 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
                         /* Sanitizers (only memory sanitizer?) do not like function call with too many
                          * arguments and trigger false positive warnings. Let's not add too many json objects
                          * at once. */
-                        r = json_append(&v, JSON_BUILD_OBJECT(
+                        r = json_variant_merge_objectb(
+                                        &v, JSON_BUILD_OBJECT(
                                                        JSON_BUILD_PAIR("isReported", JSON_BUILD_BOOLEAN(e->reported_by_loader)),
                                                        JSON_BUILD_PAIR_CONDITION(e->tries_left != UINT_MAX, "triesLeft", JSON_BUILD_UNSIGNED(e->tries_left)),
                                                        JSON_BUILD_PAIR_CONDITION(e->tries_done != UINT_MAX, "triesDone", JSON_BUILD_UNSIGNED(e->tries_done)),
index 3ff2726d4ab08a0e64b4336661007299f0851d5d..904b897984a52d7aa449bbec3ff7456c96c75d79 100644 (file)
@@ -480,6 +480,7 @@ int bus_verify_polkit_async(
         assert(call);
         assert(action);
         assert(registry);
+        assert(ret_error);
 
         r = check_good_user(call, good_user);
         if (r != 0)
index 12c805f449d0b5b3939b4a8c240cdc6e72a47b0e..8acf3c232b857761f7230882cebbf6ec5c6212f8 100644 (file)
@@ -111,27 +111,10 @@ int dissect_fstype_ok(const char *fstype) {
 
 int probe_sector_size(int fd, uint32_t *ret) {
 
-        struct gpt_header {
-                char signature[8];
-                le32_t revision;
-                le32_t header_size;
-                le32_t crc32;
-                le32_t reserved;
-                le64_t my_lba;
-                le64_t alternate_lba;
-                le64_t first_usable_lba;
-                le64_t last_usable_lba;
-                sd_id128_t disk_guid;
-                le64_t partition_entry_lba;
-                le32_t number_of_partition_entries;
-                le32_t size_of_partition_entry;
-                le32_t partition_entry_array_crc32;
-        } _packed_;
-
         /* Disk images might be for 512B or for 4096 sector sizes, let's try to auto-detect that by searching
          * for the GPT headers at the relevant byte offsets */
 
-        assert_cc(sizeof(struct gpt_header) == 92);
+        assert_cc(sizeof(GptHeader) == 92);
 
         /* We expect a sector size in the range 512…4096. The GPT header is located in the second
          * sector. Hence it could be at byte 512 at the earliest, and at byte 4096 at the latest. And we must
@@ -151,24 +134,12 @@ int probe_sector_size(int fd, uint32_t *ret) {
 
         /* Let's see if we find the GPT partition header with various expected sector sizes */
         for (uint32_t sz = 512; sz <= 4096; sz <<= 1) {
-                struct gpt_header *p;
+                const GptHeader *p;
 
                 assert(sizeof(sectors) >= sz * 2);
-                p = (struct gpt_header*) (sectors + sz);
-
-                if (memcmp(p->signature, (const char[8]) { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }, 8) != 0)
-                        continue;
-
-                if (le32toh(p->revision) != UINT32_C(0x00010000)) /* the only known revision of the spec: 1.0 */
-                        continue;
-
-                if (le32toh(p->header_size) < sizeof(struct gpt_header))
-                        continue;
-
-                if (le32toh(p->header_size) > 4096) /* larger than a sector? something is off… */
-                        continue;
+                p = (const GptHeader*) (sectors + sz);
 
-                if (le64toh(p->my_lba) != 1) /* this sector must claim to be at sector offset 1 */
+                if (!gpt_header_has_signature(p))
                         continue;
 
                 if (found != 0)
index da095935e52e73c50c2aa503c40030dd0ada0863..7428152faa3f635b01d95d7333aa250eb8510230 100644 (file)
@@ -403,7 +403,7 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e
                         /* If we have a build-id, merge it in the same JSON object so that it appears all
                          * nicely together in the logs/metadata. */
                         if (id_json) {
-                                r = json_variant_merge(&v, id_json);
+                                r = json_variant_merge_object(&v, id_json);
                                 if (r < 0)
                                         return log_error_errno(r, "json_variant_merge of package meta with buildId failed: %m");
                         }
@@ -419,7 +419,7 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e
                         if (r < 0)
                                 return log_error_errno(r, "Failed to build JSON object: %m");
 
-                        r = json_variant_merge(c->package_metadata, w);
+                        r = json_variant_merge_object(c->package_metadata, w);
                         if (r < 0)
                                 return log_error_errno(r, "json_variant_merge of package meta with buildId failed: %m");
 
@@ -712,7 +712,7 @@ static int parse_elf(int fd, const char *executable, char **ret, JsonVariant **r
                 if (r < 0)
                         return log_warning_errno(r, "Failed to build JSON object: %m");
 
-                r = json_variant_merge(&elf_metadata, json_architecture);
+                r = json_variant_merge_object(&elf_metadata, json_architecture);
                 if (r < 0)
                         return log_warning_errno(r, "Failed to merge JSON objects: %m");
 
@@ -722,7 +722,7 @@ static int parse_elf(int fd, const char *executable, char **ret, JsonVariant **r
 #endif
 
         /* We always at least have the ELF type, so merge that (and possibly the arch). */
-        r = json_variant_merge(&elf_metadata, package_metadata);
+        r = json_variant_merge_object(&elf_metadata, package_metadata);
         if (r < 0)
                 return log_warning_errno(r, "Failed to merge JSON objects: %m");
 
index dd96261888c07f5fddfb4e4c5c169c4c5cd5731c..2846bad5b802a76ec572adac72a61ed9a96beb99 100644 (file)
@@ -273,7 +273,7 @@ Architecture gpt_partition_type_uuid_to_arch(sd_id128_t id) {
 int gpt_partition_label_valid(const char *s) {
         _cleanup_free_ char16_t *recoded = NULL;
 
-        recoded = utf8_to_utf16(s, strlen(s));
+        recoded = utf8_to_utf16(s, SIZE_MAX);
         if (!recoded)
                 return -ENOMEM;
 
@@ -336,3 +336,24 @@ bool gpt_partition_type_knows_no_auto(GptPartitionType type) {
                       PARTITION_XBOOTLDR,
                       PARTITION_SWAP);
 }
+
+bool gpt_header_has_signature(const GptHeader *p) {
+        assert(p);
+
+        if (memcmp(p->signature, (const char[8]) { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }, 8) != 0)
+                return false;
+
+        if (le32toh(p->revision) != UINT32_C(0x00010000)) /* the only known revision of the spec: 1.0 */
+                return false;
+
+        if (le32toh(p->header_size) < sizeof(GptHeader))
+                return false;
+
+        if (le32toh(p->header_size) > 4096) /* larger than a sector? something is off… */
+                return false;
+
+        if (le64toh(p->my_lba) != 1) /* this sector must claim to be at sector offset 1 */
+                return false;
+
+        return true;
+}
index 8623a8664e5413bdb368bfc449e8b68f81d80ae2..21976e5311f95d9e3bbabf1c36afc86c6c800f79 100644 (file)
@@ -1,11 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <uchar.h>
+
 #include "sd-gpt.h"
 #include "sd-id128.h"
 
 #include "architecture.h"
 #include "id128-util.h"
+#include "sparse-endian.h"
 
 /* maximum length of gpt label */
 #define GPT_LABEL_MAX 36
@@ -69,3 +72,31 @@ const char *gpt_partition_type_mountpoint_nulstr(GptPartitionType type);
 bool gpt_partition_type_knows_read_only(GptPartitionType type);
 bool gpt_partition_type_knows_growfs(GptPartitionType type);
 bool gpt_partition_type_knows_no_auto(GptPartitionType type);
+
+typedef struct {
+        uint8_t partition_type_guid[16];
+        uint8_t unique_partition_guid[16];
+        le64_t starting_lba;
+        le64_t ending_lba;
+        le64_t attributes;
+        char16_t partition_name[36];
+} _packed_ GptPartitionEntry;
+
+typedef struct {
+        char signature[8];
+        le32_t revision;
+        le32_t header_size;
+        le32_t crc32;
+        le32_t reserved;
+        le64_t my_lba;
+        le64_t alternate_lba;
+        le64_t first_usable_lba;
+        le64_t last_usable_lba;
+        uint8_t disk_guid[16];
+        le64_t partition_entry_lba;
+        le32_t number_of_partition_entries;
+        le32_t size_of_partition_entry;
+        le32_t partition_entry_array_crc32;
+} _packed_ GptHeader;
+
+bool gpt_header_has_signature(const GptHeader *p);
index a6e23d82cd2e6ce32209473418fea5eda5fe8959..e17f89564d64079fea2baa84ca3c7ce29b56b6de 100644 (file)
@@ -1958,6 +1958,20 @@ int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *valu
         return 1;
 }
 
+int json_variant_set_fieldb(JsonVariant **v, const char *field, ...) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        va_list ap;
+        int r;
+
+        va_start(ap, field);
+        r = json_buildv(&w, ap);
+        va_end(ap);
+        if (r < 0)
+                return r;
+
+        return json_variant_set_field(v, field, w);
+}
+
 int json_variant_set_field_string(JsonVariant **v, const char *field, const char *value) {
         _cleanup_(json_variant_unrefp) JsonVariant *m = NULL;
         int r;
@@ -2013,7 +2027,7 @@ int json_variant_set_field_strv(JsonVariant **v, const char *field, char **l) {
         return json_variant_set_field(v, field, m);
 }
 
-int json_variant_merge(JsonVariant **v, JsonVariant *m) {
+int json_variant_merge_object(JsonVariant **v, JsonVariant *m) {
         _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
         _cleanup_free_ JsonVariant **array = NULL;
         size_t v_elements, m_elements, k;
@@ -2076,6 +2090,20 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) {
         return 1;
 }
 
+int json_variant_merge_objectb(JsonVariant **v, ...) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        va_list ap;
+        int r;
+
+        va_start(ap, v);
+        r = json_buildv(&w, ap);
+        va_end(ap);
+        if (r < 0)
+                return r;
+
+        return json_variant_merge_object(v, w);
+}
+
 int json_variant_append_array(JsonVariant **v, JsonVariant *element) {
         _cleanup_(json_variant_unrefp) JsonVariant *nv = NULL;
         bool blank;
@@ -2141,6 +2169,20 @@ int json_variant_append_array(JsonVariant **v, JsonVariant *element) {
         return 0;
 }
 
+int json_variant_append_arrayb(JsonVariant **v, ...) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        va_list ap;
+        int r;
+
+        va_start(ap, v);
+        r = json_buildv(&w, ap);
+        va_end(ap);
+        if (r < 0)
+                return r;
+
+        return json_variant_append_array(v, w);
+}
+
 JsonVariant *json_variant_find(JsonVariant *haystack, JsonVariant *needle) {
         JsonVariant *i;
 
@@ -4223,30 +4265,6 @@ int json_build(JsonVariant **ret, ...) {
         return r;
 }
 
-int json_appendv(JsonVariant **v, va_list ap) {
-        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
-        int r;
-
-        assert(v);
-
-        r = json_buildv(&w, ap);
-        if (r < 0)
-                return r;
-
-        return json_variant_merge(v, w);
-}
-
-int json_append(JsonVariant **v, ...) {
-        va_list ap;
-        int r;
-
-        va_start(ap, v);
-        r = json_appendv(v, ap);
-        va_end(ap);
-
-        return r;
-}
-
 int json_log_internal(
                 JsonVariant *variant,
                 int level,
index 3f86222f2ab600aedfea063acfa28696a8c0bda5..1700d1d8fa2d56073902802c9d49e838a5399444 100644 (file)
@@ -204,6 +204,7 @@ int json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char
 int json_variant_filter(JsonVariant **v, char **to_remove);
 
 int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *value);
+int json_variant_set_fieldb(JsonVariant **v, const char *field, ...);
 int json_variant_set_field_string(JsonVariant **v, const char *field, const char *value);
 int json_variant_set_field_integer(JsonVariant **v, const char *field, int64_t value);
 int json_variant_set_field_unsigned(JsonVariant **v, const char *field, uint64_t value);
@@ -213,9 +214,11 @@ int json_variant_set_field_strv(JsonVariant **v, const char *field, char **l);
 JsonVariant *json_variant_find(JsonVariant *haystack, JsonVariant *needle);
 
 int json_variant_append_array(JsonVariant **v, JsonVariant *element);
+int json_variant_append_arrayb(JsonVariant **v, ...);
 int json_variant_append_array_nodup(JsonVariant **v, JsonVariant *element);
 
-int json_variant_merge(JsonVariant **v, JsonVariant *m);
+int json_variant_merge_object(JsonVariant **v, JsonVariant *m);
+int json_variant_merge_objectb(JsonVariant **v, ...);
 
 int json_variant_strv(JsonVariant *v, char ***ret);
 
@@ -349,9 +352,6 @@ enum {
 
 int json_build(JsonVariant **ret, ...);
 int json_buildv(JsonVariant **ret, va_list ap);
- /* These two functions below are equivalent to json_build() (or json_buildv()) and json_variant_merge(). */
-int json_append(JsonVariant **v, ...);
-int json_appendv(JsonVariant **v, va_list ap);
 
 /* A bitmask of flags used by the dispatch logic. Note that this is a combined bit mask, that is generated from the bit
  * mask originally passed into json_dispatch(), the individual bitmask associated with the static JsonDispatch callout
index 2930e8c3547dbbb324b1f829cf660ab2e87ae00e..2c18ab43dd8d89dc5bceda1c5dd8669ab13dacf0 100644 (file)
@@ -344,13 +344,11 @@ shared_fdisk_sources = files(
         'fdisk-util.c',
 )
 
-if get_option('fdisk') != 'false'
-        libshared_fdisk = static_library(
-                'shared-fdisk',
-                shared_fdisk_sources,
-                include_directories : includes,
-                dependencies : [libfdisk,
-                                userspace],
-                c_args : ['-fvisibility=default'],
-                build_by_default : false)
-endif
+libshared_fdisk = static_library(
+        'shared-fdisk',
+        shared_fdisk_sources,
+        include_directories : includes,
+        dependencies : [libfdisk,
+                        userspace],
+        c_args : ['-fvisibility=default'],
+        build_by_default : false)
index f0bf821430613ffd6c90314d3d44fc01a6709277..20f93b7c8930354ed8b7a680df7ae7d080efa422 100644 (file)
@@ -1235,7 +1235,6 @@ static void sub_mount_drop(SubMount *s, size_t n) {
 
 static int get_sub_mounts(
                 const char *prefix,
-                bool clone_tree,
                 SubMount **ret_mounts,
                 size_t *ret_n_mounts) {
         _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
@@ -1287,10 +1286,7 @@ static int get_sub_mounts(
                         continue;
                 }
 
-                if (clone_tree)
-                        mount_fd = open_tree(AT_FDCWD, path, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_RECURSIVE);
-                else
-                        mount_fd = open(path, O_CLOEXEC|O_PATH);
+                mount_fd = open(path, O_CLOEXEC|O_PATH);
                 if (mount_fd < 0) {
                         if (errno == ENOENT) /* The path may be hidden by another over-mount or already unmounted. */
                                 continue;
@@ -1319,66 +1315,6 @@ static int get_sub_mounts(
         return 0;
 }
 
-static int move_sub_mounts(SubMount *mounts, size_t n) {
-        assert(mounts || n == 0);
-
-        for (size_t i = 0; i < n; i++) {
-                if (!mounts[i].path || mounts[i].mount_fd < 0)
-                        continue;
-
-                (void) mkdir_p_label(mounts[i].path, 0755);
-
-                if (move_mount(mounts[i].mount_fd, "", AT_FDCWD, mounts[i].path, MOVE_MOUNT_F_EMPTY_PATH) < 0)
-                        return log_debug_errno(errno, "Failed to move mount_fd to '%s': %m", mounts[i].path);
-        }
-
-        return 0;
-}
-
-int remount_and_move_sub_mounts(
-                const char *what,
-                const char *where,
-                const char *type,
-                unsigned long flags,
-                const char *options) {
-
-        SubMount *mounts = NULL;
-        size_t n = 0;
-        int r;
-
-        CLEANUP_ARRAY(mounts, n, sub_mount_array_free);
-
-        assert(where);
-
-        /* This is useful when creating a new network namespace. Unlike procfs, we need to remount sysfs,
-         * otherwise properties of the network interfaces in the main network namespace are still accessible
-         * through the old sysfs, e.g. /sys/class/net/eth0. All sub-mounts previously mounted on the sysfs
-         * are moved onto the new sysfs mount. */
-
-        r = path_is_mount_point(where, NULL, 0);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to determine if '%s' is a mountpoint: %m", where);
-        if (r == 0)
-                /* Shortcut. Simply mount the requested filesystem. */
-                return mount_nofollow_verbose(LOG_DEBUG, what, where, type, flags, options);
-
-        /* Get the list of sub-mounts and duplicate them. */
-        r = get_sub_mounts(where, /* clone_tree= */ true, &mounts, &n);
-        if (r < 0)
-                return r;
-
-        /* Then, remount the mount and its sub-mounts. */
-        (void) umount_recursive(where, 0);
-
-        /* Remount the target filesystem. */
-        r = mount_nofollow_verbose(LOG_DEBUG, what, where, type, flags, options);
-        if (r < 0)
-                return r;
-
-        /* Finally, move the all sub-mounts on the new target mount point. */
-        return move_sub_mounts(mounts, n);
-}
-
 int bind_mount_submounts(
                 const char *source,
                 const char *target) {
@@ -1395,7 +1331,7 @@ int bind_mount_submounts(
 
         CLEANUP_ARRAY(mounts, n, sub_mount_array_free);
 
-        r = get_sub_mounts(source, /* clone_tree= */ false, &mounts, &n);
+        r = get_sub_mounts(source, &mounts, &n);
         if (r < 0)
                 return r;
 
@@ -1430,10 +1366,6 @@ int bind_mount_submounts(
         return ret;
 }
 
-int remount_sysfs(const char *where) {
-        return remount_and_move_sub_mounts("sysfs", where, "sysfs", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
-}
-
 int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mode_t mode) {
         assert(st);
         assert(dest);
index 7ee6750044bca8e358409d2ad33af589bc07a821..fb496e156b2c94c237d1ac4658be587db84cd79a 100644 (file)
@@ -124,14 +124,6 @@ int make_userns(uid_t uid_shift, uid_t uid_range, uid_t owner, RemountIdmapping
 int remount_idmap_fd(const char *p, int userns_fd);
 int remount_idmap(const char *p, uid_t uid_shift, uid_t uid_range, uid_t owner, RemountIdmapping idmapping);
 
-int remount_and_move_sub_mounts(
-                const char *what,
-                const char *where,
-                const char *type,
-                unsigned long flags,
-                const char *options);
-int remount_sysfs(const char *where);
-
 int bind_mount_submounts(
                 const char *source,
                 const char *target);
index 533fd0f0d387b3eef3e13fdf56c781b823bb803f..a2d48fa03687f5f54dbff43f981b36824c8b8479 100644 (file)
@@ -64,7 +64,7 @@ static int load_user(
                 else if (r < 0)
                         return r;
                 else {
-                        r = json_variant_merge(&v, privileged_v);
+                        r = json_variant_merge_object(&v, privileged_v);
                         if (r < 0)
                                 return r;
 
@@ -208,7 +208,7 @@ static int load_group(
                 else if (r < 0)
                         return r;
                 else {
-                        r = json_variant_merge(&v, privileged_v);
+                        r = json_variant_merge_object(&v, privileged_v);
                         if (r < 0)
                                 return r;
 
index 2d19172c6f263981aabd85803760669d36f28ec9..a01095288cc5a6d611412ba1bf15c6f9f0cb3064 100644 (file)
@@ -46,15 +46,73 @@ bool running_in_chroot_or_offline(void) {
 }
 
 const Verb* verbs_find_verb(const char *name, const Verb verbs[]) {
+        assert(verbs);
+
         for (size_t i = 0; verbs[i].dispatch; i++)
-                if (streq_ptr(name, verbs[i].verb) ||
-                    (!name && FLAGS_SET(verbs[i].flags, VERB_DEFAULT)))
-                        return &verbs[i];
+                if (name ? streq(name, verbs[i].verb) : FLAGS_SET(verbs[i].flags, VERB_DEFAULT))
+                        return verbs + i;
 
         /* At the end of the list? */
         return NULL;
 }
 
+static const Verb* verbs_find_prefix_verb(const char *name, const Verb verbs[]) {
+        size_t best_distance = SIZE_MAX;
+        const Verb *best = NULL;
+
+        assert(verbs);
+
+        if (!name)
+                return NULL;
+
+        for (size_t i = 0; verbs[i].dispatch; i++) {
+                const char *e;
+                size_t l;
+
+                e = startswith(verbs[i].verb, name);
+                if (!e)
+                        continue;
+
+                l = strlen(e);
+                if (l < best_distance) {
+                        best_distance = l;
+                        best = verbs + i;
+                }
+        }
+
+        return best;
+}
+
+static const Verb* verbs_find_closest_verb(const char *name, const Verb verbs[]) {
+        ssize_t best_distance = SSIZE_MAX;
+        const Verb *best = NULL;
+
+        assert(verbs);
+
+        if (!name)
+                return NULL;
+
+        for (size_t i = 0; verbs[i].dispatch; i++) {
+                ssize_t distance;
+
+                distance = strlevenshtein(verbs[i].verb, name);
+                if (distance < 0) {
+                        log_debug_errno(distance, "Failed to determine Levenshtein distance between %s and %s: %m", verbs[i].verb, name);
+                        return NULL;
+                }
+
+                if (distance > 5) /* If the distance is just too far off, don't make a bad suggestion */
+                        continue;
+
+                if (distance < best_distance) {
+                        best_distance = distance;
+                        best = verbs + i;
+                }
+        }
+
+        return best;
+}
+
 int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
         const Verb *verb;
         const char *name;
@@ -73,12 +131,21 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
 
         verb = verbs_find_verb(name, verbs);
         if (!verb) {
-                if (name)
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Unknown command verb %s.", name);
-                else
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Command verb required.");
+                if (name) {
+                        /* Be helperful to the user, and give a hint what the user might have wanted to
+                         * type. We search with two mechanisms: a simple prefix match and â€“ if that didn't
+                         * yield results â€“, a Levenshtein word distance based match. */
+                        verb = verbs_find_prefix_verb(name, verbs);
+                        if (!verb)
+                                verb = verbs_find_closest_verb(name, verbs);
+                        if (verb)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Unknown command verb '%s', did you mean '%s'?", name, verb->verb);
+
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command verb '%s'.", name);
+                }
+
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Command verb required.");
         }
 
         if (!name)
@@ -86,27 +153,19 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
 
         if (verb->min_args != VERB_ANY &&
             (unsigned) left < verb->min_args)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Too few arguments.");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too few arguments.");
 
         if (verb->max_args != VERB_ANY &&
             (unsigned) left > verb->max_args)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Too many arguments.");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many arguments.");
 
         if ((verb->flags & VERB_ONLINE_ONLY) && running_in_chroot_or_offline()) {
                 log_info("Running in chroot, ignoring command '%s'", name ?: verb->verb);
                 return 0;
         }
 
-        if (name)
-                return verb->dispatch(left, argv, userdata);
-        else {
-                char* fake[2] = {
-                        (char*) verb->verb,
-                        NULL
-                };
+        if (!name)
+                return verb->dispatch(1, STRV_MAKE(verb->verb), userdata);
 
-                return verb->dispatch(1, fake, userdata);
+        return verb->dispatch(left, argv, userdata);
         }
-}
index 98640c026b3215f144b73d08acb436ef8c260e51..fbb6bcdc83ca2a569ad859c5728e648d2634a896 100644 (file)
@@ -223,7 +223,7 @@ static int need_reload(void) {
 }
 
 static int daemon_reload(void) {
-        sd_bus *bus;
+         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
 
         r = bus_connect_system_systemd(&bus);
index 7339bb687248c07b80e22fb3fa9337f96ea6a778..ddd445c9f0556ce69b9c2647fd33e7e9f7a85af2 100644 (file)
@@ -257,18 +257,30 @@ int sd_dhcp_client_set_client_id(
                 uint8_t type,
                 const uint8_t *data,
                 size_t data_len);
-__extension__ int sd_dhcp_client_set_iaid_duid(
+__extension__ int sd_dhcp_client_set_iaid_duid_llt(
                 sd_dhcp_client *client,
                 bool iaid_set,
                 uint32_t iaid,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len);
-__extension__ int sd_dhcp_client_set_iaid_duid_llt(
+                uint64_t llt_time);
+__extension__ int sd_dhcp_client_set_iaid_duid_ll(
+                sd_dhcp_client *client,
+                bool iaid_set,
+                uint32_t iaid);
+__extension__ int sd_dhcp_client_set_iaid_duid_en(
+                sd_dhcp_client *client,
+                bool iaid_set,
+                uint32_t iaid);
+__extension__ int sd_dhcp_client_set_iaid_duid_uuid(
+                sd_dhcp_client *client,
+                bool iaid_set,
+                uint32_t iaid);
+__extension__ int sd_dhcp_client_set_iaid_duid_raw(
                 sd_dhcp_client *client,
                 bool iaid_set,
                 uint32_t iaid,
-                uint64_t llt_time);
+                uint16_t duid_type,
+                const uint8_t *duid,
+                size_t duid_len);
 int sd_dhcp_client_get_client_id(
                 sd_dhcp_client *client,
                 uint8_t *ret_type,
index a9fa78569d22097b7a353989216c181ed444ba82..c85c5652c485ccb891d80a329e18e092e0656a5c 100644 (file)
@@ -211,14 +211,11 @@ int sd_dhcp6_client_set_mac(
                 const uint8_t *addr,
                 size_t addr_len,
                 uint16_t arp_type);
-int sd_dhcp6_client_set_duid(
-                sd_dhcp6_client *client,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len);
-int sd_dhcp6_client_set_duid_llt(
-                sd_dhcp6_client *client,
-                uint64_t llt_time);
+int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time);
+int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len);
 int sd_dhcp6_client_set_iaid(
                 sd_dhcp6_client *client,
                 uint32_t iaid);
index 5e8d06f18f4bc044d1e86aeb635da3aa07347815..98fda297a72ee7cf85f6a247490d3fa966d9a467 100644 (file)
@@ -53,6 +53,7 @@ int sd_radv_get_ifname(sd_radv *ra, const char **ret);
 int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
 int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu);
 int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit);
+int sd_radv_set_retransmit(sd_radv *ra, uint32_t retransmit_msec);
 int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t lifetime_usec);
 int sd_radv_set_managed_information(sd_radv *ra, int managed);
 int sd_radv_set_other_information(sd_radv *ra, int other);
index 0be66c2c7bf0f5649800c288d1e715977c47647e..847415b9ae43ed6333f5d2c5d8cbd0d21d7bc938 100644 (file)
@@ -282,7 +282,11 @@ static void test_exec_cpuaffinity(Manager *m) {
 
 static void test_exec_credentials(Manager *m) {
         test(m, "exec-set-credential.service", 0, CLD_EXITED);
+        test(m, "exec-set-credential-with-mount-namespace.service", 0, CLD_EXITED);
+        test(m, "exec-set-credential-with-seccomp.service", 0, CLD_EXITED);
         test(m, "exec-load-credential.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED);
+        test(m, "exec-load-credential-with-mount-namespace.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED);
+        test(m, "exec-load-credential-with-seccomp.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED);
         test(m, "exec-credentials-dir-specifier.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED);
 }
 
index 64024958ac4a8999c42a37ec8fb9628988d524ce..46ef5bfa96004aec54e5e10620502f2761229b65 100644 (file)
@@ -648,7 +648,7 @@ TEST(variant) {
         test_variant_one("[ 0, -0, 0.0, -0.0, 0.000, -0.000, 0e0, -0e0, 0e+0, -0e-0, 0e-0, -0e000, 0e+000 ]", test_zeroes);
 }
 
-TEST(json_append) {
+TEST(json_variant_merge_objectb) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
 
         assert_se(json_build(&v, JSON_BUILD_OBJECT(
@@ -656,9 +656,9 @@ TEST(json_append) {
                                              JSON_BUILD_PAIR("c", JSON_BUILD_CONST_STRING("y")),
                                              JSON_BUILD_PAIR("a", JSON_BUILD_CONST_STRING("z")))) >= 0);
 
-        assert_se(json_append(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("b", JSON_BUILD_STRING("x")))) >= 0);
-        assert_se(json_append(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("c", JSON_BUILD_STRING("y")))) >= 0);
-        assert_se(json_append(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_STRING("z")))) >= 0);
+        assert_se(json_variant_merge_objectb(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("b", JSON_BUILD_STRING("x")))) >= 0);
+        assert_se(json_variant_merge_objectb(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("c", JSON_BUILD_STRING("y")))) >= 0);
+        assert_se(json_variant_merge_objectb(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_STRING("z")))) >= 0);
 
         assert_se(json_variant_equal(v, w));
 }
index 0898e68cb5d50cad3e96da2f9c5a5d17640b0cae..1cfc1f3ae552ceda604317200a1b7ead94102d62 100644 (file)
 #include "tests.h"
 #include "tmpfile-util.h"
 
-TEST(remount_and_move_sub_mounts) {
-        int r;
-
-        if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)
-                return (void) log_tests_skipped("not running privileged");
-
-        r = safe_fork("(remount-and-move-sub-mounts)",
-                      FORK_RESET_SIGNALS |
-                      FORK_CLOSE_ALL_FDS |
-                      FORK_DEATHSIG |
-                      FORK_WAIT |
-                      FORK_REOPEN_LOG |
-                      FORK_LOG |
-                      FORK_NEW_MOUNTNS |
-                      FORK_MOUNTNS_SLAVE,
-                      NULL);
-        assert_se(r >= 0);
-        if (r == 0) {
-                _cleanup_free_ char *d = NULL, *fn = NULL;
-
-                assert_se(mkdtemp_malloc(NULL, &d) >= 0);
-
-                assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", d, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
-
-                assert_se(fn = path_join(d, "memo"));
-                assert_se(write_string_file(fn, d, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
-                assert_se(access(fn, F_OK) >= 0);
-
-                /* Create fs tree */
-                FOREACH_STRING(p, "sub1", "sub1/hoge", "sub1/foo", "sub2", "sub2/aaa", "sub2/bbb") {
-                        _cleanup_free_ char *where = NULL, *filename = NULL;
-
-                        assert_se(where = path_join(d, p));
-                        assert_se(mkdir_p(where, 0755) >= 0);
-                        assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", where, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
-
-                        assert_se(filename = path_join(where, "memo"));
-                        assert_se(write_string_file(filename, where, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
-                        assert_se(access(filename, F_OK) >= 0);
-                }
-
-                /* Hide sub1. */
-                FOREACH_STRING(p, "sub1", "sub1/hogehoge", "sub1/foofoo") {
-                        _cleanup_free_ char *where = NULL, *filename = NULL;
-
-                        assert_se(where = path_join(d, p));
-                        assert_se(mkdir_p(where, 0755) >= 0);
-                        assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", where, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
-
-                        assert_se(filename = path_join(where, "memo"));
-                        assert_se(write_string_file(filename, where, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
-                        assert_se(access(filename, F_OK) >= 0);
-                }
-
-                /* Remount the main fs. */
-                r = remount_and_move_sub_mounts("tmpfs", d, "tmpfs", MS_NOSUID|MS_NODEV, NULL);
-                if (r == -EINVAL || (r < 0 && ERRNO_IS_NOT_SUPPORTED(r))) {
-                        log_tests_skipped_errno(r, "The kernel seems too old: %m");
-                        _exit(EXIT_SUCCESS);
-                }
-
-                /* Check the file in the main fs does not exist. */
-                assert_se(access(fn, F_OK) < 0 && errno == ENOENT);
-
-                /* Check the files in sub-mounts are kept. */
-                FOREACH_STRING(p, "sub1", "sub1/hogehoge", "sub1/foofoo", "sub2", "sub2/aaa", "sub2/bbb") {
-                        _cleanup_free_ char *where = NULL, *filename = NULL, *content = NULL;
-
-                        assert_se(where = path_join(d, p));
-                        assert_se(filename = path_join(where, "memo"));
-                        assert_se(read_full_file(filename, &content, NULL) >= 0);
-                        assert_se(streq(content, where));
-                }
-
-                /* umount sub1, and check if the previously hidden sub-mounts are dropped. */
-                FOREACH_STRING(p, "sub1/hoge", "sub1/foo") {
-                        _cleanup_free_ char *where = NULL;
-
-                        assert_se(where = path_join(d, p));
-                        assert_se(access(where, F_OK) < 0 && errno == ENOENT);
-                }
-
-                _exit(EXIT_SUCCESS);
-        }
-}
-
-TEST(remount_sysfs) {
-        int r;
-
-        if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)
-                return (void) log_tests_skipped("not running privileged");
-
-        if (path_is_fs_type("/sys", SYSFS_MAGIC) <= 0)
-                return (void) log_tests_skipped("sysfs is not mounted on /sys");
-
-        if (access("/sys/class/net/dummy-test-mnt", F_OK) < 0)
-                return (void) log_tests_skipped_errno(errno, "The network interface dummy-test-mnt does not exit");
-
-        r = safe_fork("(remount-sysfs)",
-                      FORK_RESET_SIGNALS |
-                      FORK_CLOSE_ALL_FDS |
-                      FORK_DEATHSIG |
-                      FORK_WAIT |
-                      FORK_REOPEN_LOG |
-                      FORK_LOG |
-                      FORK_NEW_MOUNTNS |
-                      FORK_MOUNTNS_SLAVE,
-                      NULL);
-        assert_se(r >= 0);
-        if (r == 0) {
-                assert_se(unshare(CLONE_NEWNET) >= 0);
-
-                /* Even unshare()ed, the interfaces in the main namespace can be accessed through sysfs. */
-                assert_se(access("/sys/class/net/lo", F_OK) >= 0);
-                assert_se(access("/sys/class/net/dummy-test-mnt", F_OK) >= 0);
-
-                r = remount_sysfs("/sys");
-                if (r == -EINVAL || (r < 0 && ERRNO_IS_NOT_SUPPORTED(r))) {
-                        log_tests_skipped_errno(r, "The kernel seems too old: %m");
-                        _exit(EXIT_SUCCESS);
-                }
-
-                /* After remounting sysfs, the interfaces in the main namespace cannot be accessed. */
-                assert_se(access("/sys/class/net/lo", F_OK) >= 0);
-                assert_se(access("/sys/class/net/dummy-test-mnt", F_OK) < 0 && errno == ENOENT);
-
-                _exit(EXIT_SUCCESS);
-        }
-}
-
 TEST(mount_option_mangle) {
         char *opts = NULL;
         unsigned long f;
@@ -636,17 +506,4 @@ TEST(bind_mount_submounts) {
         assert_se(umount_recursive(b, 0) >= 0);
 }
 
-static int intro(void) {
-         /* Create a dummy network interface for testing remount_sysfs(). */
-        (void) system("ip link add dummy-test-mnt type dummy");
-
-        return 0;
-}
-
-static int outro(void) {
-        (void) system("ip link del dummy-test-mnt");
-
-        return 0;
-}
-
-DEFINE_TEST_MAIN_FULL(LOG_DEBUG, intro, outro);
+DEFINE_TEST_MAIN(LOG_DEBUG);
index 25aafc35ca837dda241061be465256c6d6325439..436a784e86808c2ace8a2fb506222e8d88893277 100644 (file)
@@ -194,6 +194,7 @@ TEST(protect_kernel_logs) {
                                     NULL,
                                     NULL,
                                     NULL,
+                                    -EBADF,
                                     NULL,
                                     0,
                                     NULL,
index 77afd2f6b9eb81f1c23701fc153674c04a26647b..56f3de83b6f7dc88d81053e9e7868db27b03633b 100644 (file)
@@ -96,6 +96,7 @@ int main(int argc, char *argv[]) {
                             tmp_dir,
                             var_tmp_dir,
                             NULL,
+                            -EBADF,
                             NULL,
                             0,
                             NULL,
index 6ec70054e785c2340a977e54cf7d4fc5373be427..b5f0008d76bef481b49198af55205965f2c8ec46 100644 (file)
@@ -1292,4 +1292,28 @@ TEST(strextendn) {
         x = mfree(x);
 }
 
+TEST(strlevenshtein) {
+        assert_se(strlevenshtein(NULL, NULL) == 0);
+        assert_se(strlevenshtein("", "") == 0);
+        assert_se(strlevenshtein("", NULL) == 0);
+        assert_se(strlevenshtein(NULL, "") == 0);
+
+        assert_se(strlevenshtein("a", "a") == 0);
+        assert_se(strlevenshtein("a", "b") == 1);
+        assert_se(strlevenshtein("b", "a") == 1);
+        assert_se(strlevenshtein("a", "") == 1);
+        assert_se(strlevenshtein("", "a") == 1);
+
+        assert_se(strlevenshtein("xxx", "xxx") == 0);
+        assert_se(strlevenshtein("xxx", "yyy") == 3);
+        assert_se(strlevenshtein("yyy", "xxx") == 3);
+        assert_se(strlevenshtein("xx", "xxx") == 1);
+        assert_se(strlevenshtein("xxx", "xx") == 1);
+        assert_se(strlevenshtein("x", "xxx") == 2);
+        assert_se(strlevenshtein("xxx", "x") == 2);
+
+        assert_se(strlevenshtein("sitting", "kitten") == 3);
+        assert_se(strlevenshtein("sunday", "saturday") == 3);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 1b7da9d511e0e6bec4bee2952878abdf86cb5778..a0d7dc14ef1d75395305e360a08ac99cd143416b 100644 (file)
@@ -183,7 +183,7 @@ TEST(utf16_to_utf8) {
         assert_se(b);
 
         free(a);
-        a = utf16_to_utf8(b, char16_strlen(b) * 2);
+        a = utf16_to_utf8(b, SIZE_MAX);
         assert_se(a);
         assert_se(strlen(a) == sizeof(utf8));
         assert_se(memcmp(a, utf8, sizeof(utf8)) == 0);
@@ -218,10 +218,10 @@ TEST(utf8_to_utf16) {
                 _cleanup_free_ char16_t *a = NULL;
                 _cleanup_free_ char *b = NULL;
 
-                a = utf8_to_utf16(p, strlen(p));
+                a = utf8_to_utf16(p, SIZE_MAX);
                 assert_se(a);
 
-                b = utf16_to_utf8(a, char16_strlen(a) * 2);
+                b = utf16_to_utf8(a, SIZE_MAX);
                 assert_se(b);
                 assert_se(streq(p, b));
         }
index 11b91e30bb6921de74b0a801e1c587b8b8193864..45f777525c341edc7029063fb92d79e3afa6d08d 100644 (file)
@@ -36,6 +36,15 @@ fuzz_regression_tests += { 'fuzz-unit-file' : dict }
 
 ############################################################
 
+# TODO: Use native string formatting with meson >= 1.3.0
+if get_option('auto_features').enabled()
+        sanitize_auto_features = 'enabled'
+elif get_option('auto_features').disabled()
+        sanitize_auto_features = 'disabled'
+else
+        sanitize_auto_features = 'auto'
+endif
+
 sanitize_address_undefined = custom_target(
         'sanitize-address-undefined-fuzzers',
         output : 'sanitize-address-undefined-fuzzers',
@@ -43,11 +52,11 @@ sanitize_address_undefined = custom_target(
                    project_source_root,
                    '@OUTPUT@',
                    'fuzzers',
-                   '-Dfuzz-tests=true -Db_lundef=false -Db_sanitize=address,undefined --optimization=@0@ @1@ -Dc_args=@2@ -Dcpp_args=@2@ -Dskip-deps=@3@'.format(
+                   '-Dfuzz-tests=true -Db_lundef=false -Db_sanitize=address,undefined --optimization=@0@ @1@ -Dc_args=@2@ -Dcpp_args=@2@ --auto-features=@3@'.format(
                            get_option('optimization'),
                            get_option('werror') ? '--werror' : '',
                            '-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION',
-                           get_option('skip-deps')
+                           sanitize_auto_features
                    ),
                    ' '.join(cc.cmd_array()),
                    cxx_cmd])
diff --git a/test/test-execute/exec-load-credential-with-mount-namespace.service b/test/test-execute/exec-load-credential-with-mount-namespace.service
new file mode 100644 (file)
index 0000000..fd71cf6
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for LoadCredential=
+
+[Service]
+ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+Type=oneshot
+LoadCredential=test-execute.load-credential
+PrivateMounts=yes
diff --git a/test/test-execute/exec-load-credential-with-seccomp.service b/test/test-execute/exec-load-credential-with-seccomp.service
new file mode 100644 (file)
index 0000000..67303f2
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for LoadCredential=
+
+[Service]
+ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+Type=oneshot
+LoadCredential=test-execute.load-credential
+SystemCallFilter=~open_tree move_mount
diff --git a/test/test-execute/exec-set-credential-with-mount-namespace.service b/test/test-execute/exec-set-credential-with-mount-namespace.service
new file mode 100644 (file)
index 0000000..67d15e5
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for SetCredential=
+
+[Service]
+ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+Type=oneshot
+SetCredential=test-execute.set-credential:hoge
+PrivateMounts=yes
diff --git a/test/test-execute/exec-set-credential-with-seccomp.service b/test/test-execute/exec-set-credential-with-seccomp.service
new file mode 100644 (file)
index 0000000..778777b
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for SetCredential=
+
+[Service]
+ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+Type=oneshot
+SetCredential=test-execute.set-credential:hoge
+SystemCallFilter=~open_tree move_mount
index edd5b7f50174c3a731bbacb64e1555588fa63be7..1b1a56cd704ba758e311c2e17497cff4ad3adc39 100644 (file)
@@ -11,5 +11,5 @@ Gateway=_ipv6ra
 Destination=2001:1234:5:9fff:ff:ff:ff:ff/128
 
 [DHCPv6]
-DUIDType=custom
+DUIDType=0x42
 DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00
index 2af783ad14e267604743d6b6cb0e9207196dda6b..44db736d0107db41a5d2546952cd6a14f9eefed5 100644 (file)
@@ -10,9 +10,9 @@ Address=149.10.124.58/28
 [Route]
 Destination=2001:1234:5:8fff:ff:ff:ff:ff/128
 TCPCongestionControlAlgorithm=dctcp
-TCPRetransmissionTimeOutSec=300s
+TCPRetransmissionTimeoutSec=300s
 
 [Route]
 Destination=149.10.124.66
 TCPCongestionControlAlgorithm=dctcp
-TCPRetransmissionTimeOutSec=300s
+TCPRetransmissionTimeoutSec=300s
index 5910d0d8da6a6f125d629cd1f83f36f3ce9bc94a..171152040b94ddab437469c521c67f07de50db89 100755 (executable)
@@ -5034,20 +5034,10 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
         self.assertNotIn('192.168.5', output)
 
-        # checking semi-static route
-        output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
-        print(output)
-        self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
-
-        # Confirm that ipv6 token is not set in the kernel
-        output = check_output('ip token show dev veth99')
-        print(output)
-        self.assertRegex(output, 'token :: dev veth99')
-
         print('## dnsmasq log')
         output = read_dnsmasq_log_file()
         print(output)
-        self.assertIn('DHCPSOLICIT(veth-peer) 00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
+        self.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
         self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
         self.assertNotIn('DHCPREQUEST(veth-peer)', output)
         self.assertIn('DHCPREPLY(veth-peer)', output)
index 5a07d14d04387bf1946c1b03b930eb7946d2a824..a87933486961e540134960dae069f0c738f99a1a 100755 (executable)
@@ -52,7 +52,7 @@ systemctl daemon-reload
 [[ "$(systemctl show -P UnitFileState foo-fstab-opt.mount)" == bad ]]
 rm -f /etc/fstab
 
-[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab /etc/fstab
+[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
 systemctl daemon-reload
 
 # c) Mount unit
index 9bcdec9298a1db1cedc656d4606730b22e7709ca..8d7a2e92ab0752fadfb680616fd874ea155ecc08 100755 (executable)
@@ -341,7 +341,7 @@ testcase_nspawn_settings() {
     rm -f "/etc/systemd/nspawn/$container.nspawn"
     mkdir -p "$root/tmp" "$root"/opt/{tmp,inaccessible,also-inaccessible}
 
-    for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-ipvlan{1,2}; do
+    for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
         ip link add "$dev" type dummy
     done
     udevadm settle
@@ -395,8 +395,8 @@ VirtualEthernet=yes
 VirtualEthernetExtra=my-fancy-veth1
 VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
 Interface=sd-shared1 sd-shared2:sd-shared2
-MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2
-IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2
+MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
+IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
 Zone=sd-zone0
 Port=80
 Port=81:8181
index 7e21cdd603059e76bc9da2fcc87aefb58260ad11..0c4e3205eb87a6112f848760b8f152b7574905ee 100755 (executable)
@@ -429,6 +429,7 @@ EOF
                "type" : "swap",
                "label" : "label2",
                "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+               "partno" : 0,
                "file" : "$defs/root.conf",
                "node" : "$imgs/zzz1",
                "offset" : 1048576,
@@ -489,6 +490,7 @@ EOF
                "type" : "swap",
                "label" : "label1",
                "uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0",
+               "partno" : 0,
                "file" : "$defs/1/root1.conf",
                "node" : "$imgs/zzz1",
                "offset" : 1048576,
@@ -504,6 +506,7 @@ EOF
                "type" : "swap",
                "label" : "label2",
                "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+               "partno" : 1,
                "file" : "$defs/2/root2.conf",
                "node" : "$imgs/zzz2",
                "offset" : 34603008,
index 419805de7a29462fb1571c408dfb1f9f4fb779bf..df3d39eba6d5bfb18a86e6b15e92d827eb80d1ca 100755 (executable)
@@ -27,10 +27,12 @@ build="$WORK/build"
 rm -rf "$build"
 mkdir -p "$build"
 
+meson_args=("-Db_lundef=false")
+
 if [ -z "$FUZZING_ENGINE" ]; then
-    fuzzflag="llvm-fuzz=true"
+    meson_args+=("-Dllvm-fuzz=true")
 else
-    fuzzflag="oss-fuzz=true"
+    meson_args+=("-Doss-fuzz=true" "--auto-features=disabled")
 
     apt-get update
     apt-get install -y gperf m4 gettext python3-pip \
@@ -67,7 +69,7 @@ else
     fi
 fi
 
-if ! meson setup "$build" "-D$fuzzflag" -Db_lundef=false; then
+if ! meson setup "$build" "${meson_args[@]}"; then
     cat "$build/meson-logs/meson-log.txt"
     exit 1
 fi
index cb1be8359965080dcaf1a16644487476cfc5595d..2e649d6e5f0e6f43b9905299c86748fe664454c3 100644 (file)
@@ -62,7 +62,7 @@ units = [
         },
         {
           'file' : 'systemd-bsod.service.in',
-          'conditions' : ['HAVE_QRENCODE','ENABLE_INITRD'],
+          'conditions' : ['HAVE_QRENCODE', 'ENABLE_INITRD'],
           'symlinks' : ['initrd.target.wants/'],
         },
         {
index 306d4a66239b3e672cf532913452d07da07fdbde..2d2f988fbfc60b272247f7965fe240be9b8aa339 100644 (file)
@@ -8,13 +8,13 @@
 #  (at your option) any later version.
 
 [Unit]
-Description=Displays emergency message full screen.
-Documentation=man:systemd-bsod(8)
+Description=Displays emergency message in full screen.
+Documentation=man:systemd-bsod.service(8)
 ConditionVirtualization=no
 DefaultDependencies=no
-Conflicts=shutdown.target
-Before=shutdown.target
 After=systemd-battery-check.service
+Before=shutdown.target
+Conflicts=shutdown.target
 
 [Service]
 RemainAfterExit=yes