]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #31899 from yuwata/sd-journal-add-match
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 25 Mar 2024 17:56:58 +0000 (02:56 +0900)
committerGitHub <noreply@github.com>
Mon, 25 Mar 2024 17:56:58 +0000 (02:56 +0900)
sd-journal: introduce two helpers for adding journal filter

112 files changed:
.gitmodules
TODO
hwdb.d/60-evdev.hwdb
hwdb.d/60-keyboard.hwdb
man/common-variables.xml
man/homectl.xml
man/org.freedesktop.home1.xml
man/systemd-analyze.xml
man/systemd-vmspawn.xml
meson.build
mkosi.conf
mkosi.images/base/mkosi.conf
mkosi.images/base/mkosi.conf.d/10-arch/mkosi.build.chroot
mkosi.images/base/mkosi.conf.d/10-centos-fedora/mkosi.conf
mkosi.images/base/mkosi.conf.d/10-debian-ubuntu/mkosi.conf
mkosi.images/base/mkosi.postinst.chroot
mkosi.images/system/mkosi.extra/usr/lib/sysusers.d/testuser.conf [new file with mode: 0644]
mkosi.images/system/mkosi.extra/usr/lib/tmpfiles.d/testuser.conf [new file with mode: 0644]
pkg/arch
pkg/debian
pkg/fedora
po/fr.po
src/analyze/analyze-dot.c
src/analyze/analyze-security.c
src/analyze/analyze.c
src/basic/log.c
src/basic/log.h
src/basic/path-util.h
src/basic/stat-util.c
src/basic/string-table.h
src/basic/virt.c
src/boot/efi/meson.build
src/core/bpf-socket-bind.c
src/core/bpf/socket_bind/socket-bind-api.bpf.h
src/core/bpf/socket_bind/socket-bind.bpf.c
src/core/dynamic-user.c
src/core/exec-invoke.c
src/core/execute.c
src/core/import-creds.c
src/core/load-fragment.c
src/core/main.c
src/core/mount.c
src/core/service.c
src/core/socket.c
src/core/socket.h
src/core/swap.c
src/core/unit.h
src/home/home-util.h
src/home/homectl.c
src/home/homed-home-bus.c
src/home/homed-home.c
src/home/homed-home.h
src/home/homed-manager-bus.c
src/home/homed-manager.c
src/home/homed-operation.h
src/home/homework-luks.c
src/home/homework-password-cache.c
src/home/homework-password-cache.h
src/home/homework.c
src/journal/journalctl.c
src/libsystemd-network/fuzz-ndisc-rs.c
src/libsystemd-network/icmp6-packet.c
src/libsystemd-network/icmp6-packet.h
src/libsystemd-network/icmp6-util-unix.c
src/libsystemd-network/icmp6-util-unix.h
src/libsystemd-network/icmp6-util.c
src/libsystemd-network/icmp6-util.h
src/libsystemd-network/ndisc-option.c
src/libsystemd-network/ndisc-option.h
src/libsystemd-network/sd-ndisc.c
src/libsystemd-network/test-ndisc-rs.c
src/login/logind-dbus.c
src/login/logind-dbus.h
src/login/logind-session.c
src/machine/machine.c
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp-server.h
src/nspawn/nspawn-register.c
src/nspawn/nspawn.c
src/run/run.c
src/shared/bus-unit-util.c
src/shared/bus-unit-util.h
src/shared/cryptsetup-util.c
src/shared/cryptsetup-util.h
src/shared/dissect-image.c
src/shared/install.c
src/shared/tests.h
src/shared/tpm2-util.c
src/ssh-generator/ssh-generator.c
src/sysusers/sysusers.c
src/test/test-hashmap.c
src/test/test-hmac.c
src/test/test-mountpoint-util.c
src/test/test-os-util.c
src/test/test-path-util.c
src/test/test-pretty-print.c
src/test/test-recovery-key.c
src/test/test-user-util.c
src/vmspawn/vmspawn-scope.c
src/vmspawn/vmspawn.c
test/README.testsuite
test/TEST-04-JOURNAL/test.sh
test/meson.build
test/test-network/systemd-networkd-tests.py
test/units/testsuite-04.corrupted-journals.sh
test/units/testsuite-04.journal.sh
test/units/testsuite-07.exec-context.sh
test/units/testsuite-46.sh
tools/check-efi-alignment.py [new file with mode: 0755]
tools/elf2efi.py

index ba9f6d7f27e729618fb09ba2d811dd35331761e3..64d938fbf9989bc0c03bfe276ff9efdbc9d122a8 100644 (file)
@@ -1,10 +1,10 @@
 [submodule "pkg/fedora"]
        path = pkg/fedora
-       url = https://src.fedoraproject.org/rpms/systemd
+       url = https://src.fedoraproject.org/rpms/systemd.git
        branch = rawhide
 [submodule "pkg/opensuse"]
        path = pkg/opensuse
-       url = https://src.opensuse.org/rpm/systemd
+       url = https://src.opensuse.org/rpm/systemd.git
        branch = factory
 [submodule "pkg/debian"]
        path = pkg/debian
@@ -12,9 +12,9 @@
        branch = debian/master
 [submodule "pkg/centos"]
        path = pkg/centos
-       url = https://git.centos.org/rpms/systemd
+       url = https://git.centos.org/rpms/systemd.git
        branch = c9s-sig-hyperscale
 [submodule "pkg/arch"]
        path = pkg/arch
-       url = https://gitlab.archlinux.org/daandemeyer/systemd.git
-       branch = strip
+       url = https://gitlab.archlinux.org/archlinux/packaging/packages/systemd.git
+       branch = main
diff --git a/TODO b/TODO
index 96d606ca346684758adebaaa2d786b8b8119ad35..097c639d1382addf2947b56aeb88240e112ff6c4 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2277,8 +2277,6 @@ Features:
   - fingerprint authentication, pattern authentication, â€¦
   - make sure "classic" user records can also be managed by homed
   - make size of $XDG_RUNTIME_DIR configurable in user record
-  - query password from kernel keyring first
-  - update even if record is "absent"
   - move acct mgmt stuff from pam_systemd_home to pam_systemd?
   - when "homectl --pkcs11-token-uri=" is used, synthesize ssh-authorized-keys records for all keys we have private keys on the stick for
   - make slice for users configurable (requires logind rework)
index 43017dc00f2e565d4f09dcc2959005cd13b196cb..c603f9ca2cc4bca2bb06df22f55e9d1f8eff6861 100644 (file)
@@ -497,6 +497,11 @@ evdev:input:b0003v256Cp0068*
  EVDEV_ABS_00=::200
  EVDEV_ABS_01=::200
 
+# HUION Kamvas 19 Pro (GT-1902)
+evdev:input:b0003v256Cp006Be0110*
+ EVDEV_ABS_00=::68
+ EVDEV_ABS_01=::123
+
 #########################################
 # Lenovo
 #########################################
index 06411bf6204b390260e2c9785c3efffbf267c2fd..2e1838744ba1f8db430761a07b1f97137183a7bc 100644 (file)
@@ -1941,6 +1941,16 @@ evdev:name:Toshiba*input*device:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnSatellite*P75-A:
  KEYBOARD_KEY_13f=f21                                   # Touchpad toggle
  KEYBOARD_KEY_9e=wlan
 
+# Portege Z830 ACPI quickstart buttons
+evdev:name:Quickstart Button 1:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnPORTEGEZ830:*
+ KEYBOARD_KEY_1=prog1                                  # TOSHIBA eco button
+
+evdev:name:Quickstart Button 2:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnPORTEGEZ830:*
+ KEYBOARD_KEY_1=prog2                                  # TOSHIBA Presentation button
+
+evdev:name:Quickstart Button 3:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnPORTEGEZ830:*
+ KEYBOARD_KEY_1=f21                                    # Touchpad toggle
+
 ###########################################################
 # VIA
 ###########################################################
index 0113d1b6bb259dc5b7446a78c7590df503011182..58a285c83359e2590e5d63880289348a70cb350d 100644 (file)
       <term><varname>$SYSTEMD_LOG_LEVEL</varname></term>
 
       <listitem><para id='log-level-body'>The maximum log level of emitted messages (messages with a higher
-      log level, i.e. less important ones, will be suppressed). Either one of (in order of decreasing
-      importance) <constant>emerg</constant>, <constant>alert</constant>, <constant>crit</constant>,
-      <constant>err</constant>, <constant>warning</constant>, <constant>notice</constant>,
-      <constant>info</constant>, <constant>debug</constant>, or an integer in the range 0…7. See
+      log level, i.e. less important ones, will be suppressed). Takes a comma-separated list of values. A
+      value may be either one of (in order of decreasing importance) <constant>emerg</constant>,
+      <constant>alert</constant>, <constant>crit</constant>, <constant>err</constant>,
+      <constant>warning</constant>, <constant>notice</constant>, <constant>info</constant>,
+      <constant>debug</constant>, or an integer in the range 0…7. See
       <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-      for more information.</para>
-      </listitem>
+      for more information. Each value may optionally be prefixed with one of <constant>console</constant>,
+      <constant>syslog</constant>, <constant>kmsg</constant> or <constant>journal</constant> followed by a
+      colon to set the maximum log level for that specific log target (e.g.
+      <constant>SYSTEMD_LOG_LEVEL=debug,console:info</constant> specifies to log at debug level except when
+      logging to the console which should be at info level). Note that the global maximum log level takes
+      priority over any per target maximum log levels.</para></listitem>
     </varlistentry>
 
     <varlistentry id='log-color'>
index 1a0535cd4a66089dc9d5aa4bf1394010930e2ee5..f1bade205343244717b0a750679a29fa814bef1c 100644 (file)
         <xi:include href="version-info.xml" xpointer="v245"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--offline</option></term>
+
+        <listitem><para>Do not attempt to update the copy of the user record and blob directory that is embedded inside
+        of the home area. This allows for operation on home areas that are absent, or without needing to authenticate as
+        the user being modified.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="host" />
       <xi:include href="user-system-options.xml" xpointer="machine" />
 
index 6fe3bb3ce0821abf45ace659dcbe9e3008771665..9334f1a596ec82cabcc2c43e279b8da18a0d1ffb 100644 (file)
@@ -320,10 +320,10 @@ node /org/freedesktop/home1 {
       interface.</para>
 
       <para><function>UpdateHome()</function> updates a locally registered user record. Takes a fully
-      specified JSON user record as argument (including the <literal>secret</literal> section). A user with a
-      matching name and realm must be registered locally already, and the last change timestamp of the newly
-      supplied record must be newer than the previously existing user record. Note this operation updates the
-      user record only, it does not propagate passwords/authentication tokens from the user record to the
+      specified JSON user record as argument (possibly including the <literal>secret</literal> section). A user
+      with a matching name and realm must be registered locally already, and the last change timestamp of the
+      newly supplied record must be newer than the previously existing user record. Note this operation updates
+      the user record only, it does not propagate passwords/authentication tokens from the user record to the
       storage back-end, or resizes the storage back-end. Typically a home directory is first updated, and then
       the password of the underlying storage updated using <function>ChangePasswordHome()</function> as well
       as the storage resized using <function>ResizeHome()</function>. This method is equivalent to
@@ -334,17 +334,24 @@ node /org/freedesktop/home1 {
       Directories</ulink> for more info). The <varname>blobs</varname> argument works in the same way as
       <function>CreateHomeEx()</function>, so check there for details. The new blob directory contents passed into
       this method will completely replace the user's existing blob directory. The <varname>flags</varname> argument
-      may be used for future expansion, but for now pass 0. This method is equivalent to <function>UpdateEx()</function>
-      on the <classname>org.freedesktop.home1.Home</classname> interface.</para>
+      can be used to further customize the behavior of this method via flags defined as follows:</para>
+      <programlisting>
+#define SD_HOMED_UPDATE_OFFLINE (UINT64_C(1) &lt;&lt; 0)
+      </programlisting>
+      <para>When <constant>SD_HOMED_UPDATE_OFFLINE</constant> (0x01) is set, no attempt is made to update the copies
+      of the user record and blob directory that are embedded into the home directory. Changes will be stored, however,
+      and may be propagated into the home directory the next time it is reconciled (most likely when the user next logs in).
+      Note that any changes made with this flag set may be lost if the home area has a newer record, which can happen
+      if the home area is updated on another machine after this method call. This method is equivalent to
+      <function>UpdateEx()</function> on the <classname>org.freedesktop.home1.Home</classname> interface.</para>
 
       <para><function>ResizeHome()</function> resizes the storage associated with a user record. Takes a user
-      name, a disk size in bytes and a user record consisting only of the <literal>secret</literal> section
-      as argument. If the size is specified as <constant>UINT64_MAX</constant> the storage is resized to the
-      size already specified in the user record. Typically, if the user record is updated using
+      name, a disk size in bytes, and optionally a user record consisting only of the <literal>secret</literal>
+      section as arguments. If the size is specified as <constant>UINT64_MAX</constant> the storage is resized to
+      the size already specified in the user record. Typically, if the user record is updated using
       <function>UpdateHome()</function> above this is used to propagate the size configured there-in down to
-      the underlying storage back-end. This method is equivalent to
-      <function>Resize()</function> on the <classname>org.freedesktop.home1.Home</classname>
-      interface.</para>
+      the underlying storage back-end. This method is equivalent to <function>Resize()</function> on the
+      <classname>org.freedesktop.home1.Home</classname> interface.</para>
 
       <para><function>ChangePasswordHome()</function> changes the passwords/authentication tokens of a home
       directory. Takes a user name, and two JSON user record objects, each consisting only of the
index 639a7231cb21eff922333ad00a190fa38ef8c5d6..ca108c5cf9d35f00c45d13764266e18d17d985e1 100644 (file)
@@ -1018,16 +1018,12 @@ x86-64      native</programlisting>
         <term><option>--order</option></term>
         <term><option>--require</option></term>
 
-        <listitem><para>When used in conjunction with the
-        <command>dot</command> command (see above), selects which
-        dependencies are shown in the dependency graph. If
-        <option>--order</option> is passed, only dependencies of type
-        <varname>After=</varname> or <varname>Before=</varname> are
-        shown. If <option>--require</option> is passed, only
-        dependencies of type <varname>Requires=</varname>,
-        <varname>Requisite=</varname>,
-        <varname>Wants=</varname> and <varname>Conflicts=</varname>
-        are shown. If neither is passed, this shows dependencies of
+        <listitem><para>When used in conjunction with the <command>dot</command> command (see above),
+        selects which dependencies are shown in the dependency graph. If <option>--order</option> is passed,
+        only dependencies of type <varname>After=</varname> or <varname>Before=</varname> are shown.
+        If <option>--require</option> is passed, only dependencies of type <varname>Requires=</varname>,
+        <varname>Requisite=</varname>, <varname>BindsTo=</varname>, <varname>Wants=</varname>, and
+        <varname>Conflicts=</varname> are shown. If neither is passed, this shows dependencies of
         all these types.</para>
 
         <xi:include href="version-info.xml" xpointer="v198"/></listitem>
index b75158811f80a6ecb3cdfdd8ab2ffb60ed257496..90ddc9b6740ba3afde539932cd1eb0bd5cf660cd 100644 (file)
           <xi:include href="version-info.xml" xpointer="v256"/>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term><option>--pass-ssh-key=</option><replaceable>BOOL</replaceable></term>
+
+          <listitem><para>By default an SSH key is generated to allow <command>systemd-vmspawn</command> to open
+          a D-Bus connection to the VM's systemd bus. Setting this to "no" will disable SSH key generation.</para>
+
+          <para>The generated keys are ephemeral. That is they are valid only for the current invocation of <command>systemd-vmspawn</command>,
+          and are typically not persisted.</para>
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><option>--ssh-key-type=</option><replaceable>TYPE</replaceable></term>
+
+          <listitem><para>Configures the type of SSH key to generate, see
+          <citerefentry><refentrytitle>ssh-keygen</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+          for more information.</para>
+
+          <para>By default <literal>ed25519</literal> keys are generated, however <literal>rsa</literal> keys
+          may also be useful if the VM has a particularly old version of <command>sshd</command></para>.
+
+          <xi:include href="version-info.xml" xpointer="v256"/>
+          </listitem>
+        </varlistentry>
       </variablelist>
     </refsect2>
 
index 8d1cd8a9ed0ee7a0c153a941257ba975463fac64..897b39386904a7e75b9dfeef374903b64c64a369 100644 (file)
@@ -1251,7 +1251,8 @@ foreach ident : ['crypt_set_metadata_size',
                  'crypt_reencrypt_init_by_passphrase',
                  'crypt_reencrypt',
                  'crypt_set_data_offset',
-                 'crypt_set_keyring_to_link']
+                 'crypt_set_keyring_to_link',
+                 'crypt_resume_by_volume_key']
         have_ident = have and cc.has_function(
                 ident,
                 prefix : '#include <libcryptsetup.h>',
@@ -1564,7 +1565,8 @@ conf.set10('ENABLE_IMPORTD', have)
 have = get_option('homed').require(
         conf.get('HAVE_OPENSSL') == 1 and
         conf.get('HAVE_LIBFDISK') == 1 and
-        conf.get('HAVE_LIBCRYPTSETUP') == 1,
+        conf.get('HAVE_LIBCRYPTSETUP') == 1 and
+        conf.get('HAVE_CRYPT_RESUME_BY_VOLUME_KEY') == 1,
         error_message : 'openssl, fdisk and libcryptsetup required').allowed()
 conf.set10('ENABLE_HOMED', have)
 
@@ -1693,7 +1695,6 @@ if conf.get('BPF_FRAMEWORK') == 1
                 '-std=gnu11',
                 '-fno-stack-protector',
                 '-O2',
-                '-mkernel=5.2',
                 '-mcpu=v3',
                 '-mco-re',
                 '-gbtf',
@@ -1742,7 +1743,7 @@ if conf.get('BPF_FRAMEWORK') == 1
 
         bpf_o_unstripped_cmd += ['-I.']
 
-        if not meson.is_cross_build() and bpf_compiler == 'clang'
+        if not meson.is_cross_build()
                 target_triplet_cmd = run_command('gcc', '-dumpmachine', check: false)
                 if target_triplet_cmd.returncode() == 0
                         target_triplet = target_triplet_cmd.stdout().strip()
@@ -1828,6 +1829,7 @@ conf.set10('ENABLE_UKIFY', want_ukify)
 
 #####################################################################
 
+check_efi_alignment_py = find_program('tools/check-efi-alignment.py')
 check_version_history_py = find_program('tools/check-version-history.py')
 elf2efi_py = find_program('tools/elf2efi.py')
 export_dbus_interfaces_py = find_program('tools/dbus_exporter.py')
index 19a3851fd132650920a2e65b032bd729a5eee77e..a5953d7613639eccf7c34ea3414c97d2f9015836 100644 (file)
@@ -41,3 +41,5 @@ KernelCommandLineExtra=systemd.crash_shell
                        enforcing=0
                        systemd.early_core_pattern=/core
                        systemd.firstboot=no
+                       systemd.setenv=SYSTEMD_ENABLE_LOG_CONTEXT=yes
+                       SYSTEMD_ENABLE_LOG_CONTEXT=yes
index fcefc3ead2bbf106cfd152f2f8a67a0164566178..aed8bb5ba8457901057560ca538493a3430a9edb 100644 (file)
@@ -31,6 +31,7 @@ Packages=
         kexec-tools
         kmod
         less
+        man
         mtools
         nano
         nftables
index aff566f94c24fb409cce0f334bff3b760bd5fda7..1c5f582701863147d26cc1c3eb090cf1879fdbfe 100755 (executable)
@@ -23,7 +23,7 @@ mount --mkdir --rbind "$PWD/pkg/$PKG_SUBDIR" "pkg/$PKG_SUBDIR/src/"
 # tmpfs during the build script so these changes don't end up in the image itself.
 tee --append /etc/makepkg.conf >/dev/null <<EOF
 CFLAGS="$CFLAGS -Og"
-OPTIONS=(!strip docs !libtool !staticlibs emptydirs !zipman purge debug !lto)
+OPTIONS=(!strip docs !libtool !staticlibs emptydirs !zipman purge !debug !lto)
 EOF
 
 # Linting the PKGBUILD takes multiple seconds every build so avoid that by nuking all the linting functions.
@@ -35,6 +35,10 @@ else
     TS="${SOURCE_DATE_EPOCH:-$(date +%s)}"
 fi
 
+sed --in-place "pkg/$PKG_SUBDIR/PKGBUILD" \
+    --expression "s/^_tag=.*/_tag=$(cat meson.version)/" \
+    --expression "s/^pkgrel=.*/pkgrel=$(date "+%Y%m%d%H%M%S" --date "@$TS")/"
+
 # We get around makepkg's root check by setting EUID to something else.
 # shellcheck disable=SC2046
 env --chdir="pkg/$PKG_SUBDIR" \
@@ -43,11 +47,9 @@ env --chdir="pkg/$PKG_SUBDIR" \
     --noextract \
     $( ((WITH_TESTS)) || echo --nocheck) \
     --force \
-    UPSTREAM=1 \
-    QUIET=1 \
+    _systemd_UPSTREAM=1 \
+    _systemd_QUIET=1 \
     BUILDDIR="$PWD/pkg/$PKG_SUBDIR" \
     PKGDEST="$PACKAGEDIR" \
     PKGEXT=".pkg.tar" \
-    PKGVER="$(cat meson.version)" \
-    PKGREL="$(date "+%Y%m%d%H%M%S" --date "@$TS")" \
     MESON_EXTRA_CONFIGURE_OPTIONS="-D mode=developer -D b_sanitize=${SANITIZERS:-none}"
index 4c80b0a36fd228e11a366d4c6188b27e1c850183..a9ee4a29062a7af0ff66724850c2a5858ff0bc1b 100644 (file)
@@ -35,6 +35,7 @@ Packages=
         libasan
         libcap-ng-utils
         libubsan
+        man-db
         netcat
         openssh-clients
         openssh-server
@@ -51,4 +52,6 @@ Packages=
         vim-common
 
 InitrdPackages=
+        setools
+        selinux-policy
         tpm2-tools
index 47f3b7f488b9ebf81bca7ca10ab97b6e5f71766c..07c9b3a805c834552fc8afbc23ef157c53dfa766 100644 (file)
@@ -41,6 +41,7 @@ Packages=
         libcap-ng-utils
         libtss2-rc0
         libtss2-tcti-device0
+        man-db
         netcat-openbsd
         openssh-client
         openssh-server
index 26459b165deadda9aa89686a2f7cef07248fa7e8..d1052694aa711012d58f703c8fb16f3904300d4b 100755 (executable)
@@ -65,17 +65,6 @@ fi
 mountpoint -q /etc/resolv.conf && umount /etc/resolv.conf
 rm -f /etc/resolv.conf
 
-. /usr/lib/os-release
-
-mkdir -p /usr/lib/sysusers.d
-cat >/usr/lib/sysusers.d/testuser.conf <<EOF
-u      testuser        4711    "Test User"     /home/testuser
-EOF
-mkdir -p /usr/lib/tmpfiles.d
-cat >/usr/lib/tmpfiles.d/testuser.conf <<EOF
-q      /home/testuser  0700    4711    4711
-EOF
-
 # sbsign is not available on CentOS Stream
 if command -v sbsign &>/dev/null; then
     # Ensure that side-loaded PE addons are loaded if signed, and ignored if not
diff --git a/mkosi.images/system/mkosi.extra/usr/lib/sysusers.d/testuser.conf b/mkosi.images/system/mkosi.extra/usr/lib/sysusers.d/testuser.conf
new file mode 100644 (file)
index 0000000..9d65a0e
--- /dev/null
@@ -0,0 +1 @@
+u      testuser        4711    "Test User"     /home/testuser
diff --git a/mkosi.images/system/mkosi.extra/usr/lib/tmpfiles.d/testuser.conf b/mkosi.images/system/mkosi.extra/usr/lib/tmpfiles.d/testuser.conf
new file mode 100644 (file)
index 0000000..1b6ecb6
--- /dev/null
@@ -0,0 +1 @@
+q      /home/testuser  0700    4711    4711
index b33762d07ce712f5c675bdc8f882a096aa59bf09..3b86b9146b84d499789ba924a9dd4ac643d796ab 160000 (submodule)
--- a/pkg/arch
+++ b/pkg/arch
@@ -1 +1 @@
-Subproject commit b33762d07ce712f5c675bdc8f882a096aa59bf09
+Subproject commit 3b86b9146b84d499789ba924a9dd4ac643d796ab
index 3b4728136724b960f453de26e59ee39061575a8f..1932e19d92daef5928a1402073ad3b5aa6fc0767 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 3b4728136724b960f453de26e59ee39061575a8f
+Subproject commit 1932e19d92daef5928a1402073ad3b5aa6fc0767
index 2e32a339a10caad9392a7049bccfd1c4cd7c24cc..f1d38667ef013aa832f43ea7b5861efd29b09fee 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 2e32a339a10caad9392a7049bccfd1c4cd7c24cc
+Subproject commit f1d38667ef013aa832f43ea7b5861efd29b09fee
index 4b4c3fb15e6aaac5afa6898cc1686be5843015c2..864ed0678787abcfe6e88c8cc85c70923cb703e7 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
@@ -6,21 +6,21 @@
 # Julien Humbert <julroy67@gmail.com>, 2020, 2021.
 # Arnaud T. <listes.00@gmail.com>, 2021.
 # blutch112 <vincent.lefebvre59@gmail.com>, 2022.
-# Pierre GRASSER <pierre.grasser@proton.me>, 2023.
+# Pierre GRASSER <pierre.grasser@proton.me>, 2023, 2024.
 msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2024-03-04 10:09+0100\n"
-"PO-Revision-Date: 2023-10-12 16:36+0000\n"
+"PO-Revision-Date: 2024-03-22 10:36+0000\n"
 "Last-Translator: Pierre GRASSER <pierre.grasser@proton.me>\n"
 "Language-Team: French <https://translate.fedoraproject.org/projects/systemd/"
-"master/fr/>\n"
+"main/fr/>\n"
 "Language: fr\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 5.0.2\n"
+"X-Generator: Weblate 5.4\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -143,26 +143,23 @@ msgstr ""
 
 #: src/home/org.freedesktop.home1.policy:73
 msgid "Inhibit automatic lock of a home area"
-msgstr ""
+msgstr "Empêcher le verrouillage automatique d'un espace personnel"
 
 #: src/home/org.freedesktop.home1.policy:74
-#, fuzzy
 msgid ""
 "Authentication is required to inhibit automatic lock of a user's home area."
 msgstr ""
-"Authentification requise pour mettre Ã  jour l’espace personnel d’un "
-"utilisateur."
+"Authentification requise pour empêcher le verrouillage automatique de "
+"l'espace personnel d'un utilisateur."
 
 #: src/home/org.freedesktop.home1.policy:83
-#, fuzzy
 msgid "Activate a home area"
-msgstr "Créer un espace personnel"
+msgstr "Activer un espace personnel"
 
 #: src/home/org.freedesktop.home1.policy:84
-#, fuzzy
 msgid "Authentication is required to activate a user's home area."
 msgstr ""
-"Authentification requise pour créer l’espace personnel d’un utilisateur."
+"Authentification requise pour activer l’espace personnel d’un utilisateur."
 
 #: src/home/pam_systemd_home.c:293
 #, c-format
@@ -394,52 +391,39 @@ msgid "Authentication is required to get system description."
 msgstr "Authentification requise pour obtenir la description du système."
 
 #: src/import/org.freedesktop.import1.policy:22
-#, fuzzy
 msgid "Import a disk image"
-msgstr "Importer une image de machine virtuelle ou de conteneur"
+msgstr "Importer une image disque"
 
 #: src/import/org.freedesktop.import1.policy:23
-#, fuzzy
 msgid "Authentication is required to import an image"
-msgstr ""
-"Authentification requise pour importer une image de machine virtuelle ou de "
-"conteneur"
+msgstr "Authentification requise pour importer une image"
 
 #: src/import/org.freedesktop.import1.policy:32
-#, fuzzy
 msgid "Export a disk image"
-msgstr "Exporter une image de machine virtuelle ou de conteneur"
+msgstr "Exporter une image disque"
 
 #: src/import/org.freedesktop.import1.policy:33
-#, fuzzy
 msgid "Authentication is required to export disk image"
-msgstr ""
-"Authentification requise pour exporter une image de machine virtuelle ou de "
-"conteneur"
+msgstr "Authentification requise pour exporter une image disque."
 
 #: src/import/org.freedesktop.import1.policy:42
-#, fuzzy
 msgid "Download a disk image"
-msgstr "Télécharger une image de machine virtuelle (VM) ou de conteneur"
+msgstr "Télécharger une image disque"
 
 #: src/import/org.freedesktop.import1.policy:43
-#, fuzzy
 msgid "Authentication is required to download a disk image"
-msgstr ""
-"Authentification requise pour télécharger une image de machine virtuelle ou "
-"de conteneur"
+msgstr "Authentification requise pour télécharger une image disque."
 
 #: src/import/org.freedesktop.import1.policy:52
 msgid "Cancel transfer of a disk image"
-msgstr ""
+msgstr "Annuler le transfert d'une image disque"
 
 #: src/import/org.freedesktop.import1.policy:53
-#, fuzzy
 msgid ""
 "Authentication is required to cancel the ongoing transfer of a disk image"
 msgstr ""
-"Authentification requise pour changer le mot de passe de l’espace personnel "
-"d’un utilisateur."
+"Authentification requise pour annuler le transfert en cours d'une image "
+"disque."
 
 #: src/locale/org.freedesktop.locale1.policy:22
 msgid "Set system locale"
index bf8aa8148fc82ed3db1feade7abd37e9929fe380..9e92d59bceeeeb55b3318a9c20afa7cfd3255d11 100644 (file)
@@ -13,14 +13,15 @@ static int graph_one_property(
                 const UnitInfo *u,
                 const char *prop,
                 const char *color,
-                char *patterns[],
-                char *from_patterns[],
-                char *to_patterns[]) {
+                char **patterns,
+                char **from_patterns,
+                char **to_patterns) {
 
         _cleanup_strv_free_ char **units = NULL;
         bool match_patterns;
         int r;
 
+        assert(bus);
         assert(u);
         assert(prop);
         assert(color);
@@ -51,7 +52,13 @@ static int graph_one_property(
         return 0;
 }
 
-static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
+static int graph_one(
+                sd_bus *bus,
+                const UnitInfo *u,
+                char **patterns,
+                char **from_patterns,
+                char **to_patterns) {
+
         int r;
 
         assert(bus);
@@ -67,12 +74,19 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *fro
                 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
                 if (r < 0)
                         return r;
+
                 r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
                 if (r < 0)
                         return r;
+
+                r = graph_one_property(bus, u, "BindsTo", "gold", patterns, from_patterns, to_patterns);
+                if (r < 0)
+                        return r;
+
                 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
                 if (r < 0)
                         return r;
+
                 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
                 if (r < 0)
                         return r;
@@ -85,6 +99,9 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
         _cleanup_strv_free_ char **expanded_patterns = NULL;
         int r;
 
+        assert(bus);
+        assert(ret);
+
         STRV_FOREACH(pattern, patterns) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
@@ -110,10 +127,9 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
 
-                if (!streq(*pattern, unit_id)) {
+                if (!streq(*pattern, unit_id))
                         if (strv_extend(&expanded_patterns, unit_id) < 0)
                                 return log_oom();
-                }
         }
 
         *ret = TAKE_PTR(expanded_patterns); /* do not free */
@@ -128,8 +144,8 @@ int verb_dot(int argc, char *argv[], void *userdata) {
         _cleanup_strv_free_ char **expanded_patterns = NULL;
         _cleanup_strv_free_ char **expanded_from_patterns = NULL;
         _cleanup_strv_free_ char **expanded_to_patterns = NULL;
-        int r;
         UnitInfo u;
+        int r;
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
@@ -170,6 +186,7 @@ int verb_dot(int argc, char *argv[], void *userdata) {
 
         log_info("   Color legend: black     = Requires\n"
                  "                 dark blue = Requisite\n"
+                 "                 gold      = BindsTo\n"
                  "                 dark grey = Wants\n"
                  "                 red       = Conflicts\n"
                  "                 green     = After\n");
index c1b35ba8525d7cee3d91dbbd98ea02c680597663..486143b7f0620dff04342940da9d2c06971f08c4 100644 (file)
@@ -1756,15 +1756,14 @@ static int assess(const SecurityInfo *info,
                         (void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 7);
         }
 
-        for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) {
-                const struct security_assessor *a = security_assessor_table + i;
+        FOREACH_ARRAY(a, security_assessor_table, ELEMENTSOF(security_assessor_table)) {
                 _cleanup_free_ char *d = NULL;
                 uint64_t badness;
                 void *data;
                 uint64_t weight = access_weight(a, policy);
                 uint64_t range = access_range(a, policy);
 
-                data = (uint8_t *) info + a->offset;
+                data = (uint8_t*) info + a->offset;
 
                 if (a->default_dependencies_only && !info->default_dependencies) {
                         badness = UINT64_MAX;
index b449e538f31e33b82d4e4763075353c10b8b68bc..cf4894a9d3bef362978b20afb06f195f94512015 100644 (file)
@@ -572,10 +572,9 @@ static int parse_argv(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Option --threshold= is only supported for security right now.");
 
-        if (arg_runtime_scope == RUNTIME_SCOPE_GLOBAL &&
-            !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
+        if (arg_runtime_scope == RUNTIME_SCOPE_GLOBAL && !streq_ptr(argv[optind], "unit-paths"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Option --global only makes sense with verbs dot, unit-paths, verify.");
+                                       "Option --global only makes sense with verb unit-paths.");
 
         if (streq_ptr(argv[optind], "cat-config") && arg_runtime_scope == RUNTIME_SCOPE_USER)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
index 172a2c8ada086cbf4d3ea98c671c5b92ee4e5556..6faa6ad4fcef8de0e3f28211a388ded74f594d46 100644 (file)
@@ -49,6 +49,12 @@ static void *log_syntax_callback_userdata = NULL;
 
 static LogTarget log_target = LOG_TARGET_CONSOLE;
 static int log_max_level = LOG_INFO;
+static int log_target_max_level[] = {
+        [LOG_TARGET_CONSOLE] = INT_MAX,
+        [LOG_TARGET_KMSG]    = INT_MAX,
+        [LOG_TARGET_SYSLOG]  = INT_MAX,
+        [LOG_TARGET_JOURNAL] = INT_MAX,
+};
 static int log_facility = LOG_DAEMON;
 static bool ratelimit_kmsg = true;
 
@@ -439,6 +445,9 @@ static int write_to_console(
         if (console_fd < 0)
                 return 0;
 
+        if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_CONSOLE])
+                return 0;
+
         if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
                 xsprintf(prefix, "<%i>", level);
                 iovec[n++] = IOVEC_MAKE_STRING(prefix);
@@ -523,6 +532,9 @@ static int write_to_syslog(
         if (syslog_fd < 0)
                 return 0;
 
+        if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_SYSLOG])
+                return 0;
+
         xsprintf(header_priority, "<%i>", level);
 
         t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
@@ -592,6 +604,9 @@ static int write_to_kmsg(
         if (kmsg_fd < 0)
                 return 0;
 
+        if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_KMSG])
+                return 0;
+
         if (ratelimit_kmsg && !ratelimit_below(&ratelimit)) {
                 if (ratelimit_num_dropped(&ratelimit) > 1)
                         return 0;
@@ -719,6 +734,9 @@ static int write_to_journal(
         if (journal_fd < 0)
                 return 0;
 
+        if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_JOURNAL])
+                return 0;
+
         iovec_len = MIN(6 + _log_context_num_fields * 2, IOVEC_MAX);
         iovec = newa(struct iovec, iovec_len);
 
@@ -988,7 +1006,7 @@ void log_assert_failed_return(
 
         PROTECT_ERRNO;
         log_assert(LOG_DEBUG, text, file, line, func,
-                   "Assertion '%s' failed at %s:%u, function %s(). Ignoring.");
+                   "Assertion '%s' failed at %s:%u, function %s(), ignoring.");
 }
 
 int log_oom_internal(int level, const char *file, int line, const char *func) {
@@ -1218,11 +1236,74 @@ int log_set_target_from_string(const char *e) {
 int log_set_max_level_from_string(const char *e) {
         int r;
 
-        r = log_level_from_string(e);
+        for (;;) {
+                _cleanup_free_ char *word = NULL, *prefix = NULL;
+                LogTarget target;
+                const char *colon;
+
+                r = extract_first_word(&e, &word, ",", 0);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                colon = strchr(word, ':');
+                if (!colon) {
+                        r = log_level_from_string(word);
+                        if (r < 0)
+                                return r;
+
+                        log_set_max_level(r);
+                        continue;
+                }
+
+                prefix = strndup(word, colon - word);
+                if (!prefix)
+                        return -ENOMEM;
+
+                target = log_target_from_string(prefix);
+                if (target < 0)
+                        return target;
+
+                if (target >= _LOG_TARGET_SINGLE_MAX)
+                        return -EINVAL;
+
+                r = log_level_from_string(colon + 1);
+                if (r < 0)
+                        return r;
+
+                log_target_max_level[target] = r;
+        }
+
+        return 0;
+}
+
+int log_max_levels_to_string(int level, char **ret) {
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        assert(ret);
+
+        r = log_level_to_string_alloc(level, &s);
         if (r < 0)
                 return r;
 
-        log_set_max_level(r);
+        for (LogTarget target = 0; target < _LOG_TARGET_SINGLE_MAX; target++) {
+                _cleanup_free_ char *l = NULL;
+
+                if (log_target_max_level[target] == INT_MAX)
+                        continue;
+
+                r = log_level_to_string_alloc(log_target_max_level[target], &l);
+                if (r < 0)
+                        return r;
+
+                r = strextendf_with_separator(&s, ",", "%s:%s", log_target_to_string(target), l);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = TAKE_PTR(s);
         return 0;
 }
 
@@ -1265,7 +1346,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                         return 0;
 
                 if (log_set_target_from_string(value) < 0)
-                        log_warning("Failed to parse log target '%s'. Ignoring.", value);
+                        log_warning("Failed to parse log target '%s', ignoring.", value);
 
         } else if (proc_cmdline_key_streq(key, "systemd.log_level")) {
 
@@ -1273,32 +1354,32 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                         return 0;
 
                 if (log_set_max_level_from_string(value) < 0)
-                        log_warning("Failed to parse log level '%s'. Ignoring.", value);
+                        log_warning("Failed to parse log level setting '%s', ignoring.", value);
 
         } else if (proc_cmdline_key_streq(key, "systemd.log_color")) {
 
                 if (log_show_color_from_string(value ?: "1") < 0)
-                        log_warning("Failed to parse log color setting '%s'. Ignoring.", value);
+                        log_warning("Failed to parse log color setting '%s', ignoring.", value);
 
         } else if (proc_cmdline_key_streq(key, "systemd.log_location")) {
 
                 if (log_show_location_from_string(value ?: "1") < 0)
-                        log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
+                        log_warning("Failed to parse log location setting '%s', ignoring.", value);
 
         } else if (proc_cmdline_key_streq(key, "systemd.log_tid")) {
 
                 if (log_show_tid_from_string(value ?: "1") < 0)
-                        log_warning("Failed to parse log tid setting '%s'. Ignoring.", value);
+                        log_warning("Failed to parse log tid setting '%s', ignoring.", value);
 
         } else if (proc_cmdline_key_streq(key, "systemd.log_time")) {
 
                 if (log_show_time_from_string(value ?: "1") < 0)
-                        log_warning("Failed to parse log time setting '%s'. Ignoring.", value);
+                        log_warning("Failed to parse log time setting '%s', ignoring.", value);
 
         } else if (proc_cmdline_key_streq(key, "systemd.log_ratelimit_kmsg")) {
 
                 if (log_set_ratelimit_kmsg_from_string(value ?: "1") < 0)
-                        log_warning("Failed to parse log ratelimit kmsg boolean '%s'. Ignoring.", value);
+                        log_warning("Failed to parse log ratelimit kmsg boolean '%s', ignoring.", value);
         }
 
         return 0;
@@ -1318,31 +1399,31 @@ void log_parse_environment_variables(void) {
 
         e = getenv("SYSTEMD_LOG_TARGET");
         if (e && log_set_target_from_string(e) < 0)
-                log_warning("Failed to parse log target '%s'. Ignoring.", e);
+                log_warning("Failed to parse log target '%s', ignoring.", e);
 
         e = getenv("SYSTEMD_LOG_LEVEL");
         if (e && log_set_max_level_from_string(e) < 0)
-                log_warning("Failed to parse log level '%s'. Ignoring.", e);
+                log_warning("Failed to parse log level '%s', ignoring.", e);
 
         e = getenv("SYSTEMD_LOG_COLOR");
         if (e && log_show_color_from_string(e) < 0)
-                log_warning("Failed to parse log color '%s'. Ignoring.", e);
+                log_warning("Failed to parse log color '%s', ignoring.", e);
 
         e = getenv("SYSTEMD_LOG_LOCATION");
         if (e && log_show_location_from_string(e) < 0)
-                log_warning("Failed to parse log location '%s'. Ignoring.", e);
+                log_warning("Failed to parse log location '%s', ignoring.", e);
 
         e = getenv("SYSTEMD_LOG_TIME");
         if (e && log_show_time_from_string(e) < 0)
-                log_warning("Failed to parse log time '%s'. Ignoring.", e);
+                log_warning("Failed to parse log time '%s', ignoring.", e);
 
         e = getenv("SYSTEMD_LOG_TID");
         if (e && log_show_tid_from_string(e) < 0)
-                log_warning("Failed to parse log tid '%s'. Ignoring.", e);
+                log_warning("Failed to parse log tid '%s', ignoring.", e);
 
         e = getenv("SYSTEMD_LOG_RATELIMIT_KMSG");
         if (e && log_set_ratelimit_kmsg_from_string(e) < 0)
-                log_warning("Failed to parse log ratelimit kmsg boolean '%s'. Ignoring.", e);
+                log_warning("Failed to parse log ratelimit kmsg boolean '%s', ignoring.", e);
 }
 
 void log_parse_environment(void) {
index 23f54b88fb0189503094c0e2f8eb979fe5018a7f..fc80902c450e2d924d715b1a917b8537ab064cda 100644 (file)
@@ -18,15 +18,16 @@ struct signalfd_siginfo;
 
 typedef enum LogTarget{
         LOG_TARGET_CONSOLE,
-        LOG_TARGET_CONSOLE_PREFIXED,
         LOG_TARGET_KMSG,
         LOG_TARGET_JOURNAL,
-        LOG_TARGET_JOURNAL_OR_KMSG,
         LOG_TARGET_SYSLOG,
+        LOG_TARGET_CONSOLE_PREFIXED,
+        LOG_TARGET_JOURNAL_OR_KMSG,
         LOG_TARGET_SYSLOG_OR_KMSG,
         LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */
         LOG_TARGET_NULL,
-        _LOG_TARGET_MAX,
+        _LOG_TARGET_SINGLE_MAX = LOG_TARGET_SYSLOG + 1,
+        _LOG_TARGET_MAX = LOG_TARGET_NULL + 1,
         _LOG_TARGET_INVALID = -EINVAL,
 } LogTarget;
 
@@ -59,6 +60,7 @@ void log_settle_target(void);
 void log_set_max_level(int level);
 int log_set_max_level_from_string(const char *e);
 int log_get_max_level(void) _pure_;
+int log_max_levels_to_string(int level, char **ret);
 
 void log_set_facility(int facility);
 
index 47699e6414210e7efb2f9464068019664f5beda3..6d88c54d7f0bdad66912d6f9f813ce49e85b7814 100644 (file)
@@ -105,10 +105,6 @@ static inline int path_simplify_alloc(const char *path, char **ret) {
         return 0;
 }
 
-static inline bool path_equal_ptr(const char *a, const char *b) {
-        return !!a == !!b && (!a || path_equal(a, b));
-}
-
 /* Note: the search terminates on the first NULL item. */
 #define PATH_IN_SET(p, ...) path_strv_contains(STRV_MAKE(__VA_ARGS__), p)
 
index c208d7c59dcd3d47522bfdcb350ba77d855dab12..a833aa260cc967948bf15ef4160e1ef45fdccbeb 100644 (file)
@@ -229,7 +229,7 @@ int null_or_empty_path_with_root(const char *fn, const char *root) {
          * When looking under root_dir, we can't expect /dev/ to be mounted,
          * so let's see if the path is a (possibly dangling) symlink to /dev/null. */
 
-        if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null"))
+        if (path_equal(path_startswith(fn, root ?: "/"), "dev/null"))
                 return true;
 
         r = chase_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st);
index 3be70dfadedf6655120fd260fca0e418378e2344..d1d90df786b56bb917d5ebdf60bf77e7bc018216 100644 (file)
@@ -47,10 +47,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
                         s = strdup(name##_table[i]);                    \
                         if (!s)                                         \
                                 return -ENOMEM;                         \
-                } else {                                                \
-                        if (asprintf(&s, "%i", i) < 0)                  \
-                                return -ENOMEM;                         \
-                }                                                       \
+                } else if (asprintf(&s, "%i", i) < 0)                   \
+                        return -ENOMEM;                                 \
                 *str = s;                                               \
                 return 0;                                               \
         }
index b976d2af14f669c561c90bd51604cee68c1adee0..7e2c0781fe12870bcd9e4bc36488c90ce5930c13 100644 (file)
@@ -179,6 +179,7 @@ static Virtualization detect_vm_dmi_vendor(void) {
                 { "VMW",                   VIRTUALIZATION_VMWARE    },
                 { "innotek GmbH",          VIRTUALIZATION_ORACLE    },
                 { "VirtualBox",            VIRTUALIZATION_ORACLE    },
+                { "Oracle Corporation",    VIRTUALIZATION_ORACLE    }, /* Detect VirtualBox on some proprietary systems via the board_vendor */
                 { "Xen",                   VIRTUALIZATION_XEN       },
                 { "Bochs",                 VIRTUALIZATION_BOCHS     },
                 { "Parallels",             VIRTUALIZATION_PARALLELS },
index 483e0ade0ce549815b50f16c181f48786c139426..7a60b0ec7ed1be70cfff6fa12cadf8e8f43f3dcb 100644 (file)
@@ -212,7 +212,7 @@ endif
 efi_arch_c_args = {
         'aarch64'     : ['-mgeneral-regs-only'],
         'arm'         : ['-mgeneral-regs-only'],
-       # Until -mgeneral-regs-only is supported in LoongArch, use the following option instead:
+        # Until -mgeneral-regs-only is supported in LoongArch, use the following option instead:
         'loongarch64' : ['-mno-lsx', '-mno-lasx'],
         # Pass -m64/32 explicitly to make building on x32 work.
         'x86_64'      : ['-m64', '-march=x86-64', '-mno-red-zone', '-mgeneral-regs-only'],
@@ -407,6 +407,11 @@ foreach efi_elf_binary : efi_elf_binaries
         if name == 'addon@0@.efi.stub'.format(efi_arch)
                 efi_addon = exe.full_path()
         endif
+
+        test('check-alignment-@0@'.format(name),
+             check_efi_alignment_py,
+             args : exe.full_path(),
+             suite : 'efi')
 endforeach
 
 alias_target('systemd-boot', boot_targets)
index 465216a7d0dfd29708ffe42b5d4d1f481fa6ac15..3008d8249c69f38bf84d1dd9ed87f258b49c7de0 100644 (file)
@@ -32,6 +32,15 @@ static int update_rules_map(
 
         assert(map_fd >= 0);
 
+        if (!head) {
+                static const struct socket_bind_rule val = {
+                        .address_family = SOCKET_BIND_RULE_AF_MATCH_NOTHING,
+                };
+
+                if (sym_bpf_map_update_elem(map_fd, &i, &val, BPF_ANY) != 0)
+                        return -errno;
+        }
+
         LIST_FOREACH(socket_bind_items, item, head) {
                 struct socket_bind_rule val = {
                         .address_family = (uint32_t) item->address_family,
index 277b9bbde25714882e993ba55b1277f270d60a73..4fe08f1f444d25a5a1939e6d921d9dfa72fb06f2 100644 (file)
@@ -7,13 +7,17 @@
  */
 
 #include <linux/types.h>
+#include <stdint.h>
 
 /*
  * Bind rule is matched with socket fields accessible to cgroup/bind{4,6} hook
  * through bpf_sock_addr struct.
- * 'address_family' is expected to be one of AF_UNSPEC, AF_INET or AF_INET6.
+ * 'address_family' is expected to be one of AF_UNSPEC, AF_INET, AF_INET6 or the
+ * magic SOCKET_BIND_RULE_AF_MATCH_NOTHING.
  * Matching by family is bypassed for rules with AF_UNSPEC set, which makes the
  * rest of a rule applicable for both IPv4 and IPv6 addresses.
+ * If SOCKET_BIND_RULE_AF_MATCH_NOTHING is set the rule fails unconditionally
+ * and other checks are skipped.
  * If matching by family is either successful or bypassed, a rule and a socket
  * are matched by ip protocol.
  * If 'protocol' is 0, matching is bypassed.
@@ -49,3 +53,4 @@ struct socket_bind_rule {
 };
 
 #define SOCKET_BIND_MAX_RULES 128
+#define SOCKET_BIND_RULE_AF_MATCH_NOTHING UINT32_MAX
index b7972a887a4c8948bc1e62512e952839bbd6a23a..da9f9d13de7803f11376c29696eb2d84e0134765 100644 (file)
@@ -55,6 +55,9 @@ static __always_inline bool match(
                 __u32 protocol,
                 __u16 port,
                 const struct socket_bind_rule *r) {
+        if (r->address_family == SOCKET_BIND_RULE_AF_MATCH_NOTHING)
+                return false;
+
         return match_af(address_family, r) &&
                 match_protocol(protocol, r) &&
                 match_user_port(port, r);
index 4f8f76473334ae5faafdf1f4fb26c7d69a1ca901..24ed889b61ef5fa1bb0795ee0dec50e42a0df6f8 100644 (file)
@@ -143,7 +143,6 @@ static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret)
 }
 
 static int make_uid_symlinks(uid_t uid, const char *name, bool b) {
-
         char path1[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
         const char *path2;
         int r = 0, k;
@@ -754,7 +753,6 @@ int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret) {
 
 int dynamic_creds_make(Manager *m, const char *user, const char *group, DynamicCreds **ret) {
         _cleanup_(dynamic_creds_unrefp) DynamicCreds *creds = NULL;
-        bool acquired = false;
         int r;
 
         assert(m);
@@ -777,20 +775,14 @@ int dynamic_creds_make(Manager *m, const char *user, const char *group, DynamicC
                 r = dynamic_user_acquire(m, user, &creds->user);
                 if (r < 0)
                         return r;
-
-                acquired = true;
         }
 
-        if (creds->user && (!group || streq_ptr(user, group)))
-                creds->group = dynamic_user_ref(creds->user);
-        else if (group) {
+        if (group && !streq_ptr(user, group)) {
                 r = dynamic_user_acquire(m, group, &creds->group);
-                if (r < 0) {
-                        if (acquired)
-                                creds->user = dynamic_user_unref(creds->user);
+                if (r < 0)
                         return r;
-                }
-        }
+        } else
+                creds->group = ASSERT_PTR(dynamic_user_ref(creds->user));
 
         *ret = TAKE_PTR(creds);
 
index a1401f3e4903a9586778cffadfacb0f1472c5e87..47339bddafc220cde91f5f79e672e2fbc66974cd 100644 (file)
@@ -1915,7 +1915,7 @@ static int build_environment(
                  * to inherit the $TERM set for PID 1. This is useful for containers so that the $TERM the
                  * container manager passes to PID 1 ends up all the way in the console login shown. */
 
-                if (path_equal_ptr(tty_path, "/dev/console") && getppid() == 1)
+                if (path_equal(tty_path, "/dev/console") && getppid() == 1)
                         term = getenv("TERM");
                 else if (tty_path && in_charset(skip_dev_prefix(tty_path), ALPHANUMERICAL)) {
                         _cleanup_free_ char *key = NULL;
index fb3e9b79cbeef1c6d08ed5a69be519b38c1b6443..864e3d492121937b25bd6c2294a7bc0dd7ab18fa 100644 (file)
@@ -362,7 +362,7 @@ int exec_spawn(Unit *unit,
                PidRef *ret) {
 
         char serialization_fd_number[DECIMAL_STR_MAX(int) + 1];
-        _cleanup_free_ char *subcgroup_path = NULL, *log_level = NULL, *executor_path = NULL;
+        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL, *executor_path = NULL;
         _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
         _cleanup_fdset_free_ FDSet *fdset = NULL;
         _cleanup_fclose_ FILE *f = NULL;
@@ -435,9 +435,9 @@ int exec_spawn(Unit *unit,
         /* If LogLevelMax= is specified, then let's use the specified log level at the beginning of the
          * executor process. To achieve that the specified log level is passed as an argument, rather than
          * the one for the manager process. */
-        r = log_level_to_string_alloc(context->log_level_max >= 0 ? context->log_level_max : log_get_max_level(), &log_level);
+        r = log_max_levels_to_string(context->log_level_max >= 0 ? context->log_level_max : log_get_max_level(), &max_log_levels);
         if (r < 0)
-                return log_unit_error_errno(unit, r, "Failed to convert log level to string: %m");
+                return log_unit_error_errno(unit, r, "Failed to convert max log levels to string: %m");
 
         r = fd_get_path(unit->manager->executor_fd, &executor_path);
         if (r < 0)
@@ -450,7 +450,7 @@ int exec_spawn(Unit *unit,
                         FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
                         STRV_MAKE(executor_path,
                                   "--deserialize", serialization_fd_number,
-                                  "--log-level", log_level,
+                                  "--log-level", max_log_levels,
                                   "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
                         environ,
                         cg_unified() > 0 ? subcgroup_path : NULL,
@@ -1403,7 +1403,7 @@ bool exec_context_maintains_privileges(const ExecContext *c) {
         if (!c->user)
                 return true;
 
-        if (streq(c->user, "root") || streq(c->user, "0"))
+        if (STR_IN_SET(c->user, "root", "0"))
                 return true;
 
         return false;
index 18e4bce67f8a0f7af960bf633811ef8e1a7a22f4..90d87e078d3a5847db26e9eed91b4254eaf66f23 100644 (file)
@@ -753,7 +753,7 @@ static int merge_credentials_trusted(const char *creds_dir) {
                 return 0;
 
         /* Do not try to merge initrd credentials into foreign credentials directories */
-        if (!path_equal_ptr(creds_dir, SYSTEM_CREDENTIALS_DIRECTORY)) {
+        if (!path_equal(creds_dir, SYSTEM_CREDENTIALS_DIRECTORY)) {
                 log_debug("Not importing initrd credentials, as foreign $CREDENTIALS_DIRECTORY has been set.");
                 return 0;
         }
index d751b3316c438d07c42610adf50f5ff520d37e40..be8907f2d192c3e565e4ff333688a4e84f956eea 100644 (file)
@@ -249,7 +249,7 @@ int unit_is_likely_recursive_template_dependency(Unit *u, const char *name, cons
 
         /* Fragment paths should also be equal as a custom fragment for a specific template instance
          * wouldn't necessarily lead to infinite recursion. */
-        if (!path_equal_ptr(u->fragment_path, fragment_path))
+        if (!path_equal(u->fragment_path, fragment_path))
                 return false;
 
         if (!contains_instance_specifier_superset(format))
index 551fb425c43e2ffb286fae8d50715671d51c7cc3..1c57cce748365d94ded08882d6eddbdf85c16076 100644 (file)
@@ -1476,7 +1476,7 @@ static int fixup_environment(void) {
                 return -errno;
 
         /* The kernels sets HOME=/ for init. Let's undo this. */
-        if (path_equal_ptr(getenv("HOME"), "/"))
+        if (path_equal(getenv("HOME"), "/"))
                 assert_se(unsetenv("HOME") == 0);
 
         return 0;
@@ -1508,32 +1508,37 @@ static int become_shutdown(int objective, int retval) {
                 [MANAGER_KEXEC]    = "kexec",
         };
 
-        char log_level[STRLEN("--log-level=") + DECIMAL_STR_MAX(int)],
-             timeout[STRLEN("--timeout=") + DECIMAL_STR_MAX(usec_t) + STRLEN("us")],
+        char timeout[STRLEN("--timeout=") + DECIMAL_STR_MAX(usec_t) + STRLEN("us")],
              exit_code[STRLEN("--exit-code=") + DECIMAL_STR_MAX(uint8_t)];
 
         _cleanup_strv_free_ char **env_block = NULL;
+        _cleanup_free_ char *max_log_levels = NULL;
         usec_t watchdog_timer = 0;
         int r;
 
         assert(objective >= 0 && objective < _MANAGER_OBJECTIVE_MAX);
         assert(table[objective]);
 
-        xsprintf(log_level, "--log-level=%d", log_get_max_level());
         xsprintf(timeout, "--timeout=%" PRI_USEC "us", arg_defaults.timeout_stop_usec);
 
-        const char* command_line[10] = {
+        const char* command_line[11] = {
                 SYSTEMD_SHUTDOWN_BINARY_PATH,
                 table[objective],
-                log_level,
                 timeout,
                 /* Note that the last position is a terminator and must contain NULL. */
         };
-        size_t pos = 4;
+        size_t pos = 3;
 
         assert(command_line[pos-1]);
         assert(!command_line[pos]);
 
+        (void) log_max_levels_to_string(log_get_max_level(), &max_log_levels);
+
+        if (max_log_levels) {
+                command_line[pos++] = "--log-level";
+                command_line[pos++] = max_log_levels;
+        }
+
         switch (log_get_target()) {
 
         case LOG_TARGET_KMSG:
index 0d346c985f7084e449107794cc29dbb433231e9c..175af5498af37fffc62ca190ed475cae6bbdc806 100644 (file)
@@ -1689,6 +1689,7 @@ static int mount_setup_new_unit(
                 Unit **ret) {
 
         _cleanup_(unit_freep) Unit *u = NULL;
+        Mount *mnt;
         int r;
 
         assert(m);
@@ -1700,24 +1701,26 @@ static int mount_setup_new_unit(
         if (r < 0)
                 return r;
 
+        mnt = ASSERT_PTR(MOUNT(u));
+
         r = free_and_strdup(&u->source_path, "/proc/self/mountinfo");
         if (r < 0)
                 return r;
 
-        r = free_and_strdup(&MOUNT(u)->where, where);
+        r = free_and_strdup(&mnt->where, where);
         if (r < 0)
                 return r;
 
-        r = update_parameters_proc_self_mountinfo(MOUNT(u), what, options, fstype);
+        r = update_parameters_proc_self_mountinfo(mnt, what, options, fstype);
         if (r < 0)
                 return r;
 
         /* This unit was generated because /proc/self/mountinfo reported it. Remember this, so that by the
          * time we load the unit file for it (and thus add in extra deps right after) we know what source to
          * attributes the deps to. */
-        MOUNT(u)->from_proc_self_mountinfo = true;
+        mnt->from_proc_self_mountinfo = true;
 
-        r = mount_add_non_exec_dependencies(MOUNT(u));
+        r = mount_add_non_exec_dependencies(mnt);
         if (r < 0)
                 return r;
 
@@ -1742,6 +1745,7 @@ static int mount_setup_existing_unit(
         int r;
 
         assert(u);
+        assert(where);
         assert(ret_flags);
 
         if (!m->where) {
@@ -1774,7 +1778,7 @@ static int mount_setup_existing_unit(
 
         m->from_proc_self_mountinfo = true;
 
-        if (IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_ERROR)) {
+        if (UNIT_IS_LOAD_ERROR(u->load_state)) {
                 /* The unit was previously not found or otherwise not loaded. Now that the unit shows up in
                  * /proc/self/mountinfo we should reconsider it this, hence set it to UNIT_LOADED. */
                 u->load_state = UNIT_LOADED;
index 38fb07d5948d6b7705d41763632bb446c4d28c88..953cb2934aacaf5fc5429bbe30e1e6fef46c91d8 100644 (file)
@@ -4722,6 +4722,7 @@ int service_set_socket_fd(
 
         assert(s);
         assert(fd >= 0);
+        assert(sock);
 
         /* This is called by the socket code when instantiating a new service for a stream socket and the socket needs
          * to be configured. We take ownership of the passed fd on success. */
@@ -4753,12 +4754,13 @@ int service_set_socket_fd(
                         return r;
         }
 
-        r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false, UNIT_DEPENDENCY_IMPLICIT);
+        r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_TRIGGERED_BY, UNIT(sock), false, UNIT_DEPENDENCY_IMPLICIT);
         if (r < 0)
-                return r;
+                return log_unit_debug_errno(UNIT(s), r,
+                                            "Failed to add After=/TriggeredBy= dependencies on socket unit: %m");
 
         s->socket_fd = fd;
-        s->socket_peer = socket_peer_ref(peer);
+        s->socket_peer = peer;
         s->socket_fd_selinux_context_net = selinux_context_net;
 
         unit_ref_set(&s->accept_socket, UNIT(s), UNIT(sock));
index cc986ddd3fa6607510a52ddeaf5d608b9611d88e..45656cbda77943db36676f5dd20bf403406d82ad 100644 (file)
@@ -1369,6 +1369,8 @@ clear:
 }
 
 int socket_load_service_unit(Socket *s, int cfd, Unit **ret) {
+        int r;
+
         /* Figure out what the unit that will be used to handle the connections on the socket looks like.
          *
          * If cfd < 0, then we don't have a connection yet. In case of Accept=yes sockets, use a fake
@@ -1388,7 +1390,6 @@ int socket_load_service_unit(Socket *s, int cfd, Unit **ret) {
 
         /* Build the instance name and load the unit */
         _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL;
-        int r;
 
         r = unit_name_to_prefix(UNIT(s)->id, &prefix);
         if (r < 0)
@@ -2295,8 +2296,8 @@ static void socket_enter_running(Socket *s, int cfd_in) {
 
                 if (!pending) {
                         if (!UNIT_ISSET(s->service)) {
-                                r = log_unit_warning_errno(UNIT(s), SYNTHETIC_ERRNO(ENOENT),
-                                                           "Service to activate vanished, refusing activation.");
+                                log_unit_warning(UNIT(s),
+                                                 "Service to activate vanished, refusing activation.");
                                 goto fail;
                         }
 
@@ -2340,18 +2341,15 @@ static void socket_enter_running(Socket *s, int cfd_in) {
                 }
 
                 r = socket_load_service_unit(s, cfd, &service);
-                if (r < 0) {
-                        if (ERRNO_IS_DISCONNECT(r))
-                                return;
-
-                        log_unit_warning_errno(UNIT(s), r, "Failed to load connection service unit: %m");
+                if (ERRNO_IS_NEG_DISCONNECT(r))
+                        return;
+                if (r < 0 || UNIT_IS_LOAD_ERROR(service->load_state)) {
+                        log_unit_warning_errno(UNIT(s), r < 0 ? r : service->load_error,
+                                               "Failed to load connection service unit: %m");
                         goto fail;
                 }
-
-                r = unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, service,
-                                              false, UNIT_DEPENDENCY_IMPLICIT);
-                if (r < 0) {
-                        log_unit_warning_errno(UNIT(s), r, "Failed to add Before=/Triggers= dependencies on connection unit: %m");
+                if (service->load_state == UNIT_MASKED) {
+                        log_unit_warning(UNIT(s), "Connection service unit is masked, refusing.");
                         goto fail;
                 }
 
@@ -2366,7 +2364,10 @@ static void socket_enter_running(Socket *s, int cfd_in) {
                         goto fail;
                 }
 
-                TAKE_FD(cfd); /* We passed ownership of the fd to the service now. Forget it here. */
+                /* We passed ownership of the fd and socket peer to the service now. */
+                TAKE_FD(cfd);
+                TAKE_PTR(p);
+
                 s->n_connections++;
 
                 r = manager_add_job(UNIT(s)->manager, JOB_START, service, JOB_REPLACE, NULL, &error, NULL);
@@ -2388,13 +2389,9 @@ refuse:
         return;
 
 queue_error:
-        if (ERRNO_IS_RESOURCE(r))
-                log_unit_warning(UNIT(s), "Failed to queue service startup job: %s",
-                                 bus_error_message(&error, r));
-        else
-                log_unit_warning(UNIT(s), "Failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s",
-                                 cfd >= 0 ? "template" : "non-template",
-                                 bus_error_message(&error, r));
+        log_unit_warning_errno(UNIT(s), r, "Failed to queue service startup job%s: %s",
+                               cfd >= 0 && !ERRNO_IS_RESOURCE(r) ? " (Maybe the service is missing or is a template unit?)" : "",
+                               bus_error_message(&error, r));
 
 fail:
         socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
@@ -3259,12 +3256,11 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use
         return 0;
 }
 
-int socket_collect_fds(Socket *s, int **fds) {
-        size_t k = 0, n = 0;
-        int *rfds;
+int socket_collect_fds(Socket *s, int **ret) {
+        size_t n = 0, k = 0;
 
         assert(s);
-        assert(fds);
+        assert(ret);
 
         /* Called from the service code for requesting our fds */
 
@@ -3274,25 +3270,25 @@ int socket_collect_fds(Socket *s, int **fds) {
                 n += p->n_auxiliary_fds;
         }
 
-        if (n <= 0) {
-                *fds = NULL;
+        if (n == 0) {
+                *ret = NULL;
                 return 0;
         }
 
-        rfds = new(int, n);
-        if (!rfds)
+        int *fds = new(int, n);
+        if (!fds)
                 return -ENOMEM;
 
         LIST_FOREACH(port, p, s->ports) {
                 if (p->fd >= 0)
-                        rfds[k++] = p->fd;
-                for (size_t i = 0; i < p->n_auxiliary_fds; ++i)
-                        rfds[k++] = p->auxiliary_fds[i];
+                        fds[k++] = p->fd;
+                FOREACH_ARRAY(i, p->auxiliary_fds, p->n_auxiliary_fds)
+                        fds[k++] = *i;
         }
 
         assert(k == n);
 
-        *fds = rfds;
+        *ret = fds;
         return (int) n;
 }
 
index 5efe01d2bf07ad2797f3b203786b6c277a1dafbe..973a697f86154ed10aec7a6b1562d9a536674cc0 100644 (file)
@@ -171,7 +171,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p);
 DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPeer*, socket_peer_unref);
 
 /* Called from the service code when collecting fds */
-int socket_collect_fds(Socket *s, int **fds);
+int socket_collect_fds(Socket *s, int **ret);
 
 /* Called from the service code when a per-connection service ended */
 void socket_connection_unref(Socket *s);
index e2019587ff40e7b07a3615ef142e3d0c9b0946db..9e200086f37817cd42cbb75eef383d520ef2d89b 100644 (file)
@@ -374,11 +374,10 @@ static int swap_setup_unit(
                 int priority,
                 bool set_flags) {
 
+        _cleanup_(unit_freep) Unit *new = NULL;
         _cleanup_free_ char *e = NULL;
-        bool new;
         Unit *u;
         Swap *s;
-        SwapParameters *p;
         int r;
 
         assert(m);
@@ -395,51 +394,42 @@ static int swap_setup_unit(
 
                 if (s->from_proc_swaps &&
                     !path_equal(s->parameters_proc_swaps.what, what_proc_swaps))
-                        return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
-                                               "Swap %s appeared twice with different device paths %s and %s, refusing.",
-                                               e, s->parameters_proc_swaps.what, what_proc_swaps);
-
-                new = false;
+                        return log_unit_error_errno(u, SYNTHETIC_ERRNO(EEXIST),
+                                                    "Swap appeared twice with different device paths %s and %s, refusing.",
+                                                    s->parameters_proc_swaps.what, what_proc_swaps);
         } else {
-                new = true;
-
-                r = unit_new_for_name(m, sizeof(Swap), e, &u);
-                if (r < 0) {
-                        log_unit_warning_errno(u, r, "Failed to load swap unit: %m");
-                        goto fail;
-                }
+                r = unit_new_for_name(m, sizeof(Swap), e, &new);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to load swap unit '%s': %m", e);
 
+                u = new;
                 s = ASSERT_PTR(SWAP(u));
 
                 s->what = strdup(what);
-                if (s->what) {
-                        r = log_oom();
-                        goto fail;
-                }
+                if (!s->what)
+                        return log_oom();
 
                 unit_add_to_load_queue(u);
         }
 
-        p = &s->parameters_proc_swaps;
+        SwapParameters *p = &s->parameters_proc_swaps;
 
-        if (!s->parameters_proc_swaps.what) {
+        if (!p->what) {
                 p->what = strdup(what_proc_swaps);
-                if (!p->what) {
-                        r = log_oom();
-                        goto fail;
-                }
+                if (!p->what)
+                        return log_oom();
         }
 
-        /* The unit is definitely around now, mark it as loaded if it was previously referenced but could not be
-         * loaded. After all we can load it now, from the data in /proc/swaps. */
-        if (IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_ERROR)) {
+        /* The unit is definitely around now, mark it as loaded if it was previously referenced but
+         * could not be loaded. After all we can load it now, from the data in /proc/swaps. */
+        if (UNIT_IS_LOAD_ERROR(u->load_state)) {
                 u->load_state = UNIT_LOADED;
                 u->load_error = 0;
         }
 
         if (set_flags) {
                 s->is_active = true;
-                s->just_activated = !SWAP(u)->from_proc_swaps;
+                s->just_activated = !s->from_proc_swaps;
         }
 
         s->from_proc_swaps = true;
@@ -448,13 +438,9 @@ static int swap_setup_unit(
         p->priority_set = true;
 
         unit_add_to_dbus_queue(u);
-        return 0;
+        TAKE_PTR(new);
 
-fail:
-        if (!new)
-                unit_free(u);
-
-        return r;
+        return 0;
 }
 
 static void swap_process_new(Manager *m, const char *device, int prio, bool set_flags) {
index 7134d362fe11120bea2b4e0d8fbb353b5086c0a3..0cfd90b3d4c5a174a81379f92f7bae3cf617cd98 100644 (file)
@@ -67,6 +67,10 @@ static inline bool UNIT_IS_LOAD_COMPLETE(UnitLoadState t) {
         return t >= 0 && t < _UNIT_LOAD_STATE_MAX && t != UNIT_STUB && t != UNIT_MERGED;
 }
 
+static inline bool UNIT_IS_LOAD_ERROR(UnitLoadState t) {
+        return IN_SET(t, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_ERROR);
+}
+
 /* Stores the 'reason' a dependency was created as a bit mask, i.e. due to which configuration source it came to be. We
  * use this so that we can selectively flush out parts of dependencies again. Note that the same dependency might be
  * created as a result of multiple "reasons", hence the bitmask. */
index f2e5787a546f578ff51460b97f5b2e70a014213f..42131b9f41ddeee4c2405fd4ccc9f091f5967c94 100644 (file)
@@ -9,6 +9,13 @@
 #include "time-util.h"
 #include "user-record.h"
 
+/* Flags supported by UpdateEx() */
+#define SD_HOMED_UPDATE_OFFLINE (UINT64_C(1) << 0)
+#define SD_HOMED_UPDATE_FLAGS_ALL (SD_HOMED_UPDATE_OFFLINE)
+
+/* Flags supported by CreateHomeEx() */
+#define SD_HOMED_CREATE_FLAGS_ALL (0)
+
 /* Put some limits on disk sizes: not less than 5M, not more than 5T */
 #define USER_DISK_SIZE_MIN (UINT64_C(5)*1024*1024)
 #define USER_DISK_SIZE_MAX (UINT64_C(5)*1024*1024*1024*1024)
index 11a138070b1fd09b2dc28bb4a9cdb88e36bd3b9d..2a7917d7f9f846e53ee90a640d345f14a24f54b4 100644 (file)
@@ -61,6 +61,7 @@ static bool arg_legend = true;
 static bool arg_ask_password = true;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static const char *arg_host = NULL;
+static bool arg_offline = false;
 static const char *arg_identity = NULL;
 static JsonVariant *arg_identity_extra = NULL;
 static JsonVariant *arg_identity_extra_privileged = NULL;
@@ -1712,6 +1713,7 @@ static int update_home(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *buffer = NULL;
         _cleanup_hashmap_free_ Hashmap *blobs = NULL;
         const char *username;
+        uint64_t flags = 0;
         int r;
 
         if (argc >= 2)
@@ -1754,6 +1756,9 @@ static int update_home(int argc, char *argv[], void *userdata) {
         if (arg_and_resize || arg_and_change_password)
                 log_info("Updating home directory.");
 
+        if (arg_offline)
+                flags |= SD_HOMED_UPDATE_OFFLINE;
+
         for (;;) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
@@ -1777,7 +1782,7 @@ static int update_home(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return bus_log_create_error(r);
 
-                r = sd_bus_message_append(m, "t", UINT64_C(0));
+                r = sd_bus_message_append(m, "t", flags);
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -2564,6 +2569,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --no-pager                Do not pipe output into a pager\n"
                "     --no-legend               Do not show the headers and footers\n"
                "     --no-ask-password         Do not ask for system passwords\n"
+               "     --offline                 Don't update record embedded in home directory\n"
                "  -H --host=[USER@]HOST        Operate on remote host\n"
                "  -M --machine=CONTAINER       Operate on local container\n"
                "     --identity=PATH           Read JSON identity from file\n"
@@ -2723,6 +2729,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_NO_PAGER,
                 ARG_NO_LEGEND,
                 ARG_NO_ASK_PASSWORD,
+                ARG_OFFLINE,
                 ARG_REALM,
                 ARG_EMAIL_ADDRESS,
                 ARG_DISK_SIZE,
@@ -2808,6 +2815,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "no-pager",                    no_argument,       NULL, ARG_NO_PAGER                    },
                 { "no-legend",                   no_argument,       NULL, ARG_NO_LEGEND                   },
                 { "no-ask-password",             no_argument,       NULL, ARG_NO_ASK_PASSWORD             },
+                { "offline",                     no_argument,       NULL, ARG_OFFLINE                     },
                 { "host",                        required_argument, NULL, 'H'                             },
                 { "machine",                     required_argument, NULL, 'M'                             },
                 { "identity",                    required_argument, NULL, 'I'                             },
@@ -2933,6 +2941,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_ask_password = false;
                         break;
 
+                case ARG_OFFLINE:
+                        arg_offline = true;
+                        break;
+
                 case 'H':
                         arg_transport = BUS_TRANSPORT_REMOTE;
                         arg_host = optarg;
index 624cbdb3d33d90847a1d6dc0bee55e4ec2bc268e..23578fe314be5cbbd961e634460c5c971fa36ddc 100644 (file)
@@ -6,6 +6,7 @@
 #include "bus-polkit.h"
 #include "fd-util.h"
 #include "format-util.h"
+#include "home-util.h"
 #include "homed-bus.h"
 #include "homed-home-bus.h"
 #include "homed-home.h"
@@ -432,8 +433,8 @@ int bus_home_update_record(
         if (r < 0)
                 return r;
 
-        if (flags != 0)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Provided flags are unsupported.");
+        if ((flags & ~SD_HOMED_UPDATE_FLAGS_ALL) != 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags provided.");
 
         r = home_verify_polkit_async(
                         h,
@@ -457,6 +458,8 @@ int bus_home_update_record(
         if (r < 0)
                 return r;
 
+        h->current_operation->call_flags = flags;
+
         return 1;
 }
 
@@ -473,7 +476,7 @@ int bus_home_method_update(
 
         assert(message);
 
-        r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_REQUIRE_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_PERMISSIVE, &hr, error);
+        r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_PERMISSIVE, &hr, error);
         if (r < 0)
                 return r;
 
@@ -521,7 +524,7 @@ int bus_home_method_resize(
         if (r == 0)
                 return 1; /* Will call us back */
 
-        r = home_resize(h, sz, secret, /* automatic= */ false, error);
+        r = home_resize(h, sz, secret, error);
         if (r < 0)
                 return r;
 
index e1379f51f5840e8bd3ac7d71f3889258d27e2786..447e8c597cadd12ca9ce33b75157d1355432fce3 100644 (file)
@@ -955,10 +955,13 @@ static void home_create_finish(Home *h, int ret, UserRecord *hr) {
 
 static void home_change_finish(Home *h, int ret, UserRecord *hr) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        uint64_t flags;
         int r;
 
         assert(h);
 
+        flags = h->current_operation ? h->current_operation->call_flags : 0;
+
         if (ret < 0) {
                 (void) home_count_bad_authentication(h, ret, /* save= */ true);
 
@@ -969,17 +972,22 @@ static void home_change_finish(Home *h, int ret, UserRecord *hr) {
         }
 
         if (hr) {
-                r = home_set_record(h, hr);
-                if (r < 0)
-                        log_warning_errno(r, "Failed to update home record, ignoring: %m");
-                else {
+                if (!FLAGS_SET(flags, SD_HOMED_UPDATE_OFFLINE)) {
                         r = user_record_good_authentication(h->record);
                         if (r < 0)
                                 log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+                }
 
+                r = home_set_record(h, hr);
+                if (r >= 0)
                         r = home_save_record(h);
-                        if (r < 0)
-                                log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+                if (r < 0) {
+                        if (FLAGS_SET(flags, SD_HOMED_UPDATE_OFFLINE)) {
+                                log_error_errno(r, "Failed to update home record and write it to disk: %m");
+                                sd_bus_error_set(&error, SD_BUS_ERROR_FAILED, "Failed to cache changes to home record");
+                                goto finish;
+                        } else
+                                log_warning_errno(r, "Failed to update home record, ignoring: %m");
                 }
         }
 
@@ -1312,6 +1320,11 @@ static int home_start_work(
                                 _exit(EXIT_FAILURE);
                         }
 
+                if (setenv("SYSTEMD_HOMEWORK_UPDATE_OFFLINE", one_zero(FLAGS_SET(flags, SD_HOMED_UPDATE_OFFLINE)), 1) < 0) {
+                        log_error_errno(errno, "Failed to set $SYSTEMD_HOMEWORK_UPDATE_OFFLINE: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
                 r = setenv_systemd_exec_pid(true);
                 if (r < 0)
                         log_warning_errno(r, "Failed to update $SYSTEMD_EXEC_PID, ignoring: %m");
@@ -1783,7 +1796,9 @@ int home_update(Home *h, UserRecord *hr, Hashmap *blobs, uint64_t flags, sd_bus_
         case HOME_UNFIXATED:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_UNFIXATED, "Home %s has not been fixated yet.", h->user_name);
         case HOME_ABSENT:
-                return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+                if (!FLAGS_SET(flags, SD_HOMED_UPDATE_OFFLINE))
+                        return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+                break; /* offline updates are compatible w/ an absent home area */
         case HOME_LOCKED:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
         case HOME_INACTIVE:
@@ -1810,7 +1825,6 @@ int home_update(Home *h, UserRecord *hr, Hashmap *blobs, uint64_t flags, sd_bus_
 int home_resize(Home *h,
                 uint64_t disk_size,
                 UserRecord *secret,
-                bool automatic,
                 sd_bus_error *error) {
 
         _cleanup_(user_record_unrefp) UserRecord *c = NULL;
@@ -1886,7 +1900,7 @@ int home_resize(Home *h,
                 c = TAKE_PTR(signed_c);
         }
 
-        r = home_update_internal(h, automatic ? "resize-auto" : "resize", c, secret, NULL, 0, error);
+        r = home_update_internal(h, "resize", c, secret, NULL, 0, error);
         if (r < 0)
                 return r;
 
@@ -2756,7 +2770,9 @@ int home_create_fifo(Home *h, bool please_suspend) {
 
                 (void) sd_event_source_set_description(*ss, "acquire-ref");
 
-                r = sd_event_source_set_priority(*ss, SD_EVENT_PRIORITY_IDLE-1);
+                /* We need to notice dropped refs before we process new bus requests (which
+                 * might try to obtain new refs) */
+                r = sd_event_source_set_priority(*ss, SD_EVENT_PRIORITY_NORMAL-10);
                 if (r < 0)
                         return r;
 
index 7f42461a45df64873e5bd3b265a61ab04b37319e..c0f1b83debad6ea0d5ca3d0758b29dccfec56fd4 100644 (file)
@@ -192,7 +192,7 @@ int home_deactivate(Home *h, bool force, sd_bus_error *error);
 int home_create(Home *h, UserRecord *secret, Hashmap *blobs, uint64_t flags, sd_bus_error *error);
 int home_remove(Home *h, sd_bus_error *error);
 int home_update(Home *h, UserRecord *new_record, Hashmap *blobs, uint64_t flags, sd_bus_error *error);
-int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, bool automatic, sd_bus_error *error);
+int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *error);
 int home_passwd(Home *h, UserRecord *new_secret, UserRecord *old_secret, sd_bus_error *error);
 int home_unregister(Home *h, sd_bus_error *error);
 int home_lock(Home *h, sd_bus_error *error);
index 403a7d0213b40d18353482dac95d0a4cb81df0ca..58cd0371057575d6559f25c3884650a1fbd77d87 100644 (file)
@@ -6,6 +6,7 @@
 #include "bus-common-errors.h"
 #include "bus-polkit.h"
 #include "format-util.h"
+#include "home-util.h"
 #include "homed-bus.h"
 #include "homed-home-bus.h"
 #include "homed-manager-bus.h"
@@ -507,8 +508,8 @@ static int method_create_home(sd_bus_message *message, void *userdata, sd_bus_er
                 r = sd_bus_message_read(message, "t", &flags);
                 if (r < 0)
                         return r;
-                if (flags != 0)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Provided flags are unsupported.");
+                if ((flags & ~SD_HOMED_CREATE_FLAGS_ALL) != 0)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags provided.");
         }
 
         r = bus_verify_polkit_async(
@@ -538,6 +539,8 @@ static int method_create_home(sd_bus_message *message, void *userdata, sd_bus_er
         if (r < 0)
                 return r;
 
+        h->current_operation->call_flags = flags;
+
         return 1;
 
 fail:
index 0653eeef784cfda56668cf5ae6d09f2054e76d72..5f345b3d407664db18d440d0d7ee626a9fbb1fa4 100644 (file)
@@ -991,7 +991,7 @@ static int manager_connect_bus(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to request name: %m");
 
-        r = sd_bus_attach_event(m->bus, m->event, 0);
+        r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL);
         if (r < 0)
                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
 
@@ -2025,7 +2025,7 @@ static int manager_rebalance_apply(Manager *m) {
 
                 h->rebalance_pending = false;
 
-                r = home_resize(h, h->rebalance_goal, /* secret= */ NULL, /* automatic= */ true, &error);
+                r = home_resize(h, h->rebalance_goal, /* secret= */ NULL, &error);
                 if (r < 0)
                         log_warning_errno(r, "Failed to resize home '%s' for rebalancing, ignoring: %s",
                                           h->user_name, bus_error_message(&error, r));
index 004246a4e642a9711c5f13e1d40420409937d3ac..af165bb4a5213fe231a35341328e3b231d795baf 100644 (file)
@@ -39,6 +39,7 @@ typedef struct Operation {
         sd_bus_message *message;
 
         UserRecord *secret;
+        uint64_t call_flags; /* flags passed into UpdateEx() or CreateHomeEx() */
         int send_fd;   /* pipe fd for AcquireHome() which is taken already when we start the operation */
 
         int result;    /* < 0 if not completed yet, == 0 on failure, > 0 on success */
index 8dcb5c5d46c3ea1202b9ded60b7871425b6ce4c6..d70926fe33deae3a1a52c172216b8fafd4d656d2 100644 (file)
@@ -256,43 +256,30 @@ static int run_fsck(const char *node, const char *fstype) {
 
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(key_serial_t, keyring_unlink, -1);
 
-static int upload_to_keyring(
-                UserRecord *h,
-                const char *password,
-                key_serial_t *ret_key_serial) {
+static int upload_to_keyring(UserRecord *h, const void *vk, size_t vks, key_serial_t *ret) {
 
         _cleanup_free_ char *name = NULL;
         key_serial_t serial;
 
         assert(h);
-        assert(password);
-
-        /* If auto-shrink-on-logout is turned on, we need to keep the key we used to unlock the LUKS volume
-         * around, since we'll need it when automatically resizing (since we can't ask the user there
-         * again). We do this by uploading it into the kernel keyring, specifically the "session" one. This
-         * is done under the assumption systemd-homed gets its private per-session keyring (i.e. default
-         * service behaviour, given that KeyringMode=private is the default). It will survive between our
-         * systemd-homework invocations that way.
-         *
-         * If auto-shrink-on-logout is disabled we'll skip this step, to be frugal with sensitive data. */
-
-        if (user_record_auto_resize_mode(h) != AUTO_RESIZE_SHRINK_AND_GROW) {  /* Won't need it */
-                if (ret_key_serial)
-                        *ret_key_serial = -1;
-                return 0;
-        }
+        assert(vk);
+        assert(vks > 0);
+
+        /* We upload the LUKS volume key into the kernel session keyring, under the assumption that
+         * systemd-homed gets its own private session keyring (i.e. the default service behavior, given
+         * that KeyringMode=private is the default). That way, the key will survive between invocations
+         * of systemd-homework. */
 
         name = strjoin("homework-user-", h->user_name);
         if (!name)
                 return -ENOMEM;
 
-        serial = add_key("user", name, password, strlen(password), KEY_SPEC_SESSION_KEYRING);
+        serial = add_key("user", name, vk, vks, KEY_SPEC_SESSION_KEYRING);
         if (serial == -1)
                 return -errno;
 
-        if (ret_key_serial)
-                *ret_key_serial = serial;
-
+        if (ret)
+                *ret = serial;
         return 1;
 }
 
@@ -301,13 +288,14 @@ static int luks_try_passwords(
                 struct crypt_device *cd,
                 char **passwords,
                 void *volume_key,
-                size_t *volume_key_size,
-                key_serial_t *ret_key_serial) {
+                size_t *volume_key_size) {
 
         int r;
 
         assert(h);
         assert(cd);
+        assert(volume_key);
+        assert(volume_key_size);
 
         STRV_FOREACH(pp, passwords) {
                 size_t vks = *volume_key_size;
@@ -320,16 +308,6 @@ static int luks_try_passwords(
                                 *pp,
                                 strlen(*pp));
                 if (r >= 0) {
-                        if (ret_key_serial) {
-                                /* If ret_key_serial is non-NULL, let's try to upload the password that
-                                 * worked, and return its serial. */
-                                r = upload_to_keyring(h, *pp, ret_key_serial);
-                                if (r < 0) {
-                                        log_debug_errno(r, "Failed to upload LUKS password to kernel keyring, ignoring: %m");
-                                        *ret_key_serial = -1;
-                                }
-                        }
-
                         *volume_key_size = vks;
                         return 0;
                 }
@@ -340,6 +318,66 @@ static int luks_try_passwords(
         return -ENOKEY;
 }
 
+static int luks_get_volume_key(
+                UserRecord *h,
+                struct crypt_device *cd,
+                const PasswordCache *cache,
+                void *volume_key,
+                size_t *volume_key_size,
+                key_serial_t *ret_key_serial) {
+
+        char **list;
+        size_t vks;
+        int r;
+
+        assert(h);
+        assert(cd);
+        assert(volume_key);
+        assert(volume_key_size);
+
+        if (cache && cache->volume_key) {
+                /* Shortcut: If volume key was loaded from the keyring then just use it */
+                if (cache->volume_key_size > *volume_key_size)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOBUFS),
+                                               "LUKS volume key from kernel keyring too big for buffer (need %zu bytes, have %zu)",
+                                               cache->volume_key_size, *volume_key_size);
+                memcpy(volume_key, cache->volume_key, cache->volume_key_size);
+                *volume_key_size = cache->volume_key_size;
+                if (ret_key_serial)
+                        *ret_key_serial = -1; /* Key came from keyring. No need to re-upload it */
+                return 0;
+        }
+
+        vks = *volume_key_size;
+
+        FOREACH_ARGUMENT(list,
+                         cache ? cache->pkcs11_passwords : NULL,
+                         cache ? cache->fido2_passwords : NULL,
+                         h->password) {
+
+                r = luks_try_passwords(h, cd, list, volume_key, &vks);
+                if (r == -ENOKEY)
+                        continue;
+                if (r < 0)
+                        return r;
+
+                /* We got a volume key! */
+
+                if (ret_key_serial) {
+                        r = upload_to_keyring(h, volume_key, vks, ret_key_serial);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to upload LUKS volume key to kernel keyring, ignoring: %m");
+                                *ret_key_serial = -1;
+                        }
+                }
+
+                *volume_key_size = vks;
+                return 0;
+        }
+
+        return -ENOKEY;
+}
+
 static int luks_setup(
                 UserRecord *h,
                 const char *node,
@@ -348,7 +386,6 @@ static int luks_setup(
                 const char *cipher,
                 const char *cipher_mode,
                 uint64_t volume_key_size,
-                char **passwords,
                 const PasswordCache *cache,
                 bool discard,
                 struct crypt_device **ret,
@@ -414,18 +451,7 @@ static int luks_setup(
         if (!vk)
                 return log_oom();
 
-        r = -ENOKEY;
-        char **list;
-        FOREACH_ARGUMENT(list,
-                         cache ? cache->keyring_passswords : NULL,
-                         cache ? cache->pkcs11_passwords : NULL,
-                         cache ? cache->fido2_passwords : NULL,
-                         passwords) {
-
-                r = luks_try_passwords(h, cd, list, vk, &vks, ret_key_serial ? &key_serial : NULL);
-                if (r != -ENOKEY)
-                        break;
-        }
+        r = luks_get_volume_key(h, cd, cache, vk, &vks, ret_key_serial ? &key_serial : NULL);
         if (r == -ENOKEY)
                 return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
@@ -556,18 +582,7 @@ static int luks_open(
         if (!vk)
                 return log_oom();
 
-        r = -ENOKEY;
-        char **list;
-        FOREACH_ARGUMENT(list,
-                         cache ? cache->keyring_passswords : NULL,
-                         cache ? cache->pkcs11_passwords : NULL,
-                         cache ? cache->fido2_passwords : NULL,
-                         h->password) {
-
-                r = luks_try_passwords(h, setup->crypt_device, list, vk, &vks, NULL);
-                if (r != -ENOKEY)
-                        break;
-        }
+        r = luks_get_volume_key(h, setup->crypt_device, cache, vk, &vks, NULL);
         if (r == -ENOKEY)
                 return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
@@ -1401,7 +1416,6 @@ int home_setup_luks(
                                h->luks_cipher,
                                h->luks_cipher_mode,
                                h->luks_volume_key_size,
-                               h->password,
                                cache,
                                user_record_luks_discard(h) || user_record_luks_offline_discard(h),
                                &setup->crypt_device,
@@ -3619,18 +3633,7 @@ int home_passwd_luks(
         if (!volume_key)
                 return log_oom();
 
-        r = -ENOKEY;
-        char **list;
-        FOREACH_ARGUMENT(list,
-                         cache ? cache->keyring_passswords : NULL,
-                         cache ? cache->pkcs11_passwords : NULL,
-                         cache ? cache->fido2_passwords : NULL,
-                         h->password) {
-
-                r = luks_try_passwords(h, setup->crypt_device, list, volume_key, &volume_key_size, NULL);
-                if (r != -ENOKEY)
-                        break;
-        }
+        r = luks_get_volume_key(h, setup->crypt_device, cache, volume_key, &volume_key_size, NULL);
         if (r == -ENOKEY)
                 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords.");
         if (r < 0)
@@ -3673,11 +3676,6 @@ int home_passwd_luks(
                         return log_error_errno(r, "Failed to set up LUKS password: %m");
 
                 log_info("Updated LUKS key slot %zu.", i);
-
-                /* If we changed the password, then make sure to update the copy in the keyring, so that
-                 * auto-rebalance continues to work. We only do this if we operate on an active home dir. */
-                if (i == 0 && FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED))
-                        upload_to_keyring(h, effective_passwords[i], NULL);
         }
 
         return 1;
@@ -3715,35 +3713,10 @@ int home_lock_luks(UserRecord *h, HomeSetup *setup) {
         return 0;
 }
 
-static int luks_try_resume(
-                struct crypt_device *cd,
-                const char *dm_name,
-                char **password) {
-
-        int r;
-
-        assert(cd);
-        assert(dm_name);
-
-        STRV_FOREACH(pp, password) {
-                r = sym_crypt_resume_by_passphrase(
-                                cd,
-                                dm_name,
-                                CRYPT_ANY_SLOT,
-                                *pp,
-                                strlen(*pp));
-                if (r >= 0) {
-                        log_info("Resumed LUKS device %s.", dm_name);
-                        return 0;
-                }
-
-                log_debug_errno(r, "Password %zu didn't work for resuming device: %m", (size_t) (pp - password));
-        }
-
-        return -ENOKEY;
-}
-
 int home_unlock_luks(UserRecord *h, HomeSetup *setup, const PasswordCache *cache) {
+        _cleanup_(keyring_unlinkp) key_serial_t key_serial = -1;
+        _cleanup_(erase_and_freep) void *vk = NULL;
+        size_t vks;
         int r;
 
         assert(h);
@@ -3756,22 +3729,27 @@ int home_unlock_luks(UserRecord *h, HomeSetup *setup, const PasswordCache *cache
 
         log_info("Discovered used LUKS device %s.", setup->dm_node);
 
-        r = -ENOKEY;
-        char **list;
-        FOREACH_ARGUMENT(list,
-                         cache ? cache->pkcs11_passwords : NULL,
-                         cache ? cache->fido2_passwords : NULL,
-                         h->password) {
+        r = sym_crypt_get_volume_key_size(setup->crypt_device);
+        if (r <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size");
+        vks = (size_t) r;
 
-                r = luks_try_resume(setup->crypt_device, setup->dm_name, list);
-                if (r != -ENOKEY)
-                        break;
-        }
+        vk = malloc(vks);
+        if (!vk)
+                return log_oom();
+
+        r = luks_get_volume_key(h, setup->crypt_device, cache, vk, &vks, &key_serial);
         if (r == -ENOKEY)
                 return log_error_errno(r, "No valid password for LUKS superblock.");
+        if (r < 0)
+                return log_error_errno(r, "Failed to unlock LUKS superblock: %m");
+
+        r = sym_crypt_resume_by_volume_key(setup->crypt_device, setup->dm_name, vk, vks);
         if (r < 0)
                 return log_error_errno(r, "Failed to resume LUKS superblock: %m");
 
+        TAKE_KEY_SERIAL(key_serial); /* Leave key in kernel keyring */
+
         log_info("LUKS device resumed.");
         return 0;
 }
index 00a0f69bc91fe37c065da932529623d34afaff23..b8202ef69543ae0dde1246479e737bc9af5fc9c3 100644 (file)
@@ -9,49 +9,41 @@ void password_cache_free(PasswordCache *cache) {
         if (!cache)
                 return;
 
+        cache->volume_key = erase_and_free(cache->volume_key);
         cache->pkcs11_passwords = strv_free_erase(cache->pkcs11_passwords);
         cache->fido2_passwords = strv_free_erase(cache->fido2_passwords);
-        cache->keyring_passswords = strv_free_erase(cache->keyring_passswords);
 }
 
 void password_cache_load_keyring(UserRecord *h, PasswordCache *cache) {
-        _cleanup_(erase_and_freep) void *p = NULL;
         _cleanup_free_ char *name = NULL;
-        char **strv;
+        _cleanup_(erase_and_freep) void *vk = NULL;
+        size_t vks;
         key_serial_t serial;
-        size_t sz;
         int r;
 
         assert(h);
         assert(cache);
 
-        /* Loads the password we need to for automatic resizing from the kernel keyring */
-
         name = strjoin("homework-user-", h->user_name);
         if (!name)
                 return (void) log_oom();
 
         serial = request_key("user", name, NULL, 0);
-        if (serial == -1)
-                return (void) log_debug_errno(errno, "Failed to request key '%s', ignoring: %m", name);
-
-        r = keyring_read(serial, &p, &sz);
+        if (serial == -1) {
+                if (errno == ENOKEY) {
+                        log_info("Home volume key is not available in kernel keyring.");
+                        return;
+                }
+                return (void) log_warning_errno(errno, "Failed to request key '%s', ignoring: %m", name);
+        }
+
+        r = keyring_read(serial, &vk, &vks);
         if (r < 0)
-                return (void) log_debug_errno(r, "Failed to read keyring key '%s', ignoring: %m", name);
-
-        if (memchr(p, 0, sz))
-                return (void) log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Cached password contains embedded NUL byte, ignoring.");
-
-        strv = new(char*, 2);
-        if (!strv)
-                return (void) log_oom();
-
-        strv[0] = TAKE_PTR(p); /* Note that keyring_read() will NUL terminate implicitly, hence we don't have
-                                * to NUL terminate manually here: it's a valid string. */
-        strv[1] = NULL;
+                return (void) log_warning_errno(r, "Failed to read keyring key '%s', ignoring: %m", name);
 
-        strv_free_erase(cache->keyring_passswords);
-        cache->keyring_passswords = strv;
+        log_info("Successfully acquired home volume key from kernel keyring.");
 
-        log_debug("Successfully acquired home key from kernel keyring.");
+        erase_and_free(cache->volume_key);
+        cache->volume_key = TAKE_PTR(vk);
+        cache->volume_key_size = vks;
 }
index fdfbcfe4e0ccae3c61d92bdf67c21255e7ec49d9..e2d86eb9393f03776e3ad8fc767b32ac3f0d6e77 100644 (file)
@@ -5,8 +5,9 @@
 #include "user-record.h"
 
 typedef struct PasswordCache {
-        /* Passwords acquired from the kernel keyring */
-        char **keyring_passswords;
+        /* The volume key from the kernel keyring */
+        void *volume_key;
+        size_t volume_key_size;
 
         /* Decoding passwords from security tokens is expensive and typically requires user interaction,
          * hence cache any we already figured out. */
@@ -20,9 +21,12 @@ static inline bool password_cache_contains(const PasswordCache *cache, const cha
         if (!cache)
                 return false;
 
+        /* Used to decide whether or not to set a minimal PBKDF, under the assumption that if
+         * the cache contains a password then the password came from a hardware token of some kind
+         * and is thus naturally high-entropy. */
+
         return strv_contains(cache->pkcs11_passwords, p) ||
-                strv_contains(cache->fido2_passwords, p) ||
-                strv_contains(cache->keyring_passswords, p);
+                strv_contains(cache->fido2_passwords, p);
 }
 
 void password_cache_load_keyring(UserRecord *h, PasswordCache *cache);
index 531443e7576fcf47dc2b591191d019fc28f2a76f..afc114229869a89fee06ae26ab1a1a709f9bf6a0 100644 (file)
@@ -66,9 +66,25 @@ int user_record_authenticate(
          * times over the course of an operation (think: on login we authenticate the host user record, the
          * record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a list of
          * passwords we already decrypted, so that we don't have to do the (slow and potentially interactive)
-         * PKCS#11/FIDO2 dance for the relevant token again and again. */
+         * PKCS#11/FIDO2 dance for the relevant token again and again.
+         *
+         * The 'cache' parameter might also contain the LUKS volume key, loaded from the kernel keyring.
+         * In this case, authentication becomes optional - if a secret section is provided it will be
+         * verified, but if missing then authentication is skipped entirely. Thus, callers should
+         * consider carefuly whether it is safe to load the volume key into 'cache' before doing so.
+         * Note that most of the time this is safe, because the home area must be active for the key
+         * to exist in the keyring, and the user would have had to authenticate when activating their
+         * home area; however, for some methods (i.e. ChangePassword, Authenticate) it makes more sense
+         * to force re-authentication. */
+
+        /* First, let's see if we already have a volume key from the keyring */
+        if (cache && cache->volume_key &&
+            json_variant_is_blank_object(json_variant_by_key(secret->json, "secret"))) {
+                log_info("LUKS volume key from keyring unlocks user record.");
+                return 1;
+        }
 
-        /* First, let's see if the supplied plain-text passwords work? */
+        /* Next, let's see if the supplied plain-text passwords work? */
         r = user_record_test_password(h, secret);
         if (r == -ENOKEY)
                 need_password = true;
@@ -101,7 +117,7 @@ int user_record_authenticate(
         else
                 log_info("None of the supplied plaintext passwords unlock the user record's hashed recovery keys.");
 
-        /* Second, test cached PKCS#11 passwords */
+        /* Next, test cached PKCS#11 passwords */
         for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++)
                 STRV_FOREACH(pp, cache->pkcs11_passwords) {
                         r = test_password_one(h->pkcs11_encrypted_key[n].hashed_password, *pp);
@@ -113,7 +129,7 @@ int user_record_authenticate(
                         }
                 }
 
-        /* Third, test cached FIDO2 passwords */
+        /* Next, test cached FIDO2 passwords */
         for (size_t n = 0; n < h->n_fido2_hmac_salt; n++)
                 /* See if any of the previously calculated passwords work */
                 STRV_FOREACH(pp, cache->fido2_passwords) {
@@ -126,7 +142,7 @@ int user_record_authenticate(
                         }
                 }
 
-        /* Fourth, let's see if any of the PKCS#11 security tokens are plugged in and help us */
+        /* Next, let's see if any of the PKCS#11 security tokens are plugged in and help us */
         for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) {
 #if HAVE_P11KIT
                 _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
@@ -182,7 +198,7 @@ int user_record_authenticate(
 #endif
         }
 
-        /* Fifth, let's see if any of the FIDO2 security tokens are plugged in and help us */
+        /* Next, let's see if any of the FIDO2 security tokens are plugged in and help us */
         for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) {
 #if HAVE_LIBFIDO2
                 _cleanup_(erase_and_freep) char *decrypted_password = NULL;
@@ -1535,20 +1551,32 @@ static int home_remove(UserRecord *h) {
         return 0;
 }
 
-static int home_validate_update(UserRecord *h, HomeSetup *setup, HomeSetupFlags *flags) {
-        bool has_mount = false;
-        int r;
-
+static int home_basic_validate_update(UserRecord *h) {
         assert(h);
-        assert(setup);
 
         if (!h->user_name)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks user name, refusing.");
+
         if (!uid_is_valid(h->uid))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing.");
+
         if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Processing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
 
+        return 0;
+}
+
+static int home_validate_update(UserRecord *h, HomeSetup *setup, HomeSetupFlags *flags) {
+        bool has_mount = false;
+        int r;
+
+        assert(h);
+        assert(setup);
+
+        r = home_basic_validate_update(h);
+        if (r < 0)
+                return r;
+
         r = user_record_test_home_directory_and_warn(h);
         if (r < 0)
                 return r;
@@ -1594,17 +1622,31 @@ static int home_update(UserRecord *h, Hashmap *blobs, UserRecord **ret) {
         _cleanup_(home_setup_done) HomeSetup setup = HOME_SETUP_INIT;
         _cleanup_(password_cache_free) PasswordCache cache = {};
         HomeSetupFlags flags = 0;
+        bool offline;
         int r;
 
         assert(h);
         assert(ret);
 
-        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
-        if (r < 0)
-                return r;
-        assert(r > 0); /* Insist that a password was verified */
+        offline = getenv_bool("SYSTEMD_HOMEWORK_UPDATE_OFFLINE") > 0;
 
-        r = home_validate_update(h, &setup, &flags);
+        if (!offline) {
+                password_cache_load_keyring(h, &cache);
+
+                r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
+                if (r < 0)
+                        return r;
+                assert(r > 0); /* Insist that a password was verified */
+
+                r = home_validate_update(h, &setup, &flags);
+        } else {
+                /* In offline mode we skip all authentication, since we're
+                 * not propagating anything into the home area. The new home
+                 * records's authentication will still be checked when the user
+                 * next logs in, so this is fine */
+
+                r = home_basic_validate_update(h);
+        }
         if (r < 0)
                 return r;
 
@@ -1612,6 +1654,11 @@ static int home_update(UserRecord *h, Hashmap *blobs, UserRecord **ret) {
         if (r < 0)
                 return r;
 
+        if (offline) {
+                log_info("Offline update requested. Not touching embedded records.");
+                return user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_PERMISSIVE, ret);
+        }
+
         r = home_setup(h, flags, &setup, &cache, &header_home);
         if (r < 0)
                 return r;
@@ -1654,7 +1701,7 @@ static int home_update(UserRecord *h, Hashmap *blobs, UserRecord **ret) {
         return 0;
 }
 
-static int home_resize(UserRecord *h, bool automatic, UserRecord **ret) {
+static int home_resize(UserRecord *h, UserRecord **ret) {
         _cleanup_(home_setup_done) HomeSetup setup = HOME_SETUP_INIT;
         _cleanup_(password_cache_free) PasswordCache cache = {};
         HomeSetupFlags flags = 0;
@@ -1666,25 +1713,16 @@ static int home_resize(UserRecord *h, bool automatic, UserRecord **ret) {
         if (h->disk_size == UINT64_MAX)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No target size specified, refusing.");
 
-        if (automatic)
-                /* In automatic mode don't want to ask the user for the password, hence load it from the kernel keyring */
-                password_cache_load_keyring(h, &cache);
-        else {
-                /* In manual mode let's ensure the user is fully authenticated */
-                r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
-                if (r < 0)
-                        return r;
-                assert(r > 0); /* Insist that a password was verified */
-        }
+        password_cache_load_keyring(h, &cache);
 
-        r = home_validate_update(h, &setup, &flags);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
         if (r < 0)
                 return r;
+        assert(r > 0); /* Insist that a password was verified */
 
-        /* In automatic mode let's skip syncing identities, because we can't validate them, since we can't
-         * ask the user for reauthentication */
-        if (automatic)
-                flags |= HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES;
+        r = home_validate_update(h, &setup, &flags);
+        if (r < 0)
+                return r;
 
         switch (user_record_storage(h)) {
 
@@ -1883,6 +1921,9 @@ static int home_lock(UserRecord *h) {
 
         unit_freezer_done(&freezer); /* Don't thaw the user session. */
 
+        /* Explicitly flush any per-user key from the keyring */
+        (void) keyring_flush(h);
+
         log_info("Everything completed.");
         return 1;
 }
@@ -2050,10 +2091,8 @@ static int run(int argc, char *argv[]) {
                 r = home_remove(home);
         else if (streq(argv[1], "update"))
                 r = home_update(home, blobs, &new_home);
-        else if (streq(argv[1], "resize")) /* Resize on user request */
-                r = home_resize(home, false, &new_home);
-        else if (streq(argv[1], "resize-auto")) /* Automatic resize */
-                r = home_resize(home, true, &new_home);
+        else if (streq(argv[1], "resize"))
+                r = home_resize(home, &new_home);
         else if (streq(argv[1], "passwd"))
                 r = home_passwd(home, &new_home);
         else if (streq(argv[1], "inspect"))
index 15f3f0e025dad71eb6c4c550f7c58982f3c076e8..898e08774a26ab8922110a4c61345a838e894ddd 100644 (file)
@@ -344,7 +344,7 @@ static int help_facilities(void) {
         for (int i = 0; i < LOG_NFACILITIES; i++) {
                 _cleanup_free_ char *t = NULL;
 
-                if (log_facility_unshifted_to_string_alloc(i, &t))
+                if (log_facility_unshifted_to_string_alloc(i, &t) < 0)
                         return log_oom();
                 puts(t);
         }
index a89e2b0ed7c874ee169cd1490d5af9c6b43b83bb..eed670fb6f017180c09784f02be677aa3ecdfa39 100644 (file)
@@ -9,22 +9,19 @@
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "fuzz.h"
+#include "icmp6-packet.h"
 #include "icmp6-util-unix.h"
 #include "ndisc-internal.h"
+#include "ndisc-option.h"
 #include "socket-util.h"
 
-int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+static void test_with_sd_ndisc(const uint8_t *data, size_t size) {
         struct ether_addr mac_addr = {
                 .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
         };
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
 
-        if (outside_size_range(size, 0, 2048))
-                return 0;
-
-        fuzz_setup_logging();
-
         assert_se(sd_event_new(&e) >= 0);
         assert_se(sd_ndisc_new(&nd) >= 0);
         assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
@@ -35,6 +32,46 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         (void) sd_event_run(e, UINT64_MAX);
         assert_se(sd_ndisc_stop(nd) >= 0);
         close(test_fd[1]);
+}
+
+static void test_with_icmp6_packet(const uint8_t *data, size_t size) {
+        static const struct sockaddr_in6 dst = {
+                .sin6_family = AF_INET6,
+                .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+        };
+
+        _cleanup_close_pair_ int fd_pair[2] = EBADF_PAIR;
+        _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+        _cleanup_set_free_ Set *options = NULL;
+
+        assert_se(socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, fd_pair) >= 0);
+        assert_se(write(fd_pair[1], data, size) == (ssize_t) size);
+
+        if (icmp6_packet_receive(fd_pair[0], &packet) < 0)
+                return;
+
+        if (ndisc_parse_options(packet, &options) < 0)
+                return;
+
+        if (ndisc_send(fd_pair[1], &dst, icmp6_packet_get_header(packet), options) < 0)
+                return;
+
+        packet = icmp6_packet_unref(packet);
+        options = set_free(options);
+
+        if (icmp6_packet_receive(fd_pair[0], &packet) < 0)
+                return;
+
+        (void) ndisc_parse_options(packet, &options);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+        if (outside_size_range(size, 0, 2048))
+                return 0;
+
+        fuzz_setup_logging();
 
+        test_with_sd_ndisc(data, size);
+        test_with_icmp6_packet(data, size);
         return 0;
 }
index a0b0e84f5498cb61389c0708cced56fb645a399e..35459d11a29003a81c18ed0347880b4889e4703f 100644 (file)
@@ -52,7 +52,7 @@ int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret) {
         return 0;
 }
 
-static const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p) {
+const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p) {
         assert(p);
 
         if (p->raw_size < sizeof(struct icmp6_hdr))
index d77d2e8537a7a53ca8e9c294ca4514ad137b2719..7d02534a8c27c84042b7f2960149e063ae906d41 100644 (file)
@@ -23,6 +23,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(ICMP6Packet*, icmp6_packet_unref);
 
 int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret);
 int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret);
+const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p);
 int icmp6_packet_get_type(ICMP6Packet *p);
 
 int icmp6_packet_receive(int fd, ICMP6Packet **ret);
index 5eac4a0e5357ef27a88b030067998ac613acb6b0..c400e4205ec24456b2a6fdfea09774f431f21cef 100644 (file)
@@ -1,12 +1,12 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <netinet/icmp6.h>
 #include <netinet/ip6.h>
 #include <unistd.h>
 
 #include "fd-util.h"
 #include "icmp6-util-unix.h"
 
-send_ra_t send_ra_function = NULL;
 int test_fd[2] = EBADF_PAIR;
 
 static struct in6_addr dummy_link_local = {
@@ -23,11 +23,8 @@ int icmp6_bind(int ifindex, bool is_router) {
         return test_fd[is_router];
 }
 
-int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
-        if (!send_ra_function)
-                return 0;
-
-        return send_ra_function(0);
+int icmp6_send(int fd, const struct sockaddr_in6 *dst, const struct iovec *iov, size_t n_iov) {
+        return writev(fd, iov, n_iov);
 }
 
 int icmp6_receive(
index a9cb05a96e39eaa6e498b145f10ebc5901eca0cb..d7b0cc84b366666a9d803dd50a2f2a0ef45b6ed0 100644 (file)
@@ -3,7 +3,4 @@
 
 #include "icmp6-util.h"
 
-typedef int (*send_ra_t)(uint8_t flags);
-
-extern send_ra_t send_ra_function;
 extern int test_fd[2];
index 559167847f0980e34833c06786e6290bf370b0da..7b3786e518e60a23816a323becfecf37b2385bfa 100644 (file)
@@ -88,37 +88,15 @@ int icmp6_bind(int ifindex, bool is_router) {
         return TAKE_FD(s);
 }
 
-int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
-        struct sockaddr_in6 dst = {
-                .sin6_family = AF_INET6,
-                .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
-        };
-        struct {
-                struct nd_router_solicit rs;
-                struct nd_opt_hdr rs_opt;
-                struct ether_addr rs_opt_mac;
-        } _packed_ rs = {
-                .rs.nd_rs_type = ND_ROUTER_SOLICIT,
-                .rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
-                .rs_opt.nd_opt_len = 1,
-        };
-        struct iovec iov = {
-                .iov_base = &rs,
-                .iov_len = sizeof(rs),
-        };
+int icmp6_send(int fd, const struct sockaddr_in6 *dst, const struct iovec *iov, size_t n_iov) {
         struct msghdr msg = {
-                .msg_name = &dst,
-                .msg_namelen = sizeof(dst),
-                .msg_iov = &iov,
-                .msg_iovlen = 1,
+                .msg_name = (struct sockaddr_in6*) dst,
+                .msg_namelen = sizeof(struct sockaddr_in6),
+                .msg_iov = (struct iovec*) iov,
+                .msg_iovlen = n_iov,
         };
 
-        assert(s >= 0);
-        assert(ether_addr);
-
-        rs.rs_opt_mac = *ether_addr;
-
-        if (sendmsg(s, &msg, 0) < 0)
+        if (sendmsg(fd, &msg, 0) < 0)
                 return -errno;
 
         return 0;
index 72db688b0b9e8c6ab21f905a4ecb973d9d615048..49cdcba3ff711f89d80f8eff57f58af98fc02e47 100644 (file)
@@ -6,7 +6,9 @@
 ***/
 
 #include <net/ethernet.h>
+#include <netinet/in.h>
 #include <stdbool.h>
+#include <sys/uio.h>
 
 #include "time-util.h"
 
@@ -19,7 +21,7 @@
               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
 
 int icmp6_bind(int ifindex, bool is_router);
-int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
+int icmp6_send(int fd, const struct sockaddr_in6 *dst, const struct iovec *iov, size_t n_iov);
 int icmp6_receive(
                 int fd,
                 void *buffer,
index 380e71e76433870d1a7a0f85efb7b0d9d8dc78a3..9b4f48700d30faa3cfd4fd5628a202ae715890a3 100644 (file)
@@ -5,7 +5,9 @@
 #include "dns-domain.h"
 #include "ether-addr-util.h"
 #include "hostname-util.h"
+#include "icmp6-util.h"
 #include "in-addr-util.h"
+#include "iovec-util.h"
 #include "missing_network.h"
 #include "ndisc-option.h"
 #include "network-common.h"
@@ -65,6 +67,13 @@ static sd_ndisc_option* ndisc_option_new(uint8_t type, size_t offset) {
         return p;
 }
 
+static void ndisc_raw_done(sd_ndisc_raw *raw) {
+        if (!raw)
+                return;
+
+        free(raw->bytes);
+}
+
 static void ndisc_rdnss_done(sd_ndisc_rdnss *rdnss) {
         if (!rdnss)
                 return;
@@ -84,6 +93,10 @@ sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option) {
                 return NULL;
 
         switch (option->type) {
+        case 0:
+                ndisc_raw_done(&option->raw);
+                break;
+
         case SD_NDISC_OPTION_RDNSS:
                 ndisc_rdnss_done(&option->rdnss);
                 break;
@@ -111,6 +124,9 @@ static int ndisc_option_compare_func(const sd_ndisc_option *x, const sd_ndisc_op
                 return r;
 
         switch (x->type) {
+        case 0:
+                return memcmp_nn(x->raw.bytes, x->raw.length, y->raw.bytes, y->raw.length);
+
         case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
         case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
         case SD_NDISC_OPTION_REDIRECTED_HEADER:
@@ -155,6 +171,10 @@ static void ndisc_option_hash_func(const sd_ndisc_option *option, struct siphash
         siphash24_compress_typesafe(option->type, state);
 
         switch (option->type) {
+        case 0:
+                siphash24_compress(option->raw.bytes, option->raw.length, state);
+                break;
+
         case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
         case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
         case SD_NDISC_OPTION_REDIRECTED_HEADER:
@@ -199,6 +219,44 @@ static int ndisc_option_consume(Set **options, sd_ndisc_option *p) {
         return set_ensure_consume(options, &ndisc_option_hash_ops, p);
 }
 
+int ndisc_option_add_raw(Set **options, size_t offset, size_t length, const uint8_t *bytes) {
+        _cleanup_free_ uint8_t *copy = NULL;
+
+        assert(options);
+        assert(bytes);
+
+        if (length == 0)
+                return -EINVAL;
+
+        copy = newdup(uint8_t, bytes, length);
+        if (!copy)
+                return -ENOMEM;
+
+        sd_ndisc_option *p = ndisc_option_new(/* type = */ 0, offset);
+        if (!p)
+                return -ENOMEM;
+
+        p->raw = (sd_ndisc_raw) {
+                .bytes = TAKE_PTR(copy),
+                .length = length,
+        };
+
+        return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_build_raw(const sd_ndisc_option *option, uint8_t **ret) {
+        assert(option);
+        assert(option->type == 0);
+        assert(ret);
+
+        _cleanup_free_ uint8_t *buf = newdup(uint8_t, option->raw.bytes, option->raw.length);
+        if (!buf)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
 int ndisc_option_add_link_layer_address(Set **options, uint8_t opt, size_t offset, const struct ether_addr *mac) {
         assert(options);
         assert(IN_SET(opt, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
@@ -232,6 +290,25 @@ static int ndisc_option_parse_link_layer_address(Set **options, size_t offset, s
         return ndisc_option_add_link_layer_address(options, opt[0], offset, &mac);
 }
 
+static int ndisc_option_build_link_layer_address(const sd_ndisc_option *option, uint8_t **ret) {
+        assert(option);
+        assert(IN_SET(option->type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
+        assert(ret);
+
+        assert_cc(2 + sizeof(struct ether_addr) == 8);
+
+        _cleanup_free_ uint8_t *buf = new(uint8_t, 2 + sizeof(struct ether_addr));
+        if (!buf)
+                return -ENOMEM;
+
+        buf[0] = option->type;
+        buf[1] = 1;
+        memcpy(buf + 2, &option->mac, sizeof(struct ether_addr));
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
 int ndisc_option_add_prefix(
                 Set **options,
                 size_t offset,
@@ -292,6 +369,31 @@ static int ndisc_option_parse_prefix(Set **options, size_t offset, size_t len, c
         return ndisc_option_add_prefix(options, offset, flags, pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix, valid, pref);
 }
 
+static int ndisc_option_build_prefix(const sd_ndisc_option *option, uint8_t **ret) {
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_PREFIX_INFORMATION);
+        assert(ret);
+
+        assert_cc(sizeof(struct nd_opt_prefix_info) % 8 == 0);
+
+        _cleanup_free_ struct nd_opt_prefix_info *buf = new(struct nd_opt_prefix_info, 1);
+        if (!buf)
+                return -ENOMEM;
+
+        *buf = (struct nd_opt_prefix_info) {
+                .nd_opt_pi_type = SD_NDISC_OPTION_PREFIX_INFORMATION,
+                .nd_opt_pi_len = sizeof(struct nd_opt_prefix_info) / 8,
+                .nd_opt_pi_prefix_len = option->prefix.prefixlen,
+                .nd_opt_pi_flags_reserved = option->prefix.flags,
+                .nd_opt_pi_valid_time = usec_to_be32_sec(option->prefix.valid_lifetime),
+                .nd_opt_pi_preferred_time = usec_to_be32_sec(option->prefix.preferred_lifetime),
+                .nd_opt_pi_prefix = option->prefix.address,
+        };
+
+        *ret = (uint8_t*) TAKE_PTR(buf);
+        return 0;
+}
+
 int ndisc_option_add_redirected_header(Set **options, size_t offset, const struct ip6_hdr *hdr) {
         assert(options);
         assert(hdr);
@@ -319,6 +421,32 @@ static int ndisc_option_parse_redirected_header(Set **options, size_t offset, si
         return ndisc_option_add_redirected_header(options, offset, (const struct ip6_hdr*) (opt + sizeof(struct nd_opt_rd_hdr)));
 }
 
+static int ndisc_option_build_redirected_header(const sd_ndisc_option *option, uint8_t **ret) {
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_REDIRECTED_HEADER);
+        assert(ret);
+
+        assert_cc((sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr)) % 8 == 0);
+
+        size_t len = DIV_ROUND_UP(sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr), 8);
+
+        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+        if (!buf)
+                return -ENOMEM;
+
+        uint8_t *p;
+        p = mempcpy(buf,
+                    &(const struct nd_opt_rd_hdr) {
+                            .nd_opt_rh_type = SD_NDISC_OPTION_REDIRECTED_HEADER,
+                            .nd_opt_rh_len = len,
+                    },
+                    sizeof(struct nd_opt_rd_hdr));
+        memcpy(p, &option->hdr, sizeof(struct ip6_hdr));
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
 int ndisc_option_add_mtu(Set **options, size_t offset, uint32_t mtu) {
         assert(options);
 
@@ -348,6 +476,27 @@ static int ndisc_option_parse_mtu(Set **options, size_t offset, size_t len, cons
         return ndisc_option_add_mtu(options, offset, be32toh(pm->nd_opt_mtu_mtu));
 }
 
+static int ndisc_option_build_mtu(const sd_ndisc_option *option, uint8_t **ret) {
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_MTU);
+        assert(ret);
+
+        assert_cc(sizeof(struct nd_opt_mtu) % 8 == 0);
+
+        _cleanup_free_ struct nd_opt_mtu *buf = new(struct nd_opt_mtu, 1);
+        if (!buf)
+                return -ENOMEM;
+
+        *buf = (struct nd_opt_mtu) {
+                .nd_opt_mtu_type = SD_NDISC_OPTION_MTU,
+                .nd_opt_mtu_len = sizeof(struct nd_opt_mtu) / 8,
+                .nd_opt_mtu_mtu = htobe32(option->mtu),
+        };
+
+        *ret = (uint8_t*) TAKE_PTR(buf);
+        return 0;
+}
+
 int ndisc_option_add_route(
                 Set **options,
                 size_t offset,
@@ -413,6 +562,30 @@ static int ndisc_option_parse_route(Set **options, size_t offset, size_t len, co
         return ndisc_option_add_route(options, offset, preference, prefixlen, &prefix, lifetime);
 }
 
+static int ndisc_option_build_route(const sd_ndisc_option *option, uint8_t **ret) {
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_ROUTE_INFORMATION);
+        assert(option->route.prefixlen <= 128);
+        assert(ret);
+
+        size_t len = 1 + DIV_ROUND_UP(option->route.prefixlen, 64);
+        be32_t lifetime = usec_to_be32_sec(option->route.lifetime);
+
+        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+        if (!buf)
+                return -ENOMEM;
+
+        buf[0] = SD_NDISC_OPTION_ROUTE_INFORMATION;
+        buf[1] = len;
+        buf[2] = option->route.prefixlen;
+        buf[3] = option->route.preference << 3;
+        memcpy(buf + 4, &lifetime, sizeof(be32_t));
+        memcpy_safe(buf + 8, &option->route.address, (len - 1) * 8);
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
 int ndisc_option_add_rdnss(
                 Set **options,
                 size_t offset,
@@ -459,6 +632,29 @@ static int ndisc_option_parse_rdnss(Set **options, size_t offset, size_t len, co
         return ndisc_option_add_rdnss(options, offset, n_addrs, (const struct in6_addr*) (opt + 8), lifetime);
 }
 
+static int ndisc_option_build_rdnss(const sd_ndisc_option *option, uint8_t **ret) {
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_RDNSS);
+        assert(ret);
+
+        size_t len = option->rdnss.n_addresses * 2 + 1;
+        be32_t lifetime = usec_to_be32_sec(option->rdnss.lifetime);
+
+        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+        if (!buf)
+                return -ENOMEM;
+
+        buf[0] = SD_NDISC_OPTION_RDNSS;
+        buf[1] = len;
+        buf[2] = 0;
+        buf[3] = 0;
+        memcpy(buf + 4, &lifetime, sizeof(be32_t));
+        memcpy(buf + 8, option->rdnss.addresses, sizeof(struct in6_addr) * option->rdnss.n_addresses);
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
 int ndisc_option_add_flags_extension(Set **options, size_t offset, uint64_t flags) {
         assert(options);
 
@@ -488,6 +684,23 @@ static int ndisc_option_parse_flags_extension(Set **options, size_t offset, size
         return ndisc_option_add_flags_extension(options, offset, flags);
 }
 
+static int ndisc_option_build_flags_extension(const sd_ndisc_option *option, uint8_t **ret) {
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_FLAGS_EXTENSION);
+        assert(ret);
+
+        _cleanup_free_ uint8_t *buf = new(uint8_t, 8);
+        if (!buf)
+                return 0;
+
+        unaligned_write_be64(buf, (option->extended_flags & UINT64_C(0x00ffffffffffff00)) << 8);
+        buf[0] = SD_NDISC_OPTION_FLAGS_EXTENSION;
+        buf[1] = 1;
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
 int ndisc_option_add_dnssl(Set **options, size_t offset, char * const *domains, usec_t lifetime) {
         int r;
 
@@ -592,6 +805,50 @@ static int ndisc_option_parse_dnssl(Set **options, size_t offset, size_t len, co
         return ndisc_option_add_dnssl(options, offset, l, lifetime);
 }
 
+static int ndisc_option_build_dnssl(const sd_ndisc_option *option, uint8_t **ret) {
+        int r;
+
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_DNSSL);
+        assert(ret);
+
+        size_t len = 8;
+        STRV_FOREACH(s, option->dnssl.domains)
+                len += strlen(*s) + 2;
+        len = DIV_ROUND_UP(len, 8);
+
+        be32_t lifetime = usec_to_be32_sec(option->dnssl.lifetime);
+
+        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+        if (!buf)
+                return -ENOMEM;
+
+        buf[0] = SD_NDISC_OPTION_DNSSL;
+        buf[1] = len;
+        buf[2] = 0;
+        buf[3] = 0;
+        memcpy(buf + 4, &lifetime, sizeof(be32_t));
+
+        size_t remaining = len * 8 - 8;
+        uint8_t *p = buf + 8;
+
+        STRV_FOREACH(s, option->dnssl.domains) {
+                r = dns_name_to_wire_format(*s, p, remaining, /* canonical = */ false);
+                if (r < 0)
+                        return r;
+
+                assert(remaining >= (size_t) r);
+                p += r;
+                remaining -= r;
+        }
+
+        if (remaining > 0)
+                memset(p, 0, remaining);
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
 int ndisc_option_add_captive_portal(Set **options, size_t offset, const char *portal) {
         assert(options);
 
@@ -640,6 +897,30 @@ static int ndisc_option_parse_captive_portal(Set **options, size_t offset, size_
         return ndisc_option_add_captive_portal(options, offset, portal);
 }
 
+static int ndisc_option_build_captive_portal(const sd_ndisc_option *option, uint8_t **ret) {
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_CAPTIVE_PORTAL);
+        assert(ret);
+
+        size_t len_portal = strlen(option->captive_portal);
+        size_t len = DIV_ROUND_UP(len_portal + 1 + 2, 8);
+
+        _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+        if (!buf)
+                return -ENOMEM;
+
+        buf[0] = SD_NDISC_OPTION_CAPTIVE_PORTAL;
+        buf[1] = len;
+
+        uint8_t *p = mempcpy(buf + 2, option->captive_portal, len_portal);
+        size_t remaining = len * 8 - 2 - len_portal;
+        if (remaining > 0)
+                memset(p, 0, remaining);
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
 static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX] = {
         [PREFIX_LENGTH_CODE_96] = 96,
         [PREFIX_LENGTH_CODE_64] = 64,
@@ -731,6 +1012,37 @@ static int ndisc_option_parse_prefix64(Set **options, size_t offset, size_t len,
         return ndisc_option_add_prefix64(options, offset, prefixlen, &prefix, lifetime);
 }
 
+static int ndisc_option_build_prefix64(const sd_ndisc_option *option, uint8_t **ret) {
+        int r;
+
+        assert(option);
+        assert(option->type == SD_NDISC_OPTION_PREF64);
+        assert(ret);
+
+        uint8_t code;
+        r = pref64_prefix_length_to_plc(option->prefix64.prefixlen, &code);
+        if (r < 0)
+                return r;
+
+        uint16_t lifetime;
+        if (option->prefix64.lifetime >= PREF64_SCALED_LIFETIME_MASK * USEC_PER_SEC)
+                lifetime = PREF64_SCALED_LIFETIME_MASK;
+        else
+                lifetime = (uint16_t) DIV_ROUND_UP(option->prefix64.lifetime, USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK;
+
+        _cleanup_free_ uint8_t *buf = new(uint8_t, 2 * 8);
+        if (!buf)
+                return -ENOMEM;
+
+        buf[0] = SD_NDISC_OPTION_PREF64;
+        buf[1] = 2;
+        unaligned_write_be16(buf + 2, lifetime | code);
+        memcpy(buf + 4, &option->prefix64.prefix, 12);
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
 static int ndisc_option_parse_default(Set **options, size_t offset, size_t len, const uint8_t *opt) {
         assert(options);
         assert(opt);
@@ -788,6 +1100,10 @@ int ndisc_parse_options(ICMP6Packet *packet, Set **ret_options) {
                         return log_debug_errno(r, "Failed to parse NDisc option header: %m");
 
                 switch (type) {
+                case 0:
+                        r = -EBADMSG;
+                        break;
+
                 case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
                 case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
                         r = ndisc_option_parse_link_layer_address(&options, offset, length, opt);
@@ -853,3 +1169,94 @@ int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret) {
                 *ret = p->mac;
         return 0;
 }
+
+int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options) {
+        int r;
+
+        assert(fd >= 0);
+        assert(dst);
+        assert(hdr);
+
+        struct iovec *iov = NULL;
+        size_t n_iov = 0;
+        CLEANUP_ARRAY(iov, n_iov, iovec_array_free);
+
+        iov = new(struct iovec, 1 + set_size(options));
+        if (!iov)
+                return -ENOMEM;
+
+        r = ndisc_header_size(hdr->icmp6_type);
+        if (r < 0)
+                return r;
+        size_t hdr_size = r;
+
+        _cleanup_free_ uint8_t *copy = newdup(uint8_t, hdr, hdr_size);
+        if (!copy)
+                return -ENOMEM;
+
+        iov[n_iov++] = IOVEC_MAKE(TAKE_PTR(copy), hdr_size);
+
+        const sd_ndisc_option *option;
+        SET_FOREACH(option, options) {
+                _cleanup_free_ uint8_t *buf = NULL;
+
+                switch (option->type) {
+                case 0:
+                        r = ndisc_option_build_raw(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
+                case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
+                        r = ndisc_option_build_link_layer_address(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_PREFIX_INFORMATION:
+                        r = ndisc_option_build_prefix(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_REDIRECTED_HEADER:
+                        r = ndisc_option_build_redirected_header(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_MTU:
+                        r = ndisc_option_build_mtu(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_ROUTE_INFORMATION:
+                        r = ndisc_option_build_route(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_RDNSS:
+                        r = ndisc_option_build_rdnss(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_FLAGS_EXTENSION:
+                        r = ndisc_option_build_flags_extension(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_DNSSL:
+                        r = ndisc_option_build_dnssl(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_CAPTIVE_PORTAL:
+                        r = ndisc_option_build_captive_portal(option, &buf);
+                        break;
+
+                case SD_NDISC_OPTION_PREF64:
+                        r = ndisc_option_build_prefix64(option, &buf);
+                        break;
+
+                default:
+                        continue;
+                }
+                if (r == -ENOMEM)
+                        return log_oom_debug();
+                if (r < 0)
+                        log_debug_errno(r, "Failed to build NDisc option %u, ignoring: %m", option->type);
+
+                iov[n_iov++] = IOVEC_MAKE(buf, buf[1] * 8);
+                TAKE_PTR(buf);
+        }
+
+        return icmp6_send(fd, dst, iov, n_iov);
+}
index 45108ee1aa58f93a2ef4521c9ee7d9ccdc9330ba..2669d5d5c91157ca8c3a5837892aa788c1e119b7 100644 (file)
@@ -3,8 +3,10 @@
 
 #include <inttypes.h>
 #include <net/ethernet.h>
+#include <netinet/icmp6.h>
 #include <netinet/in.h>
 #include <netinet/ip6.h>
+#include <sys/uio.h>
 
 #include "sd-ndisc-protocol.h"
 
 #include "set.h"
 #include "time-util.h"
 
+typedef struct sd_ndisc_raw {
+        uint8_t *bytes;
+        size_t length;
+} sd_ndisc_raw;
+
 /* Mostly equivalent to struct nd_opt_prefix_info, but using usec_t. */
 typedef struct sd_ndisc_prefix {
         uint8_t flags;
@@ -51,6 +58,7 @@ typedef struct sd_ndisc_option {
         size_t offset;
 
         union {
+                sd_ndisc_raw raw;           /* for testing or unsupported options */
                 struct ether_addr mac;      /* SD_NDISC_OPTION_SOURCE_LL_ADDRESS or SD_NDISC_OPTION_TARGET_LL_ADDRESS */
                 sd_ndisc_prefix prefix;     /* SD_NDISC_OPTION_PREFIX_INFORMATION */
                 struct ip6_hdr hdr;         /* SD_NDISC_OPTION_REDIRECTED_HEADER */
@@ -107,6 +115,11 @@ static inline sd_ndisc_option* ndisc_option_get(Set *options, uint8_t type) {
 
 int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret);
 
+int ndisc_option_add_raw(
+                Set **options,
+                size_t offset,
+                size_t length,
+                const uint8_t *bytes);
 int ndisc_option_add_link_layer_address(
                 Set **options,
                 uint8_t opt,
@@ -160,3 +173,5 @@ int ndisc_option_add_prefix64(
                 uint8_t prefixlen,
                 const struct in6_addr *prefix,
                 usec_t lifetime);
+
+int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options);
index 939fa87d302355aef2edaac891c0696d9ca1c60e..4dbf1b95d6e17f161e93cb87de11202a1baa4888 100644 (file)
@@ -9,6 +9,7 @@
 #include "sd-ndisc.h"
 
 #include "alloc-util.h"
+#include "ether-addr-util.h"
 #include "event-util.h"
 #include "fd-util.h"
 #include "icmp6-util.h"
@@ -268,6 +269,29 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
         return 0;
 }
 
+static int ndisc_send_router_solicitation(sd_ndisc *nd) {
+        static const struct sockaddr_in6 dst = {
+                .sin6_family = AF_INET6,
+                .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+        };
+        static const struct nd_router_solicit header = {
+                .nd_rs_type = ND_ROUTER_SOLICIT,
+        };
+
+        _cleanup_set_free_ Set *options = NULL;
+        int r;
+
+        assert(nd);
+
+        if (!ether_addr_is_null(&nd->mac_addr)) {
+                r = ndisc_option_add_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, 0, &nd->mac_addr);
+                if (r < 0)
+                        return r;
+        }
+
+        return ndisc_send(nd->fd, &dst, &header.nd_rs_hdr, options);
+}
+
 static usec_t ndisc_timeout_compute_random(usec_t val) {
         /* compute a time that is random within Â±10% of the given value */
         return val - val / 10 +
@@ -301,7 +325,7 @@ static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
         if (r < 0)
                 goto fail;
 
-        r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
+        r = ndisc_send_router_solicitation(nd);
         if (r < 0)
                 log_ndisc_errno(nd, r, "Failed to send Router Solicitation, next solicitation in %s, ignoring: %m",
                                 FORMAT_TIMESPAN(nd->retransmit_time, USEC_PER_SEC));
index 4541605940f4c7c4d5aff876dbc37055ca526048..5bf3e84db99ee31873b13bd1c367195019cc74fa 100644 (file)
@@ -12,6 +12,7 @@
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "hexdecoct.h"
+#include "icmp6-packet.h"
 #include "icmp6-util-unix.h"
 #include "socket-util.h"
 #include "strv.h"
@@ -23,7 +24,6 @@ static struct ether_addr mac_addr = {
 };
 
 static bool verbose = false;
-static sd_ndisc *test_timeout_nd;
 
 static void router_dump(sd_ndisc_router *rt) {
         struct in6_addr addr;
@@ -232,12 +232,18 @@ static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, void *message, v
         sd_event_exit(e, 0);
 }
 
+static int on_recv_rs(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+        assert_se(icmp6_packet_receive(fd, &packet) >= 0);
+
+        return send_ra(0);
+}
+
 TEST(rs) {
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
         _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
 
-        send_ra_function = send_ra;
-
         assert_se(sd_event_new(&e) >= 0);
 
         assert_se(sd_ndisc_new(&nd) >= 0);
@@ -261,9 +267,12 @@ TEST(rs) {
 
         assert_se(sd_ndisc_start(nd) >= 0);
 
+        assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs, nd) >= 0);
+        assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+
         assert_se(sd_event_loop(e) >= 0);
 
-        test_fd[1] = safe_close(test_fd[1]);
+        test_fd[1] = -EBADF;
 }
 
 static int send_ra_invalid_domain(uint8_t flags) {
@@ -312,12 +321,18 @@ static int send_ra_invalid_domain(uint8_t flags) {
         return 0;
 }
 
+static int on_recv_rs_invalid_domain(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+        assert_se(icmp6_packet_receive(fd, &packet) >= 0);
+
+        return send_ra_invalid_domain(0);
+}
+
 TEST(invalid_domain) {
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
         _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
 
-        send_ra_function = send_ra_invalid_domain;
-
         assert_se(sd_event_new(&e) >= 0);
 
         assert_se(sd_ndisc_new(&nd) >= 0);
@@ -335,19 +350,22 @@ TEST(invalid_domain) {
 
         assert_se(sd_ndisc_start(nd) >= 0);
 
+        assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_invalid_domain, nd) >= 0);
+        assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+
         assert_se(sd_event_loop(e) >= 0);
 
-        test_fd[1] = safe_close(test_fd[1]);
+        test_fd[1] = -EBADF;
 }
 
-static int test_timeout_value(uint8_t flags) {
+static int on_recv_rs_timeout(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+        sd_ndisc *nd = ASSERT_PTR(userdata);
         static int count = 0;
         static usec_t last = 0;
-        sd_ndisc *nd = test_timeout_nd;
         usec_t min, max;
 
-        assert_se(nd);
-        assert_se(nd->event);
+        assert_se(icmp6_packet_receive(fd, &packet) >= 0);
 
         if (++count >= 20)
                 sd_event_exit(nd->event, 0);
@@ -391,17 +409,14 @@ static int test_timeout_value(uint8_t flags) {
 
 TEST(timeout) {
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
         _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
 
-        send_ra_function = test_timeout_value;
-
         assert_se(sd_event_new(&e) >= 0);
 
         assert_se(sd_ndisc_new(&nd) >= 0);
         assert_se(nd);
 
-        test_timeout_nd = nd;
-
         assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
 
         assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
@@ -413,9 +428,12 @@ TEST(timeout) {
 
         assert_se(sd_ndisc_start(nd) >= 0);
 
+        assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_timeout, nd) >= 0);
+        assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+
         assert_se(sd_event_loop(e) >= 0);
 
-        test_fd[1] = safe_close(test_fd[1]);
+        test_fd[1] = -EBADF;
 }
 
 DEFINE_TEST_MAIN(LOG_DEBUG);
index c342522e3f82dd992618bb2ec10c6aa28d510c69..5d27491af3a5fbec9dd0265e93912a8f1427a829 100644 (file)
@@ -4230,6 +4230,7 @@ int manager_start_scope(
                 Manager *manager,
                 const char *scope,
                 const PidRef *pidref,
+                bool allow_pidfd,
                 const char *slice,
                 const char *description,
                 const char * const *requires,
@@ -4299,7 +4300,7 @@ int manager_start_scope(
         if (r < 0)
                 return r;
 
-        r = bus_append_scope_pidref(m, pidref);
+        r = bus_append_scope_pidref(m, pidref, allow_pidfd);
         if (r < 0)
                 return r;
 
@@ -4330,8 +4331,27 @@ int manager_start_scope(
                 return r;
 
         r = sd_bus_call(manager->bus, m, 0, error, &reply);
-        if (r < 0)
+        if (r < 0) {
+                /* If this failed with a property we couldn't write, this is quite likely because the server
+                 * doesn't support PIDFDs yet, let's try without. */
+                if (allow_pidfd &&
+                    sd_bus_error_has_names(error, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
+                        return manager_start_scope(
+                                        manager,
+                                        scope,
+                                        pidref,
+                                        /* allow_pidfd = */ false,
+                                        slice,
+                                        description,
+                                        requires,
+                                        extra_after,
+                                        requires_mounts_for,
+                                        more_properties,
+                                        error,
+                                        ret_job);
+
                 return r;
+        }
 
         return strdup_job(reply, ret_job);
 }
index 5c864a9bc056a867b4e4a159208b724ebbec8828..8459d048b8ed882bb7165089fe29116b850021f9 100644 (file)
@@ -28,6 +28,7 @@ int manager_start_scope(
                 Manager *manager,
                 const char *scope,
                 const PidRef *pidref,
+                bool allow_pidfd,
                 const char *slice,
                 const char *description,
                 const char * const *requires,
index 2713215e10a47aa9b8ced28d22807cc50eb01ca8..4713aa05b55c8a6adf28bd6157a1824e1d2c04ec 100644 (file)
@@ -742,6 +742,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
                         s->manager,
                         scope,
                         &s->leader,
+                        /* allow_pidfd = */ true,
                         s->user->slice,
                         description,
                         /* These should have been pulled in explicitly in user_start(). Just to be sure. */
index af8a88f26caea134fd3a8f922092deeda7e7fb71..309bffa3c8075bbe0b0ccd5aa2008cf7cd7dedf5 100644 (file)
@@ -385,7 +385,7 @@ static int machine_start_scope(
         if (r < 0)
                 return r;
 
-        r = bus_append_scope_pidref(m, &machine->leader);
+        r = bus_append_scope_pidref(m, &machine->leader, /* allow_pidfd = */ true);
         if (r < 0)
                 return r;
 
index 38cdadcf61bb1a93b06160f8c4d1e0fbcfaf0c52..542298c8c3019a7460f45faca518fe8a95f25c05 100644 (file)
@@ -1495,6 +1495,10 @@ static int address_acquire(Link *link, const Address *address, union in_addr_uni
         assert(address);
         assert(ret);
 
+        r = address_acquire_from_dhcp_server_leases_file(link, address, ret);
+        if (r != -ENOENT)
+                return r;
+
         r = address_pool_acquire(link->manager, address->family, address->prefixlen, &a);
         if (r < 0)
                 return r;
index 3efbe6f48e6d56e13661127287af8ba37f47fd92..ac6179a9248f4770ae9d3e270497937842afb7dc 100644 (file)
@@ -57,6 +57,7 @@ struct Address {
         bool scope_set:1;
         bool ip_masquerade_done:1;
         bool requested_as_null:1;
+        bool used_by_dhcp_server:1;
 
         /* duplicate_address_detection is only used by static or IPv4 dynamic addresses.
          * To control DAD for IPv6 dynamic addresses, set IFA_F_NODAD to flags. */
index a1869a855998b2c5666c4626a7eba1f73eb3e0df..292022f32283978aff6b11cc1376edf9ad537c44 100644 (file)
@@ -7,6 +7,7 @@
 #include "sd-dhcp-server.h"
 
 #include "dhcp-protocol.h"
+#include "dhcp-server-lease-internal.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "network-common.h"
@@ -82,6 +83,7 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
                         /* TODO: check if the prefix length is small enough for the pool. */
 
                         network->dhcp_server_address = address;
+                        address->used_by_dhcp_server = true;
                         break;
                 }
                 if (!network->dhcp_server_address) {
@@ -128,6 +130,7 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
                 a->prefixlen = network->dhcp_server_address_prefixlen;
                 a->in_addr.in = network->dhcp_server_address_in_addr;
                 a->requested_as_null = !in4_addr_is_set(&network->dhcp_server_address_in_addr);
+                a->used_by_dhcp_server = true;
 
                 r = address_section_verify(a);
                 if (r < 0)
@@ -144,6 +147,49 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
         return 0;
 }
 
+int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *address, union in_addr_union *ret) {
+        struct in_addr a;
+        uint8_t prefixlen;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(address);
+        assert(ret);
+
+        /* If the DHCP server address is configured as a null address, reuse the server address of the
+         * previous instance. */
+        if (address->family != AF_INET)
+                return -ENOENT;
+
+        if (!address->used_by_dhcp_server)
+                return -ENOENT;
+
+        if (!link_dhcp4_server_enabled(link))
+                return -ENOENT;
+
+        if (link->manager->persistent_storage_fd < 0)
+                return -EBUSY; /* The persistent storage is not ready, try later again. */
+
+        _cleanup_free_ char *lease_file = path_join("dhcp-server-lease", link->ifname);
+        if (!lease_file)
+                return -ENOMEM;
+
+        r = dhcp_server_leases_file_get_server_address(
+                        link->manager->persistent_storage_fd,
+                        lease_file,
+                        &a,
+                        &prefixlen);
+        if (r < 0)
+                return r;
+
+        if (prefixlen != address->prefixlen)
+                return -ENOENT;
+
+        ret->in = a;
+        return 0;
+}
+
 int link_start_dhcp4_server(Link *link) {
         int r;
 
index dbc7d95cadba470a3f94f01f8ba99f19c5e607a5..e839fac00b44675b4251779bc8799213c7c78bd6 100644 (file)
@@ -2,14 +2,16 @@
 #pragma once
 
 #include "conf-parser.h"
+#include "in-addr-util.h"
 #include "set.h"
 
+typedef struct Address Address;
 typedef struct Link Link;
 typedef struct Manager Manager;
 typedef struct Network Network;
 
 int network_adjust_dhcp_server(Network *network, Set **addresses);
-
+int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *address, union in_addr_union *ret);
 int link_request_dhcp_server(Link *link);
 
 int link_start_dhcp4_server(Link *link);
index 358958b82b3d6f1b9d8e692591915e014edc2366..b63516d13efc0d87b34825e1ad518d64080c5623 100644 (file)
@@ -297,15 +297,12 @@ int allocate_scope(
 
         description = strjoina("Container ", machine_name);
 
-        if (allow_pidfd) {
-                _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
-                r = pidref_set_pid(&pidref, pid);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to allocate PID reference: %m");
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        r = pidref_set_pid(&pidref, pid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate PID reference: %m");
 
-                r = bus_append_scope_pidref(m, &pidref);
-        } else
-                r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) pid);
+        r = bus_append_scope_pidref(m, &pidref, allow_pidfd);
         if (r < 0)
                 return bus_log_create_error(r);
 
index 3189c03639535eb92c2bd585204371273accecce..3d0a9a73e3d68d2f59a5d587f61587f2cc571e9d 100644 (file)
@@ -3958,7 +3958,7 @@ static int outer_child(
 
         if (dissected_image) {
                 /* Now we know the uid shift, let's now mount everything else that might be in the image. */
-                r = dissected_image_mount(
+                r = dissected_image_mount_and_warn(
                                 dissected_image,
                                 directory,
                                 arg_uid_shift,
@@ -3967,10 +3967,8 @@ static int outer_child(
                                 determine_dissect_image_flags()|
                                 DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|
                                 (idmap ? DISSECT_IMAGE_MOUNT_IDMAPPED : 0));
-                if (r == -EUCLEAN)
-                        return log_error_errno(r, "File system check for image failed: %m");
                 if (r < 0)
-                        return log_error_errno(r, "Failed to mount image file system: %m");
+                        return r;
         }
 
         if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
index e8dc3388c05e9650c9ad9a456b66a85b27c08a27..93e061a3c311f2d1e9f84cec1750e6a914b34754 100644 (file)
@@ -1265,18 +1265,13 @@ static int transient_scope_set_properties(sd_bus_message *m, bool allow_pidfd) {
         if (r < 0)
                 return r;
 
-        if (allow_pidfd) {
-                _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
 
-                r = pidref_set_self(&pidref);
-                if (r < 0)
-                        return r;
+        r = pidref_set_self(&pidref);
+        if (r < 0)
+                return r;
 
-                r = bus_append_scope_pidref(m, &pidref);
-        } else
-                r = sd_bus_message_append(
-                                m, "(sv)",
-                                "PIDs", "au", 1, getpid_cached());
+        r = bus_append_scope_pidref(m, &pidref, allow_pidfd);
         if (r < 0)
                 return bus_log_create_error(r);
 
index 7d847b94252f79fd5324f166c60562aec11c5259..2fcfb1d3b96c9affc9f93dd4176c58f0e8713be5 100644 (file)
@@ -2821,13 +2821,13 @@ int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char
         return 0;
 }
 
-int bus_append_scope_pidref(sd_bus_message *m, const PidRef *pidref) {
+int bus_append_scope_pidref(sd_bus_message *m, const PidRef *pidref, bool allow_pidfd) {
         assert(m);
 
         if (!pidref_is_set(pidref))
                 return -ESRCH;
 
-        if (pidref->fd >= 0)
+        if (pidref->fd >= 0 && allow_pidfd)
                 return sd_bus_message_append(
                                 m, "(sv)",
                                 "PIDFDs", "ah", 1, pidref->fd);
index a1518158ff4691f8551064ea8a7db6151fc5eb6f..e4168a44254116ff24cc89f3a001db40afa3f5dc 100644 (file)
@@ -26,7 +26,7 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
 int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment);
 int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l);
 
-int bus_append_scope_pidref(sd_bus_message *m, const PidRef *pidref);
+int bus_append_scope_pidref(sd_bus_message *m, const PidRef *pidref, bool allow_pidfd);
 
 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet);
 
index 77ac85965f8d0be50ea57a1f033d8c4b3a1a5841..cbbc85a5cc52a8f5f41dc904088a7f91bcf41b7f 100644 (file)
@@ -33,7 +33,9 @@ DLSYM_FUNCTION(crypt_keyslot_destroy);
 DLSYM_FUNCTION(crypt_keyslot_max);
 DLSYM_FUNCTION(crypt_load);
 DLSYM_FUNCTION(crypt_resize);
-DLSYM_FUNCTION(crypt_resume_by_passphrase);
+#if HAVE_CRYPT_RESUME_BY_VOLUME_KEY
+DLSYM_FUNCTION(crypt_resume_by_volume_key);
+#endif
 DLSYM_FUNCTION(crypt_set_data_device);
 DLSYM_FUNCTION(crypt_set_debug_level);
 DLSYM_FUNCTION(crypt_set_log_callback);
@@ -276,7 +278,9 @@ int dlopen_cryptsetup(void) {
                         DLSYM_ARG(crypt_keyslot_max),
                         DLSYM_ARG(crypt_load),
                         DLSYM_ARG(crypt_resize),
-                        DLSYM_ARG(crypt_resume_by_passphrase),
+#if HAVE_CRYPT_RESUME_BY_VOLUME_KEY
+                        DLSYM_ARG(crypt_resume_by_volume_key),
+#endif
                         DLSYM_ARG(crypt_set_data_device),
                         DLSYM_ARG(crypt_set_debug_level),
                         DLSYM_ARG(crypt_set_log_callback),
index 8a4416b4fcbe39e1e9698aa004b4a2e7447fc652..f00ac367b6078dbea88e9ad9c47c5912c2c2f0db 100644 (file)
@@ -41,7 +41,9 @@ DLSYM_PROTOTYPE(crypt_keyslot_destroy);
 DLSYM_PROTOTYPE(crypt_keyslot_max);
 DLSYM_PROTOTYPE(crypt_load);
 DLSYM_PROTOTYPE(crypt_resize);
-DLSYM_PROTOTYPE(crypt_resume_by_passphrase);
+#if HAVE_CRYPT_RESUME_BY_VOLUME_KEY
+DLSYM_PROTOTYPE(crypt_resume_by_volume_key);
+#endif
 DLSYM_PROTOTYPE(crypt_set_data_device);
 DLSYM_PROTOTYPE(crypt_set_debug_level);
 DLSYM_PROTOTYPE(crypt_set_log_callback);
index ad6c5c065f066ae9df84f0de47e39382ab230c70..a0832c98281faffdbd3dbbc9ac24141b86d6f0b4 100644 (file)
@@ -2278,19 +2278,19 @@ int dissected_image_mount_and_warn(
 
         r = dissected_image_mount(m, where, uid_shift, uid_range, userns_fd, flags);
         if (r == -ENXIO)
-                return log_error_errno(r, "Not root file system found in image.");
+                return log_error_errno(r, "Failed to mount image: No root file system found in image.");
         if (r == -EMEDIUMTYPE)
-                return log_error_errno(r, "No suitable os-release/extension-release file in image found.");
+                return log_error_errno(r, "Failed to mount image: No suitable os-release/extension-release file in image found.");
         if (r == -EUNATCH)
-                return log_error_errno(r, "Encrypted file system discovered, but decryption not requested.");
+                return log_error_errno(r, "Failed to mount image: Encrypted file system discovered, but decryption not requested.");
         if (r == -EUCLEAN)
-                return log_error_errno(r, "File system check on image failed.");
+                return log_error_errno(r, "Failed to mount image: File system check on image failed.");
         if (r == -EBUSY)
-                return log_error_errno(r, "File system already mounted elsewhere.");
+                return log_error_errno(r, "Failed to mount image: File system already mounted elsewhere.");
         if (r == -EAFNOSUPPORT)
-                return log_error_errno(r, "File system type not supported or not known.");
+                return log_error_errno(r, "Failed to mount image: File system type not supported or not known.");
         if (r == -EIDRM)
-                return log_error_errno(r, "File system is too uncommon, refused.");
+                return log_error_errno(r, "Failed to mount image: File system is too uncommon, refused.");
         if (r < 0)
                 return log_error_errno(r, "Failed to mount image: %m");
 
index 8fe0165225ccbd6bf7fa6f053013fc720db6b7ca..15cab9a6127e5b31de6492fd69ecb8682a6fa800 100644 (file)
@@ -162,9 +162,10 @@ static int path_is_generator(const LookupPaths *lp, const char *path) {
         if (r < 0)
                 return r;
 
-        return path_equal_ptr(parent, lp->generator) ||
-               path_equal_ptr(parent, lp->generator_early) ||
-               path_equal_ptr(parent, lp->generator_late);
+        return PATH_IN_SET(parent,
+                           lp->generator,
+                           lp->generator_early,
+                           lp->generator_late);
 }
 
 static int path_is_transient(const LookupPaths *lp, const char *path) {
@@ -178,7 +179,7 @@ static int path_is_transient(const LookupPaths *lp, const char *path) {
         if (r < 0)
                 return r;
 
-        return path_equal_ptr(parent, lp->transient);
+        return path_equal(parent, lp->transient);
 }
 
 static int path_is_control(const LookupPaths *lp, const char *path) {
@@ -192,8 +193,9 @@ static int path_is_control(const LookupPaths *lp, const char *path) {
         if (r < 0)
                 return r;
 
-        return path_equal_ptr(parent, lp->persistent_control) ||
-               path_equal_ptr(parent, lp->runtime_control);
+        return PATH_IN_SET(parent,
+                           lp->persistent_control,
+                           lp->runtime_control);
 }
 
 static int path_is_config(const LookupPaths *lp, const char *path, bool check_parent) {
@@ -214,8 +216,9 @@ static int path_is_config(const LookupPaths *lp, const char *path, bool check_pa
                 path = parent;
         }
 
-        return path_equal_ptr(path, lp->persistent_config) ||
-               path_equal_ptr(path, lp->runtime_config);
+        return PATH_IN_SET(path,
+                           lp->persistent_config,
+                           lp->runtime_config);
 }
 
 static int path_is_runtime(const LookupPaths *lp, const char *path, bool check_parent) {
@@ -241,12 +244,13 @@ static int path_is_runtime(const LookupPaths *lp, const char *path, bool check_p
                 path = parent;
         }
 
-        return path_equal_ptr(path, lp->runtime_config) ||
-               path_equal_ptr(path, lp->generator) ||
-               path_equal_ptr(path, lp->generator_early) ||
-               path_equal_ptr(path, lp->generator_late) ||
-               path_equal_ptr(path, lp->transient) ||
-               path_equal_ptr(path, lp->runtime_control);
+        return PATH_IN_SET(path,
+                           lp->runtime_config,
+                           lp->generator,
+                           lp->generator_early,
+                           lp->generator_late,
+                           lp->transient,
+                           lp->runtime_control);
 }
 
 static int path_is_vendor_or_generator(const LookupPaths *lp, const char *path) {
@@ -1038,7 +1042,7 @@ static int find_symlinks_in_scope(
                 if (r > 0) {
                         /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
 
-                        if (path_equal_ptr(*p, lp->persistent_config)) {
+                        if (path_equal(*p, lp->persistent_config)) {
                                 /* This is the best outcome, let's return it immediately. */
                                 *state = UNIT_FILE_ENABLED;
                                 return 1;
@@ -1059,7 +1063,7 @@ static int find_symlinks_in_scope(
                                 enabled_at_all = true;
 
                 } else if (same_name_link) {
-                        if (path_equal_ptr(*p, lp->persistent_config))
+                        if (path_equal(*p, lp->persistent_config))
                                 same_name_link_config = true;
                         else {
                                 r = path_is_runtime(lp, *p, false);
index 644417f7c5974abdbabd4ab0a650d3707e6987e9..85f9463f9b5c5f1a2bcc69c4e6d8eabe4c2244a7 100644 (file)
@@ -211,50 +211,142 @@ static inline int run_test_table(void) {
                 }                                                                                               \
          })
 
+#define ASSERT_TRUE(expr)                                                                                       \
+        ({                                                                                                      \
+                if (!(expr)) {                                                                                  \
+                        log_error("%s:%i: Assertion failed: expected \"%s\" to be true",                        \
+                                  PROJECT_FILE, __LINE__, #expr);                                               \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
+#define ASSERT_FALSE(expr)                                                                                      \
+        ({                                                                                                      \
+                if ((expr)) {                                                                                   \
+                        log_error("%s:%i: Assertion failed: expected \"%s\" to be false",                       \
+                                  PROJECT_FILE, __LINE__, #expr);                                               \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
+#define ASSERT_NULL(expr)                                                                                       \
+        ({                                                                                                      \
+                if ((expr) != NULL) {                                                                           \
+                        log_error("%s:%i: Assertion failed: expected \"%s\" to be NULL",                        \
+                                  PROJECT_FILE, __LINE__, #expr);                                               \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
+#define ASSERT_NOT_NULL(expr)                                                                                   \
+        ({                                                                                                      \
+                if ((expr) == NULL) {                                                                           \
+                        log_error("%s:%i: Assertion failed: expected \"%s\" to be not NULL",                    \
+                                  PROJECT_FILE, __LINE__, #expr);                                               \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
+#define ASSERT_STREQ(expr1, expr2)                                                                              \
+        ({                                                                                                      \
+                const char* _expr1 = (expr1);                                                                   \
+                const char* _expr2 = (expr2);                                                                   \
+                if (strcmp(_expr1, _expr2) != 0) {                                                              \
+                        log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"",           \
+                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _expr1, _expr2);                      \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
 /* DECIMAL_STR_FMT() uses _Generic which cannot be used in string concatenation so we have to format the
  * input into strings first and then format those into the final assertion message. */
 
-#define ASSERT_EQ(expr1, expr2)                                                                                  \
-        ({                                                                                                       \
-                typeof(expr1) _expr1 = (expr1);                                                                  \
-                typeof(expr2) _expr2 = (expr2);                                                                  \
-                if (_expr1 != _expr2) {                                                                          \
-                        char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))];                                            \
-                        char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))];                                            \
-                        xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1);                                      \
-                        xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2);                                      \
-                        log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"",            \
-                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2);                     \
-                        abort();                                                                                 \
-                }                                                                                                \
+#define ASSERT_EQ(expr1, expr2)                                                                                 \
+        ({                                                                                                      \
+                typeof(expr1) _expr1 = (expr1);                                                                 \
+                typeof(expr2) _expr2 = (expr2);                                                                 \
+                if (_expr1 != _expr2) {                                                                         \
+                        char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))];                                           \
+                        char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))];                                           \
+                        xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1);                                     \
+                        xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2);                                     \
+                        log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"",           \
+                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2);                    \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
+#define ASSERT_GE(expr1, expr2)                                                                                 \
+        ({                                                                                                      \
+                typeof(expr1) _expr1 = (expr1);                                                                 \
+                typeof(expr2) _expr2 = (expr2);                                                                 \
+                if (_expr1 < _expr2) {                                                                          \
+                        char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))];                                           \
+                        char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))];                                           \
+                        xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1);                                     \
+                        xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2);                                     \
+                        log_error("%s:%i: Assertion failed: expected \"%s >= %s\", but \"%s < %s\"",            \
+                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2);                    \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
+#define ASSERT_LE(expr1, expr2)                                                                                 \
+        ({                                                                                                      \
+                typeof(expr1) _expr1 = (expr1);                                                                 \
+                typeof(expr2) _expr2 = (expr2);                                                                 \
+                if (_expr1 > _expr2) {                                                                          \
+                        char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))];                                           \
+                        char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))];                                           \
+                        xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1);                                     \
+                        xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2);                                     \
+                        log_error("%s:%i: Assertion failed: expected \"%s <= %s\", but \"%s > %s\"",            \
+                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2);                    \
+                        abort();                                                                                \
+                }                                                                                               \
+        })
+
+#define ASSERT_NE(expr1, expr2)                                                                                 \
+        ({                                                                                                      \
+                typeof(expr1) _expr1 = (expr1);                                                                 \
+                typeof(expr2) _expr2 = (expr2);                                                                 \
+                if (_expr1 == _expr2) {                                                                         \
+                        char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))];                                           \
+                        char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))];                                           \
+                        xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1);                                     \
+                        xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2);                                     \
+                        log_error("%s:%i: Assertion failed: expected \"%s != %s\", but \"%s == %s\"",           \
+                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2);                    \
+                        abort();                                                                                \
+                }                                                                                               \
         })
 
-#define ASSERT_GE(expr1, expr2)                                                                                  \
-        ({                                                                                                       \
-                typeof(expr1) _expr1 = (expr1);                                                                  \
-                typeof(expr2) _expr2 = (expr2);                                                                  \
-                if (_expr1 < _expr2) {                                                                           \
-                        char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))];                                            \
-                        char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))];                                            \
-                        xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1);                                      \
-                        xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2);                                      \
-                        log_error("%s:%i: Assertion failed: expected \"%s >= %s\", but \"%s < %s\"",             \
-                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2);                     \
-                        abort();                                                                                 \
-                }                                                                                                \
+#define ASSERT_GT(expr1, expr2)                                                                                 \
+        ({                                                                                                      \
+                typeof(expr1) _expr1 = (expr1);                                                                 \
+                typeof(expr2) _expr2 = (expr2);                                                                 \
+                if (!(_expr1 > _expr2)) {                                                                       \
+                        char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))];                                           \
+                        char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))];                                           \
+                        xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1);                                     \
+                        xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2);                                     \
+                        log_error("%s:%i: Assertion failed: expected \"%s > %s\", but \"%s <= %s\"",            \
+                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2);                    \
+                        abort();                                                                                \
+                }                                                                                               \
         })
 
-#define ASSERT_LE(expr1, expr2)                                                                                  \
-        ({                                                                                                       \
-                typeof(expr1) _expr1 = (expr1);                                                                  \
-                typeof(expr2) _expr2 = (expr2);                                                                  \
-                if (_expr1 > _expr2) {                                                                           \
-                        char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))];                                            \
-                        char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))];                                            \
-                        xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1);                                      \
-                        xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2);                                      \
-                        log_error("%s:%i: Assertion failed: expected \"%s <= %s\", but \"%s > %s\"",             \
-                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2);                     \
-                        abort();                                                                                 \
-                }                                                                                                \
+#define ASSERT_LT(expr1, expr2)                                                                                 \
+        ({                                                                                                      \
+                typeof(expr1) _expr1 = (expr1);                                                                 \
+                typeof(expr2) _expr2 = (expr2);                                                                 \
+                if (!(_expr1 < _expr2)) {                                                                       \
+                        char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))];                                           \
+                        char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))];                                           \
+                        xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1);                                     \
+                        xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2);                                     \
+                        log_error("%s:%i: Assertion failed: expected \"%s < %s\", but \"%s >= %s\"",            \
+                                  PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2);                    \
+                        abort();                                                                                \
+                }                                                                                               \
         })
index 666dd970e6f0e2bad7543cccde491a150dbb4e40..203cf5fb3adcd5a42eafb5b221c577399df212ed 100644 (file)
@@ -5525,13 +5525,13 @@ int tpm2_unseal(Tpm2Context *c,
         if (r < 0)
                 return r;
 
-        _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
-        r = tpm2_make_encryption_session(c, primary_handle, hmac_key, &encryption_session);
-        if (r < 0)
-                return r;
-
         _cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
         for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
+                _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
+                r = tpm2_make_encryption_session(c, primary_handle, hmac_key, &encryption_session);
+                if (r < 0)
+                        return r;
+
                 _cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
                 _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
                 r = tpm2_make_policy_session(
index 613e88dd194f699535fd85221a6cb053f894f17f..5e037835435501060e8292b01b11ffa984e64800 100644 (file)
@@ -108,8 +108,9 @@ static int make_sshd_template_unit(
                         "Description=OpenSSH Per-Connection Server Daemon\n"
                         "Documentation=man:systemd-ssh-generator(8) man:sshd(8)\n"
                         "[Service]\n"
-                        "ExecStart=-%s -i\n"
-                        "StandardInput=socket",
+                        "ExecStart=-%s -i -o \"AuthorizedKeysFile ${CREDENTIALS_DIRECTORY}/ssh.ephemeral-authorized_keys-all .ssh/authorized_keys\"\n"
+                        "StandardInput=socket\n"
+                        "ImportCredential=ssh.ephemeral-authorized_keys-all",
                         sshd_binary);
 
                 r = fflush_and_check(f);
index b071623780a7d5076464ce0d865e1f3fb2b39558..52f4a47aa1f5aec8281743e46b94c27aa6df752a 100644 (file)
@@ -1642,7 +1642,7 @@ static int item_equivalent(Item *a, Item *b) {
 
         const char *a_shell = pick_shell(a),
                    *b_shell = pick_shell(b);
-        if (!path_equal_ptr(a_shell, b_shell) &&
+        if (!path_equal(a_shell, b_shell) &&
             !(is_nologin_shell(a_shell) && is_nologin_shell(b_shell))) {
                 _cleanup_free_ char *pa = NULL, *pb = NULL;
 
index 5daa0e64f6542d40a24882a5fadf39601de71402..cf4fca27e3f7aef5b55cf9658004d449eddff689 100644 (file)
@@ -42,7 +42,7 @@ TEST(trivial_compare_func) {
 }
 
 TEST(string_compare_func) {
-        assert_se(string_compare_func("fred", "wilma") != 0);
+        ASSERT_NE(string_compare_func("fred", "wilma"), 0);
         assert_se(string_compare_func("fred", "fred") == 0);
 }
 
index 1b788b191c0b76cf5c6106d095e38e29aa137d00..18c81adbcdbfbdb9df3a792be946561f11c10597 100644 (file)
@@ -19,7 +19,7 @@ TEST(hmac) {
                               "",
                               result);
         hex_result = hexmem(result, sizeof(result));
-        assert_se(streq_ptr(hex_result, "cadd5e42114351181f3abff477641d88efb57d2b5641a1e5c6d623363a6d3bad"));
+        ASSERT_STREQ(hex_result, "cadd5e42114351181f3abff477641d88efb57d2b5641a1e5c6d623363a6d3bad");
         hex_result = mfree(hex_result);
 
         hmac_sha256_by_string("waldo",
index 6d784a9ae83cc422d810a28ca5017e1376853346..affcaccc007fa8f961cb76f5808eb47565b792dd 100644 (file)
@@ -152,7 +152,7 @@ TEST(path_is_mount_point) {
          */
 
         /* file mountpoints */
-        assert_se(mkdtemp(tmp_dir) != NULL);
+        ASSERT_NOT_NULL(mkdtemp(tmp_dir));
         file1 = path_join(tmp_dir, "file1");
         assert_se(file1);
         file2 = path_join(tmp_dir, "file2");
index 84e55e175445bfc63cbb5b5354d7ef5f4484f0b5..e51ab9d29086bb28f7543e3f88e1bd683962cbeb 100644 (file)
 #include "tmpfile-util.h"
 
 TEST(path_is_os_tree) {
-        assert_se(path_is_os_tree("/") > 0);
-        assert_se(path_is_os_tree("/etc") == 0);
+        ASSERT_GT(path_is_os_tree("/"), 0);
+        ASSERT_EQ(path_is_os_tree("/etc"), 0);
         assert_se(path_is_os_tree("/idontexist") == -ENOENT);
 }
 
 TEST(parse_os_release) {
         /* Let's assume that we're running in a valid system, so os-release is available */
         _cleanup_free_ char *id = NULL, *id2 = NULL, *name = NULL, *foobar = NULL;
-        assert_se(parse_os_release(NULL, "ID", &id) == 0);
+        ASSERT_EQ(parse_os_release(NULL, "ID", &id), 0);
         log_info("ID: %s", id);
 
-        assert_se(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1) == 0);
-        assert_se(parse_os_release(NULL, "ID", &id2) == 0);
+        ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1), 0);
+        ASSERT_EQ(parse_os_release(NULL, "ID", &id2), 0);
         log_info("ID: %s", strnull(id2));
 
         _cleanup_(unlink_tempfilep) char tmpfile[] = "/tmp/test-os-util.XXXXXX";
-        assert_se(write_tmpfile(tmpfile,
+        ASSERT_EQ(write_tmpfile(tmpfile,
                                 "ID=the-id  \n"
-                                "NAME=the-name") == 0);
+                                "NAME=the-name"), 0);
 
-        assert_se(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1) == 0);
-        assert_se(parse_os_release(NULL, "ID", &id, "NAME", &name) == 0);
+        ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1), 0);
+        ASSERT_EQ(parse_os_release(NULL, "ID", &id, "NAME", &name), 0);
         log_info("ID: %s NAME: %s", id, name);
         assert_se(streq(id, "the-id"));
         assert_se(streq(name, "the-name"));
 
         _cleanup_(unlink_tempfilep) char tmpfile2[] = "/tmp/test-os-util.XXXXXX";
-        assert_se(write_tmpfile(tmpfile2,
+        ASSERT_EQ(write_tmpfile(tmpfile2,
                                 "ID=\"ignored\"  \n"
                                 "ID=\"the-id\"  \n"
-                                "NAME='the-name'") == 0);
+                                "NAME='the-name'"), 0);
 
-        assert_se(setenv("SYSTEMD_OS_RELEASE", tmpfile2, 1) == 0);
-        assert_se(parse_os_release(NULL, "ID", &id, "NAME", &name) == 0);
+        ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", tmpfile2, 1), 0);
+        ASSERT_EQ(parse_os_release(NULL, "ID", &id, "NAME", &name), 0);
         log_info("ID: %s NAME: %s", id, name);
         assert_se(streq(id, "the-id"));
         assert_se(streq(name, "the-name"));
 
-        assert_se(parse_os_release(NULL, "FOOBAR", &foobar) == 0);
+        ASSERT_EQ(parse_os_release(NULL, "FOOBAR", &foobar), 0);
         log_info("FOOBAR: %s", strnull(foobar));
-        assert_se(foobar == NULL);
+        ASSERT_NULL(foobar);
 
         assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0);
 }
@@ -70,6 +70,7 @@ TEST(parse_extension_release) {
 
         assert_se(a = path_join(tempdir, "/usr/lib/extension-release.d/extension-release.test"));
         assert_se(mkdir_parents(a, 0777) >= 0);
+        ASSERT_GE(mkdir_parents(a, 0777), 0);
 
         r = write_string_file(a, "ID=the-id  \n VERSION_ID=the-version-id", WRITE_STRING_FILE_CREATE);
         if (r < 0)
@@ -87,42 +88,42 @@ TEST(parse_extension_release) {
         if (r < 0)
                 log_error_errno(r, "Failed to write file: %m");
 
-        assert_se(parse_extension_release(tempdir, IMAGE_CONFEXT, "tester", false, "ID", &id, "VERSION_ID", &version_id) == 0);
+        ASSERT_EQ(parse_extension_release(tempdir, IMAGE_CONFEXT, "tester", false, "ID", &id, "VERSION_ID", &version_id), 0);
         log_info("ID: %s VERSION_ID: %s", id, version_id);
         assert_se(streq(id, "the-id"));
         assert_se(streq(version_id, "the-version-id"));
 
         assert_se(parse_extension_release(tempdir, IMAGE_CONFEXT, "tester", false, "FOOBAR", &foobar) == 0);
         log_info("FOOBAR: %s", strnull(foobar));
-        assert_se(foobar == NULL);
+        ASSERT_NULL(foobar);
 
         assert_se(parse_extension_release(tempdir, IMAGE_SYSEXT, "test", false, "FOOBAR", &foobar) == 0);
         log_info("FOOBAR: %s", strnull(foobar));
-        assert_se(foobar == NULL);
+        ASSERT_NULL(foobar);
 }
 
 TEST(load_os_release_pairs) {
         _cleanup_(unlink_tempfilep) char tmpfile[] = "/tmp/test-os-util.XXXXXX";
-        assert_se(write_tmpfile(tmpfile,
+        ASSERT_EQ(write_tmpfile(tmpfile,
                                 "ID=\"ignored\"  \n"
                                 "ID=\"the-id\"  \n"
-                                "NAME='the-name'") == 0);
+                                "NAME='the-name'"), 0);
 
-        assert_se(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1) == 0);
+        ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1), 0);
 
         _cleanup_strv_free_ char **pairs = NULL;
-        assert_se(load_os_release_pairs(NULL, &pairs) == 0);
+        ASSERT_EQ(load_os_release_pairs(NULL, &pairs), 0);
         assert_se(strv_equal(pairs, STRV_MAKE("ID", "the-id",
                                               "NAME", "the-name")));
 
-        assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0);
+        ASSERT_EQ(unsetenv("SYSTEMD_OS_RELEASE"), 0);
 }
 
 TEST(os_release_support_ended) {
         int r;
 
-        assert_se(os_release_support_ended("1999-01-01", false, NULL) == true);
-        assert_se(os_release_support_ended("2037-12-31", false, NULL) == false);
+        ASSERT_TRUE(os_release_support_ended("1999-01-01", false, NULL));
+        ASSERT_FALSE(os_release_support_ended("2037-12-31", false, NULL));
         assert_se(os_release_support_ended("-1-1-1", true, NULL) == -EINVAL);
 
         r = os_release_support_ended(NULL, false, NULL);
index ca11bf3a29c854073bc031f1e9dd9d3c9fde252e..5f36ae49f05ff3e7f9fc7ab10c0e11aa14836044 100644 (file)
@@ -23,7 +23,7 @@ TEST(print_paths) {
 }
 
 TEST(path) {
-        assert_se(path_is_absolute("/"));
+        assert_se( path_is_absolute("/"));
         assert_se(!path_is_absolute("./"));
 
         assert_se(streq(basename("./aa/bb/../file.da."), "file.da."));
@@ -31,17 +31,19 @@ TEST(path) {
         assert_se(streq(basename("/aa///file..."), "file..."));
         assert_se(streq(basename("file.../"), ""));
 
-        assert_se(PATH_IN_SET("/bin", "/", "/bin", "/foo"));
-        assert_se(PATH_IN_SET("/bin", "/bin"));
-        assert_se(PATH_IN_SET("/bin", "/foo/bar", "/bin"));
-        assert_se(PATH_IN_SET("/", "/", "/", "/foo/bar"));
+        assert_se( PATH_IN_SET("/bin", "/", "/bin", "/foo"));
+        assert_se( PATH_IN_SET("/bin", "/bin"));
+        assert_se( PATH_IN_SET("/bin", "/foo/bar", "/bin"));
+        assert_se( PATH_IN_SET("/", "/", "/", "/foo/bar"));
         assert_se(!PATH_IN_SET("/", "/abc", "/def"));
 
-        assert_se(path_equal_ptr(NULL, NULL));
-        assert_se(path_equal_ptr("/a", "/a"));
-        assert_se(!path_equal_ptr("/a", "/b"));
-        assert_se(!path_equal_ptr("/a", NULL));
-        assert_se(!path_equal_ptr(NULL, "/a"));
+        assert_se( path_equal(NULL, NULL));
+        assert_se( path_equal("/a", "/a"));
+        assert_se(!path_equal("/a", "/b"));
+        assert_se(!path_equal("/a", NULL));
+        assert_se(!path_equal(NULL, "/a"));
+        assert_se(!path_equal("a", NULL));
+        assert_se(!path_equal(NULL, "a"));
 }
 
 TEST(is_path) {
@@ -506,24 +508,24 @@ TEST(prefixes) {
                 log_error("---%s---", s);
                 assert_se(streq(s, values[i++]));
         }
-        assert_se(values[i] == NULL);
+        ASSERT_NULL(values[i]);
 
         i = 1;
         PATH_FOREACH_PREFIX(s, "/a/b/c/d") {
                 log_error("---%s---", s);
                 assert_se(streq(s, values[i++]));
         }
-        assert_se(values[i] == NULL);
+        ASSERT_NULL(values[i]);
 
         i = 0;
         PATH_FOREACH_PREFIX_MORE(s, "////a////b////c///d///////")
                 assert_se(streq(s, values[i++]));
-        assert_se(values[i] == NULL);
+        ASSERT_NULL(values[i]);
 
         i = 1;
         PATH_FOREACH_PREFIX(s, "////a////b////c///d///////")
                 assert_se(streq(s, values[i++]));
-        assert_se(values[i] == NULL);
+        ASSERT_NULL(values[i]);
 
         PATH_FOREACH_PREFIX(s, "////")
                 assert_not_reached();
@@ -759,11 +761,11 @@ static void test_prefix_root_one(const char *r, const char *p, const char *expec
         const char *t;
 
         assert_se(s = path_join(r, p));
-        assert_se(path_equal_ptr(s, expected));
+        assert_se(path_equal(s, expected));
 
         t = prefix_roota(r, p);
         assert_se(t);
-        assert_se(path_equal_ptr(t, expected));
+        assert_se(path_equal(t, expected));
 }
 
 TEST(prefix_root) {
index 52b2bc861e8a8e8b6bf4a89e23acdf0709295621..9ab52c60129f168f9a4f6e5bdcee7dbf05fb63ef 100644 (file)
@@ -22,7 +22,7 @@ static void test_draw_cylon_one(unsigned pos) {
 
         memset(buf, 0xff, sizeof(buf));
         draw_cylon(buf, sizeof(buf), CYLON_WIDTH, pos);
-        assert_se(strlen(buf) < sizeof(buf));
+        ASSERT_LE(strlen(buf), sizeof(buf));
 }
 
 TEST(draw_cylon) {
index 909ef4ccf265119f90d8b03e6ed4608cff13f7d5..2e901dc9bcfff46132f5288f3dd9a7715da789df 100644 (file)
@@ -17,7 +17,7 @@ TEST(make_recovery_key) {
         /* Check for successful recovery-key creation */
         r = make_recovery_key(&recovery_key);
         assert_se(r == 0);
-        assert_se(recovery_key != NULL);
+        ASSERT_NOT_NULL(recovery_key);
 
         /* Check that length of formatted key is 72 with 64 modhex characters */
         length = strlen(recovery_key);
index db76cde0a263c4b301b6cff11d64f35d595b8b52..f115a477738e67f5b7712cb640be6e272a8b26c0 100644 (file)
@@ -137,8 +137,8 @@ TEST(parse_uid) {
 }
 
 TEST(uid_ptr) {
-        assert_se(UID_TO_PTR(0) != NULL);
-        assert_se(UID_TO_PTR(1000) != NULL);
+        ASSERT_NOT_NULL(UID_TO_PTR(0));
+        ASSERT_NOT_NULL(UID_TO_PTR(1000));
 
         assert_se(PTR_TO_UID(UID_TO_PTR(0)) == 0);
         assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000);
index ff986b09d65f94d55ae7d1d71f66bd9e3aa4ab48..a8d27afa113e50618a296d980cd3a54edaa17857 100644 (file)
@@ -61,15 +61,12 @@ int start_transient_scope(sd_bus *bus, const char *machine_name, bool allow_pidf
                                   "AddRef",      "b",  1,
                                   "CollectMode", "s",  "inactive-or-failed");
 
-        if (allow_pidfd) {
-                _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
-                r = pidref_set_pid(&pidref, getpid_cached());
-                if (r < 0)
-                        return log_error_errno(r, "Failed to allocate PID reference: %m");
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        r = pidref_set_self(&pidref);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate PID reference: %m");
 
-                r = bus_append_scope_pidref(m, &pidref);
-        } else
-                r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, getpid_cached());
+        r = bus_append_scope_pidref(m, &pidref, allow_pidfd);
         if (r < 0)
                 return bus_log_create_error(r);
 
index 5ab0053be9142e27a48e2ce431d72875286a9048..8d2348d74872846d863cd5ba6a98b601785b80d2 100644 (file)
@@ -13,6 +13,7 @@
 #include "dirent-util.h"
 #include "fd-util.h"
 #include "discover-image.h"
+#include "pidref.h"
 #include "sd-daemon.h"
 #include "sd-event.h"
 #include "sd-id128.h"
@@ -33,6 +34,7 @@
 #include "gpt.h"
 #include "hexdecoct.h"
 #include "hostname-util.h"
+#include "io-util.h"
 #include "kernel-image.h"
 #include "log.h"
 #include "machine-credential.h"
@@ -54,6 +56,7 @@
 #include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
+#include "time-util.h"
 #include "tmpfile-util.h"
 #include "unit-name.h"
 #include "vmspawn-mount.h"
@@ -92,6 +95,8 @@ static sd_id128_t arg_uuid = {};
 static char **arg_kernel_cmdline_extra = NULL;
 static char **arg_extra_drives = NULL;
 static char *arg_background = NULL;
+static bool arg_pass_ssh_key = true;
+static char *arg_ssh_key_type = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -107,6 +112,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_forward_journal, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline_extra, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_extra_drives, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_ssh_key_type, freep);
 
 static int help(void) {
         _cleanup_free_ char *link = NULL;
@@ -158,6 +164,8 @@ static int help(void) {
                "\n%3$sIntegration:%4$s\n"
                "     --forward-journal=FILE|DIR\n"
                "                           Forward the VM's journal to the host\n"
+               "     --pass-ssh-key=BOOL   Create an SSH key to access the VM\n"
+               "     --ssh-key-type=TYPE   Choose what type of SSH key to pass\n"
                "\n%3$sInput/Output:%4$s\n"
                "     --console=MODE        Console mode (interactive, native, gui)\n"
                "     --background=COLOR    Set ANSI color for background\n"
@@ -200,6 +208,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SECURE_BOOT,
                 ARG_PRIVATE_USERS,
                 ARG_FORWARD_JOURNAL,
+                ARG_PASS_SSH_KEY,
+                ARG_SSH_KEY_TYPE,
                 ARG_SET_CREDENTIAL,
                 ARG_LOAD_CREDENTIAL,
                 ARG_FIRMWARE,
@@ -239,6 +249,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "secure-boot",       required_argument, NULL, ARG_SECURE_BOOT       },
                 { "private-users",     required_argument, NULL, ARG_PRIVATE_USERS     },
                 { "forward-journal",   required_argument, NULL, ARG_FORWARD_JOURNAL   },
+                { "pass-ssh-key",      required_argument, NULL, ARG_PASS_SSH_KEY      },
+                { "ssh-key-type",      required_argument, NULL, ARG_SSH_KEY_TYPE      },
                 { "set-credential",    required_argument, NULL, ARG_SET_CREDENTIAL    },
                 { "load-credential",   required_argument, NULL, ARG_LOAD_CREDENTIAL   },
                 { "firmware",          required_argument, NULL, ARG_FIRMWARE          },
@@ -442,6 +454,23 @@ static int parse_argv(int argc, char *argv[]) {
                                 return r;
                         break;
 
+                case ARG_PASS_SSH_KEY:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --pass-ssh-key= argument: %s", optarg);
+
+                        arg_pass_ssh_key = r;
+                        break;
+
+                case ARG_SSH_KEY_TYPE:
+                        if (!string_is_safe(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid value for --arg-ssh-key-type=: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_ssh_key_type, optarg);
+                        if (r < 0)
+                                return r;
+                        break;
+
                 case ARG_SET_CREDENTIAL: {
                         r = machine_credential_set(&arg_credentials, optarg);
                         if (r < 0)
@@ -1100,11 +1129,55 @@ static void set_window_title(PTYForward *f) {
         if (dot)
                 (void) pty_forward_set_title_prefix(f, dot);
 }
+static int generate_ssh_keypair(const char *key_path, const char *key_type) {
+        _cleanup_free_ char *ssh_keygen = NULL;
+        _cleanup_strv_free_ char **cmdline = NULL;
+        int r;
+
+        assert(key_path);
+
+        r = find_executable("ssh-keygen", &ssh_keygen);
+        if (r < 0)
+                return log_error_errno(r, "Failed to find ssh-keygen: %m");
+
+        cmdline = strv_new(ssh_keygen, "-f", key_path, /* don't encrypt the key */ "-N", "");
+        if (!cmdline)
+                return log_oom();
+
+        if (key_type) {
+                r = strv_extend_many(&cmdline, "-t", key_type);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *joined = quote_command_line(cmdline, SHELL_ESCAPE_EMPTY);
+                if (!joined)
+                        return log_oom();
+
+                log_debug("Executing: %s", joined);
+        }
+
+        r = safe_fork(
+                        ssh_keygen,
+                        FORK_WAIT|FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REARRANGE_STDIO,
+                        NULL);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                execv(ssh_keygen, cmdline);
+                log_error_errno(errno, "Failed to execve %s: %m", ssh_keygen);
+                _exit(EXIT_FAILURE);
+        }
+
+        return 0;
+}
 
 static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
         _cleanup_(ovmf_config_freep) OvmfConfig *ovmf_config = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_free_ char *machine = NULL, *qemu_binary = NULL, *mem = NULL, *trans_scope = NULL, *kernel = NULL;
+        _cleanup_(rm_rf_physical_and_freep) char *ssh_private_key_path = NULL, *ssh_public_key_path = NULL;
         _cleanup_close_ int notify_sock_fd = -EBADF;
         _cleanup_strv_free_ char **cmdline = NULL;
         _cleanup_free_ int *pass_fds = NULL;
@@ -1683,6 +1756,46 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                         return r;
         }
 
+        if (arg_pass_ssh_key) {
+                _cleanup_free_ char *scope_prefix = NULL, *privkey_path = NULL, *pubkey_path = NULL;
+                const char *key_type = arg_ssh_key_type ?: "ed25519";
+
+                r = unit_name_to_prefix(trans_scope, &scope_prefix);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to strip .scope suffix from scope: %m");
+
+                privkey_path = strjoin(arg_runtime_directory, "/", scope_prefix, "-", key_type);
+                if (!privkey_path)
+                        return log_oom();
+
+                pubkey_path = strjoin(privkey_path, ".pub");
+                if (!pubkey_path)
+                        return log_oom();
+
+                r = generate_ssh_keypair(privkey_path, key_type);
+                if (r < 0)
+                        return r;
+
+                ssh_private_key_path = TAKE_PTR(privkey_path);
+                ssh_public_key_path = TAKE_PTR(pubkey_path);
+        }
+
+        if (ssh_public_key_path && ssh_private_key_path) {
+                _cleanup_free_ char *scope_prefix = NULL, *cred_path = NULL;
+
+                cred_path = strjoin("ssh.ephemeral-authorized_keys-all:", ssh_public_key_path);
+                if (!cred_path)
+                        return log_oom();
+
+                r = machine_credential_load(&arg_credentials, cred_path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to load credential %s: %m", cred_path);
+
+                r = unit_name_to_prefix(trans_scope, &scope_prefix);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to strip .scope suffix from scope: %m");
+        }
+
         if (ARCHITECTURE_SUPPORTS_SMBIOS)
                 FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) {
                         _cleanup_free_ char *cred_data_b64 = NULL;
index 48c871f5e818521d2bc60f26d5075c1e3a59f95a..0b5fec7d3fec31ab58ebf5001e219d71138385a0 100644 (file)
@@ -149,6 +149,19 @@ failed run, can be downloaded from the artifacts.tar.gz archive which will be
 reachable in the same URL parent directory as the logs.gz that gets linked on
 the Github CI status.
 
+The log URL can be derived following a simple algorithm, however the test
+completion timestamp is needed and it's not easy to find without access to the
+log itself. For example, a noble s390x job started on 2024-03-23 at 02:09:11
+will be stored at the following URL:
+
+https://autopkgtest.ubuntu.com/results/autopkgtest-noble-upstream-systemd-ci-systemd-ci/noble/s390x/s/systemd-upstream/20240323_020911_e8e88@/log.gz
+
+The 5 characters at the end of the last directory are not random, but the first
+5 characters of a SHA1 hash generated based on the set of parameters given to
+the build plus the completion timestamp, such as:
+
+$ echo -n 'systemd-upstream {"build-git": "https://salsa.debian.org/systemd-team/systemd.git#debian/master", "env": ["UPSTREAM_REPO=https://github.com/systemd/systemd.git", "CFLAGS=-O0", "DEB_BUILD_PROFILES=pkg.systemd.upstream noudeb", "TEST_UPSTREAM=1", "CONFFLAGS_UPSTREAM=--werror -Dslow-tests=true", "UPSTREAM_PULL_REQUEST=31444", "GITHUB_STATUSES_URL=https://api.github.com/repos/systemd/systemd/statuses/c27f600a1c47f10b22964eaedfb5e9f0d4279cd9"], "ppas": ["upstream-systemd-ci/systemd-ci"], "submit-time": "2024-02-27 17:06:27", "uuid": "02cd262f-af22-4f82-ac91-53fa5a9e7811"}' | sha1sum | cut -c1-5
+
 To add new dependencies or new binaries to the packages used during the tests,
 a merge request can be sent to: https://salsa.debian.org/systemd-team/systemd
 targeting the 'upstream-ci' branch.
index 42574ce8ea7cfecf782f179132f0359efa1c95df..f8f3a4b3e32cf3f521b3e44ece906004497dbe57 100755 (executable)
@@ -11,9 +11,6 @@ test_append_files() {
     local workspace="${1:?}"
     local dropin_dir
 
-    mkdir -p "$workspace/test-journals/"
-    cp -av "${TEST_BASE_DIR:?}/test-journals/"* "$workspace/test-journals/"
-
     image_install curl setterm unzstd
     image_install -o openssl
     # Necessary for RH-based systems, otherwise MHD fails with:
index b47fa612b15d13d5d54fcccd49ae4b06ee903da4..aadf37a139ea610050bdb9dc1b437782d5c513cf 100644 (file)
@@ -4,6 +4,7 @@ if install_tests
         foreach subdir : [
                 'auxv',
                 'journal-data',
+                'test-journals',
                 'units',
                 'test-execute',
                 'test-fstab-generator',
index 1bf79d974c1743c505cdc989852a45ae80c7aaf2..866c548fbe404a4c25511f6e7bb662647d010490 100755 (executable)
@@ -5771,6 +5771,28 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         output = networkctl_status('veth-peer')
         self.assertIn(f'Offered DHCP leases: {client_address}', output)
 
+        # Check if the same addresses are used even if the service is restarted.
+        restart_networkd()
+        self.wait_online('veth99:routable', 'veth-peer:routable')
+
+        output = check_output('ip -4 address show dev veth-peer')
+        print(output)
+        self.assertIn(f'{server_address}', output)
+
+        output = check_output('ip -4 address show dev veth99')
+        print(output)
+        self.assertIn(f'{client_address}', output)
+
+        output = networkctl_status('veth99')
+        print(output)
+        self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)')
+        self.assertIn(f'Gateway: {server_address}', output)
+        self.assertIn(f'DNS: {server_address}', output)
+        self.assertIn(f'NTP: {server_address}', output)
+
+        output = networkctl_status('veth-peer')
+        self.assertIn(f'Offered DHCP leases: {client_address}', output)
+
     def test_dhcp_server_with_uplink(self):
         copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
                           '12-dummy.netdev', '25-dhcp-server-uplink.network')
index 2123b10152893758f4141564c25e4785b04d901f..479011ea78f43b81d0c162f281f30357ea67ba06 100755 (executable)
@@ -6,11 +6,11 @@ set -o pipefail
 JOURNAL_DIR="$(mktemp -d)"
 REMOTE_OUT="$(mktemp -d)"
 # tar on C8S doesn't support the --zstd option
-unzstd --stdout "/test-journals/afl-corrupted-journals.tar.zst" | tar -xC "$JOURNAL_DIR/"
+unzstd --stdout "/usr/lib/systemd/tests/testdata/test-journals/afl-corrupted-journals.tar.zst" | tar -xC "$JOURNAL_DIR/"
 while read -r file; do
     filename="${file##*/}"
     unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
-done < <(find /test-journals/corrupted/ -name "*.zst")
+done < <(find /usr/lib/systemd/tests/testdata/test-journals/corrupted/ -name "*.zst")
 # First, try each of them sequentially. Skip this part when running with plain
 # QEMU, as it is excruciatingly slow
 # Note: we care only about exit code 124 (timeout) and special bash exit codes
index 40ae39557beb4a6a0bbe3e05bf2d5cb67e0ec9cf..90d865cdf571c74f4dbbe30015e7bad84ec49660 100755 (executable)
@@ -244,7 +244,7 @@ JOURNAL_DIR="$(mktemp -d)"
 while read -r file; do
     filename="${file##*/}"
     unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
-done < <(find /test-journals/no-rtc -name "*.zst")
+done < <(find /usr/lib/systemd/tests/testdata/test-journals/no-rtc -name "*.zst")
 
 journalctl --directory="$JOURNAL_DIR" --list-boots --output=json >/tmp/lb1
 diff -u /tmp/lb1 - <<'EOF'
index e1e4367cc63f5234038486790edf8a7669d8508c..10388d8526b1e59da0abe9179761899d075ac811 100755 (executable)
@@ -195,6 +195,8 @@ if ! systemd-detect-virt -cq; then
             bash -xec 'timeout 1s nc -6 -u -l ::1 9999; exit 42'
         systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
             bash -xec 'timeout 1s nc -4 -l 127.0.0.1 6666; exit 42'
+        systemd-run --wait -p SuccessExitStatus="1 2" --pipe -p SocketBindDeny=any \
+            bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
         # Consequently, we should succeed when binding to a socket on the allow list
         # and keep listening on it until we're killed by `timeout` (EC 124)
         systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
index 4bf9922ef3aec57b7ac69206284c674827491673..b20b39d762b97fce1ed9c7999982838e3430a073 100755 (executable)
@@ -42,13 +42,16 @@ mount -t tmpfs tmpfs /home -o size=290M
 
 # we enable --luks-discard= since we run our tests in a tight VM, hence don't
 # needlessly pressure for storage. We also set the cheapest KDF, since we don't
-# want to waste CI CPU cycles on it.
+# want to waste CI CPU cycles on it. We also effectively disable rate-limiting on
+# the user by allowing 1000 logins per second
 NEWPASSWORD=xEhErW0ndafV4s homectl create test-user \
            --disk-size=min \
            --luks-discard=yes \
            --image-path=/home/test-user.home \
            --luks-pbkdf-type=pbkdf2 \
-           --luks-pbkdf-time-cost=1ms
+           --luks-pbkdf-time-cost=1ms \
+           --rate-limit-interval=1s \
+           --rate-limit-burst=1000
 inspect test-user
 
 PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
@@ -74,13 +77,19 @@ inspect test-user
 homectl deactivate test-user
 inspect test-user
 
+homectl update test-user --real-name "Offline test" --offline
+inspect test-user
+
 PASSWORD=xEhErW0ndafV4s homectl activate test-user
 inspect test-user
 
+# Ensure that the offline changes were propagated in
+grep "Offline test" /home/test-user/.identity
+
 homectl deactivate test-user
 inspect test-user
 
-PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Offline test"
+PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inactive test"
 inspect test-user
 
 PASSWORD=xEhErW0ndafV4s homectl activate test-user
@@ -89,6 +98,37 @@ inspect test-user
 homectl deactivate test-user
 inspect test-user
 
+# Do some keyring tests, but only on real kernels, since keyring access inside of containers will fail
+# (See: https://github.com/systemd/systemd/issues/17606)
+if ! systemd-detect-virt -cq ; then
+        PASSWORD=xEhErW0ndafV4s homectl activate test-user
+        inspect test-user
+
+        # Key should now be in the keyring
+        homectl update test-user --real-name "Keyring Test"
+        inspect test-user
+
+        # These commands shouldn't use the keyring
+        (! timeout 5s homectl authenticate test-user )
+        (! NEWPASSWORD="foobar" timeout 5s homectl passwd test-user )
+
+        homectl lock test-user
+        inspect test-user
+
+        # Key should be gone from keyring
+        (! timeout 5s homectl update test-user --real-name "Keyring Test 2" )
+
+        PASSWORD=xEhErW0ndafV4s homectl unlock test-user
+        inspect test-user
+
+        # Key should have been re-instantiated into the keyring
+        homectl update test-user --real-name "Keyring Test 3"
+        inspect test-user
+
+        homectl deactivate test-user
+        inspect test-user
+fi
+
 # Do some resize tests, but only if we run on real kernels, as quota inside of containers will fail
 if ! systemd-detect-virt -cq ; then
     # grow while inactive
@@ -131,7 +171,9 @@ if ! systemd-detect-virt -cq ; then
            --luks-discard=yes \
            --image-path=/home/test-user2.home \
            --luks-pbkdf-type=pbkdf2 \
-           --luks-pbkdf-time-cost=1ms
+           --luks-pbkdf-time-cost=1ms \
+           --rate-limit-interval=1s \
+           --rate-limit-burst=1000
     inspect test-user2
 
     # activate second user
@@ -150,24 +192,24 @@ if ! systemd-detect-virt -cq ; then
     homectl rebalance
     inspect test-user
     inspect test-user2
+
+    wait_for_state test-user2 active
+    homectl deactivate test-user2
+    wait_for_state test-user2 inactive
+    homectl remove test-user2
 fi
 
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
 (! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
-# CAREFUL adding more `homectl with` tests here. Auth can get rate-limited and cause the tests to fail.
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
+(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
 
 wait_for_state test-user inactive
 homectl remove test-user
 
-if ! systemd-detect-virt -cq ; then
-    wait_for_state test-user2 active
-    homectl deactivate test-user2
-    wait_for_state test-user2 inactive
-    homectl remove test-user2
-fi
-
 # blob directory tests
 # See docs/USER_RECORD_BLOB_DIRS.md
 checkblob() {
@@ -196,6 +238,7 @@ dd if=/dev/urandom of=/tmp/external-toobig bs=1M count=65
 NEWPASSWORD=EMJuc3zQaMibJo homectl create blob-user \
            --disk-size=min --luks-discard=yes \
            --luks-pbkdf-type=pbkdf2 --luks-pbkdf-time-cost=1ms \
+           --rate-limit-interval=1s --rate-limit-burst=1000 \
            --uid=12345 \
            --blob=/tmp/blob1
 inspect blob-user
@@ -297,6 +340,16 @@ checkblob barely-fits /tmp/external-barely-fits
 (! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b Ñ„айл=/tmp/external-test3 )
 (! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b special@chars=/tmp/external-test3 )
 
+# Make sure offline updates to blobs get propagated in
+homectl deactivate blob-user
+inspect blob-user
+homectl update blob-user --offline -b barely-fits= -b propagated=/tmp/external-test3
+inspect blob-user
+PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
+inspect blob-user
+(! checkblob barely-fits /tmp/external-barely-fits )
+checkblob propagated /tmp/external-test3
+
 homectl deactivate blob-user
 wait_for_state blob-user inactive
 homectl remove blob-user
@@ -466,6 +519,8 @@ if command -v ssh &>/dev/null && command -v sshd &>/dev/null && ! [[ -v ASAN_OPT
                        --luks-discard=yes \
                        --luks-pbkdf-type=pbkdf2 \
                        --luks-pbkdf-time-cost=1ms \
+                       --rate-limit-interval=1s \
+                       --rate-limit-burst=1000 \
                        --enforce-password-policy=no \
                        --ssh-authorized-keys=@/tmp/homed.id_ecdsa.pub \
                        --stop-delay=0 \
diff --git a/tools/check-efi-alignment.py b/tools/check-efi-alignment.py
new file mode 100755 (executable)
index 0000000..bb33ac0
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# vi: set tw=110 sw=4 ts=4 et:
+
+import sys
+
+import pefile
+
+
+def main():
+    pe = pefile.PE(sys.argv[1], fast_load=True)
+
+    for section in pe.sections:
+        name = section.Name.rstrip(b"\x00").decode()
+        file_addr = section.PointerToRawData
+        virt_addr = section.VirtualAddress
+        print(f"{name:10s} file=0x{file_addr:08x} virt=0x{virt_addr:08x}")
+
+        if file_addr % 512 != 0:
+            print(f"File address of {name} section is not aligned to 512 bytes", file=sys.stderr)
+            return 1
+
+        if virt_addr % 512 != 0:
+            print(f"Virt address of {name} section is not aligned to 512 bytes", file=sys.stderr)
+            return 1
+
+if __name__ == '__main__':
+    if len(sys.argv) != 2:
+        print(f"Usage: {sys.argv[0]} pe-image")
+        sys.exit(1)
+
+    sys.exit(main())
index 5411a023411b14662ba7a300ede6584a8f4afa23..cb1a284a601020373dd74a8d66ab99543d8ebf3f 100755 (executable)
@@ -26,6 +26,7 @@ import hashlib
 import io
 import os
 import pathlib
+import sys
 import time
 import typing
 from ctypes import (
@@ -55,26 +56,26 @@ from elftools.elf.relocation import (
 
 class PeCoffHeader(LittleEndianStructure):
     _fields_ = (
-        ("Machine", c_uint16),
-        ("NumberOfSections", c_uint16),
-        ("TimeDateStamp", c_uint32),
+        ("Machine",              c_uint16),
+        ("NumberOfSections",     c_uint16),
+        ("TimeDateStamp",        c_uint32),
         ("PointerToSymbolTable", c_uint32),
-        ("NumberOfSymbols", c_uint32),
+        ("NumberOfSymbols",      c_uint32),
         ("SizeOfOptionalHeader", c_uint16),
-        ("Characteristics", c_uint16),
+        ("Characteristics",      c_uint16),
     )
 
 
 class PeDataDirectory(LittleEndianStructure):
     _fields_ = (
         ("VirtualAddress", c_uint32),
-        ("Size", c_uint32),
+        ("Size",           c_uint32),
     )
 
 
 class PeRelocationBlock(LittleEndianStructure):
     _fields_ = (
-        ("PageRVA", c_uint32),
+        ("PageRVA",   c_uint32),
         ("BlockSize", c_uint32),
     )
 
@@ -86,62 +87,62 @@ class PeRelocationBlock(LittleEndianStructure):
 class PeRelocationEntry(LittleEndianStructure):
     _fields_ = (
         ("Offset", c_uint16, 12),
-        ("Type", c_uint16, 4),
+        ("Type",   c_uint16, 4),
     )
 
 
 class PeOptionalHeaderStart(LittleEndianStructure):
     _fields_ = (
-        ("Magic", c_uint16),
-        ("MajorLinkerVersion", c_uint8),
-        ("MinorLinkerVersion", c_uint8),
-        ("SizeOfCode", c_uint32),
-        ("SizeOfInitializedData", c_uint32),
+        ("Magic",                   c_uint16),
+        ("MajorLinkerVersion",      c_uint8),
+        ("MinorLinkerVersion",      c_uint8),
+        ("SizeOfCode",              c_uint32),
+        ("SizeOfInitializedData",   c_uint32),
         ("SizeOfUninitializedData", c_uint32),
-        ("AddressOfEntryPoint", c_uint32),
-        ("BaseOfCode", c_uint32),
+        ("AddressOfEntryPoint",     c_uint32),
+        ("BaseOfCode",              c_uint32),
     )
 
 
 class PeOptionalHeaderMiddle(LittleEndianStructure):
     _fields_ = (
-        ("SectionAlignment", c_uint32),
-        ("FileAlignment", c_uint32),
+        ("SectionAlignment",            c_uint32),
+        ("FileAlignment",               c_uint32),
         ("MajorOperatingSystemVersion", c_uint16),
         ("MinorOperatingSystemVersion", c_uint16),
-        ("MajorImageVersion", c_uint16),
-        ("MinorImageVersion", c_uint16),
-        ("MajorSubsystemVersion", c_uint16),
-        ("MinorSubsystemVersion", c_uint16),
-        ("Win32VersionValue", c_uint32),
-        ("SizeOfImage", c_uint32),
-        ("SizeOfHeaders", c_uint32),
-        ("CheckSum", c_uint32),
-        ("Subsystem", c_uint16),
-        ("DllCharacteristics", c_uint16),
+        ("MajorImageVersion",           c_uint16),
+        ("MinorImageVersion",           c_uint16),
+        ("MajorSubsystemVersion",       c_uint16),
+        ("MinorSubsystemVersion",       c_uint16),
+        ("Win32VersionValue",           c_uint32),
+        ("SizeOfImage",                 c_uint32),
+        ("SizeOfHeaders",               c_uint32),
+        ("CheckSum",                    c_uint32),
+        ("Subsystem",                   c_uint16),
+        ("DllCharacteristics",          c_uint16),
     )
 
 
 class PeOptionalHeaderEnd(LittleEndianStructure):
     _fields_ = (
-        ("LoaderFlags", c_uint32),
-        ("NumberOfRvaAndSizes", c_uint32),
-        ("ExportTable", PeDataDirectory),
-        ("ImportTable", PeDataDirectory),
-        ("ResourceTable", PeDataDirectory),
-        ("ExceptionTable", PeDataDirectory),
-        ("CertificateTable", PeDataDirectory),
-        ("BaseRelocationTable", PeDataDirectory),
-        ("Debug", PeDataDirectory),
-        ("Architecture", PeDataDirectory),
-        ("GlobalPtr", PeDataDirectory),
-        ("TLSTable", PeDataDirectory),
-        ("LoadConfigTable", PeDataDirectory),
-        ("BoundImport", PeDataDirectory),
-        ("IAT", PeDataDirectory),
+        ("LoaderFlags",           c_uint32),
+        ("NumberOfRvaAndSizes",   c_uint32),
+        ("ExportTable",           PeDataDirectory),
+        ("ImportTable",           PeDataDirectory),
+        ("ResourceTable",         PeDataDirectory),
+        ("ExceptionTable",        PeDataDirectory),
+        ("CertificateTable",      PeDataDirectory),
+        ("BaseRelocationTable",   PeDataDirectory),
+        ("Debug",                 PeDataDirectory),
+        ("Architecture",          PeDataDirectory),
+        ("GlobalPtr",             PeDataDirectory),
+        ("TLSTable",              PeDataDirectory),
+        ("LoadConfigTable",       PeDataDirectory),
+        ("BoundImport",           PeDataDirectory),
+        ("IAT",                   PeDataDirectory),
         ("DelayImportDescriptor", PeDataDirectory),
-        ("CLRRuntimeHeader", PeDataDirectory),
-        ("Reserved", PeDataDirectory),
+        ("CLRRuntimeHeader",      PeDataDirectory),
+        ("Reserved",              PeDataDirectory),
     )
 
 
@@ -152,44 +153,44 @@ class PeOptionalHeader(LittleEndianStructure):
 class PeOptionalHeader32(PeOptionalHeader):
     _anonymous_ = ("Start", "Middle", "End")
     _fields_ = (
-        ("Start", PeOptionalHeaderStart),
-        ("BaseOfData", c_uint32),
-        ("ImageBase", c_uint32),
-        ("Middle", PeOptionalHeaderMiddle),
+        ("Start",              PeOptionalHeaderStart),
+        ("BaseOfData",         c_uint32),
+        ("ImageBase",          c_uint32),
+        ("Middle",             PeOptionalHeaderMiddle),
         ("SizeOfStackReserve", c_uint32),
-        ("SizeOfStackCommit", c_uint32),
-        ("SizeOfHeapReserve", c_uint32),
-        ("SizeOfHeapCommit", c_uint32),
-        ("End", PeOptionalHeaderEnd),
+        ("SizeOfStackCommit",  c_uint32),
+        ("SizeOfHeapReserve",  c_uint32),
+        ("SizeOfHeapCommit",   c_uint32),
+        ("End",                PeOptionalHeaderEnd),
     )
 
 
 class PeOptionalHeader32Plus(PeOptionalHeader):
     _anonymous_ = ("Start", "Middle", "End")
     _fields_ = (
-        ("Start", PeOptionalHeaderStart),
-        ("ImageBase", c_uint64),
-        ("Middle", PeOptionalHeaderMiddle),
+        ("Start",              PeOptionalHeaderStart),
+        ("ImageBase",          c_uint64),
+        ("Middle",             PeOptionalHeaderMiddle),
         ("SizeOfStackReserve", c_uint64),
-        ("SizeOfStackCommit", c_uint64),
-        ("SizeOfHeapReserve", c_uint64),
-        ("SizeOfHeapCommit", c_uint64),
-        ("End", PeOptionalHeaderEnd),
+        ("SizeOfStackCommit",  c_uint64),
+        ("SizeOfHeapReserve",  c_uint64),
+        ("SizeOfHeapCommit",   c_uint64),
+        ("End",                PeOptionalHeaderEnd),
     )
 
 
 class PeSection(LittleEndianStructure):
     _fields_ = (
-        ("Name", c_char * 8),
-        ("VirtualSize", c_uint32),
-        ("VirtualAddress", c_uint32),
-        ("SizeOfRawData", c_uint32),
-        ("PointerToRawData", c_uint32),
+        ("Name",                 c_char * 8),
+        ("VirtualSize",          c_uint32),
+        ("VirtualAddress",       c_uint32),
+        ("SizeOfRawData",        c_uint32),
+        ("PointerToRawData",     c_uint32),
         ("PointerToRelocations", c_uint32),
         ("PointerToLinenumbers", c_uint32),
-        ("NumberOfRelocations", c_uint16),
-        ("NumberOfLinenumbers", c_uint16),
-        ("Characteristics", c_uint32),
+        ("NumberOfRelocations",  c_uint16),
+        ("NumberOfLinenumbers",  c_uint16),
+        ("Characteristics",      c_uint32),
     )
 
     def __init__(self):
@@ -206,12 +207,13 @@ assert sizeof(PeOptionalHeader32Plus) == 240
 
 PE_CHARACTERISTICS_RX = 0x60000020  # CNT_CODE|MEM_READ|MEM_EXECUTE
 PE_CHARACTERISTICS_RW = 0xC0000040  # CNT_INITIALIZED_DATA|MEM_READ|MEM_WRITE
-PE_CHARACTERISTICS_R = 0x40000040  # CNT_INITIALIZED_DATA|MEM_READ
+PE_CHARACTERISTICS_R  = 0x40000040  # CNT_INITIALIZED_DATA|MEM_READ
 
 IGNORE_SECTIONS = [
     ".eh_frame",
     ".eh_frame_hdr",
     ".ARM.exidx",
+    ".relro_padding",
 ]
 
 IGNORE_SECTION_TYPES = [
@@ -246,9 +248,12 @@ def align_down(x: int, align: int) -> int:
 
 
 def next_section_address(sections: typing.List[PeSection]) -> int:
-    return align_to(
-        sections[-1].VirtualAddress + sections[-1].VirtualSize, SECTION_ALIGNMENT
-    )
+    return align_to(sections[-1].VirtualAddress + sections[-1].VirtualSize,
+                    SECTION_ALIGNMENT)
+
+
+class BadSectionError(ValueError):
+    "One of the sections is in a bad state"
 
 
 def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]:
@@ -261,8 +266,9 @@ def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]:
     relro = None
     for elf_seg in elf.iter_segments():
         if elf_seg["p_type"] == "PT_LOAD" and elf_seg["p_align"] != SECTION_ALIGNMENT:
-            raise RuntimeError("ELF segments are not properly aligned.")
-        elif elf_seg["p_type"] == "PT_GNU_RELRO":
+            raise BadSectionError(f"ELF segment {elf_seg['p_type']} is not properly aligned"
+                                  f" ({elf_seg['p_align']} != {SECTION_ALIGNMENT})")
+        if elf_seg["p_type"] == "PT_GNU_RELRO":
             relro = elf_seg
 
     for elf_s in elf.iter_sections():
@@ -270,10 +276,14 @@ def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]:
             elf_s["sh_flags"] & SH_FLAGS.SHF_ALLOC == 0
             or elf_s["sh_type"] in IGNORE_SECTION_TYPES
             or elf_s.name in IGNORE_SECTIONS
+            or elf_s["sh_size"] == 0
         ):
             continue
         if elf_s["sh_type"] not in ["SHT_PROGBITS", "SHT_NOBITS"]:
-            raise RuntimeError(f"Unknown section {elf_s.name}.")
+            raise BadSectionError(f"Unknown section {elf_s.name} with type {elf_s['sh_type']}")
+        if elf_s.name == '.got':
+            # FIXME: figure out why those sections are inserted
+            print("WARNING: Non-empty .got section", file=sys.stderr)
 
         if elf_s["sh_flags"] & SH_FLAGS.SHF_EXECINSTR:
             rwx = PE_CHARACTERISTICS_RX
@@ -305,7 +315,7 @@ def iter_copy_sections(elf: ELFFile) -> typing.Iterator[PeSection]:
 
 
 def convert_sections(elf: ELFFile, opt: PeOptionalHeader) -> typing.List[PeSection]:
-    last_vma = 0
+    last_vma = (0, 0)
     sections = []
 
     for pe_s in iter_copy_sections(elf):
@@ -325,10 +335,11 @@ def convert_sections(elf: ELFFile, opt: PeOptionalHeader) -> typing.List[PeSecti
             PE_CHARACTERISTICS_R: b".rodata",
         }[pe_s.Characteristics]
 
-        # This can happen if not building with `-z separate-code`.
-        if pe_s.VirtualAddress < last_vma:
-            raise RuntimeError("Overlapping PE sections.")
-        last_vma = pe_s.VirtualAddress + pe_s.VirtualSize
+        # This can happen if not building with '-z separate-code'.
+        if pe_s.VirtualAddress < sum(last_vma):
+            raise BadSectionError(f"Section {pe_s.Name.decode()!r} @0x{pe_s.VirtualAddress:x} overlaps"
+                                  f" previous section @0x{last_vma[0]:x}+0x{last_vma[1]:x}=@0x{sum(last_vma):x}")
+        last_vma = (pe_s.VirtualAddress, pe_s.VirtualSize)
 
         if pe_s.Name == b".text":
             opt.BaseOfCode = pe_s.VirtualAddress
@@ -355,9 +366,9 @@ def copy_sections(
         if not elf_s:
             continue
         if elf_s.data_alignment > 1 and SECTION_ALIGNMENT % elf_s.data_alignment != 0:
-            raise RuntimeError(f"ELF section {name} is not aligned.")
+            raise BadSectionError(f"ELF section {name} is not aligned")
         if elf_s["sh_flags"] & (SH_FLAGS.SHF_EXECINSTR | SH_FLAGS.SHF_WRITE) != 0:
-            raise RuntimeError(f"ELF section {name} is not read-only data.")
+            raise BadSectionError(f"ELF section {name} is not read-only data")
 
         pe_s = PeSection()
         pe_s.Name = name.encode()
@@ -376,12 +387,8 @@ def apply_elf_relative_relocation(
     sections: typing.List[PeSection],
     addend_size: int,
 ):
-    # fmt: off
-    [target] = [
-        pe_s for pe_s in sections
-        if pe_s.VirtualAddress <= reloc["r_offset"] < pe_s.VirtualAddress + len(pe_s.data)
-    ]
-    # fmt: on
+    [target] = [pe_s for pe_s in sections
+                if pe_s.VirtualAddress <= reloc["r_offset"] < pe_s.VirtualAddress + len(pe_s.data)]
 
     addend_offset = reloc["r_offset"] - target.VirtualAddress
 
@@ -425,9 +432,10 @@ def convert_elf_reloc_table(
             continue
 
         if reloc["r_info_type"] == RELATIVE_RELOC:
-            apply_elf_relative_relocation(
-                reloc, elf_image_base, sections, elf.elfclass // 8
-            )
+            apply_elf_relative_relocation(reloc,
+                                          elf_image_base,
+                                          sections,
+                                          elf.elfclass // 8)
 
             # Now that the ELF relocation has been applied, we can create a PE relocation.
             block_rva = reloc["r_offset"] & ~0xFFF
@@ -442,7 +450,7 @@ def convert_elf_reloc_table(
 
             continue
 
-        raise RuntimeError(f"Unsupported relocation {reloc}")
+        raise BadSectionError(f"Unsupported relocation {reloc}")
 
 
 def convert_elf_relocations(
@@ -453,27 +461,25 @@ def convert_elf_relocations(
 ) -> typing.Optional[PeSection]:
     dynamic = elf.get_section_by_name(".dynamic")
     if dynamic is None:
-        raise RuntimeError("ELF .dynamic section is missing.")
+        raise BadSectionError("ELF .dynamic section is missing")
 
     [flags_tag] = dynamic.iter_tags("DT_FLAGS_1")
     if not flags_tag["d_val"] & ENUM_DT_FLAGS_1["DF_1_PIE"]:
-        raise RuntimeError("ELF file is not a PIE.")
+        raise ValueError("ELF file is not a PIE")
 
     # This checks that the ELF image base is 0.
     symtab = elf.get_section_by_name(".symtab")
     if symtab:
         exe_start = symtab.get_symbol_by_name("__executable_start")
         if exe_start and exe_start[0]["st_value"] != 0:
-            raise RuntimeError("Unexpected ELF image base.")
-
-    opt.SizeOfHeaders = align_to(
-        PE_OFFSET
-        + len(PE_MAGIC)
-        + sizeof(PeCoffHeader)
-        + sizeof(opt)
-        + sizeof(PeSection) * max(len(sections) + 1, minimum_sections),
-        FILE_ALIGNMENT,
-    )
+            raise ValueError("Unexpected ELF image base")
+
+    opt.SizeOfHeaders = align_to(PE_OFFSET
+                                 + len(PE_MAGIC)
+                                 + sizeof(PeCoffHeader)
+                                 + sizeof(opt)
+                                 + sizeof(PeSection) * max(len(sections) + 1, minimum_sections),
+                                 FILE_ALIGNMENT)
 
     # We use the basic VMA layout from the ELF image in the PE image. This could cause the first
     # section to overlap the PE image headers during runtime at VMA 0. We can simply apply a fixed
@@ -482,9 +488,8 @@ def convert_elf_relocations(
     # the ELF portions of the image.
     segment_offset = 0
     if sections[0].VirtualAddress < opt.SizeOfHeaders:
-        segment_offset = align_to(
-            opt.SizeOfHeaders - sections[0].VirtualAddress, SECTION_ALIGNMENT
-        )
+        segment_offset = align_to(opt.SizeOfHeaders - sections[0].VirtualAddress,
+                                  SECTION_ALIGNMENT)
 
     opt.AddressOfEntryPoint = elf["e_entry"] + segment_offset
     opt.BaseOfCode += segment_offset
@@ -494,10 +499,12 @@ def convert_elf_relocations(
     pe_reloc_blocks: typing.Dict[int, PeRelocationBlock] = {}
     for reloc_type, reloc_table in dynamic.get_relocation_tables().items():
         if reloc_type not in ["REL", "RELA"]:
-            raise RuntimeError("Unsupported relocation type {elf_reloc_type}.")
-        convert_elf_reloc_table(
-            elf, reloc_table, opt.ImageBase + segment_offset, sections, pe_reloc_blocks
-        )
+            raise BadSectionError(f"Unsupported relocation type {reloc_type}")
+        convert_elf_reloc_table(elf,
+                                reloc_table,
+                                opt.ImageBase + segment_offset,
+                                sections,
+                                pe_reloc_blocks)
 
     for pe_s in sections:
         pe_s.VirtualAddress += segment_offset
@@ -517,9 +524,7 @@ def convert_elf_relocations(
             block.entries.append(PeRelocationEntry())
 
         block.PageRVA += segment_offset
-        block.BlockSize = (
-            sizeof(PeRelocationBlock) + sizeof(PeRelocationEntry) * n_relocs
-        )
+        block.BlockSize = sizeof(PeRelocationBlock) + sizeof(PeRelocationEntry) * n_relocs
         data += block
         for entry in sorted(block.entries, key=lambda e: e.Offset):
             data += entry
@@ -539,7 +544,10 @@ def convert_elf_relocations(
 
 
 def write_pe(
-    file, coff: PeCoffHeader, opt: PeOptionalHeader, sections: typing.List[PeSection]
+    file,
+    coff: PeCoffHeader,
+    opt: PeOptionalHeader,
+    sections: typing.List[PeSection],
 ):
     file.write(b"MZ")
     file.seek(0x3C, io.SEEK_SET)
@@ -552,7 +560,8 @@ def write_pe(
     offset = opt.SizeOfHeaders
     for pe_s in sorted(sections, key=lambda s: s.VirtualAddress):
         if pe_s.VirtualAddress < opt.SizeOfHeaders:
-            raise RuntimeError(f"Section {pe_s.Name} overlapping PE headers.")
+            raise BadSectionError(f"Section {pe_s.Name} @0x{pe_s.VirtualAddress:x} overlaps"
+                                  " PE headers ending at 0x{opt.SizeOfHeaders:x}")
 
         pe_s.PointerToRawData = offset
         file.write(pe_s)
@@ -570,9 +579,9 @@ def write_pe(
 def elf2efi(args: argparse.Namespace):
     elf = ELFFile(args.ELF)
     if not elf.little_endian:
-        raise RuntimeError("ELF file is not little-endian.")
+        raise ValueError("ELF file is not little-endian")
     if elf["e_type"] not in ["ET_DYN", "ET_EXEC"]:
-        raise RuntimeError("Unsupported ELF type.")
+        raise ValueError(f"Unsupported ELF type {elf['e_type']}")
 
     pe_arch = {
         "EM_386": 0x014C,
@@ -583,7 +592,7 @@ def elf2efi(args: argparse.Namespace):
         "EM_X86_64": 0x8664,
     }.get(elf["e_machine"])
     if pe_arch is None:
-        raise RuntimeError(f"Unsupported ELF arch {elf['e_machine']}")
+        raise ValueError(f"Unsupported ELF architecture {elf['e_machine']}")
 
     coff = PeCoffHeader()
     opt = PeOptionalHeader32() if elf.elfclass == 32 else PeOptionalHeader32Plus()
@@ -636,7 +645,7 @@ def elf2efi(args: argparse.Namespace):
     write_pe(args.PE, coff, opt, sections)
 
 
-def main():
+def create_parser() -> argparse.ArgumentParser:
     parser = argparse.ArgumentParser(description="Convert ELF binaries to PE/EFI")
     parser.add_argument(
         "--version-major",
@@ -690,7 +699,11 @@ def main():
         default="",
         help="Copy these sections if found",
     )
+    return parser
+
 
+def main():
+    parser = create_parser()
     elf2efi(parser.parse_args())