]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #15840 from Werkov/mkosi-opensuse
authorLennart Poettering <lennart@poettering.net>
Tue, 26 May 2020 08:02:29 +0000 (10:02 +0200)
committerGitHub <noreply@github.com>
Tue, 26 May 2020 08:02:29 +0000 (10:02 +0200)
mkosi: openSUSE updates

158 files changed:
.github/workflows/main.yml
NEWS
README.md
TODO
docs/CODE_QUALITY.md
docs/TRANSIENT-SETTINGS.md
hwdb.d/60-sensor.hwdb
hwdb.d/70-mouse.hwdb
hwdb.d/parse_hwdb.py
man/crypttab.xml
man/homed.conf.xml
man/kernel-command-line.xml
man/logind.conf.xml
man/org.freedesktop.login1.xml
man/pam_systemd_home.xml
man/repart.d.xml
man/sd_bus_add_match.xml
man/sd_bus_add_object.xml
man/sd_bus_call.xml
man/sd_bus_call_method.xml
man/sd_bus_close.xml
man/sd_bus_get_current_handler.xml
man/sd_bus_request_name.xml
man/sd_bus_slot_get_bus.xml
man/sysctl.d.xml
man/systemd-cryptsetup@.service.xml
man/systemd-update-done.service.xml
man/systemd.mount.xml
man/systemd.network.xml
man/systemd.unit.xml
man/yubikey-crypttab.sh
meson.build
meson_options.txt
rules.d/60-persistent-storage.rules
rules.d/99-systemd.rules.in
shell-completion/zsh/_resolvectl
src/basic/blockdev-util.c
src/basic/blockdev-util.h
src/basic/btrfs-util.c
src/basic/efivars.c
src/basic/fileio.c
src/basic/fs-util.c
src/basic/fs-util.h
src/basic/glob-util.c
src/basic/glob-util.h
src/basic/macro.h
src/basic/memory-util.h
src/basic/path-util.c
src/basic/proc-cmdline.c
src/basic/proc-cmdline.h
src/boot/bootctl.c
src/busctl/busctl.c
src/core/dbus-manager.c
src/core/dbus-mount.c
src/core/hostname-setup.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/mount.c
src/core/mount.h
src/coredump/coredumpctl.c
src/cryptsetup/cryptsetup-generator.c
src/cryptsetup/cryptsetup-pkcs11.c
src/cryptsetup/cryptsetup-pkcs11.h
src/cryptsetup/cryptsetup-util.c [new file with mode: 0644]
src/cryptsetup/cryptsetup-util.h [new file with mode: 0644]
src/cryptsetup/cryptsetup.c
src/fstab-generator/fstab-generator.c
src/home/homed-home.c
src/home/homed-manager.c
src/home/homed.conf
src/home/homework-cifs.c
src/home/homework-luks.c
src/home/homework-mount.c
src/home/homework-mount.h
src/home/homework.c
src/home/homework.h
src/home/pam_systemd_home.c
src/hostname/hostnamed.c
src/journal-remote/journal-upload-journal.c
src/journal-remote/journal-upload.c
src/journal/journal-file.c
src/journal/journal-internal.h
src/journal/journal-send.c
src/journal/journalctl.c
src/journal/sd-journal.c
src/libsystemd-network/dhcp-identifier.c
src/libsystemd-network/dhcp-identifier.h
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h
src/libsystemd/sd-bus/bus-message.c
src/libsystemd/sd-path/sd-path.c
src/login/logind-core.c
src/login/logind-dbus.c
src/login/logind-gperf.gperf
src/login/logind.conf.in
src/login/logind.h
src/login/pam_systemd.c
src/login/user-runtime-dir.c
src/network/netdev/ipvlan.c
src/network/netdev/ipvlan.h
src/network/networkctl.c
src/network/networkd-address.c
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp4.h
src/network/networkd-dhcp6.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/nspawn/nspawn-settings.c
src/nspawn/nspawn-settings.h
src/partition/makefs.c
src/partition/repart.c
src/partition/test-repart.sh
src/resolve/resolved-manager.h
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/condition.c
src/shared/conf-parser.c
src/shared/conf-parser.h
src/shared/dissect-image.c
src/shared/install.c
src/shared/ipvlan-util.c [new file with mode: 0644]
src/shared/ipvlan-util.h [new file with mode: 0644]
src/shared/meson.build
src/sysctl/sysctl.c
src/systemctl/systemctl.c
src/systemd/sd-dhcp-client.h
src/systemd/sd-dhcp6-client.h
src/test/test-fs-util.c
src/udev/net/link-config.c
src/udev/udev-builtin-path_id.c
src/udev/udev-rules.c
test/TEST-03-JOBS/test.sh
test/TEST-13-NSPAWN-SMOKE/test.sh
test/TEST-14-MACHINE-ID/test.sh
test/TEST-21-SYSUSERS/test.sh
test/fuzz/fuzz-bus-message/oss-fuzz-19446 [new file with mode: 0644]
test/fuzz/fuzz-network-parser/directives.network
test/fuzz/fuzz-network-parser/github-15885 [new file with mode: 0644]
test/fuzz/fuzz-unit-file/directives.service
test/fuzz/meson.build
test/test-functions
tools/oss-fuzz.sh
travis-ci/managers/debian.sh
travis-ci/managers/fuzzit.sh
units/meson.build

index ee5dd098e9b89ae58df688444bd44d84d1dda9a5..b46b1d619e27fa0a3091efb90d892683a38917fb 100644 (file)
@@ -18,6 +18,7 @@ on:
 jobs:
  Fuzzing:
    runs-on: ubuntu-latest
+   if: github.repository == 'systemd/systemd'
    steps:
    - name: Build Fuzzers
      id: build
diff --git a/NEWS b/NEWS
index e7aaa12ca095448a91063aaa0069a54af5a1d060..4c4e92b763574ef833949d9a2d1aecc71394becb 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -819,6 +819,9 @@ CHANGES WITH 243:
         * systemd-networkd's TUN support gained a new setting VnetHeader= for
           tweaking Generic Segment Offload support.
 
+        * The address family for policy rules may be specified using the new
+          Family= option in the [RoutingPolicyRule] section.
+
         * networkctl gained a new "delete" command for removing virtual network
           devices, as well as a new "--stats" switch for showing device
           statistics.
index bfb2b3fbd40182460cbabc02dc198c8e390a50ba..8e72fbc2bef59476cc970fef914e0c8d099055b9 100644 (file)
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ System and Service Manager
 [![Coverity Scan Status](https://scan.coverity.com/projects/350/badge.svg)](https://scan.coverity.com/projects/350)<br/>
 [![Fuzzit Status](https://app.fuzzit.dev/badge?org_id=systemd&branch=master)](https://app.fuzzit.dev/orgs/systemd/dashboard)<br/>
 [![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/systemd.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#systemd)<br/>
+[![CIFuzz](https://github.com/systemd/systemd/workflows/CIFuzz/badge.svg)](https://github.com/systemd/systemd/actions)<br/>
 [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)<br/>
 [![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)<br/>
 [![Language Grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/systemd/systemd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/systemd/systemd/context:cpp)<br/>
diff --git a/TODO b/TODO
index 792f2d4be1a33dc9f8ea730973b0ddbc69dd5080..8327c941068fc62abe3c8d63306f3c51c8e3e6b8 100644 (file)
--- a/TODO
+++ b/TODO
@@ -22,6 +22,15 @@ Janitorial Clean-ups:
 
 Features:
 
+* build short web pages out of each catalog entry, build them along with man
+  pages, and include hyperlinks to them in the journal output
+
+* machined: add API to acquire UID range. add API to mount/dissect loopback
+  file. Both protected by PK. Then make nspawn use these APIs to run
+  unprivileged containers. i.e. push the truly privileged bits into machined,
+  so that the client side can remain entirely unprivileged, with SUID or
+  anything like that.
+
 * add "throttling" to sd-event event sources: optionally, when we wake up too
   often for one, let's turn it off entirely for a while. Use that for the
   /proc/self/mountinfo logic.
@@ -75,7 +84,7 @@ Features:
 * homed: as an extension to the directory+subvolume backend: if located on
   especially marked fs, then sync down password into LUKS header of that fs,
   and always verify passwords against it too. Bootstrapping is a problem
-  though: if noone is logged in (or no other user even exists yet), how do you
+  though: if no one is logged in (or no other user even exists yet), how do you
   unlock the volume in order to create the first user and add the first pw.
 
 * homed: support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt
@@ -252,8 +261,7 @@ Features:
   that are linked to these places instead of copied. After all they are
   constant vendor data.
 
-* maybe add kernel cmdline params: 1) to force first-boot mode + 2) to force
-  random seed crediting
+* maybe add kernel cmdline params: to force random seed crediting
 
 * nspawn: on cgroupsv1 issue cgroup empty handler process based on host events,
   so that we make cgroup agent logic safe
@@ -1204,17 +1212,14 @@ Features:
 * networkd:
    - add more keys to [Route] and [Address] sections
    - add support for more DHCPv4 options (and, longer term, other kinds of dynamic config)
-   - add proper initrd support (in particular generate .network/.link files based on /proc/cmdline)
    - add reduced [Link] support to .network files
-   - add Scope= parsing option for [Network]
    - properly handle routerless dhcp leases
    - work with non-Ethernet devices
-   - add support for more bond options
    - dhcp: do we allow configuring dhcp routes on interfaces that are not the one we got the dhcp info from?
    - the DHCP lease data (such as NTP/DNS) is still made available when
      a carrier is lost on a link. It should be removed instantly.
    - expose in the API the following bits:
-         - option 15, domain name and/or option 119, search list
+         - option 15, domain name
          - option 12, hostname and/or option 81, fqdn
          - option 123, 144, geolocation
          - option 252, configure http proxy (PAC/wpad)
@@ -1223,12 +1228,8 @@ Features:
    - allow Name= to be specified repeatedly in the [Match] section. Maybe also
      support Name=foo*|bar*|baz ?
    - duplicate address check for static IPs (like ARPCHECK in network-scripts)
-   - allow DUID/IAID to be customized, see issue #394.
    - whenever uplink info changes, make DHCP server send out FORCERENEW
 
-* networkd-wait-online:
-   - make operstates to wait for configurable?
-
 * dhcp:
    - figure out how much we can increase Maximum Message Size
 
@@ -1252,20 +1253,14 @@ External:
    - natively watch for dbus-*.service symlinks (PENDING)
    - teach dbus to activate all services it finds in /etc/systemd/services/org-*.service
 
-* fix alsa mixer restore to not print error when no config is stored
-
 * make cryptsetup lower --iter-time
 
-* patch kernel for xattr support in /dev, /proc/, /sys?
-
 * kernel: add device_type = "fb", "fbcon" to class "graphics"
 
 * /usr/bin/service should actually show the new command line
 
 * fedora: suggest auto-restart on failure, but not on success and not on coredump. also, ask people to think about changing the start limit logic. Also point people to RestartPreventExitStatus=, SuccessExitStatus=
 
-* fedora: F20: go timer units all the way, leave cron.daily for cron
-
 * neither pkexec nor sudo initialize environ[] from the PAM environment?
 
 * fedora: update policy to declare access mode and ownership of unit files to root:root 0644, and add an rpmlint check for it
index 9f261474acdba3795c9770f154c714db06c0ae40..a724d663f69b66dad3fb7b8210dc17f2338a303a 100644 (file)
@@ -71,5 +71,8 @@ available functionality:
     See [Testing systemd using sanitizers](https://systemd.io/TESTING_WITH_SANITIZERS)
     for more information.
 
+16. Fossies provides [source code misspelling reports](https://fossies.org/features.html#codespell).
+    The systemd report can be found [here](https://fossies.org/linux/test/systemd-master.tar.gz/codespell.html).
+
 Access to Coverity and oss-fuzz reports is limited. Please reach out to the
 maintainers if you need access.
index d5a80466769841be47e77e173875713c0bfdd927..d9b1c20c77f3e1c0f42b62da1bf32d2e9b9d6e9c 100644 (file)
@@ -332,6 +332,7 @@ All mount unit settings are available to transient units:
 ✓ SloppyOptions=
 ✓ LazyUnmount=
 ✓ ForceUnmount=
+✓ ReadWriteOnly=
 ```
 
 ## Automount Unit Settings
index 0763f040f11ff238a7c6c9a12ad3ffc841bf670d..49bb90f58ec0f2d884dbd0ab0950aca5e226a4dc 100644 (file)
@@ -93,6 +93,9 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnAcer*:pnSwitchSW312-31:*
 sensor:modalias:acpi:BOSC0200*:dmi:*svn*Acer*:*pn*Spin*SP111-33*
  ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
 
+sensor:modalias:acpi:BOSC0200*:dmi:*svnAcer*:*pnSpinSP111-34*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+
 #########################################
 # Archos
 #########################################
index c759527e2a2ce4031ed792b1439949296dd05019..ef5a473e87ae5ea990db48d9955fa81c1f7d3acf 100644 (file)
@@ -50,8 +50,6 @@
 #    MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL
 #    MOUSE_WHEEL_CLICK_COUNT
 #    MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL
-#    MOUSE_WHEEL_TILT_HORIZONTAL
-#    MOUSE_WHEEL_TILT_VERTICAL
 #
 #########################################
 #         ID_INPUT_TRACKBALL            #
 # MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL works the same way but also follows the
 # rules of MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL.
 
-#########################################
-#    MOUSE_WHEEL_TILT_HORIZONTAL        #
-#    MOUSE_WHEEL_TILT_VERTICAL          #
-#########################################
-#
-# Indicates that the respective axis is not a mouse wheel rotation but a
-# tilt along that axis. Wheel tilt is most commonly used for horizontal
-# scroll wheel emulation on mice with only a single vertical wheel.
-#
-# The vertical and horizontal Axes are independently marked as tilt axes,
-# for example it is permitted to have a MOUSE_WHEEL_CLICK_COUNT or
-# MOUSE_WHEEL_CLICK_ANGLE for the vertical axis and mark the horizontal axis
-# marked as as MOUSE_WHEEL_TILT_HORIZONTAL.
-#
-# It is a bug to have either CLICK_COUNT or CLICK_ANGLE set on the same axis
-# as WHEEL_TILT. Applications should give priority to WHEEL_TILT and ignore
-# other settings.
-#
-# This is a flag only, permitted values: 0 or 1
-
 #
 # Sort by brand, type (usb, bluetooth), DPI, frequency.
 # For mice with switchable resolution, sort by the starred entry.
index 579c45fda0218281080d94aef71493e1d171eeb3..abef56728fcbf86162e8cd81e1f6bc5efbac1ead 100755 (executable)
@@ -115,8 +115,6 @@ def property_grammar():
              ('ID_INPUT_TOUCHPAD', Literal('1')),
              ('ID_INPUT_TOUCHSCREEN', Literal('1')),
              ('ID_INPUT_TRACKBALL', Literal('1')),
-             ('MOUSE_WHEEL_TILT_HORIZONTAL', Literal('1')),
-             ('MOUSE_WHEEL_TILT_VERTICAL', Literal('1')),
              ('POINTINGSTICK_SENSITIVITY', INTEGER),
              ('POINTINGSTICK_CONST_ACCEL', REAL),
              ('ID_INPUT_JOYSTICK_INTEGRATION', Or(('internal', 'external'))),
index 9b6fffd154ef9ad9f1779d6ce119432eb569439d..3942fe67f9b8eede6b080b5f249d0369435420d9 100644 (file)
@@ -41,7 +41,7 @@
     character are ignored. Each of the remaining lines describes one
     encrypted block device. Fields are delimited by white space.</para>
 
-    <para>Each line is in the form<programlisting><replaceable>name</replaceable> <replaceable>encrypted-device</replaceable> <replaceable>password</replaceable> <replaceable>options</replaceable></programlisting>
+    <para>Each line is in the form<programlisting><replaceable>volume-name</replaceable> <replaceable>encrypted-device</replaceable> <replaceable>key-file</replaceable> <replaceable>options</replaceable></programlisting>
     The first two fields are mandatory, the remaining two are
     optional.</para>
 
     it is opened as a LUKS device; otherwise, it is assumed to be in
     raw dm-crypt (plain mode) format.</para>
 
-    <para>The first field contains the name of the resulting encrypted
-    block device; the device is set up within
-    <filename>/dev/mapper/</filename>.</para>
+    <para>The first field contains the name of the resulting encrypted volume; its block device is set up
+    below <filename>/dev/mapper/</filename>.</para>
 
     <para>The second field contains a path to the underlying block
     device or file, or a specification of a block device via
     <literal>UUID=</literal> followed by the UUID.</para>
 
-    <para>The third field specifies the encryption password. If the
-    field is not present or the password is set to
-    <literal>none</literal> or <literal>-</literal>, the password has
-    to be manually entered during system boot. Otherwise, the field is
-    interpreted as an absolute path to a file containing the encryption
-    password. For swap encryption, <filename>/dev/urandom</filename>
-    or the hardware device <filename>/dev/hw_random</filename> can be
-    used as the password file; using <filename>/dev/random</filename>
-    may prevent boot completion if the system does not have enough
-    entropy to generate a truly random encryption key.</para>
+    <para>The third field specifies an absolute path to a file to read the encryption key from. If the field
+    is not present or set to <literal>none</literal> or <literal>-</literal>, a key file named after the
+    volume to unlock (i.e. the first column of the line), suffixed with <filename>.key</filename> is
+    automatically loaded from the <filename>/etc/cryptsetup-keys.d/</filename> and
+    <filename>/run/cryptsetup-keys.d/</filename> directories, if present. Otherwise, the password has to be
+    manually entered during system boot. For swap encryption, <filename>/dev/urandom</filename> may be used
+    as key file.</para>
 
     <para>The fourth field, if present, is a comma-delimited list of
     options. The following options are recognized:</para>
         size is then given by the key size.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>keyfile-erase</option></term>
+
+        <listitem><para>If enabled, the specified key file is erased after the volume is activated or when
+        activation fails. This is in particular useful when the key file is only acquired transiently before
+        activation (e.g. via a file in <filename>/run/</filename>, generated by a service running before
+        activation), and shall be removed after use. Defaults to off.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>key-slot=</option></term>
 
         before it is used to unlock the LUKS volume.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>try-empty-password=</option></term>
+
+        <listitem><para>Takes a boolean argument. If enabled, right before asking the user for a password it
+        is first attempted to unlock the volume with an empty password. This is useful for systems that are
+        initialized with an encrypted volume with only an empty password set, which shall be replaced with a
+        suitable password during first boot, but after activation.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>x-systemd.device-timeout=</option></term>
 
index 03590feeaa6e8b577c4ba3aba702fd2eb9bf9a68..3e08b1a091c799df110fd1cbe821af43e8337795 100644 (file)
@@ -53,7 +53,7 @@
         <literal>cifs</literal>. For details about these options, see
         <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. If not
         configured or assigned the empty string, the default storage is automatically determined: if not
-        running in a container enviroment and <filename>/home/</filename> is not itself encrypted, defaults
+        running in a container environment and <filename>/home/</filename> is not itself encrypted, defaults
         to <literal>luks</literal>. Otherwise defaults to <literal>subvolume</literal> if
         <filename>/home/</filename> is on a btrfs file system, and <literal>directory</literal>
         otherwise. Note that the storage selected on the <command>homectl</command> command line always takes
index 4aac14ea180c8bf39961b3dc231ea6606888350b..0f2972a0b6840346228a56198f3e8d6b8d85eddc 100644 (file)
 
         <listitem><para>Takes a boolean argument, defaults to on. If off,
         <citerefentry><refentrytitle>systemd-firstboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-        will not query the user for basic system settings, even if the system boots up for the first time and the
-        relevant settings are not initialized yet.</para></listitem>
+        will not query the user for basic system settings, even if the system boots up for the first time and
+        the relevant settings are not initialized yet. Not to be confused with
+        <varname>systemd.condition-first-boot=</varname> (see below), which overrides the result of the
+        <varname>ConditionFirstBoot=</varname> unit file condition, and thus controls more than just
+        <filename>systemd-firstboot.service</filename> behaviour.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.condition-needs-update=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If specified, overrides the result of
+        <varname>ConditionNeedsUpdate=</varname> unit condition checks. See
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        details.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.condition-first-boot=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If specified, overrides the result of
+        <varname>ConditionFirstBoot=</varname> unit condition checks. See
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        details. Not to be confused with <varname>systemd.firstboot=</varname> which only controls behaviour
+        of the <filename>systemd-firstboot.service</filename> system service but has no effect on the
+        condition check (see above).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.clock-usec=</varname></term>
+
+        <listitem><para>Takes a decimal, numeric timestamp in µs since January 1st 1970, 00:00am, to set the
+        system clock to. The system time is set to the specified timestamp early during
+        boot. It is not propagated to the hardware clock (RTC).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.hostname=</varname></term>
+
+        <listitem><para>Accepts a hostname to set during early boot. If specified takes precedence over what
+        is set in <filename>/etc/hostname</filename>. Note that this does not bar later runtime changes to
+        the hostname, it simply controls the initial hostname set during early boot.</para></listitem>
       </varlistentry>
     </variablelist>
 
index 4cbfd09cbf26db75774e784917decf6360f4a206..b00daf366da7971c538d541c9f210e1bba4673c2 100644 (file)
         memory as is needed.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RuntimeDirectoryInodesMax=</varname></term>
+
+        <listitem><para>Sets the limit on number of inodes for the
+        <varname>$XDG_RUNTIME_DIR</varname> runtime directory for each
+        user who logs in. Takes a number, optionally suffixed with the
+        usual K, G, M, and T suffixes, to the base 1024 (IEC).
+        Defaults to <varname>RuntimeDirectorySize=</varname> divided
+        by 4096. Note that this size is a safety limit only.
+        As each runtime directory is a tmpfs file system, it will
+        only consume as much memory as is needed.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>InhibitorsMax=</varname></term>
 
index 44ad033752d526b56c36640415a684fa919ff807..0292288d3a6402b5162c8bcf4a93b5de3c5cf924 100644 (file)
@@ -217,6 +217,8 @@ node /org/freedesktop/login1 {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t RuntimeDirectorySize = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RuntimeDirectoryInodesMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t InhibitorsMax = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t NCurrentInhibitors = ...;
@@ -425,6 +427,8 @@ node /org/freedesktop/login1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectorySize"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryInodesMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="InhibitorsMax"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="NCurrentInhibitors"/>
@@ -623,7 +627,8 @@ node /org/freedesktop/login1 {
       <varname>HandleLidSwitchExternalPower</varname>, <varname>HandleLidSwitchDocked</varname>,
       <varname>IdleActionUSec</varname>, <varname>HoldoffTimeoutUSec</varname>,
       <varname>RemoveIPC</varname>, <varname>RuntimeDirectorySize</varname>,
-      <varname>InhibitorsMax</varname>, and <varname>SessionsMax</varname>.
+      <varname>RuntimeDirectoryInodesMax</varname>, <varname>InhibitorsMax</varname>, and
+      <varname>SessionsMax</varname>.
       </para>
 
       <para>The <varname>IdleHint</varname> property reflects the idle hint state of the system. If the
index e0e24e588880ed047c0ddd39e4f726943564dcd2..ab02f98337202e60dbc21d42e27de819796e3c9b 100644 (file)
         the re-authentication must take place from a component running outside of the user's context, so that
         it does not require access to the user's home directory for operation. Traditionally, most desktop
         environments do not implement screen locking this way, and need to be updated
-        accordingly.</para></listitem>
+        accordingly.</para>
+
+        <para>This setting may also be controlled via the <varname>$SYSTEMD_HOME_SUSPEND</varname>
+        environment variable (see below), which <command>pam_systemd_home</command> reads during initialization and sets
+        for sessions. If both the environment variable is set and the module parameter specified the latter
+        takes precedence.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <listitem><para>Indicates that the user's home directory is managed by <filename>systemd-homed.service</filename>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>$SYSTEMD_HOME_SUSPEND=</varname></term>
+
+        <listitem><para>Indicates whether the session has been registered with the suspend mechanism enabled
+        or disabled (see above). The variable's value is either <literal>0</literal> or
+        <literal>1</literal>. Note that the module both reads the variable when initializing, and sets it for
+        sessions.</para></listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index 49936086701bf063487f9a5f08b9c95c08b16401..f2c155e2002cd093e0d351c0e3f23aefb9399c5a 100644 (file)
         automatically used.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>UUID=</varname></term>
+
+        <listitem><para>The UUID to assign to the partition if none is assigned yet. Note that this
+        setting is not used for matching. It is also not used when a UUID is already set for an existing
+        partition. It is thus only used when a partition is newly created or when an existing one had a
+        all-zero UUID set. If not specified a UUID derived from the partition type is automatically
+        used.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>Priority=</varname></term>
 
index a1065443869ceee9c582b3038bdbbb6ed37fb869..071060dde6987d98e3fb459858b61065c35c12f5 100644 (file)
@@ -33,7 +33,7 @@
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
 
-      <funcprototype>
+      <funcprototype id="sd_bus_message_handler_t">
         <funcdef>typedef int (*<function>sd_bus_message_handler_t</function>)</funcdef>
         <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
         <paramdef>void *<parameter>userdata</parameter></paramdef>
index 102ee66d297aecf6cf6302675b53e149a36dda5c..bce71bd11a121d0954dc9b5dd1a8cb8838cad256 100644 (file)
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-bus-vtable.h&gt;</funcsynopsisinfo>
 
-      <funcprototype>
-        <funcdef>typedef int (*<function>sd_bus_message_handler_t</function>)</funcdef>
-        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
-      </funcprototype>
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
 
       <funcprototype>
         <funcdef>typedef int (*<function>sd_bus_property_get_t</function>)</funcdef>
index 93462f24b594fa42eb15ff3dc39efdddc91769e9..f47f9c8526e57032e52594801860f6219c5c6896 100644 (file)
@@ -27,6 +27,8 @@
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
 
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_call</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
     received a D-Bus error reply), <parameter>ret_error</parameter> is initialized to an instance of
     <structname>sd_bus_error</structname> describing the error.</para>
 
-    <para><function>sd_bus_call_async()</function> is like <function>sd_bus_call()</function> but
-    works asynchronously. The <parameter>callback</parameter> indicates the function to call when
-    the response arrives. The <parameter>userdata</parameter> pointer will be passed to the callback
-    function, and may be chosen freely by the caller. If <parameter>slot</parameter> is not
-    <constant>NULL</constant> and <function>sd_bus_call_async()</function> succeeds,
-    <parameter>slot</parameter> is set to a slot object which can be used to cancel the method call
-    at a later time using
+    <para><function>sd_bus_call_async()</function> is like <function>sd_bus_call()</function> but works
+    asynchronously. The <parameter>callback</parameter> indicates the function to call when the response
+    arrives. The <parameter>userdata</parameter> pointer will be passed to the callback function, and may be
+    chosen freely by the caller. If <parameter>slot</parameter> is not <constant>NULL</constant> and
+    <function>sd_bus_call_async()</function> succeeds, <parameter>slot</parameter> is set to a slot object
+    which can be used to cancel the method call at a later time using
     <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
-    If <parameter>slot</parameter> is <constant>NULL</constant>, the lifetime of the method call is
-    bound to the lifetime of the bus object itself, and it cannot be cancelled independently. See
+    If <parameter>slot</parameter> is <constant>NULL</constant>, the lifetime of the method call is bound to
+    the lifetime of the bus object itself, and it cannot be cancelled independently. See
     <citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     for details. <parameter>callback</parameter> is called when a reply arrives with the reply,
-    <parameter>userdata</parameter> and an <structname>sd_bus_error</structname> output
-    parameter as its arguments. Unlike <function>sd_bus_call()</function>, the
-    <structname>sd_bus_error</structname> output parameter passed to the callback will be empty. To
-    determine whether the method call succeeded, use
+    <parameter>userdata</parameter> and an <structname>sd_bus_error</structname> output parameter as its
+    arguments. Unlike <function>sd_bus_call()</function>, the <structname>sd_bus_error</structname> output
+    parameter passed to the callback will be empty. To determine whether the method call succeeded, use
     <citerefentry><refentrytitle>sd_bus_message_is_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     on the reply message passed to the callback instead. If the callback returns zero and the
-    <structname>sd_bus_error</structname> output parameter is still empty when the callback
-    inishes, other handlers registered with functions such as
+    <structname>sd_bus_error</structname> output parameter is still empty when the callback finishes, other
+    handlers registered with functions such as
     <citerefentry><refentrytitle>sd_bus_add_filter</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
-    <citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    are given a chance to process the message. If the callback returns a non-zero value or the
-    <structname>sd_bus_error</structname> output parameter is not empty when the callback finishes,
-    no further processing of the message is done. Generally, you want to return zero from the
-    callback to give other registered handlers a chance to process the reply as well.</para>
+    <citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry> are
+    given a chance to process the message. If the callback returns a non-zero value or the
+    <structname>sd_bus_error</structname> output parameter is not empty when the callback finishes, no
+    further processing of the message is done. Generally, you want to return zero from the callback to give
+    other registered handlers a chance to process the reply as well. (Note that the
+    <structname>sd_bus_error</structname> parameter is an output parameter of the callback function, not an
+    input parameter; it can be used to propagate errors from the callback handler, it will not receive any
+    error that was received as method reply.)</para>
 
     <para>If <parameter>usec</parameter> is zero, the default D-Bus method call timeout is used. See
     <citerefentry><refentrytitle>sd_bus_get_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
index 6348a4ce7725bf9252f983e036a32ded223e63fa..ac9cf143ad18ae92271ae1e38c6a7ef41a275c61 100644 (file)
@@ -30,6 +30,8 @@
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
 
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_call_method</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
index 8d9bdb48584fdc06c752bcb6cf3ed384839d6809..42db1074700de4ea82b1f6d4a495ffb6549e6dd7 100644 (file)
 
     <para><function>sd_bus_default_flush_close()</function> is similar to
     <function>sd_bus_flush_close_unref</function>, but does not take a bus pointer argument and
-    instead iterates over any of the "default" busses opened by
+    instead iterates over any of the "default" buses opened by
     <citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_bus_default_user</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_bus_default_system</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     and similar calls. <function>sd_bus_default_flush_close()</function> is particularly useful to
-    clean up any busses opened using those calls before the program exits.</para>
+    clean up any buses opened using those calls before the program exits.</para>
   </refsect1>
 
   <refsect1>
index 07c61cd416405a421ffc4c8cac6e4896c06308c4..0a1843a849c7c4da21ae0c7ad767f6cdc0d2d7ef 100644 (file)
@@ -28,6 +28,8 @@
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
 
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
       <funcprototype>
         <funcdef>sd_bus_message_handler_t <function>sd_bus_get_current_handler</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
index 7229ef517a577051b5bfeaf78e107de75b6f1117..f8a49c006b242900886302050b24c8784c0da0fa 100644 (file)
@@ -28,6 +28,8 @@
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
 
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_request_name</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
index ed3e55b0574e5a510830195eabadd6e823c195ce..26541a9d97b410b188f6ac2fe7561de6f6099048 100644 (file)
@@ -27,6 +27,8 @@
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
 
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
       <funcprototype>
         <funcdef>sd_bus *<function>sd_bus_slot_get_bus</function></funcdef>
         <paramdef>sd_bus_slot *<parameter>slot</parameter></paramdef>
index 2735cb10dd01dbc12a8ea37d3d45d4df7526ccd4..dc10776e9968a022a9beb916b4e006b537bb5c92 100644 (file)
@@ -159,7 +159,7 @@ net.bridge.bridge-nf-call-arptables = 0
 
     <example>
       <title>Set network routing properties for all interfaces</title>
-      <para><filename>/etc/systemd/20-rp_filter.conf</filename>:</para>
+      <para><filename>/etc/sysctl.d/20-rp_filter.conf</filename>:</para>
 
       <programlisting>net.ipv4.conf.default.rp_filter = 2
 net.ipv4.conf.*.rp_filter = 2
index 0324a6744097f77fe6d4e047019481368d69012b..47051b9cef307d7cc62e0ef12c17723fab538489 100644 (file)
     <para>At early boot and when the system manager configuration is reloaded, <filename>/etc/crypttab</filename> is
     translated into <filename>systemd-cryptsetup@.service</filename> units by
     <citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+    <para>In order to unlock a volume a password or binary key is
+    required. <filename>systemd-cryptsetup@.service</filename> tries to acquire a suitable password or binary
+    key via the following mechanisms, tried in order:</para>
+
+    <orderedlist>
+      <listitem><para>If a key file is explicitly configured (via the third column in
+      <filename>/etc/crypttab</filename>), a key read from it is used. If a PKCS#11 token is configured
+      (using the <varname>pkcs11-uri=</varname> option) the key is decrypted before use.</para></listitem>
+
+      <listitem><para>If no key file is configured explicitly this way, a key file is automatically loaded
+      from <filename>/etc/cryptsetup-keys.d/<replaceable>volume</replaceable>.key</filename> and
+      <filename>/run/cryptsetup-keys.d/<replaceable>volume</replaceable>.key</filename>, if present. Here
+      too, if a PKCS#11 token is configured, any key found this way is decrypted before
+      use.</para></listitem>
+
+      <listitem><para>If the <varname>try-empty-password</varname> option is specified it is then attempted
+      to unlock the volume with an empty password.</para></listitem>
+
+      <listitem><para>The kernel keyring is then checked for a suitable cached password from previous
+      attempts.</para></listitem>
+
+      <listitem><para>Finally, the user is queried for a password, possibly multiple times.</para></listitem>
+    </orderedlist>
+
+    <para>If no suitable key may be acquired via any of the mechanisms describes above, volume activation fails.</para>
   </refsect1>
 
   <refsect1>
index ad412691a946847c39d1ae09cc501969aed5d3be..91196dff301fb92ce9cab3b2a82ac8a627b82495 100644 (file)
     <citerefentry project='man-pages'><refentrytitle>touch</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     on it.</para>
 
+    <para>Note that if the <varname>systemd.condition-needs-update=</varname> kernel command line option is
+    used it overrides the <varname>ConditionNeedsUpdate=</varname> unit condition checks. In that case
+    <filename>systemd-update-done.service</filename> will not reset the condition state until a follow-up
+    reboot where the kernel switch is not specified anymore.</para>
   </refsect1>
 
   <refsect1>
index 4200a9ed7ebe08e3848508d38663415819f267e3..07feccb54a06a38869c754888e2dc61f3a8c9634 100644 (file)
         <varname>Options=</varname> setting in a unit file.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>x-systemd.rw-only</option></term>
+
+        <listitem><para>If a mount operation fails to mount the file system
+        read-write, it normally tries mounting the file system read-only instead.
+        This option disables that behaviour, and causes the mount to fail
+        immediately instead. This option is translated into the
+        <varname>ReadWriteOnly=</varname> setting in a unit file.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>_netdev</option></term>
 
         off.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>ReadWriteOnly=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If false, a mount
+        point that shall be mounted read-write but cannot be mounted
+        so is retried to be mounted read-only. If true the operation
+        will fail immediately after the read-write mount attempt did
+        not succeed. This corresponds with
+        <citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
+        <parameter>-w</parameter> switch. Defaults to
+        off.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>ForceUnmount=</varname></term>
 
index e6d005400bc2f5defa3c117b2810c00800f2e9d7..f9e8fa5c258f64630e7dc457d43961bb1cadd83d 100644 (file)
           This happens when multicast routing is enabled.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>IPv6LinkLocalAddressGenerationMode=</varname></term>
+        <listitem>
+          <para>Specifies how IPv6 link local address is generated. Takes one of <literal>eui64</literal>,
+          <literal>none</literal>, <literal>stable-privacy</literal> and <literal>random</literal>.
+          When unset, the kernel's default will be used. Note that if <varname>LinkLocalAdressing=</varname>
+          not configured as <literal>ipv6</literal> then <varname>IPv6LinkLocalAddressGenerationMode=</varname>
+          is ignored.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>Unmanaged=</varname></term>
         <listitem>
           </listitem>
         </varlistentry>
 
+         <varlistentry>
+          <term><varname>FallbackLeaseLifetimeSec=</varname></term>
+          <listitem>
+            <para>Allows to set DHCPv4 lease lifetime when DHCPv4 server does not send the lease lifetime.
+            Takes one of <literal>forever</literal> or <literal>infinity</literal> means that the address
+            never expires. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>SendRelease=</varname></term>
           <listitem>
             option numbers, the option number is an integer in the range 1..65536.</para>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term><varname>UserClass=</varname></term>
+          <listitem>
+            <para>A DHCPv6 client can use User Class option to identify the type or category of user or applications
+            it represents. The information contained in this option is a string that represents the user class of which
+            the client is a member. Each class sets an identifying string of information to be used by the DHCP
+            service to classify clients. Special characters in the data string may be escaped using
+            <ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
+            escapes</ulink>. This setting can be specified multiple times. If an empty string is specified,
+            then all options specified earlier are cleared. Takes a whitespace-separated list of strings. Note that
+            currently NUL bytes are not allowed.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>VendorClass=</varname></term>
+          <listitem>
+            <para>A DHCPv6 client can use VendorClass option to identify the vendor that
+            manufactured the hardware on which the client is running. The information
+            contained in the data area of this option is contained in one or more opaque
+            fields that identify details of the hardware configuration. Takes a
+            whitespace-separated list of strings.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
index b91c1ad0ecca64919166fbe93562f9befdaa23cc..fa8ed1b47bbeb89d113bdbb8779f4b269b9371e9 100644 (file)
           <citerefentry><refentrytitle>systemd-update-done.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
           to make sure they run before the stamp file's modification time gets reset indicating a completed
           update.</para>
+
+          <para>If the <varname>systemd.condition-needs-update=</varname> option is specified on the kernel
+          command line (taking a boolean), it will override the result of this condition check, taking
+          precedence over any file modification time checks. If it is used
+          <filename>systemd-update-done.service</filename> will not have immediate effect on any following
+          <varname>ConditionNeedsUpdate=</varname> checks, until the system is rebooted where the kernel
+          command line option is not specified anymore.</para>
           </listitem>
         </varlistentry>
 
           (specifically: an <filename>/etc</filename> with no <filename>/etc/machine-id</filename>). This may
           be used to populate <filename>/etc</filename> on the first boot after factory reset, or when a new
           system instance boots up for the first time.</para>
+
+          <para>If the <varname>systemd.condition-first-boot=</varname> option is specified on the kernel
+          command line (taking a boolean), it will override the result of this condition check, taking
+          precedence over <filename>/etc/machine-id</filename> existence checks.</para>
           </listitem>
         </varlistentry>
 
index c0717b364ecbd87bec29bdb9080f6cfd428959cd..651246d6a1e464ffb5e38d593bcdd6be19a2d247 100644 (file)
@@ -23,8 +23,9 @@ dd if=/dev/urandom of=plaintext.bin bs=128 count=1
 base64 < plaintext.bin | tr -d '\n\r\t ' > plaintext.base64
 
 # Encrypt this newly generated (binary) LUKS decryption key using the public key whose private key is on the
-# Yubikey, store the result in /etc/encrypted-luks-key.bin, where we'll look for it during boot.
-sudo openssl rsautl -encrypt -pubin -inkey pubkey.pem -in plaintext.bin -out /etc/encrypted-luks-key.bin
+# Yubikey, store the result in /etc/cryptsetup-keys.d/mytest.key, where we'll look for it during boot.
+mkdir -p /etc/cryptsetup-keys.d
+sudo openssl rsautl -encrypt -pubin -inkey pubkey.pem -in plaintext.bin -out /etc/cryptsetup-keys.d/mytest.key
 
 # Configure the LUKS decryption key on the LUKS device. We use very low pbkdf settings since the key already
 # has quite a high quality (it comes directly from /dev/urandom after all), and thus we don't need to do much
@@ -40,8 +41,10 @@ shred -u plaintext.bin plaintext.base64
 rm pubkey.pem
 
 # Test: Let's run systemd-cryptsetup to test if this all worked. The option string should contain the full
-# PKCS#11 URI we have in the clipboard, it tells the tool how to decipher the encrypted LUKS key.
-sudo systemd-cryptsetup attach mytest /dev/sdXn /etc/encrypted-luks-key.bin 'pkcs11-uri=pkcs11:…'
+# PKCS#11 URI we have in the clipboard; it tells the tool how to decipher the encrypted LUKS key. Note that
+# systemd-cryptsetup automatically searches for the encrypted key in /etc/cryptsetup-keys.d/, hence we do
+# not need to specify the key file path explicitly here.
+sudo systemd-cryptsetup attach mytest /dev/sdXn - 'pkcs11-uri=pkcs11:…'
 
 # If that worked, let's now add the same line persistently to /etc/crypttab, for the future.
-sudo bash -c 'echo "mytest /dev/sdXn /etc/encrypted-luks-key \'pkcs11-uri=pkcs11:…\'" >> /etc/crypttab'
+sudo bash -c 'echo "mytest /dev/sdXn - \'pkcs11-uri=pkcs11:…\'" >> /etc/crypttab'
index e9eb7f9569eb3d502aeeb7fe116f01d0418fe051..b87d5c63a358d886eecee8ab75f8fdbea8d352a0 100644 (file)
@@ -90,6 +90,10 @@ sysvrcnd_path = get_option('sysvrcnd-path')
 conf.set10('HAVE_SYSV_COMPAT', sysvinit_path != '' and sysvrcnd_path != '',
            description : 'SysV init scripts and rcN.d links are supported')
 
+if get_option('hibernate') and not get_option('initrd')
+        error('hibernate depends on initrd')
+endif
+
 conf.set10('BUMP_PROC_SYS_FS_FILE_MAX', get_option('bump-proc-sys-fs-file-max'))
 conf.set10('BUMP_PROC_SYS_FS_NR_OPEN',  get_option('bump-proc-sys-fs-nr-open'))
 conf.set('HIGH_RLIMIT_NOFILE',          512*1024)
@@ -304,6 +308,7 @@ meson_build_sh = find_program('tools/meson-build.sh')
 
 want_tests = get_option('tests')
 slow_tests = want_tests != 'false' and get_option('slow-tests')
+fuzz_tests = want_tests != 'false' and get_option('fuzz-tests')
 install_tests = get_option('install-tests')
 
 if add_languages('cpp', required : fuzzer_build)
@@ -1419,6 +1424,7 @@ foreach term : ['utmp',
                 'smack',
                 'gshadow',
                 'idn',
+                'initrd',
                 'nss-myhostname',
                 'nss-systemd']
         have = get_option(term)
@@ -2222,8 +2228,10 @@ executable(
 
 if conf.get('HAVE_LIBCRYPTSETUP') == 1
         systemd_cryptsetup_sources = files('''
-                src/cryptsetup/cryptsetup.c
                 src/cryptsetup/cryptsetup-pkcs11.h
+                src/cryptsetup/cryptsetup-util.c
+                src/cryptsetup/cryptsetup-util.h
+                src/cryptsetup/cryptsetup.c
 '''.split())
 
         if conf.get('HAVE_P11KIT') == 1
@@ -2766,7 +2774,7 @@ executable(
         include_directories : includes,
         link_with : [libshared],
         install_rpath : rootlibexecdir,
-        install : true,
+        install : conf.get('ENABLE_INITRD') == 1,
         install_dir : rootlibexecdir)
 
 executable(
@@ -3343,7 +3351,7 @@ foreach tuple : sanitizers
                         if name != prev
                                 if want_tests == 'false'
                                         message('Not compiling @0@ because tests is set to false'.format(name))
-                                elif slow_tests
+                                elif slow_tests or fuzz_tests
                                         exe = custom_target(
                                                 name,
                                                 output : name,
@@ -3353,14 +3361,16 @@ foreach tuple : sanitizers
                                                            '@OUTPUT@'],
                                                 build_by_default : true)
                                 else
-                                        message('Not compiling @0@ because slow-tests is set to false'.format(name))
+                                        message('Not compiling @0@ because slow-tests/fuzz-tests is set to false'.format(name))
                                 endif
                         endif
                         prev = name
 
-                        if want_tests != 'false' and slow_tests
+                        if want_tests != 'false' and (slow_tests or fuzz_tests)
                                 test('@0@:@1@:@2@'.format(b, c, sanitizer),
                                      env,
+                                     env : ['UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1'],
+                                     timeout : 60,
                                      args : [exe.full_path(),
                                              join_paths(project_source_root, p)])
                         endif
@@ -3539,6 +3549,7 @@ foreach tuple : [
         ['openssl'],
         ['libcurl'],
         ['idn'],
+        ['initrd'],
         ['libidn2'],
         ['libidn'],
         ['libiptc'],
index 0229179c99cd8de5a75d292713a1d8b554859c21..d5ed2a7a421f457f5558d289b3f0adfed5af531c 100644 (file)
@@ -35,6 +35,8 @@ option('telinit-path', type : 'string', value : '/lib/sysvinit/telinit',
        description : 'path to telinit')
 option('rc-local', type : 'string',
        value : '/etc/rc.local')
+option('initrd', type: 'boolean',
+       description : 'install services for use when running systemd in initrd')
 
 option('quotaon-path', type : 'string', description : 'path to quotaon')
 option('quotacheck-path', type : 'string', description : 'path to quotacheck')
@@ -349,6 +351,8 @@ option('tests', type : 'combo', choices : ['true', 'unsafe', 'false'],
        description : 'enable extra tests with =unsafe')
 option('slow-tests', type : 'boolean', value : 'false',
        description : 'run the slow tests by default')
+option('fuzz-tests', type : 'boolean', value : 'false',
+       description : 'run the fuzzer regression tests by default')
 option('install-tests', type : 'boolean', value : 'false',
        description : 'install test executables')
 
index 01586690bdd3642a69112858023ebfa37b936659..821e9db86f00b5ef101aaa96e6dbde2e62ea0ba0 100644 (file)
@@ -93,6 +93,9 @@ ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id"
 KERNEL=="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-boot%n"
 KERNEL!="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}"
 ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n"
+# compatible links for ATA devices
+KERNEL!="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH_ATA_COMPAT}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH_ATA_COMPAT}"
+ENV{DEVTYPE}=="partition", ENV{ID_PATH_ATA_COMPAT}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH_ATA_COMPAT}-part%n"
 
 # legacy virtio-pci by-path links (deprecated)
 KERNEL=="vd*[!0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}"
index c34b60621683831271b5342b0eb1f0dbd1628d83..1c60eec587040252ca41e9018372ac2e939e49b2 100644 (file)
@@ -49,6 +49,7 @@ SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsys
 SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k"
 
 SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}+="bluetooth.target", ENV{SYSTEMD_USER_WANTS}+="bluetooth.target"
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0b????:*", ENV{ID_SMARTCARD_READER}="1"
 ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target", ENV{SYSTEMD_USER_WANTS}+="smartcard.target"
 SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target", ENV{SYSTEMD_USER_WANTS}+="sound.target"
 
index 7458f436a54b4297e56a9bcacbcbedae2b83448f..3ac069c6b0a73fe9e00945afe7079ed793d3b1c4 100644 (file)
         openpgp:"Retrieve openpgp keys for an email"
         query:"Resolve domain names, IPv4 and IPv6 addresses"
         reset-server-features:"Flushes all feature level information the resolver has learned about specific servers"
-        reset-statistics:"Resets the statistics counter show in statistics to zero"
+        reset-statistics:"Resets the statistics counter shown in statistics to zero"
         revert:"Revert the per-interfce DNS configuration"
         service:"Resolve DNS-SD and SRV services"
+        statistics:"Show resolver statistics"
         status:"Show the global and per-link DNS settings currently in effect"
         tlsa:"Query tlsa public keys stored as TLSA resource records"
     )
@@ -78,10 +79,6 @@ _arguments \
     '--service[Resolve services]' \
     '--service-address=no[Do not resolve address for services]' \
     '--service-txt=no[Do not resolve TXT records for services]' \
-    '--openpgp[Query OpenPGP public key]' \
-    '--tlsa[Query TLS public key]' \
     '--cname=no[Do not follow CNAME redirects]' \
     '--search=no[Do not use search domains]' \
-    '--statistics[Show resolver statistics]' \
-    '--reset-statistics[Reset resolver statistics]' \
     '*::default: _resolvectl_commands'
index 7d94c55a6dcafb69dd3b4e0b864a131958d90e7f..5f8212685b7781522fec1e7bf82ee2246d0c068c 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include <sys/file.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -29,6 +30,8 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
                 *ret = d;
                 return 0;
         }
+        if (errno != ENOENT)
+                return -errno;
 
         /* If it is a partition find the originating device */
         xsprintf_sys_block_path(p, "/partition", d);
@@ -185,3 +188,29 @@ int get_block_device_harder(const char *path, dev_t *ret) {
 
         return 1;
 }
+
+int lock_whole_block_device(dev_t devt, int operation) {
+        _cleanup_free_ char *whole_node = NULL;
+        _cleanup_close_ int lock_fd = -1;
+        dev_t whole_devt;
+        int r;
+
+        /* Let's get a BSD file lock on the whole block device, as per: https://systemd.io/BLOCK_DEVICE_LOCKING */
+
+        r = block_get_whole_disk(devt, &whole_devt);
+        if (r < 0)
+                return r;
+
+        r = device_path_make_major_minor(S_IFBLK, whole_devt, &whole_node);
+        if (r < 0)
+                return r;
+
+        lock_fd = open(whole_node, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+        if (lock_fd < 0)
+                return -errno;
+
+        if (flock(lock_fd, operation) < 0)
+                return -errno;
+
+        return TAKE_FD(lock_fd);
+}
index 6d8a796568edf5dd843e38fbd003fd9a5d54f4ea..1e7588f71cab9f1d840fc7ba7e392da15c0ef333 100644 (file)
@@ -18,3 +18,5 @@ int block_get_originating(dev_t d, dev_t *ret);
 int get_block_device(const char *path, dev_t *dev);
 
 int get_block_device_harder(const char *path, dev_t *dev);
+
+int lock_whole_block_device(dev_t devt, int operation);
index 775b97b10071732d367eb11c27f45e97f220d4bc..17ee474f7332bdaf0d054623680519dc255fe646 100644 (file)
@@ -319,7 +319,7 @@ int btrfs_get_block_device_fd(int fd, dev_t *dev) {
                         return -errno;
 
                 if (!S_ISBLK(st.st_mode))
-                        return -ENODEV;
+                        return -ENOTBLK;
 
                 if (major(st.st_rdev) == 0)
                         return -ENODEV;
index 5529113bc5997367a097bb1af7856e256f14103b..b34df46926cded800f722a74414d67ade510ab0b 100644 (file)
@@ -331,7 +331,7 @@ int systemd_efi_options_variable(char **line) {
                  * does, let's return a recognizable error (EPERM), and if not ENODATA. */
 
                 if (access(k, F_OK) < 0)
-                        return errno == -ENOENT ? -ENODATA : -errno;
+                        return errno == ENOENT ? -ENODATA : -errno;
 
                 return -EPERM;
         }
index 34ee939526f9b43b8ca8d29c1d277d56d903601a..00dce02064f579884a4feb1458fa6defeba29c7c 100644 (file)
@@ -202,6 +202,13 @@ static int write_string_file_atomic(
                 goto fail;
         }
 
+        if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) {
+                /* Sync the rename, too */
+                r = fsync_directory_of_file(fileno(f));
+                if (r < 0)
+                        return r;
+        }
+
         return 0;
 
 fail:
@@ -419,7 +426,7 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
                                 break;
                         }
 
-                        if (errno != -EINTR)
+                        if (errno != EINTR)
                                 return -errno;
                 }
 
index e13d75de2d1eac7eee2df209999c6a8e86961f8d..7bbcb6051e0c96dcc54e0d3350bb43b3a50168b9 100644 (file)
@@ -23,6 +23,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "random-util.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
@@ -1303,11 +1304,13 @@ void unlink_tempfilep(char (*p)[]) {
                 (void) unlink_noerrno(*p);
 }
 
-int unlinkat_deallocate(int fd, const char *name, int flags) {
+int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
         _cleanup_close_ int truncate_fd = -1;
         struct stat st;
         off_t l, bs;
 
+        assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0);
+
         /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
          * link to it. This is useful to ensure that other processes that might have the file open for reading won't be
          * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up
@@ -1324,7 +1327,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
          * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the
          * primary job – to delete the file – is accomplished. */
 
-        if ((flags & AT_REMOVEDIR) == 0) {
+        if (!FLAGS_SET(flags, UNLINK_REMOVEDIR)) {
                 truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
                 if (truncate_fd < 0) {
 
@@ -1340,7 +1343,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
                 }
         }
 
-        if (unlinkat(fd, name, flags) < 0)
+        if (unlinkat(fd, name, FLAGS_SET(flags, UNLINK_REMOVEDIR) ? AT_REMOVEDIR : 0) < 0)
                 return -errno;
 
         if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */
@@ -1351,7 +1354,45 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
                 return 0;
         }
 
-        if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0)
+        if (!S_ISREG(st.st_mode))
+                return 0;
+
+        if (FLAGS_SET(flags, UNLINK_ERASE) && st.st_size > 0 && st.st_nlink == 0) {
+                uint64_t left = st.st_size;
+                char buffer[64 * 1024];
+
+                /* If erasing is requested, let's overwrite the file with random data once before deleting
+                 * it. This isn't going to give you shred(1) semantics, but hopefully should be good enough
+                 * for stuff backed by tmpfs at least.
+                 *
+                 * Note that we only erase like this if the link count of the file is zero. If it is higher it
+                 * is still linked by someone else and we'll leave it to them to remove it securely
+                 * eventually! */
+
+                random_bytes(buffer, sizeof(buffer));
+
+                while (left > 0) {
+                        ssize_t n;
+
+                        n = write(truncate_fd, buffer, MIN(sizeof(buffer), left));
+                        if (n < 0) {
+                                log_debug_errno(errno, "Failed to erase data in file '%s', ignoring.", name);
+                                break;
+                        }
+
+                        assert(left >= (size_t) n);
+                        left -= n;
+                }
+
+                /* Let's refresh metadata */
+                if (fstat(truncate_fd, &st) < 0) {
+                        log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name);
+                        return 0;
+                }
+        }
+
+        /* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */
+        if (st.st_blocks == 0 || st.st_nlink > 0)
                 return 0;
 
         /* If this is a regular file, it actually took up space on disk and there are no other links it's time to
index 08892398291a5b6710f64e5d3905041bd295f39a..dd101c61cc6bc1b015babbd00fd56cd17552104a 100644 (file)
@@ -113,7 +113,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
 int access_fd(int fd, int mode);
 
 void unlink_tempfilep(char (*p)[]);
-int unlinkat_deallocate(int fd, const char *name, int flags);
+
+typedef enum UnlinkDeallocateFlags {
+        UNLINK_REMOVEDIR = 1 << 0,
+        UNLINK_ERASE     = 1 << 1,
+} UnlinkDeallocateFlags;
+
+int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags);
 
 int fsync_directory_of_file(int fd);
 int fsync_full(int fd);
index e3aa6c2e152b4f9d7ecbb583770b02145188a838..1e7e301e09875d0e72579d45a03d895e3c29135d 100644 (file)
@@ -61,11 +61,11 @@ int glob_exists(const char *path) {
         return true;
 }
 
-int glob_extend(char ***strv, const char *path) {
+int glob_extend(char ***strv, const char *path, int flags) {
         _cleanup_globfree_ glob_t g = {};
         int k;
 
-        k = safe_glob(path, GLOB_NOSORT|GLOB_BRACE, &g);
+        k = safe_glob(path, GLOB_NOSORT|GLOB_BRACE|flags, &g);
         if (k < 0)
                 return k;
 
index 8b1bb02be138471ef399b6a2442f9ff177b671aa..3d5f5435085aca376f0362fdad86c8fe8f673d3f 100644 (file)
@@ -11,7 +11,7 @@
 int safe_glob(const char *path, int flags, glob_t *pglob);
 
 int glob_exists(const char *path);
-int glob_extend(char ***strv, const char *path);
+int glob_extend(char ***strv, const char *path, int flags);
 
 #define _cleanup_globfree_ _cleanup_(globfree)
 
index 79530132e3018b1dd3d2e58615aacebe85178c11..6ee22868337744b29d1a418703012c8296813843 100644 (file)
@@ -585,4 +585,17 @@ static inline int __coverity_check_and_return__(int condition) {
         DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name);                    \
         DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
 
+/* A macro to force copying of a variable from memory. This is useful whenever we want to read something from
+ * memory and want to make sure the compiler won't optimize away the destination variable for us. It's not
+ * supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not
+ * allowed to remove our local copies of the variables. We want this to work for unaligned memory, hence
+ * memcpy() is great for our purposes. */
+#define READ_NOW(x)                                                     \
+        ({                                                              \
+                typeof(x) _copy;                                        \
+                memcpy(&_copy, &(x), sizeof(_copy));                    \
+                asm volatile ("" : : : "memory");                       \
+                _copy;                                                  \
+        })
+
 #include "log.h"
index b7e2e67e84c287f63a87d66ebe1dc4d39ff5c6d7..a6a2ccdbc299bf932c9fd3ec5bf64a491a37d6e2 100644 (file)
@@ -12,7 +12,7 @@
 
 size_t page_size(void) _pure_;
 #define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
-#define PAGE_ALIGN_DOWN(l) (l & ~(page_size() - 1))
+#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1))
 
 /* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
 static inline void memcpy_safe(void *dst, const void *src, size_t n) {
index ba13de01ff0b194f6cc9925fe0fcd6e7078458d4..986dfe94a4b719b16e13c50b94bf6d041887c42d 100644 (file)
@@ -1051,7 +1051,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
                 if (!path)
                         return -ENOMEM;
 
-                r = glob_extend(&names, path);
+                r = glob_extend(&names, path, 0);
                 if (r == -ENOENT)
                         continue;
                 if (r < 0)
index d3d99d9a7f90228222eaa09de6b778a048b8a95c..eb45682f3a4a6cdaf10422649cc5e75f552d73ab 100644 (file)
@@ -119,15 +119,18 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineF
 
         /* We parse the EFI variable first, because later settings have higher priority. */
 
-        r = systemd_efi_options_variable(&line);
-        if (r < 0 && r != -ENODATA)
-                log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
+        if (!FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) {
+                r = systemd_efi_options_variable(&line);
+                if (r < 0 && r != -ENODATA)
+                        log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
 
-        r = proc_cmdline_parse_given(line, parse_item, data, flags);
-        if (r < 0)
-                return r;
+                r = proc_cmdline_parse_given(line, parse_item, data, flags);
+                if (r < 0)
+                        return r;
+
+                line = mfree(line);
+        }
 
-        line = mfree(line);
         r = proc_cmdline(&line);
         if (r < 0)
                 return r;
@@ -218,7 +221,7 @@ static int cmdline_get_key(const char *line, const char *key, ProcCmdlineFlags f
 }
 
 int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
-        _cleanup_free_ char *line = NULL;
+        _cleanup_free_ char *line = NULL, *v = NULL;
         int r;
 
         /* Looks for a specific key on the kernel command line and (with lower priority) the EFI variable.
@@ -245,14 +248,27 @@ int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_val
         if (r < 0)
                 return r;
 
-        r = cmdline_get_key(line, key, flags, ret_value);
-        if (r != 0) /* Either error or true if found. */
+        if (FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) /* Shortcut */
+                return cmdline_get_key(line, key, flags, ret_value);
+
+        r = cmdline_get_key(line, key, flags, ret_value ? &v : NULL);
+        if (r < 0)
                 return r;
+        if (r > 0) {
+                if (ret_value)
+                        *ret_value = TAKE_PTR(v);
+
+                return r;
+        }
 
         line = mfree(line);
         r = systemd_efi_options_variable(&line);
-        if (r == -ENODATA)
+        if (r == -ENODATA) {
+                if (ret_value)
+                        *ret_value = NULL;
+
                 return false; /* Not found */
+        }
         if (r < 0)
                 return r;
 
@@ -268,17 +284,17 @@ int proc_cmdline_get_bool(const char *key, bool *ret) {
         r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v);
         if (r < 0)
                 return r;
-        if (r == 0) {
+        if (r == 0) { /* key not specified at all */
                 *ret = false;
                 return 0;
         }
 
-        if (v) { /* parameter passed */
+        if (v) { /* key with parameter passed */
                 r = parse_boolean(v);
                 if (r < 0)
                         return r;
                 *ret = r;
-        } else /* no parameter passed */
+        } else /* key without parameter passed */
                 *ret = true;
 
         return 1;
@@ -286,6 +302,7 @@ int proc_cmdline_get_bool(const char *key, bool *ret) {
 
 int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
         _cleanup_free_ char *line = NULL;
+        bool processing_efi = true;
         const char *p;
         va_list ap;
         int r, ret = 0;
@@ -296,9 +313,11 @@ int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
 
         /* This call may clobber arguments on failure! */
 
-        r = proc_cmdline(&line);
-        if (r < 0)
-                return r;
+        if (!FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) {
+                r = systemd_efi_options_variable(&line);
+                if (r < 0 && r != -ENODATA)
+                        log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
+        }
 
         p = line;
         for (;;) {
@@ -307,8 +326,22 @@ int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
                 r = proc_cmdline_extract_first(&p, &word, flags);
                 if (r < 0)
                         return r;
-                if (r == 0)
+                if (r == 0) {
+                        /* We finished with this command line. If this was the EFI one, then let's proceed with the regular one */
+                        if (processing_efi) {
+                                processing_efi = false;
+
+                                line = mfree(line);
+                                r = proc_cmdline(&line);
+                                if (r < 0)
+                                        return r;
+
+                                p = line;
+                                continue;
+                        }
+
                         break;
+                }
 
                 va_start(ap, flags);
 
index 4115fdbc99fd7d00dbfc33b19681d16d54efcac6..077d3a99fb91e72fc66aa55eb92a02576358a2d2 100644 (file)
@@ -6,9 +6,10 @@
 #include "log.h"
 
 typedef enum ProcCmdlineFlags {
-        PROC_CMDLINE_STRIP_RD_PREFIX = 1 << 0,
-        PROC_CMDLINE_VALUE_OPTIONAL  = 1 << 1,
-        PROC_CMDLINE_RD_STRICT       = 1 << 2,
+        PROC_CMDLINE_STRIP_RD_PREFIX    = 1 << 0, /* automatically strip "rd." prefix if it is set (and we are in the initrd, since otherwise we'd not consider it anyway) */
+        PROC_CMDLINE_VALUE_OPTIONAL     = 1 << 1, /* the value is optional (for boolean switches that can omit the value) */
+        PROC_CMDLINE_RD_STRICT          = 1 << 2, /* ignore this in the initrd */
+        PROC_CMDLINE_IGNORE_EFI_OPTIONS = 1 << 3, /* don't check systemd's private EFI variable */
 } ProcCmdlineFlags;
 
 typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data);
index 222b382d5729f16a8d67cf44ad9cfa4f37870054..a197668ce96e287998d31a6b2131ec4a9ae43612 100644 (file)
@@ -517,7 +517,7 @@ static int copy_file_with_version_check(const char *from, const char *to, bool f
         if (!force) {
                 fd_to = open(to, O_RDONLY|O_CLOEXEC|O_NOCTTY);
                 if (fd_to < 0) {
-                        if (errno != -ENOENT)
+                        if (errno != ENOENT)
                                 return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to);
                 } else {
                         r = version_check(fd_from, from, fd_to, to);
index 9ab9a51198e7d02fc3f4054b03ce67f3c45b15d3..7aceb1796f1621c56e2fde5c50792cd9e1a592e3 100644 (file)
@@ -856,20 +856,24 @@ static int on_interface(const char *interface, uint64_t flags, void *userdata) {
         assert(interface);
         assert(members);
 
-        m = new0(Member, 1);
+        m = new(Member, 1);
         if (!m)
                 return log_oom();
 
-        m->type = "interface";
-        m->flags = flags;
+        *m = (Member) {
+                .type = "interface",
+                .flags = flags,
+        };
 
         r = free_and_strdup(&m->interface, interface);
         if (r < 0)
                 return log_oom();
 
         r = set_put(members, m);
-        if (r <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate interface");
+        if (r == -EEXIST)
+                return log_error_errno(r,  "Invalid introspection data: duplicate interface '%s'.", interface);
+        if (r < 0)
+                return log_oom();
 
         m = NULL;
         return 0;
@@ -883,12 +887,14 @@ static int on_method(const char *interface, const char *name, const char *signat
         assert(interface);
         assert(name);
 
-        m = new0(Member, 1);
+        m = new(Member, 1);
         if (!m)
                 return log_oom();
 
-        m->type = "method";
-        m->flags = flags;
+        *m = (Member) {
+                .type = "method",
+                .flags = flags,
+        };
 
         r = free_and_strdup(&m->interface, interface);
         if (r < 0)
@@ -907,8 +913,10 @@ static int on_method(const char *interface, const char *name, const char *signat
                 return log_oom();
 
         r = set_put(members, m);
-        if (r <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate method");
+        if (r == -EEXIST)
+                return log_error_errno(r, "Invalid introspection data: duplicate method '%s' on interface '%s'.", name, interface);
+        if (r < 0)
+                return log_oom();
 
         m = NULL;
         return 0;
@@ -922,12 +930,14 @@ static int on_signal(const char *interface, const char *name, const char *signat
         assert(interface);
         assert(name);
 
-        m = new0(Member, 1);
+        m = new(Member, 1);
         if (!m)
                 return log_oom();
 
-        m->type = "signal";
-        m->flags = flags;
+        *m = (Member) {
+                .type = "signal",
+                .flags = flags,
+        };
 
         r = free_and_strdup(&m->interface, interface);
         if (r < 0)
@@ -942,8 +952,10 @@ static int on_signal(const char *interface, const char *name, const char *signat
                 return log_oom();
 
         r = set_put(members, m);
-        if (r <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate signal");
+        if (r == -EEXIST)
+                return log_error_errno(r, "Invalid introspection data: duplicate signal '%s' on interface '%s'.", name, interface);
+        if (r < 0)
+                return log_oom();
 
         m = NULL;
         return 0;
@@ -957,13 +969,15 @@ static int on_property(const char *interface, const char *name, const char *sign
         assert(interface);
         assert(name);
 
-        m = new0(Member, 1);
+        m = new(Member, 1);
         if (!m)
                 return log_oom();
 
-        m->type = "property";
-        m->flags = flags;
-        m->writable = writable;
+        *m = (Member) {
+                .type = "property",
+                .flags = flags,
+                .writable = writable,
+        };
 
         r = free_and_strdup(&m->interface, interface);
         if (r < 0)
@@ -978,8 +992,10 @@ static int on_property(const char *interface, const char *name, const char *sign
                 return log_oom();
 
         r = set_put(members, m);
-        if (r <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate property");
+        if (r == -EEXIST)
+                return log_error_errno(r, "Invalid introspection data: duplicate property '%s' on interface '%s'.", name, interface);
+        if (r < 0)
+                return log_oom();
 
         m = NULL;
         return 0;
index f8a13bd6375081631e15f288a1258e9dead3a79d..51254b92da536404a6812d22243048ded2955002 100644 (file)
@@ -244,28 +244,116 @@ static int property_get_show_status(
         return sd_bus_message_append_basic(reply, 'b', &b);
 }
 
-static int property_set_runtime_watchdog(
+static int property_get_runtime_watchdog(
                 sd_bus *bus,
                 const char *path,
                 const char *interface,
                 const char *property,
-                sd_bus_message *value,
+                sd_bus_message *reply,
                 void *userdata,
                 sd_bus_error *error) {
 
-        usec_t *t = userdata;
-        int r;
+        Manager *m = userdata;
 
+        assert(m);
         assert(bus);
+        assert(reply);
+
+        return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_RUNTIME));
+}
+
+static int property_get_reboot_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+
+        assert(m);
+        assert(bus);
+        assert(reply);
+
+        return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_REBOOT));
+}
+
+static int property_get_kexec_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+
+        assert(m);
+        assert(bus);
+        assert(reply);
+
+        return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_KEXEC));
+}
+
+static int property_set_watchdog(Manager *m, WatchdogType type, sd_bus_message *value) {
+        usec_t timeout;
+        int r;
+
+        assert(m);
         assert(value);
 
         assert_cc(sizeof(usec_t) == sizeof(uint64_t));
 
-        r = sd_bus_message_read(value, "t", t);
+        r = sd_bus_message_read(value, "t", &timeout);
         if (r < 0)
                 return r;
 
-        return watchdog_set_timeout(t);
+        return manager_set_watchdog_overridden(m, type, timeout);
+}
+
+static int property_set_runtime_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *value,
+                void *userdata,
+                sd_bus_error *error) {
+
+        return property_set_watchdog(userdata, WATCHDOG_RUNTIME, value);
+}
+
+static int property_set_reboot_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *value,
+                void *userdata,
+                sd_bus_error *error) {
+
+        return property_set_watchdog(userdata, WATCHDOG_REBOOT, value);
+}
+
+static int property_set_kexec_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *value,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+
+        assert(m);
+        assert(bus);
+        assert(value);
+
+        return property_set_watchdog(userdata, WATCHDOG_KEXEC, value);
 }
 
 static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char *name, Unit **ret_unit, sd_bus_error *error) {
@@ -2404,11 +2492,11 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0),
-        SD_BUS_WRITABLE_PROPERTY("RebootWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, reboot_watchdog), 0),
+        SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", property_get_runtime_watchdog, property_set_runtime_watchdog, 0, 0),
+        SD_BUS_WRITABLE_PROPERTY("RebootWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, 0),
         /* The following item is an obsolete alias */
-        SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, reboot_watchdog), SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_WRITABLE_PROPERTY("KExecWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, kexec_watchdog), 0),
+        SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_WRITABLE_PROPERTY("KExecWatchdogUSec", "t", property_get_kexec_watchdog, property_set_kexec_watchdog, 0, 0),
         SD_BUS_WRITABLE_PROPERTY("ServiceWatchdogs", "b", bus_property_get_bool, bus_property_set_bool, offsetof(Manager, service_watchdogs), 0),
         SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0),
         SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0),
index b6d61627ebb13b446c269856d42db10aa5acc5c3..3ab5ecc425c1e023cebbea51e82d326c88e44259 100644 (file)
@@ -51,6 +51,7 @@ const sd_bus_vtable bus_mount_vtable[] = {
         SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ForceUnmount", "b", bus_property_get_bool, offsetof(Mount, force_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("ReadWriteOnly", "b", bus_property_get_bool, offsetof(Mount, read_write_only), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -102,6 +103,9 @@ static int bus_mount_set_transient_property(
         if (streq(name, "ForceUnmount"))
                 return bus_set_transient_bool(u, name, &m->force_unmount, message, flags, error);
 
+        if (streq(name, "ReadWriteOnly"))
+                return bus_set_transient_bool(u, name, &m->read_write_only, message, flags, error);
+
         return 0;
 }
 
index 83cce88131eb656396c2ef86216060ce600b68db..6d047db8388c8e94b3947f75e8c1a4fac35e6ba1 100644 (file)
 #include "hostname-util.h"
 #include "log.h"
 #include "macro.h"
+#include "proc-cmdline.h"
 #include "string-util.h"
 #include "util.h"
 
 int hostname_setup(void) {
         _cleanup_free_ char *b = NULL;
+        const char *hn = NULL;
         bool enoent = false;
-        const char *hn;
         int r;
 
-        r = read_etc_hostname(NULL, &b);
-        if (r < 0) {
-                if (r == -ENOENT)
-                        enoent = true;
-                else
-                        log_warning_errno(r, "Failed to read configured hostname: %m");
+        r = proc_cmdline_get_key("systemd.hostname", 0, &b);
+        if (r < 0)
+                log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m");
+        else if (r > 0) {
+                if (hostname_is_valid(b, true))
+                        hn = b;
+                else  {
+                        log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
+                        b = mfree(b);
+                }
+        }
 
-                hn = NULL;
-        } else
-                hn = b;
+        if (!hn) {
+                r = read_etc_hostname(NULL, &b);
+                if (r < 0) {
+                        if (r == -ENOENT)
+                                enoent = true;
+                        else
+                                log_warning_errno(r, "Failed to read configured hostname: %m");
+                } else
+                        hn = b;
+        }
 
         if (isempty(hn)) {
-                /* Don't override the hostname if it is already set
-                 * and not explicitly configured */
+                /* Don't override the hostname if it is already set and not explicitly configured */
                 if (hostname_is_set())
                         return 0;
 
index 165b9ca9c12c260b67ae98c9d7a2c1ce93f16513..5fd58b379baa5152eede3f2d4267a8549009da1b 100644 (file)
@@ -429,6 +429,7 @@ Mount.DirectoryMode,             config_parse_mode,                  0,
 Mount.SloppyOptions,             config_parse_bool,                  0,                             offsetof(Mount, sloppy_options)
 Mount.LazyUnmount,               config_parse_bool,                  0,                             offsetof(Mount, lazy_unmount)
 Mount.ForceUnmount,              config_parse_bool,                  0,                             offsetof(Mount, force_unmount)
+Mount.ReadWriteOnly,             config_parse_bool,                  0,                             offsetof(Mount, read_write_only)
 EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
 CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
 KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
index 33b1d47da6c1817796a2f76b4f505280a2916a4c..e420239e157eb4af9d5feba7aba56d56f3e74716 100644 (file)
@@ -5157,7 +5157,7 @@ int config_parse_swap_priority(
 
         r = safe_atoi(rvalue, &priority);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Invalid swap pririty '%s', ignoring.", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Invalid swap priority '%s', ignoring.", rvalue);
                 return 0;
         }
 
index c9eaf70bd6c84d7eb231a305892f8bea4c04eba1..30e1b3d19d5586e4beb1ba3cbd75351ae992ada9 100644 (file)
@@ -146,6 +146,7 @@ static EmergencyAction arg_cad_burst_action;
 static OOMPolicy arg_default_oom_policy;
 static CPUSet arg_cpu_affinity;
 static NUMAPolicy arg_numa_policy;
+static usec_t arg_clock_usec;
 
 /* A copy of the original environment block */
 static char **saved_env = NULL;
@@ -491,6 +492,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
                 (void) parse_path_argument_and_warn(value, false, &arg_watchdog_device);
 
+        } else if (proc_cmdline_key_streq(key, "systemd.clock_usec")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = safe_atou64(value, &arg_clock_usec);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse systemd.clock_usec= argument, ignoring: %s", value);
+
         } else if (streq(key, "quiet") && !value) {
 
                 if (arg_show_status == _SHOW_STATUS_INVALID)
@@ -703,16 +713,18 @@ static void set_manager_settings(Manager *m) {
 
         assert(m);
 
-        /* Propagates the various manager settings into the manager object, i.e. properties that effect the manager
-         * itself (as opposed to just being inherited into newly allocated units, see set_manager_defaults() above). */
+        /* Propagates the various manager settings into the manager object, i.e. properties that
+         * effect the manager itself (as opposed to just being inherited into newly allocated
+         * units, see set_manager_defaults() above). */
 
         m->confirm_spawn = arg_confirm_spawn;
         m->service_watchdogs = arg_service_watchdogs;
-        m->runtime_watchdog = arg_runtime_watchdog;
-        m->reboot_watchdog = arg_reboot_watchdog;
-        m->kexec_watchdog = arg_kexec_watchdog;
         m->cad_burst_action = arg_cad_burst_action;
 
+        manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog);
+        manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog);
+        manager_set_watchdog(m, WATCHDOG_KEXEC, arg_kexec_watchdog);
+
         manager_set_show_status(m, arg_show_status, "commandline");
         m->status_unit_format = arg_status_unit_format;
 }
@@ -1504,6 +1516,9 @@ static int become_shutdown(
 static void initialize_clock(void) {
         int r;
 
+        /* This is called very early on, before we parse the kernel command line or otherwise figure out why
+         * we are running, but only once. */
+
         if (clock_is_localtime(NULL) > 0) {
                 int min;
 
@@ -1542,6 +1557,25 @@ static void initialize_clock(void) {
                 log_info("System time before build time, advancing clock.");
 }
 
+static void apply_clock_update(void) {
+        struct timespec ts;
+
+        /* This is called later than initialize_clock(), i.e. after we parsed configuration files/kernel
+         * command line and such. */
+
+        if (arg_clock_usec == 0)
+                return;
+
+        if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, arg_clock_usec)) < 0)
+                log_error_errno(errno, "Failed to set system clock to time specified on kernel command line: %m");
+        else {
+                char buf[FORMAT_TIMESTAMP_MAX];
+
+                log_info("Set system clock to %s, as specified on the kernel command line.",
+                         format_timestamp(buf, sizeof(buf), arg_clock_usec));
+        }
+}
+
 static void initialize_coredump(bool skip_setup) {
 #if ENABLE_COREDUMP
         if (getpid_cached() != 1)
@@ -1784,6 +1818,7 @@ static int invoke_main_loop(
                         (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
 
                         set_manager_defaults(m);
+                        set_manager_settings(m);
 
                         update_cpu_affinity(false);
                         update_numa_policy(false);
@@ -1974,9 +2009,6 @@ static int initialize_runtime(
                         if (r < 0)
                                 log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device);
                 }
-
-                if (timestamp_is_set(arg_runtime_watchdog))
-                        watchdog_set_timeout(&arg_runtime_watchdog);
         }
 
         if (arg_timer_slack_nsec != NSEC_INFINITY)
@@ -2250,29 +2282,6 @@ static int parse_configuration(const struct rlimit *saved_rlimit_nofile,
         /* Note that this also parses bits from the kernel command line, including "debug". */
         log_parse_environment();
 
-        return 0;
-}
-
-static int load_configuration(
-                int argc,
-                char **argv,
-                const struct rlimit *saved_rlimit_nofile,
-                const struct rlimit *saved_rlimit_memlock,
-                const char **ret_error_message) {
-        int r;
-
-        assert(saved_rlimit_nofile);
-        assert(saved_rlimit_memlock);
-        assert(ret_error_message);
-
-        (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
-
-        r = parse_argv(argc, argv);
-        if (r < 0) {
-                *ret_error_message = "Failed to parse commandline arguments";
-                return r;
-        }
-
         /* Initialize the show status setting if it hasn't been set explicitly yet */
         if (arg_show_status == _SHOW_STATUS_INVALID)
                 arg_show_status = SHOW_STATUS_YES;
@@ -2618,9 +2627,13 @@ int main(int argc, char *argv[]) {
         (void) reset_all_signal_handlers();
         (void) ignore_signals(SIGNALS_IGNORE, -1);
 
-        r = load_configuration(argc, argv, &saved_rlimit_nofile, &saved_rlimit_memlock, &error_message);
-        if (r < 0)
+        (void) parse_configuration(&saved_rlimit_nofile, &saved_rlimit_memlock);
+
+        r = parse_argv(argc, argv);
+        if (r < 0) {
+                error_message = "Failed to parse commandline arguments";
                 goto finish;
+        }
 
         r = safety_checks();
         if (r < 0)
@@ -2658,6 +2671,8 @@ int main(int argc, char *argv[]) {
         assert_se(chdir("/") == 0);
 
         if (arg_action == ACTION_RUN) {
+                /* Apply the systemd.clock_usec= kernel command line switch */
+                apply_clock_update();
 
                 /* A core pattern might have been specified via the cmdline.  */
                 initialize_core_pattern(skip_setup);
@@ -2753,8 +2768,8 @@ finish:
         pager_close();
 
         if (m) {
-                arg_reboot_watchdog = m->reboot_watchdog;
-                arg_kexec_watchdog = m->kexec_watchdog;
+                arg_reboot_watchdog = manager_get_watchdog(m, WATCHDOG_REBOOT);
+                arg_kexec_watchdog = manager_get_watchdog(m, WATCHDOG_KEXEC);
                 m = manager_free(m);
         }
 
index 1beab6baeddf482d45090548323ed423c71669b7..ac5a3b68051612ab8fcb8781be1b4e945a2c19e2 100644 (file)
@@ -110,6 +110,7 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata);
 static int manager_dispatch_timezone_change(sd_event_source *source, const struct inotify_event *event, void *userdata);
 static int manager_run_environment_generators(Manager *m);
 static int manager_run_generators(Manager *m);
+static void manager_vacuum(Manager *m);
 
 static usec_t manager_watch_jobs_next_time(Manager *m) {
         return usec_add(now(CLOCK_MONOTONIC),
@@ -181,7 +182,7 @@ static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned po
         }
 }
 
-void manager_flip_auto_status(Manager *m, bool enable, const char *reason) {
+static void manager_flip_auto_status(Manager *m, bool enable, const char *reason) {
         assert(m);
 
         if (enable) {
@@ -779,6 +780,10 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
                 .original_log_level = -1,
                 .original_log_target = _LOG_TARGET_INVALID,
 
+                .watchdog_overridden[WATCHDOG_RUNTIME] = USEC_INFINITY,
+                .watchdog_overridden[WATCHDOG_REBOOT] = USEC_INFINITY,
+                .watchdog_overridden[WATCHDOG_KEXEC] = USEC_INFINITY,
+
                 .notify_fd = -1,
                 .cgroups_agent_fd = -1,
                 .signal_fd = -1,
@@ -1596,20 +1601,6 @@ static void manager_preset_all(Manager *m) {
                 log_info("Populated /etc with preset unit settings.");
 }
 
-static void manager_vacuum(Manager *m) {
-        assert(m);
-
-        /* Release any dynamic users no longer referenced */
-        dynamic_user_vacuum(m, true);
-
-        /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
-        manager_vacuum_uid_refs(m);
-        manager_vacuum_gid_refs(m);
-
-        /* Release any runtimes no longer referenced */
-        exec_runtime_vacuum(m);
-}
-
 static void manager_ready(Manager *m) {
         assert(m);
 
@@ -2922,9 +2913,10 @@ int manager_loop(Manager *m) {
                 return log_error_errno(r, "Failed to enable SIGCHLD event source: %m");
 
         while (m->objective == MANAGER_OK) {
-                usec_t wait_usec;
+                usec_t wait_usec, watchdog_usec;
 
-                if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m))
+                watchdog_usec = manager_get_watchdog(m, WATCHDOG_RUNTIME);
+                if (timestamp_is_set(watchdog_usec))
                         watchdog_ping();
 
                 if (!ratelimit_below(&rl)) {
@@ -2955,7 +2947,7 @@ int manager_loop(Manager *m) {
                         continue;
 
                 /* Sleep for watchdog runtime wait time */
-                if (MANAGER_IS_SYSTEM(m))
+                if (timestamp_is_set(watchdog_usec))
                         wait_usec = watchdog_runtime_wait();
                 else
                         wait_usec = USEC_INFINITY;
@@ -3158,6 +3150,47 @@ static bool manager_timestamp_shall_serialize(ManagerTimestamp t) {
                        MANAGER_TIMESTAMP_UNITS_LOAD_START, MANAGER_TIMESTAMP_UNITS_LOAD_FINISH);
 }
 
+#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
+
+static void manager_serialize_uid_refs_internal(
+                Manager *m,
+                FILE *f,
+                Hashmap **uid_refs,
+                const char *field_name) {
+
+        Iterator i;
+        void *p, *k;
+
+        assert(m);
+        assert(f);
+        assert(uid_refs);
+        assert(field_name);
+
+        /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as
+         * the actual counter of it is better rebuild after a reload/reexec. */
+
+        HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
+                uint32_t c;
+                uid_t uid;
+
+                uid = PTR_TO_UID(k);
+                c = PTR_TO_UINT32(p);
+
+                if (!(c & DESTROY_IPC_FLAG))
+                        continue;
+
+                (void) serialize_item_format(f, field_name, UID_FMT, uid);
+        }
+}
+
+static void manager_serialize_uid_refs(Manager *m, FILE *f) {
+        manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid");
+}
+
+static void manager_serialize_gid_refs(Manager *m, FILE *f) {
+        manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid");
+}
+
 int manager_serialize(
                 Manager *m,
                 FILE *f,
@@ -3196,6 +3229,10 @@ int manager_serialize(
         if (m->log_target_overridden)
                 (void) serialize_item(f, "log-target-override", log_target_to_string(log_get_target()));
 
+        (void) serialize_usec(f, "runtime-watchdog-overridden", m->watchdog_overridden[WATCHDOG_RUNTIME]);
+        (void) serialize_usec(f, "reboot-watchdog-overridden", m->watchdog_overridden[WATCHDOG_REBOOT]);
+        (void) serialize_usec(f, "kexec-watchdog-overridden", m->watchdog_overridden[WATCHDOG_KEXEC]);
+
         for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
                 _cleanup_free_ char *joined = NULL;
 
@@ -3328,6 +3365,114 @@ static int manager_deserialize_units(Manager *m, FILE *f, FDSet *fds) {
         return 0;
 }
 
+usec_t manager_get_watchdog(Manager *m, WatchdogType t) {
+        assert(m);
+
+        if (MANAGER_IS_USER(m))
+                return USEC_INFINITY;
+
+        if (timestamp_is_set(m->watchdog_overridden[t]))
+                return m->watchdog_overridden[t];
+
+        return m->watchdog[t];
+}
+
+void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout) {
+        int r = 0;
+
+        assert(m);
+
+        if (MANAGER_IS_USER(m))
+                return;
+
+        if (m->watchdog[t] == timeout)
+                return;
+
+        if (t == WATCHDOG_RUNTIME)
+                if (!timestamp_is_set(m->watchdog_overridden[WATCHDOG_RUNTIME])) {
+                        if (timestamp_is_set(timeout))
+                                r = watchdog_set_timeout(&timeout);
+                        else
+                                watchdog_close(true);
+                }
+
+        if (r >= 0)
+                m->watchdog[t] = timeout;
+}
+
+int manager_set_watchdog_overridden(Manager *m, WatchdogType t, usec_t timeout) {
+        int r = 0;
+
+        assert(m);
+
+        if (MANAGER_IS_USER(m))
+                return 0;
+
+        if (m->watchdog_overridden[t] == timeout)
+                return 0;
+
+        if (t == WATCHDOG_RUNTIME) {
+                usec_t *p;
+
+                p = timestamp_is_set(timeout) ? &timeout : &m->watchdog[t];
+                if (timestamp_is_set(*p))
+                        r = watchdog_set_timeout(p);
+                else
+                        watchdog_close(true);
+        }
+
+        if (r >= 0)
+                m->watchdog_overridden[t] = timeout;
+
+        return 0;
+}
+
+static void manager_deserialize_uid_refs_one_internal(
+                Manager *m,
+                Hashmap** uid_refs,
+                const char *value) {
+
+        uid_t uid;
+        uint32_t c;
+        int r;
+
+        assert(m);
+        assert(uid_refs);
+        assert(value);
+
+        r = parse_uid(value, &uid);
+        if (r < 0 || uid == 0) {
+                log_debug("Unable to parse UID reference serialization: " UID_FMT, uid);
+                return;
+        }
+
+        r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
+        if (r < 0) {
+                log_oom();
+                return;
+        }
+
+        c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+        if (c & DESTROY_IPC_FLAG)
+                return;
+
+        c |= DESTROY_IPC_FLAG;
+
+        r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
+        if (r < 0) {
+                log_debug_errno(r, "Failed to add UID reference entry: %m");
+                return;
+        }
+}
+
+static void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
+        manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value);
+}
+
+static void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
+        manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value);
+}
+
 int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
         int r = 0;
 
@@ -3451,6 +3596,30 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         else
                                 manager_override_log_target(m, target);
 
+                } else if ((val = startswith(l, "runtime-watchdog-overridden="))) {
+                        usec_t t;
+
+                        if (deserialize_usec(val, &t) < 0)
+                                log_notice("Failed to parse runtime-watchdog-overridden value '%s', ignoring.", val);
+                        else
+                                manager_set_watchdog_overridden(m, WATCHDOG_RUNTIME, t);
+
+                } else if ((val = startswith(l, "reboot-watchdog-overridden="))) {
+                        usec_t t;
+
+                        if (deserialize_usec(val, &t) < 0)
+                                log_notice("Failed to parse reboot-watchdog-overridden value '%s', ignoring.", val);
+                        else
+                                manager_set_watchdog_overridden(m, WATCHDOG_REBOOT, t);
+
+                } else if ((val = startswith(l, "kexec-watchdog-overridden="))) {
+                        usec_t t;
+
+                        if (deserialize_usec(val, &t) < 0)
+                                log_notice("Failed to parse kexec-watchdog-overridden value '%s', ignoring.", val);
+                        else
+                                manager_set_watchdog_overridden(m, WATCHDOG_KEXEC, t);
+
                 } else if (startswith(l, "env=")) {
                         r = deserialize_environment(l + 4, &m->client_environment);
                         if (r < 0)
@@ -4301,8 +4470,6 @@ ManagerState manager_state(Manager *m) {
         return MANAGER_RUNNING;
 }
 
-#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
-
 static void manager_unref_uid_internal(
                 Manager *m,
                 Hashmap **uid_refs,
@@ -4441,97 +4608,26 @@ static void manager_vacuum_uid_refs_internal(
         }
 }
 
-void manager_vacuum_uid_refs(Manager *m) {
+static void manager_vacuum_uid_refs(Manager *m) {
         manager_vacuum_uid_refs_internal(m, &m->uid_refs, clean_ipc_by_uid);
 }
 
-void manager_vacuum_gid_refs(Manager *m) {
+static void manager_vacuum_gid_refs(Manager *m) {
         manager_vacuum_uid_refs_internal(m, &m->gid_refs, clean_ipc_by_gid);
 }
 
-static void manager_serialize_uid_refs_internal(
-                Manager *m,
-                FILE *f,
-                Hashmap **uid_refs,
-                const char *field_name) {
-
-        Iterator i;
-        void *p, *k;
-
-        assert(m);
-        assert(f);
-        assert(uid_refs);
-        assert(field_name);
-
-        /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as the actual counter
-         * of it is better rebuild after a reload/reexec. */
-
-        HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
-                uint32_t c;
-                uid_t uid;
-
-                uid = PTR_TO_UID(k);
-                c = PTR_TO_UINT32(p);
-
-                if (!(c & DESTROY_IPC_FLAG))
-                        continue;
-
-                (void) serialize_item_format(f, field_name, UID_FMT, uid);
-        }
-}
-
-void manager_serialize_uid_refs(Manager *m, FILE *f) {
-        manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid");
-}
-
-void manager_serialize_gid_refs(Manager *m, FILE *f) {
-        manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid");
-}
-
-static void manager_deserialize_uid_refs_one_internal(
-                Manager *m,
-                Hashmap** uid_refs,
-                const char *value) {
-
-        uid_t uid;
-        uint32_t c;
-        int r;
-
+static void manager_vacuum(Manager *m) {
         assert(m);
-        assert(uid_refs);
-        assert(value);
-
-        r = parse_uid(value, &uid);
-        if (r < 0 || uid == 0) {
-                log_debug("Unable to parse UID reference serialization: " UID_FMT, uid);
-                return;
-        }
 
-        r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
-        if (r < 0) {
-                log_oom();
-                return;
-        }
-
-        c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
-        if (c & DESTROY_IPC_FLAG)
-                return;
-
-        c |= DESTROY_IPC_FLAG;
-
-        r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
-        if (r < 0) {
-                log_debug_errno(r, "Failed to add UID reference entry: %m");
-                return;
-        }
-}
+        /* Release any dynamic users no longer referenced */
+        dynamic_user_vacuum(m, true);
 
-void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
-        manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value);
-}
+        /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
+        manager_vacuum_uid_refs(m);
+        manager_vacuum_gid_refs(m);
 
-void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
-        manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value);
+        /* Release any runtimes no longer referenced */
+        exec_runtime_vacuum(m);
 }
 
 int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
index 10c34f95433cfcb0eec1f16c4971dc65b77d6281..a29d976eae531cab80715d9cebb77bf906f514ac 100644 (file)
@@ -114,6 +114,13 @@ typedef enum ManagerTimestamp {
         _MANAGER_TIMESTAMP_INVALID = -1,
 } ManagerTimestamp;
 
+typedef enum WatchdogType {
+        WATCHDOG_RUNTIME,
+        WATCHDOG_REBOOT,
+        WATCHDOG_KEXEC,
+        _WATCHDOG_TYPE_MAX,
+} WatchdogType;
+
 #include "execute.h"
 #include "job.h"
 #include "path-lookup.h"
@@ -231,9 +238,8 @@ struct Manager {
         char **transient_environment;  /* The environment, as determined from config files, kernel cmdline and environment generators */
         char **client_environment;     /* Environment variables created by clients through the bus API */
 
-        usec_t runtime_watchdog;
-        usec_t reboot_watchdog;
-        usec_t kexec_watchdog;
+        usec_t watchdog[_WATCHDOG_TYPE_MAX];
+        usec_t watchdog_overridden[_WATCHDOG_TYPE_MAX];
 
         dual_timestamp timestamps[_MANAGER_TIMESTAMP_MAX];
 
@@ -510,7 +516,6 @@ void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason);
 void manager_set_first_boot(Manager *m, bool b);
 
 void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5);
-void manager_flip_auto_status(Manager *m, bool enable, const char *reason);
 
 Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);
 
@@ -524,15 +529,6 @@ int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc);
 void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now);
 int manager_ref_gid(Manager *m, gid_t gid, bool destroy_now);
 
-void manager_vacuum_uid_refs(Manager *m);
-void manager_vacuum_gid_refs(Manager *m);
-
-void manager_serialize_uid_refs(Manager *m, FILE *f);
-void manager_deserialize_uid_refs_one(Manager *m, const char *value);
-
-void manager_serialize_gid_refs(Manager *m, FILE *f);
-void manager_deserialize_gid_refs_one(Manager *m, const char *value);
-
 char *manager_taint_string(Manager *m);
 
 void manager_ref_console(Manager *m);
@@ -555,5 +551,9 @@ const char *manager_timestamp_to_string(ManagerTimestamp m) _const_;
 ManagerTimestamp manager_timestamp_from_string(const char *s) _pure_;
 ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s);
 
+usec_t manager_get_watchdog(Manager *m, WatchdogType t);
+void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout);
+int manager_set_watchdog_overridden(Manager *m, WatchdogType t, usec_t timeout);
+
 const char* oom_policy_to_string(OOMPolicy i) _const_;
 OOMPolicy oom_policy_from_string(const char *s) _pure_;
index 5463196f69a9b1e02ae768699ec3e12c91161ccf..48500b49327580434fddbb94c9651fd2a3333a36 100644 (file)
@@ -780,6 +780,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sSloppyOptions: %s\n"
                 "%sLazyUnmount: %s\n"
                 "%sForceUnmount: %s\n"
+                "%sReadWriteOnly: %s\n"
                 "%sTimeoutSec: %s\n",
                 prefix, mount_state_to_string(m->state),
                 prefix, mount_result_to_string(m->result),
@@ -795,6 +796,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, yes_no(m->sloppy_options),
                 prefix, yes_no(m->lazy_unmount),
                 prefix, yes_no(m->force_unmount),
+                prefix, yes_no(m->read_write_only),
                 prefix, format_timespan(buf, sizeof(buf), m->timeout_usec, USEC_PER_SEC));
 
         if (m->control_pid > 0)
@@ -1026,6 +1028,8 @@ static void mount_enter_mounting(Mount *m) {
                 r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL);
                 if (r >= 0 && m->sloppy_options)
                         r = exec_command_append(m->control_command, "-s", NULL);
+                if (r >= 0 && m->read_write_only)
+                        r = exec_command_append(m->control_command, "-w", NULL);
                 if (r >= 0 && p->fstype)
                         r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
                 if (r >= 0 && !isempty(opts))
@@ -1086,6 +1090,8 @@ static void mount_enter_remounting(Mount *m) {
                                      "-o", o, NULL);
                 if (r >= 0 && m->sloppy_options)
                         r = exec_command_append(m->control_command, "-s", NULL);
+                if (r >= 0 && m->read_write_only)
+                        r = exec_command_append(m->control_command, "-w", NULL);
                 if (r >= 0 && p->fstype)
                         r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
         } else
index 07fa05f3ca69635a82d4c4d5c323c58dc643a1de..a1bc2d71a64db0ac07bb288d3ab1bdefdfedd992 100644 (file)
@@ -59,6 +59,8 @@ struct Mount {
         bool lazy_unmount;
         bool force_unmount;
 
+        bool read_write_only;
+
         MountResult result;
         MountResult reload_result;
         MountResult clean_result;
index 60a02b770bee060810b4fdf1a20e23eca343a82d..ed4d06e9866dd1e4af044bc99d33e4d3b1b10139 100644 (file)
@@ -237,7 +237,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_FILE:
-                        r = glob_extend(&arg_file, optarg);
+                        r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to add paths: %m");
                         break;
index 20c752d88db24705ece07b6fbeb4417faffd6c26..7974c19a4466b7c68236d29d82a34318d92e92a2 100644 (file)
@@ -188,7 +188,11 @@ static int print_dependencies(FILE *f, const char* device_path) {
                 /* None, nothing to do */
                 return 0;
 
-        if (PATH_IN_SET(device_path, "/dev/urandom", "/dev/random", "/dev/hw_random")) {
+        if (PATH_IN_SET(device_path,
+                        "/dev/urandom",
+                        "/dev/random",
+                        "/dev/hw_random",
+                        "/dev/hwrng")) {
                 /* RNG device, add random dep */
                 fputs("After=systemd-random-seed.service\n", f);
                 return 0;
@@ -209,7 +213,9 @@ static int print_dependencies(FILE *f, const char* device_path) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to generate unit name: %m");
 
-                fprintf(f, "After=%1$s\nRequires=%1$s\n", unit);
+                fprintf(f,
+                        "After=%1$s\n"
+                        "Requires=%1$s\n", unit);
         } else {
                 /* Regular file, add mount dependency */
                 _cleanup_free_ char *escaped_path = specifier_escape(device_path);
@@ -367,12 +373,12 @@ static int create_disk(
 
         if (tmp)
                 fprintf(f,
-                        "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
+                        "ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs ext2 '/dev/mapper/%s'\n",
                         name_escaped);
 
         if (swap)
                 fprintf(f,
-                        "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
+                        "ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs swap '/dev/mapper/%s'\n",
                         name_escaped);
 
         if (keydev)
index ec9186a6aaa527fd59b0dfc425f48553cf6b11af..642a1b7d11a8843732d9e781d4e9e91830cc1d44 100644 (file)
@@ -10,6 +10,7 @@
 #include "alloc-util.h"
 #include "ask-password-api.h"
 #include "cryptsetup-pkcs11.h"
+#include "cryptsetup-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "stat-util.h"
 #include "strv.h"
 
-#define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */
-
-static int load_key_file(
-                const char *key_file,
-                size_t key_file_size,
-                uint64_t key_file_offset,
-                void **ret_encrypted_key,
-                size_t *ret_encrypted_key_size) {
-
-        _cleanup_(erase_and_freep) char *buffer = NULL;
-        _cleanup_close_ int fd = -1;
-        ssize_t n;
-        int r;
-
-        assert(key_file);
-        assert(ret_encrypted_key);
-        assert(ret_encrypted_key_size);
-
-        fd = open(key_file, O_RDONLY|O_CLOEXEC);
-        if (fd < 0)
-                return log_error_errno(errno, "Failed to load encrypted PKCS#11 key: %m");
-
-        if (key_file_size == 0) {
-                struct stat st;
-
-                if (fstat(fd, &st) < 0)
-                        return log_error_errno(errno, "Failed to stat key file: %m");
-
-                r = stat_verify_regular(&st);
-                if (r < 0)
-                        return log_error_errno(r, "Key file is not a regular file: %m");
-
-                if (st.st_size == 0)
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing.");
-                if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) {
-                        char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
-                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
-                                               "Key file larger (%s) than allowed maximum size (%s), refusing.",
-                                               format_bytes(buf1, sizeof(buf1), st.st_size),
-                                               format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX));
-                }
-
-                if (key_file_offset >= (uint64_t) st.st_size)
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing.");
-
-                key_file_size = st.st_size - key_file_offset;
-        }
-
-        buffer = malloc(key_file_size);
-        if (!buffer)
-                return log_oom();
-
-        if (key_file_offset > 0)
-                n = pread(fd, buffer, key_file_size, key_file_offset);
-        else
-                n = read(fd, buffer, key_file_size);
-        if (n < 0)
-                return log_error_errno(errno, "Failed to read PKCS#11 key file: %m");
-        if (n == 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing.");
-
-        *ret_encrypted_key = TAKE_PTR(buffer);
-        *ret_encrypted_key_size = (size_t) n;
-
-        return 0;
-}
-
 struct pkcs11_callback_data {
         const char *friendly_name;
         usec_t until;
@@ -93,11 +27,14 @@ struct pkcs11_callback_data {
         size_t encrypted_key_size;
         void *decrypted_key;
         size_t decrypted_key_size;
+        bool free_encrypted_key;
 };
 
 static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
         free(data->decrypted_key);
-        free(data->encrypted_key);
+
+        if (data->free_encrypted_key)
+                free(data->encrypted_key);
 }
 
 static int pkcs11_callback(
@@ -160,9 +97,11 @@ static int pkcs11_callback(
 int decrypt_pkcs11_key(
                 const char *friendly_name,
                 const char *pkcs11_uri,
-                const char *key_file,
+                const char *key_file,         /* We either expect key_file and associated parameters to be set (for file keys) … */
                 size_t key_file_size,
                 uint64_t key_file_offset,
+                const void *key_data,         /* … or key_data and key_data_size (for literal keys) */
+                size_t key_data_size,
                 usec_t until,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size) {
@@ -175,15 +114,24 @@ int decrypt_pkcs11_key(
 
         assert(friendly_name);
         assert(pkcs11_uri);
-        assert(key_file);
+        assert(key_file || key_data);
         assert(ret_decrypted_key);
         assert(ret_decrypted_key_size);
 
         /* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
 
-        r = load_key_file(key_file, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size);
-        if (r < 0)
-                return r;
+        if (key_data) {
+                data.encrypted_key = (void*) key_data;
+                data.encrypted_key_size = key_data_size;
+
+                data.free_encrypted_key = false;
+        } else {
+                r = load_key_file(key_file, NULL, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size);
+                if (r < 0)
+                        return r;
+
+                data.free_encrypted_key = true;
+        }
 
         r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data);
         if (r < 0)
index 264ccb66b107d279a63ec9f04cbfe0fcacbafb5c..af2487e75bdfc5d8c38dc0297af42f015d70363d 100644 (file)
@@ -14,6 +14,8 @@ int decrypt_pkcs11_key(
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
+                const void *key_data,
+                size_t key_data_size,
                 usec_t until,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size);
@@ -26,6 +28,8 @@ static inline int decrypt_pkcs11_key(
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
+                const void *key_data,
+                size_t key_data_size,
                 usec_t until,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size) {
diff --git a/src/cryptsetup/cryptsetup-util.c b/src/cryptsetup/cryptsetup-util.c
new file mode 100644 (file)
index 0000000..8ae70a5
--- /dev/null
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <unistd.h>
+
+#include "cryptsetup-util.h"
+#include "fd-util.h"
+#include "format-util.h"
+#include "memory-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "strv.h"
+
+#define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */
+
+int load_key_file(
+                const char *key_file,
+                char **search_path,
+                size_t key_file_size,
+                uint64_t key_file_offset,
+                void **ret_key,
+                size_t *ret_key_size) {
+
+        _cleanup_(erase_and_freep) char *buffer = NULL;
+        _cleanup_free_ char *discovered_path = NULL;
+        _cleanup_close_ int fd = -1;
+        ssize_t n;
+        int r;
+
+        assert(key_file);
+        assert(ret_key);
+        assert(ret_key_size);
+
+        if (strv_isempty(search_path) || path_is_absolute(key_file)) {
+                fd = open(key_file, O_RDONLY|O_CLOEXEC);
+                if (fd < 0)
+                        return log_error_errno(errno, "Failed to load key file '%s': %m", key_file);
+        } else {
+                char **i;
+
+                STRV_FOREACH(i, search_path) {
+                        _cleanup_free_ char *joined;
+
+                        joined = path_join(*i, key_file);
+                        if (!joined)
+                                return log_oom();
+
+                        fd = open(joined, O_RDONLY|O_CLOEXEC);
+                        if (fd >= 0) {
+                                discovered_path = TAKE_PTR(joined);
+                                break;
+                        }
+                        if (errno != ENOENT)
+                                return log_error_errno(errno, "Failed to load key file '%s': %m", joined);
+                }
+
+                if (!discovered_path) {
+                        /* Search path supplied, but file not found, report by returning NULL, but not failing */
+                        *ret_key = NULL;
+                        *ret_key_size = 0;
+                        return 0;
+                }
+
+                assert(fd >= 0);
+                key_file = discovered_path;
+        }
+
+        if (key_file_size == 0) {
+                struct stat st;
+
+                if (fstat(fd, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat key file '%s': %m", key_file);
+
+                r = stat_verify_regular(&st);
+                if (r < 0)
+                        return log_error_errno(r, "Key file is not a regular file: %m");
+
+                if (st.st_size == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing.");
+                if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) {
+                        char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
+                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
+                                               "Key file larger (%s) than allowed maximum size (%s), refusing.",
+                                               format_bytes(buf1, sizeof(buf1), st.st_size),
+                                               format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX));
+                }
+
+                if (key_file_offset >= (uint64_t) st.st_size)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing.");
+
+                key_file_size = st.st_size - key_file_offset;
+        }
+
+        buffer = malloc(key_file_size);
+        if (!buffer)
+                return log_oom();
+
+        if (key_file_offset > 0)
+                n = pread(fd, buffer, key_file_size, key_file_offset);
+        else
+                n = read(fd, buffer, key_file_size);
+        if (n < 0)
+                return log_error_errno(errno, "Failed to read key file '%s': %m", key_file);
+        if (n == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing.");
+
+        *ret_key = TAKE_PTR(buffer);
+        *ret_key_size = (size_t) n;
+
+        return 1;
+}
diff --git a/src/cryptsetup/cryptsetup-util.h b/src/cryptsetup/cryptsetup-util.h
new file mode 100644 (file)
index 0000000..7bb7822
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+int load_key_file(
+                const char *key_file,
+                char **search_path,
+                size_t key_file_size,
+                uint64_t key_file_offset,
+                void **ret_key,
+                size_t *ret_key_size);
index 860b29b3b6c049aac28f1293b5d094a236c4b922..8c3eafbd61c80481011e8d780453cd3686dacdfe 100644 (file)
 #include "ask-password-api.h"
 #include "crypt-util.h"
 #include "cryptsetup-pkcs11.h"
+#include "cryptsetup-util.h"
 #include "device-util.h"
 #include "escape.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "fstab-util.h"
 #include "hexdecoct.h"
 #include "log.h"
 #include "main-func.h"
+#include "memory-util.h"
 #include "mount-util.h"
 #include "nulstr-util.h"
 #include "parse-util.h"
@@ -42,6 +45,8 @@ static unsigned arg_sector_size = CRYPT_SECTOR_SIZE;
 static int arg_key_slot = CRYPT_ANY_SLOT;
 static unsigned arg_keyfile_size = 0;
 static uint64_t arg_keyfile_offset = 0;
+static bool arg_keyfile_erase = false;
+static bool arg_try_empty_password = false;
 static char *arg_hash = NULL;
 static char *arg_header = NULL;
 static unsigned arg_tries = 3;
@@ -67,12 +72,14 @@ STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_uri, freep);
 
 /* Options Debian's crypttab knows we don't:
 
-    precheck=
     check=
     checkargs=
-    noearly=
-    loud=
+    noearly
+    loud
+    quiet
     keyscript=
+    tmp= (the version without argument is supported)
+    initramfs
 */
 
 static int parse_one_option(const char *option) {
@@ -126,7 +133,8 @@ static int parse_one_option(const char *option) {
                         return 0;
                 }
 
-        } else if ((val = startswith(option, "key-slot="))) {
+        } else if ((val = startswith(option, "key-slot=")) ||
+                   (val = startswith(option, "keyslot="))) {
 
                 arg_type = ANY_LUKS;
                 r = safe_atoi(val, &arg_key_slot);
@@ -160,7 +168,20 @@ static int parse_one_option(const char *option) {
                         return 0;
                 }
 
-        } else if ((val = startswith(option, "hash="))) {
+        } else if ((val = startswith(option, "keyfile-erase="))) {
+
+                r = parse_boolean(val);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+                        return 0;
+                }
+
+                arg_keyfile_erase = r;
+
+        } else if (streq(option, "keyfile-erase"))
+                arg_keyfile_erase = true;
+
+        else if ((val = startswith(option, "hash="))) {
                 r = free_and_strdup(&arg_hash, val);
                 if (r < 0)
                         return log_oom();
@@ -202,13 +223,13 @@ static int parse_one_option(const char *option) {
                 arg_type = ANY_LUKS;
         else if (streq(option, "tcrypt"))
                 arg_type = CRYPT_TCRYPT;
-        else if (streq(option, "tcrypt-hidden")) {
+        else if (STR_IN_SET(option, "tcrypt-hidden", "tcrypthidden")) {
                 arg_type = CRYPT_TCRYPT;
                 arg_tcrypt_hidden = true;
         } else if (streq(option, "tcrypt-system")) {
                 arg_type = CRYPT_TCRYPT;
                 arg_tcrypt_system = true;
-        } else if (streq(option, "tcrypt-veracrypt")) {
+        } else if (STR_IN_SET(option, "tcrypt-veracrypt", "veracrypt")) {
                 arg_type = CRYPT_TCRYPT;
                 arg_tcrypt_veracrypt = true;
         } else if (STR_IN_SET(option, "plain", "swap", "tmp"))
@@ -242,7 +263,20 @@ static int parse_one_option(const char *option) {
                 if (r < 0)
                         return log_oom();
 
-        } else if (!streq(option, "x-initrd.attach"))
+        } else if ((val = startswith(option, "try-empty-password="))) {
+
+                r = parse_boolean(val);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+                        return 0;
+                }
+
+                arg_try_empty_password = r;
+
+        } else if (streq(option, "try-empty-password"))
+                arg_try_empty_password = true;
+
+        else if (!streq(option, "x-initrd.attach"))
                 log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
 
         return 0;
@@ -440,6 +474,8 @@ static int attach_tcrypt(
                 struct crypt_device *cd,
                 const char *name,
                 const char *key_file,
+                const void *key_data,
+                size_t key_data_size,
                 char **passwords,
                 uint32_t flags) {
 
@@ -453,7 +489,7 @@ static int attach_tcrypt(
 
         assert(cd);
         assert(name);
-        assert(key_file || (passwords && passwords[0]));
+        assert(key_file || key_data || !strv_isempty(passwords));
 
         if (arg_pkcs11_uri)
                 /* Ask for a regular password */
@@ -469,22 +505,33 @@ static int attach_tcrypt(
         if (arg_tcrypt_veracrypt)
                 params.flags |= CRYPT_TCRYPT_VERA_MODES;
 
-        if (key_file) {
-                r = read_one_line_file(key_file, &passphrase);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to read password file '%s': %m", key_file);
-                        return -EAGAIN; /* log with the actual error, but return EAGAIN */
-                }
+        if (key_data) {
+                params.passphrase = key_data;
+                params.passphrase_size = key_data_size;
+        } else {
+                if (key_file) {
+                        r = read_one_line_file(key_file, &passphrase);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to read password file '%s': %m", key_file);
+                                return -EAGAIN; /* log with the actual error, but return EAGAIN */
+                        }
 
-                params.passphrase = passphrase;
-        } else
-                params.passphrase = passwords[0];
-        params.passphrase_size = strlen(params.passphrase);
+                        params.passphrase = passphrase;
+                } else
+                        params.passphrase = passwords[0];
+
+                params.passphrase_size = strlen(params.passphrase);
+        }
 
         r = crypt_load(cd, CRYPT_TCRYPT, &params);
         if (r < 0) {
-                if (key_file && r == -EPERM) {
-                        log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file);
+                if (r == -EPERM) {
+                        if (key_data)
+                                log_error_errno(r, "Failed to activate using discovered key. (Key not correct?)");
+
+                        if (key_file)
+                                log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file);
+
                         return -EAGAIN; /* log the actual error, but return EAGAIN */
                 }
 
@@ -502,6 +549,8 @@ static int attach_luks_or_plain(
                 struct crypt_device *cd,
                 const char *name,
                 const char *key_file,
+                const void *key_data,
+                size_t key_data_size,
                 char **passwords,
                 uint32_t flags,
                 usec_t until) {
@@ -571,7 +620,7 @@ static int attach_luks_or_plain(
                 _cleanup_free_ char *friendly = NULL;
                 size_t decrypted_key_size = 0;
 
-                if (!key_file)
+                if (!key_file && !key_data)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "PKCS#11 mode selected but no key file specified, refusing.");
 
                 friendly = friendly_disk_name(crypt_get_device_name(cd), name);
@@ -584,8 +633,8 @@ static int attach_luks_or_plain(
                         r = decrypt_pkcs11_key(
                                         friendly,
                                         arg_pkcs11_uri,
-                                        key_file,
-                                        arg_keyfile_size, arg_keyfile_offset,
+                                        key_file, arg_keyfile_size, arg_keyfile_offset,
+                                        key_data, key_data_size,
                                         until,
                                         &decrypted_key, &decrypted_key_size);
                         if (r >= 0)
@@ -620,7 +669,7 @@ static int attach_luks_or_plain(
                                         return log_error_errno(r, "Failed to start device monitor: %m");
 
                                 log_notice("Security token %s not present for unlocking volume %s, please plug it in.",
-                                         arg_pkcs11_uri, friendly);
+                                           arg_pkcs11_uri, friendly);
 
                                 /* Let's immediately rescan in case the token appeared in the time we needed
                                  * to create and configure the monitor */
@@ -668,6 +717,18 @@ static int attach_luks_or_plain(
                 if (r < 0)
                         return log_error_errno(r, "Failed to activate with PKCS#11 acquired key: %m");
 
+        } else if (key_data) {
+                if (pass_volume_key)
+                        r = crypt_activate_by_volume_key(cd, name, key_data, key_data_size, flags);
+                else
+                        r = crypt_activate_by_passphrase(cd, name, arg_key_slot, key_data, key_data_size, flags);
+                if (r == -EPERM) {
+                        log_error_errno(r, "Failed to activate. (Key incorrect?)");
+                        return -EAGAIN; /* Log actual error, but return EAGAIN */
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to activate: %m");
+
         } else if (key_file) {
                 r = crypt_activate_by_keyfile_device_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
                 if (r == -EPERM) {
@@ -748,6 +809,17 @@ static uint32_t determine_flags(void) {
         return flags;
 }
 
+static void remove_and_erasep(const char **p) {
+        int r;
+
+        if (!*p)
+                return;
+
+        r = unlinkat_deallocate(AT_FDCWD, *p, UNLINK_ERASE);
+        if (r < 0 && r != -ENOENT)
+                log_warning_errno(r, "Unable to erase key file '%s', ignoring: %m", *p);
+}
+
 static int run(int argc, char *argv[]) {
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
         int r;
@@ -774,13 +846,19 @@ static int run(int argc, char *argv[]) {
                 unsigned tries;
                 usec_t until;
                 crypt_status_info status;
+                _cleanup_(remove_and_erasep) const char *destroy_key_file = NULL;
                 const char *key_file = NULL;
+                _cleanup_(erase_and_freep) void *key_data = NULL;
+                size_t key_data_size = 0;
 
                 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
 
                 if (argc < 4)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments.");
 
+                if (!filename_is_valid(argv[2]))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]);
+
                 if (argc >= 5 && !STR_IN_SET(argv[4], "", "-", "none")) {
                         if (path_is_absolute(argv[4]))
                                 key_file = argv[4];
@@ -797,6 +875,24 @@ static int run(int argc, char *argv[]) {
                 /* A delicious drop of snake oil */
                 (void) mlockall(MCL_FUTURE);
 
+                if (!key_file) {
+                        const char *fn;
+
+                        /* If a key file is not explicitly specified, search for a key in a well defined
+                         * search path, and load it. */
+
+                        fn = strjoina(argv[2], ".key");
+                        r = load_key_file(fn,
+                                          STRV_MAKE("/etc/cryptsetup-keys.d", "/run/cryptsetup-keys.d"),
+                                          0, 0,  /* Note we leave arg_keyfile_offset/arg_keyfile_size as something that only applies to arg_keyfile! */
+                                          &key_data, &key_data_size);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                log_debug("Automatically discovered key for volume '%s'.", argv[2]);
+                } else if (arg_keyfile_erase)
+                        destroy_key_file = key_file; /* let's get this baby erased when we leave */
+
                 if (arg_header) {
                         log_debug("LUKS header: %s", arg_header);
                         r = crypt_init(&cd, arg_header);
@@ -843,7 +939,7 @@ static int run(int argc, char *argv[]) {
                         }
 
                         /* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */
-                        if (!key_file) {
+                        if (!key_file && !key_data) {
                                 r = crypt_activate_by_token(cd, argv[2], CRYPT_ANY_TOKEN, NULL, flags);
                                 if (r >= 0) {
                                         log_debug("Volume %s activated with LUKS token id %i.", argv[2], r);
@@ -857,26 +953,53 @@ static int run(int argc, char *argv[]) {
                 for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
                         _cleanup_strv_free_erase_ char **passwords = NULL;
 
-                        if (!key_file && !arg_pkcs11_uri) {
-                                r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
-                                if (r == -EAGAIN)
-                                        continue;
-                                if (r < 0)
-                                        return r;
+                        /* When we were able to acquire multiple keys, let's always process them in this order:
+                         *
+                         *    1. A key acquired via PKCS#11 token
+                         *    2. The discovered key: i.e. key_data + key_data_size
+                         *    3. The configured key: i.e. key_file + arg_keyfile_offset + arg_keyfile_size
+                         *    4. The empty password, in case arg_try_empty_password is set
+                         *    5. We enquire the user for a password
+                         */
+
+                        if (!key_file && !key_data && !arg_pkcs11_uri) {
+
+                                if (arg_try_empty_password) {
+                                        /* Hmm, let's try an empty password now, but only once */
+                                        arg_try_empty_password = false;
+
+                                        key_data = strdup("");
+                                        if (!key_data)
+                                                return log_oom();
+
+                                        key_data_size = 0;
+                                } else {
+                                        /* Ask the user for a passphrase only as last resort, if we have
+                                         * nothing else to check for */
+
+                                        r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
+                                        if (r == -EAGAIN)
+                                                continue;
+                                        if (r < 0)
+                                                return r;
+                                }
                         }
 
                         if (streq_ptr(arg_type, CRYPT_TCRYPT))
-                                r = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
+                                r = attach_tcrypt(cd, argv[2], key_file, key_data, key_data_size, passwords, flags);
                         else
-                                r = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags, until);
+                                r = attach_luks_or_plain(cd, argv[2], key_file, key_data, key_data_size, passwords, flags, until);
                         if (r >= 0)
                                 break;
                         if (r != -EAGAIN)
                                 return r;
 
-                        /* Passphrase not correct? Let's try again! */
+                        /* Key not correct? Let's try again! */
+
                         key_file = NULL;
-                        arg_pkcs11_uri = NULL;
+                        key_data = erase_and_free(key_data);
+                        key_data_size = 0;
+                        arg_pkcs11_uri = mfree(arg_pkcs11_uri);
                 }
 
                 if (arg_tries != 0 && tries >= arg_tries)
@@ -884,6 +1007,9 @@ static int run(int argc, char *argv[]) {
 
         } else if (streq(argv[1], "detach")) {
 
+                if (!filename_is_valid(argv[2]))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]);
+
                 r = crypt_init_by_name(&cd, argv[2]);
                 if (r == -ENODEV) {
                         log_info("Volume %s already inactive.", argv[2]);
index 2b3e9ec7cb3dbac6122bbd5bf5d5b9a91ef15596..36a8ff23d4c3a9a48e13f83b331b5f72e844dd12 100644 (file)
@@ -35,6 +35,7 @@ typedef enum MountpointFlags {
         AUTOMOUNT = 1 << 2,
         MAKEFS    = 1 << 3,
         GROWFS    = 1 << 4,
+        RWONLY    = 1 << 5,
 } MountpointFlags;
 
 static const char *arg_dest = NULL;
@@ -472,6 +473,9 @@ static int add_mount(
         if (r < 0)
                 return r;
 
+        if (flags & RWONLY)
+                fprintf(f, "ReadWriteOnly=yes\n");
+
         r = fflush_and_check(f);
         if (r < 0)
                 return log_error_errno(r, "Failed to write unit file %s: %m", name);
@@ -583,7 +587,7 @@ static int parse_fstab(bool initrd) {
 
         while ((me = getmntent(f))) {
                 _cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL;
-                bool makefs, growfs, noauto, nofail;
+                bool makefs, growfs, noauto, nofail, rwonly;
                 int k;
 
                 if (initrd && !mount_in_initrd(me))
@@ -623,6 +627,7 @@ static int parse_fstab(bool initrd) {
 
                 makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0");
                 growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0");
+                rwonly = fstab_test_option(me->mnt_opts, "x-systemd.rw-only\0");
                 noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
                 nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
 
@@ -655,7 +660,7 @@ static int parse_fstab(bool initrd) {
                                       me->mnt_type,
                                       me->mnt_opts,
                                       me->mnt_passno,
-                                      makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT,
+                                      makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT | rwonly*RWONLY,
                                       post,
                                       fstab);
                 }
index 65e363c23c1d1b3dc0ea52d677543f8a599e5986..47ee7d23281c5e80936b6ee0fa85dc4c71c3c808 100644 (file)
@@ -292,7 +292,7 @@ int home_save_record(Home *h) {
 
         fn = strjoina("/var/lib/systemd/home/", h->user_name, ".identity");
 
-        r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600);
+        r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600|WRITE_STRING_FILE_SYNC);
         if (r < 0)
                 return r;
 
@@ -471,6 +471,8 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is currently not active", h->user_name);
         case -ENOSPC:
                 return sd_bus_error_setf(error, BUS_ERROR_NO_DISK_SPACE, "Not enough disk space for home %s", h->user_name);
+        case -EKEYREVOKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_CANT_AUTHENTICATE, "Home %s has no password or other authentication mechanism defined.", h->user_name);
         }
 
         return 0;
@@ -2364,7 +2366,7 @@ static int home_dispatch_release(Home *h, Operation *o) {
                 case HOME_UNFIXATED:
                 case HOME_ABSENT:
                 case HOME_INACTIVE:
-                        r = 0; /* done */
+                        r = 1; /* done */
                         break;
 
                 case HOME_LOCKED:
@@ -2384,7 +2386,7 @@ static int home_dispatch_release(Home *h, Operation *o) {
 
         assert(!h->current_operation);
 
-        if (r <= 0) /* failure or completed */
+        if (r != 0) /* failure or completed */
                 operation_result(o, r, &error);
         else /* ongoing */
                 h->current_operation = operation_ref(o);
@@ -2406,12 +2408,12 @@ static int home_dispatch_lock_all(Home *h, Operation *o) {
         case HOME_ABSENT:
         case HOME_INACTIVE:
                 log_info("Home %s is not active, no locking necessary.", h->user_name);
-                r = 0; /* done */
+                r = 1; /* done */
                 break;
 
         case HOME_LOCKED:
                 log_info("Home %s is already locked.", h->user_name);
-                r = 0; /* done */
+                r = 1; /* done */
                 break;
 
         case HOME_ACTIVE:
index df0ed2f4f36f550b6a7d6fc3e2f53f43fde8a155..7d951bee3b1189be626fae638b4721228137deac 100644 (file)
@@ -329,21 +329,36 @@ static int manager_add_home_by_record(
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
         unsigned line, column;
         int r, is_signed;
+        struct stat st;
         Home *h;
 
         assert(m);
         assert(name);
         assert(fname);
 
+        if (fstatat(dir_fd, fname, &st, 0) < 0)
+                return log_error_errno(errno, "Failed to stat identity record %s: %m", fname);
+
+        if (!S_ISREG(st.st_mode)) {
+                log_debug("Identity record file %s is not a regular file, ignoring.", fname);
+                return 0;
+        }
+
+        if (st.st_size == 0)
+                goto unlink_this_file;
+
         r = json_parse_file_at(NULL, dir_fd, fname, JSON_PARSE_SENSITIVE, &v, &line, &column);
         if (r < 0)
                 return log_error_errno(r, "Failed to parse identity record at %s:%u%u: %m", fname, line, column);
 
+        if (json_variant_is_blank_object(v))
+                goto unlink_this_file;
+
         hr = user_record_new();
         if (!hr)
                 return log_oom();
 
-        r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET);
+        r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG);
         if (r < 0)
                 return r;
 
@@ -394,6 +409,19 @@ static int manager_add_home_by_record(
         h->signed_locally = is_signed == USER_RECORD_SIGNED_EXCLUSIVE;
 
         return 1;
+
+unlink_this_file:
+        /* If this is an empty file, then let's just remove it. An empty file is not useful in any case, and
+         * apparently xfs likes to leave empty files around when not unmounted cleanly (see
+         * https://github.com/systemd/systemd/issues/15178 for example). Note that we don't delete non-empty
+         * files even if they are invalid, because that's just too risky, we might delete data the user still
+         * needs. But empty files are never useful, hence let's just remove them. */
+
+        if (unlinkat(dir_fd, fname, 0) < 0)
+                return log_error_errno(errno, "Failed to remove empty user record file %s: %m", fname);
+
+        log_notice("Discovered empty user record file /var/lib/systemd/home/%s, removed automatically.", fname);
+        return 0;
 }
 
 static int manager_enumerate_records(Manager *m) {
@@ -485,7 +513,7 @@ static int search_quota(uid_t uid, const char *exclude_quota_path) {
                         if (ERRNO_IS_NOT_SUPPORTED(r))
                                 log_debug_errno(r, "No UID quota support on %s, ignoring.", where);
                         else
-                                log_warning_errno(r, "Failed to query quota on %s, ignoring.", where);
+                                log_warning_errno(r, "Failed to query quota on %s, ignoring: %m", where);
 
                         continue;
                 }
@@ -1309,7 +1337,7 @@ static int manager_generate_key_pair(Manager *m) {
         if (PEM_write_PUBKEY(fpublic, m->private_key) <= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key.");
 
-        r = fflush_and_check(fpublic);
+        r = fflush_sync_and_check(fpublic);
         if (r < 0)
                 return log_error_errno(r, "Failed to write private key: %m");
 
@@ -1323,7 +1351,7 @@ static int manager_generate_key_pair(Manager *m) {
         if (PEM_write_PrivateKey(fprivate, m->private_key, NULL, NULL, 0, NULL, 0) <= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write private key pair.");
 
-        r = fflush_and_check(fprivate);
+        r = fflush_sync_and_check(fprivate);
         if (r < 0)
                 return log_error_errno(r, "Failed to write private key: %m");
 
@@ -1337,10 +1365,14 @@ static int manager_generate_key_pair(Manager *m) {
 
         if (rename(temp_private, "/var/lib/systemd/home/local.private") < 0) {
                 (void) unlink_noerrno("/var/lib/systemd/home/local.public"); /* try to remove the file we already created */
-                return log_error_errno(errno, "Failed to move privtate key file into place: %m");
+                return log_error_errno(errno, "Failed to move private key file into place: %m");
         }
         temp_private = mfree(temp_private);
 
+        r = fsync_path_at(AT_FDCWD, "/var/lib/systemd/home/");
+        if (r < 0)
+                log_warning_errno(r, "Failed to sync /var/lib/systemd/home/, ignoring: %m");
+
         return 1;
 }
 
index a8a48fe5939b80b4b29934d23c1b189920d12813..6de75fc5ca297c773973a9e702f6cf6760caca33 100644 (file)
@@ -11,6 +11,6 @@
 #
 # See homed.conf(5) for details
 
-[Resolve]
+[Home]
 #DefaultStorage=
 #DefaultFileSystemType=ext4
index 1510031a38a3dfcfaf0d263fb9970f06b8a77568..27c92e16e7281895b29b1c99a17a6e0f9bdfb0d5 100644 (file)
@@ -28,7 +28,7 @@ int home_prepare_cifs(
                 char **pw;
                 int r;
 
-                r = home_unshare_and_mount(NULL, NULL, false);
+                r = home_unshare_and_mount(NULL, NULL, false, user_record_mount_flags(h));
                 if (r < 0)
                         return r;
 
index caa4168265a0a2ef50f74119f98a1fef74415f40..e9b2fc0c0da00ea9ab6c0b7dda7a825a4247fafc 100644 (file)
@@ -727,9 +727,10 @@ static int luks_validate_home_record(
                 if (!user_record_compatible(h, lhr))
                         return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "LUKS home record not compatible with host record, refusing.");
 
-                r = user_record_authenticate(lhr, h, pkcs11_decrypted_passwords);
+                r = user_record_authenticate(lhr, h, pkcs11_decrypted_passwords, /* strict_verify= */ true);
                 if (r < 0)
                         return r;
+                assert(r > 0); /* Insist that a password was verified */
 
                 *ret_luks_home_record = TAKE_PTR(lhr);
                 return 0;
@@ -1155,7 +1156,7 @@ int home_prepare_luks(
                 if (r < 0)
                         goto fail;
 
-                r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h));
+                r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h));
                 if (r < 0)
                         goto fail;
 
@@ -2078,7 +2079,7 @@ int home_create_luks(
 
         log_info("Formatting file system completed.");
 
-        r = home_unshare_and_mount(dm_node, fstype, user_record_luks_discard(h));
+        r = home_unshare_and_mount(dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h));
         if (r < 0)
                 goto fail;
 
@@ -2283,7 +2284,7 @@ static int can_resize_fs(int fd, uint64_t old_size, uint64_t new_size) {
         return CAN_RESIZE_ONLINE;
 }
 
-static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool discard) {
+static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool discard, unsigned long flags) {
         _cleanup_free_ char *size_str = NULL;
         bool re_open = false, re_mount = false;
         pid_t resize_pid, fsck_pid;
@@ -2353,7 +2354,7 @@ static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool disc
 
         /* Re-establish mounts and reopen the directory */
         if (re_mount) {
-                r = home_mount_node(setup->dm_node, "ext4", discard);
+                r = home_mount_node(setup->dm_node, "ext4", discard, flags);
                 if (r < 0)
                         return r;
 
@@ -2773,7 +2774,7 @@ int home_resize_luks(
         if (resize_type == CAN_RESIZE_ONLINE)
                 r = resize_fs(setup->root_fd, new_fs_size, NULL);
         else
-                r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h));
+                r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h), user_record_mount_flags(h));
         if (r < 0)
                 return log_error_errno(r, "Failed to resize file system: %m");
 
index 9e1116840de2f6a789adf92c28960f38f3b9f150..51c0a3864945190b0a8a1741b43a735f295fc880 100644 (file)
@@ -20,7 +20,7 @@ static const char *mount_options_for_fstype(const char *fstype) {
         return NULL;
 }
 
-int home_mount_node(const char *node, const char *fstype, bool discard) {
+int home_mount_node(const char *node, const char *fstype, bool discard, unsigned long flags) {
         _cleanup_free_ char *joined = NULL;
         const char *options, *discard_option;
         int r;
@@ -38,7 +38,7 @@ int home_mount_node(const char *node, const char *fstype, bool discard) {
         } else
                 options = discard_option;
 
-        r = mount_verbose(LOG_ERR, node, "/run/systemd/user-home-mount", fstype, MS_NODEV|MS_NOSUID|MS_RELATIME, strempty(options));
+        r = mount_verbose(LOG_ERR, node, "/run/systemd/user-home-mount", fstype, flags|MS_RELATIME, strempty(options));
         if (r < 0)
                 return r;
 
@@ -46,7 +46,7 @@ int home_mount_node(const char *node, const char *fstype, bool discard) {
         return 0;
 }
 
-int home_unshare_and_mount(const char *node, const char *fstype, bool discard) {
+int home_unshare_and_mount(const char *node, const char *fstype, bool discard, unsigned long flags) {
         int r;
 
         if (unshare(CLONE_NEWNS) < 0)
@@ -59,7 +59,7 @@ int home_unshare_and_mount(const char *node, const char *fstype, bool discard) {
         (void) mkdir_p("/run/systemd/user-home-mount", 0700);
 
         if (node)
-                return home_mount_node(node, fstype, discard);
+                return home_mount_node(node, fstype, discard, flags);
 
         return 0;
 }
index d926756f7bc9886f074a82e0121fe61ee3ce194c..cf7c8cfcab4bd848ed98d6c71d4115cd479c76e1 100644 (file)
@@ -3,6 +3,6 @@
 
 #include <stdbool.h>
 
-int home_mount_node(const char *node, const char *fstype, bool discard);
-int home_unshare_and_mount(const char *node, const char *fstype, bool discard);
+int home_mount_node(const char *node, const char *fstype, bool discard, unsigned long flags);
+int home_unshare_and_mount(const char *node, const char *fstype, bool discard, unsigned long flags);
 int home_move_mount(const char *user_name_and_realm, const char *target);
index 71e7cfdb499aaa0585e0bda939a7ef4e2d3dd9e9..316933cf4ebf69b3be051d050a874e6e97e250ed 100644 (file)
@@ -35,7 +35,8 @@
 int user_record_authenticate(
                 UserRecord *h,
                 UserRecord *secret,
-                char ***pkcs11_decrypted_passwords) {
+                char ***pkcs11_decrypted_passwords,
+                bool strict_verify) {
 
         bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false,
                 pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false;
@@ -66,7 +67,7 @@ int user_record_authenticate(
                 return log_error_errno(r, "Failed to validate password of record: %m");
         else {
                 log_info("Provided password unlocks user record.");
-                return 0;
+                return 1;
         }
 
         /* Second, let's see if any of the PKCS#11 security tokens are plugged in and help us */
@@ -86,7 +87,7 @@ int user_record_authenticate(
                                 return log_error_errno(r, "Failed to check supplied PKCS#11 password: %m");
                         if (r > 0) {
                                 log_info("Previously acquired PKCS#11 password unlocks user record.");
-                                return 0;
+                                return 1;
                         }
                 }
 
@@ -129,7 +130,7 @@ int user_record_authenticate(
                         if (r < 0)
                                 return log_oom();
 
-                        return 0;
+                        return 1;
                 }
 #else
                 need_token = true;
@@ -156,7 +157,18 @@ int user_record_authenticate(
                 return -ENOKEY;
 
         /* Hmm, this means neither PCKS#11 nor classic hashed passwords were supplied, we cannot authenticate this reasonably */
-        return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED), "No hashed passwords and no PKCS#11 tokens defined, cannot authenticate user record.");
+        if (strict_verify)
+                return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED),
+                                       "No hashed passwords and no PKCS#11 tokens defined, cannot authenticate user record, refusing.");
+
+        /* If strict verification is off this means we are possibly in the case where we encountered an
+         * unfixated record, i.e. a synthetic one that accordingly lacks any authentication data. In this
+         * case, allow the authentication to pass for now, so that the second (or third) authentication level
+         * (the ones of the user record in the LUKS header or inside the home directory) will then catch
+         * invalid passwords. The second/third authentication always runs in strict verification mode. */
+        log_debug("No hashed passwords and no PKCS#11 tokens defined in record, cannot authenticate user record. "
+                  "Deferring to embedded user record.");
+        return 0;
 }
 
 int home_setup_undo(HomeSetup *setup) {
@@ -402,9 +414,10 @@ int home_load_embedded_identity(
                 return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "Embedded home record not compatible with host record, refusing.");
 
         /* Insist that credentials the user supplies also unlocks any embedded records. */
-        r = user_record_authenticate(embedded_home, h, pkcs11_decrypted_passwords);
+        r = user_record_authenticate(embedded_home, h, pkcs11_decrypted_passwords, /* strict_verify= */ true);
         if (r < 0)
                 return r;
+        assert(r > 0); /* Insist that a password was verified */
 
         /* At this point we have three records to deal with:
          *
@@ -615,7 +628,7 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) {
         if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Activating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ false);
         if (r < 0)
                 return r;
 
@@ -1051,7 +1064,7 @@ static int home_remove(UserRecord *h) {
                 assert(ip);
 
                 if (stat(ip, &st) < 0) {
-                        if (errno != -ENOENT)
+                        if (errno != ENOENT)
                                 return log_error_errno(errno, "Failed to stat() %s: %m", ip);
 
                 } else {
@@ -1177,9 +1190,10 @@ static int home_update(UserRecord *h, UserRecord **ret) {
         assert(h);
         assert(ret);
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ true);
         if (r < 0)
                 return r;
+        assert(r > 0); /* Insist that a password was verified */
 
         r = home_validate_update(h, &setup);
         if (r < 0)
@@ -1233,9 +1247,10 @@ static int home_resize(UserRecord *h, UserRecord **ret) {
         if (h->disk_size == UINT64_MAX)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No target size specified, refusing.");
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ true);
         if (r < 0)
                 return r;
+        assert(r > 0); /* Insist that a password was verified */
 
         r = home_validate_update(h, &setup);
         if (r < 0)
@@ -1343,7 +1358,7 @@ static int home_inspect(UserRecord *h, UserRecord **ret_home) {
         assert(h);
         assert(ret_home);
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ false);
         if (r < 0)
                 return r;
 
@@ -1413,7 +1428,7 @@ static int home_unlock(UserRecord *h) {
         /* Note that we don't check if $HOME is actually mounted, since we want to avoid disk accesses on
          * that mount until we have resumed the device. */
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ false);
         if (r < 0)
                 return r;
 
@@ -1489,6 +1504,7 @@ static int run(int argc, char *argv[]) {
          * EBUSY           → file system is currently active
          * ENOEXEC         → file system is currently not active
          * ENOSPC          → not enough disk space for operation
+         * EKEYREVOKED     → user record has not suitable hashed password or pkcs#11 entry, we cannot authenticate
          */
 
         if (streq(argv[1], "activate"))
index 3bcf3ad9b01a0ac43fbd4253edd2354cecc5a4c5..46641172a421e2e5fab5243b3455f94adc95cb4d 100644 (file)
@@ -56,6 +56,6 @@ int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_h
 int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home);
 int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup);
 
-int user_record_authenticate(UserRecord *h, UserRecord *secret, char ***pkcs11_decrypted_passwords);
+int user_record_authenticate(UserRecord *h, UserRecord *secret, char ***pkcs11_decrypted_passwords, bool strict_verify);
 
 int home_sync_and_statfs(int root_fd, struct statfs *ret);
index 7662fa69ebfc8a98571e4d0c799488055ba04bd9..baa3586bf7a2eee79d6cf9ced4fc93e43514bcf5 100644 (file)
 #include "user-record.h"
 #include "user-util.h"
 
-/* Used for the "systemd-user-record-is-homed" PAM data field, to indicate whether we know whether this user
- * record is managed by homed or by something else. */
-#define USER_RECORD_IS_HOMED INT_TO_PTR(1)
-#define USER_RECORD_IS_OTHER INT_TO_PTR(2)
-
 static int parse_argv(
                 pam_handle_t *handle,
                 int argc, const char **argv,
@@ -65,29 +60,61 @@ static int parse_argv(
         return 0;
 }
 
+static int parse_env(
+                pam_handle_t *handle,
+                bool *please_suspend) {
+
+        const char *v;
+        int r;
+
+        /* Let's read the suspend setting from an env var in addition to the PAM command line. That makes it
+         * easy to declare the features of a display manager in code rather than configuration, and this is
+         * really a feature of code */
+
+        v = pam_getenv(handle, "SYSTEMD_HOME_SUSPEND");
+        if (!v) {
+                /* Also check the process env block, so that people can control this via an env var from the
+                 * outside of our process. */
+                v = secure_getenv("SYSTEMD_HOME_SUSPEND");
+                if (!v)
+                        return 0;
+        }
+
+        r = parse_boolean(v);
+        if (r < 0)
+                pam_syslog(handle, LOG_WARNING, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v);
+        else if (please_suspend)
+                *please_suspend = r;
+
+        return 0;
+}
+
 static int acquire_user_record(
                 pam_handle_t *handle,
+                const char *username,
                 UserRecord **ret_record) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
-        const char *username = NULL, *json = NULL;
-        const void *b = NULL;
+        _cleanup_free_ char *homed_field = NULL;
+        const char *json = NULL;
         int r;
 
         assert(handle);
 
-        r = pam_get_user(handle, &username, NULL);
-        if (r != PAM_SUCCESS) {
-                pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
-                return r;
-        }
+        if (!username) {
+                r = pam_get_user(handle, &username, NULL);
+                if (r != PAM_SUCCESS) {
+                        pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
+                        return r;
+                }
 
-        if (isempty(username)) {
-                pam_syslog(handle, LOG_ERR, "User name not set.");
-                return PAM_SERVICE_ERR;
+                if (isempty(username)) {
+                        pam_syslog(handle, LOG_ERR, "User name not set.");
+                        return PAM_SERVICE_ERR;
+                }
         }
 
         /* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for
@@ -95,31 +122,30 @@ static int acquire_user_record(
         if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username, 0))
                 return PAM_USER_UNKNOWN;
 
-        /* Let's check if a previous run determined that this user is not managed by homed. If so, let's exit early */
-        r = pam_get_data(handle, "systemd-user-record-is-homed", &b);
+        /* We cache the user record in the PAM context. We use a field name that includes the username, since
+         * clients might change the user name associated with a PAM context underneath us. Notably, 'sudo'
+         * creates a single PAM context and first authenticates it with the user set to the originating user,
+         * then updates the user for the destination user and issues the session stack with the same PAM
+         * context. We thus must be prepared that the user record changes between calls and we keep any
+         * caching separate. */
+        homed_field = strjoin("systemd-home-user-record-", username);
+        if (!homed_field)
+                return pam_log_oom(handle);
+
+        /* Let's use the cache, so that we can share it between the session and the authentication hooks */
+        r = pam_get_data(handle, homed_field, (const void**) &json);
         if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
-                /* Failure */
-                pam_syslog(handle, LOG_ERR, "Failed to get PAM user-record-is-homed flag: %s", pam_strerror(handle, r));
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
                 return r;
-        } else if (b == NULL)
-                /* Nothing cached yet, need to acquire fresh */
-                json = NULL;
-        else if (b != USER_RECORD_IS_HOMED)
-                /* Definitely not a homed record */
-                return PAM_USER_UNKNOWN;
-        else {
-                /* It's a homed record, let's use the cache, so that we can share it between the session and
-                 * the authentication hooks */
-                r = pam_get_data(handle, "systemd-user-record", (const void**) &json);
-                if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
-                        pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
-                        return r;
-                }
         }
-
-        if (!json) {
+        if (r == PAM_SUCCESS && json) {
+                /* We determined earlier that this is not a homed user? Then exit early. (We use -1 as
+                 * negative cache indicator) */
+                if (json == (void*) -1)
+                        return PAM_USER_UNKNOWN;
+        } else {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-                _cleanup_free_ char *json_copy = NULL;
+                _cleanup_free_ char *generic_field = NULL, *json_copy = NULL;
 
                 r = pam_acquire_bus_connection(handle, &bus);
                 if (r != PAM_SUCCESS)
@@ -146,23 +172,38 @@ static int acquire_user_record(
                 if (r < 0)
                         return pam_bus_log_parse_error(handle, r);
 
+                /* First copy: for the homed-specific data field, i.e. where we know the user record is from
+                 * homed */
                 json_copy = strdup(json);
                 if (!json_copy)
                         return pam_log_oom(handle);
 
-                r = pam_set_data(handle, "systemd-user-record", json_copy, pam_cleanup_free);
+                r = pam_set_data(handle, homed_field, json_copy, pam_cleanup_free);
                 if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r));
+                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s",
+                                   homed_field, pam_strerror(handle, r));
                         return r;
                 }
 
-                TAKE_PTR(json_copy);
+                /* Take a second copy: for the generic data field, the one which we share with
+                 * pam_systemd. While we insist on only reusing homed records, pam_systemd is fine with homed
+                 * and non-homed user records. */
+                json_copy = strdup(json);
+                if (!json_copy)
+                        return pam_log_oom(handle);
+
+                generic_field = strjoin("systemd-user-record-", username);
+                if (!generic_field)
+                        return pam_log_oom(handle);
 
-                r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_HOMED, NULL);
+                r = pam_set_data(handle, generic_field, json_copy, pam_cleanup_free);
                 if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record is homed flag: %s", pam_strerror(handle, r));
+                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s",
+                                   homed_field, pam_strerror(handle, r));
                         return r;
                 }
+
+                TAKE_PTR(json_copy);
         }
 
         r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
@@ -181,6 +222,7 @@ static int acquire_user_record(
                 return PAM_SERVICE_ERR;
         }
 
+        /* Safety check if cached record actually matches what we are looking for */
         if (!streq_ptr(username, ur->user_name)) {
                 pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name.");
                 return PAM_SERVICE_ERR;
@@ -193,23 +235,36 @@ static int acquire_user_record(
 
 user_unknown:
         /* Cache this, so that we don't check again */
-        r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_OTHER, NULL);
+        r = pam_set_data(handle, homed_field, (void*) -1, NULL);
         if (r != PAM_SUCCESS)
-                pam_syslog(handle, LOG_ERR, "Failed to set PAM user-record-is-homed flag, ignoring: %s", pam_strerror(handle, r));
+                pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s' to invalid, ignoring: %s",
+                           homed_field, pam_strerror(handle, r));
 
         return PAM_USER_UNKNOWN;
 }
 
-static int release_user_record(pam_handle_t *handle) {
+static int release_user_record(pam_handle_t *handle, const char *username) {
+        _cleanup_free_ char *homed_field = NULL, *generic_field = NULL;
         int r, k;
 
-        r = pam_set_data(handle, "systemd-user-record", NULL, NULL);
+        assert(handle);
+        assert(username);
+
+        homed_field = strjoin("systemd-home-user-record-", username);
+        if (!homed_field)
+                return pam_log_oom(handle);
+
+        r = pam_set_data(handle, homed_field, NULL, NULL);
         if (r != PAM_SUCCESS)
-                pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data: %s", pam_strerror(handle, r));
+                pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data '%s': %s", homed_field, pam_strerror(handle, r));
+
+        generic_field = strjoin("systemd-user-record-", username);
+        if (!generic_field)
+                return pam_log_oom(handle);
 
-        k = pam_set_data(handle, "systemd-user-record-is-homed", NULL, NULL);
+        k = pam_set_data(handle, generic_field, NULL, NULL);
         if (k != PAM_SUCCESS)
-                pam_syslog(handle, LOG_ERR, "Failed to release PAM user-record-is-homed flag: %s", pam_strerror(handle, k));
+                pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data '%s': %s", generic_field, pam_strerror(handle, k));
 
         return IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA) ? k : r;
 }
@@ -395,7 +450,9 @@ static int acquire_home(
         bool do_auth = please_authenticate, home_not_active = false, home_locked = false;
         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
         _cleanup_close_ int acquired_fd = -1;
+        _cleanup_free_ char *fd_field = NULL;
         const void *home_fd_ptr = NULL;
+        const char *username = NULL;
         unsigned n_attempts = 0;
         int r;
 
@@ -409,8 +466,27 @@ static int acquire_home(
          * authenticates, while the other PAM hooks unset it so that they can a ref of their own without
          * authentication if possible, but with authentication if necessary. */
 
+        r = pam_get_user(handle, &username, NULL);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
+                return r;
+        }
+
+        if (isempty(username)) {
+                pam_syslog(handle, LOG_ERR, "User name not set.");
+                return PAM_SERVICE_ERR;
+        }
+
         /* If we already have acquired the fd, let's shortcut this */
-        r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
+        fd_field = strjoin("systemd-home-fd-", username);
+        if (!fd_field)
+                return pam_log_oom(handle);
+
+        r = pam_get_data(handle, fd_field, &home_fd_ptr);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r));
+                return r;
+        }
         if (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) >= 0)
                 return PAM_SUCCESS;
 
@@ -418,7 +494,7 @@ static int acquire_home(
         if (r != PAM_SUCCESS)
                 return r;
 
-        r = acquire_user_record(handle, &ur);
+        r = acquire_user_record(handle, username, &ur);
         if (r != PAM_SUCCESS)
                 return r;
 
@@ -534,7 +610,7 @@ static int acquire_home(
                 do_auth = true;
         }
 
-        r = pam_set_data(handle, "systemd-home-fd", FD_TO_PTR(acquired_fd), cleanup_home_fd);
+        r = pam_set_data(handle, fd_field, FD_TO_PTR(acquired_fd), cleanup_home_fd);
         if (r < 0) {
                 pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r));
                 return r;
@@ -545,7 +621,7 @@ static int acquire_home(
                 /* We likely just activated the home directory, let's flush out the user record, since a
                  * newer embedded user record might have been acquired from the activation. */
 
-                r = release_user_record(handle);
+                r = release_user_record(handle, ur->user_name);
                 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
                         return r;
         }
@@ -555,15 +631,27 @@ static int acquire_home(
         return PAM_SUCCESS;
 }
 
-static int release_home_fd(pam_handle_t *handle) {
+static int release_home_fd(pam_handle_t *handle, const char *username) {
+        _cleanup_free_ char *fd_field = NULL;
         const void *home_fd_ptr = NULL;
         int r;
 
-        r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
-        if (r == PAM_NO_MODULE_DATA || PTR_TO_FD(home_fd_ptr) < 0)
+        assert(handle);
+        assert(username);
+
+        fd_field = strjoin("systemd-home-fd-", username);
+        if (!fd_field)
+                return pam_log_oom(handle);
+
+        r = pam_get_data(handle, fd_field, &home_fd_ptr);
+        if (r == PAM_NO_MODULE_DATA || (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) < 0))
                 return PAM_NO_MODULE_DATA;
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r));
+                return r;
+        }
 
-        r = pam_set_data(handle, "systemd-home-fd", NULL, NULL);
+        r = pam_set_data(handle, fd_field, NULL, NULL);
         if (r != PAM_SUCCESS)
                 pam_syslog(handle, LOG_ERR, "Failed to release PAM home reference fd: %s", pam_strerror(handle, r));
 
@@ -577,6 +665,9 @@ _public_ PAM_EXTERN int pam_sm_authenticate(
 
         bool debug = false, suspend_please = false;
 
+        if (parse_env(handle, &suspend_please) < 0)
+                return PAM_AUTH_ERR;
+
         if (parse_argv(handle,
                        argc, argv,
                        &suspend_please,
@@ -601,6 +692,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         bool debug = false, suspend_please = false;
         int r;
 
+        if (parse_env(handle, &suspend_please) < 0)
+                return PAM_SESSION_ERR;
+
         if (parse_argv(handle,
                        argc, argv,
                        &suspend_please,
@@ -622,6 +716,12 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 return r;
         }
 
+        r = pam_putenv(handle, suspend_please ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0");
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable $SYSTEMD_HOME_SUSPEND: %s", pam_strerror(handle, r));
+                return r;
+        }
+
         /* Let's release the D-Bus connection, after all the session might live quite a long time, and we are
          * not going to process the bus connection in that time, so let's better close before the daemon
          * kicks us off because we are not processing anything. */
@@ -650,20 +750,25 @@ _public_ PAM_EXTERN int pam_sm_close_session(
         if (debug)
                 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session end");
 
+        r = pam_get_user(handle, &username, NULL);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
+                return r;
+        }
+
+        if (isempty(username)) {
+                pam_syslog(handle, LOG_ERR, "User name not set.");
+                return PAM_SERVICE_ERR;
+        }
+
         /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
          * call will be able to do its thing. */
-        r = release_home_fd(handle);
+        r = release_home_fd(handle, username);
         if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
                 return PAM_SUCCESS;
         if (r != PAM_SUCCESS)
                 return r;
 
-        r = pam_get_user(handle, &username, NULL);
-        if (r != PAM_SUCCESS) {
-                pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
-                return r;
-        }
-
         r = pam_acquire_bus_connection(handle, &bus);
         if (r != PAM_SUCCESS)
                 return r;
@@ -700,6 +805,9 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
         usec_t t;
         int r;
 
+        if (parse_env(handle, &please_suspend) < 0)
+                return PAM_AUTH_ERR;
+
         if (parse_argv(handle,
                        argc, argv,
                        &please_suspend,
@@ -715,7 +823,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
         if (r != PAM_SUCCESS)
                 return r;
 
-        r = acquire_user_record(handle, &ur);
+        r = acquire_user_record(handle, NULL, &ur);
         if (r != PAM_SUCCESS)
                 return r;
 
@@ -823,7 +931,7 @@ _public_ PAM_EXTERN int pam_sm_chauthtok(
         if (r != PAM_SUCCESS)
                 return r;
 
-        r = acquire_user_record(handle, &ur);
+        r = acquire_user_record(handle, NULL, &ur);
         if (r != PAM_SUCCESS)
                 return r;
 
index 701041c12adbcf16be7e47832d6e6c9c974b35f7..e694a95a0411327e422b3001904f2f59c0904356 100644 (file)
@@ -43,9 +43,6 @@ enum {
         PROP_CHASSIS,
         PROP_DEPLOYMENT,
         PROP_LOCATION,
-        PROP_KERNEL_NAME,
-        PROP_KERNEL_RELEASE,
-        PROP_KERNEL_VERSION,
         PROP_OS_PRETTY_NAME,
         PROP_OS_CPE_NAME,
         PROP_HOME_URL,
@@ -55,8 +52,6 @@ enum {
 typedef struct Context {
         char *data[_PROP_MAX];
         Hashmap *polkit_registry;
-        sd_id128_t uuid;
-        bool has_uuid;
 } Context;
 
 static void context_reset(Context *c) {
@@ -68,7 +63,7 @@ static void context_reset(Context *c) {
                 c->data[p] = mfree(c->data[p]);
 }
 
-static void context_clear(Context *c) {
+static void context_destroy(Context *c) {
         assert(c);
 
         context_reset(c);
@@ -77,20 +72,11 @@ static void context_clear(Context *c) {
 
 static int context_read_data(Context *c) {
         int r;
-        struct utsname u;
 
         assert(c);
 
         context_reset(c);
 
-        assert_se(uname(&u) >= 0);
-        c->data[PROP_KERNEL_NAME] = strdup(u.sysname);
-        c->data[PROP_KERNEL_RELEASE] = strdup(u.release);
-        c->data[PROP_KERNEL_VERSION] = strdup(u.version);
-        if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] ||
-            !c->data[PROP_KERNEL_VERSION])
-                return -ENOMEM;
-
         c->data[PROP_HOSTNAME] = gethostname_malloc();
         if (!c->data[PROP_HOSTNAME])
                 return -ENOMEM;
@@ -116,17 +102,6 @@ static int context_read_data(Context *c) {
         if (r < 0 && r != -ENOENT)
                 return r;
 
-        r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &c->uuid);
-        if (r == -ENOENT)
-                r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &c->uuid);
-        if (r < 0)
-                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
-                               "Failed to read product UUID, ignoring: %m");
-        else if (sd_id128_is_null(c->uuid) || sd_id128_is_allf(c->uuid))
-                log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(c->uuid));
-        else
-                c->has_uuid = true;
-
         return 0;
 }
 
@@ -411,6 +386,22 @@ static int property_get_chassis(
         return sd_bus_message_append(reply, "s", name);
 }
 
+static int property_get_uname_field(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        return sd_bus_message_append(reply, "s", (char*) &u + PTR_TO_SIZE(userdata));
+}
+
 static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         Context *c = userdata;
         const char *name;
@@ -627,13 +618,27 @@ static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *
 static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         Context *c = userdata;
+        bool has_uuid = false;
         int interactive, r;
+        sd_id128_t uuid;
 
         assert(m);
         assert(c);
 
-        if (!c->has_uuid)
-                return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, "Failed to read product UUID from /sys/class/dmi/id/product_uuid");
+        r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
+        if (r == -ENOENT)
+                r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
+        if (r < 0)
+                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+                               "Failed to read product UUID, ignoring: %m");
+        else if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
+                log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(uuid));
+        else
+                has_uuid = true;
+
+        if (!has_uuid)
+                return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID,
+                                        "Failed to read product UUID from firmware.");
 
         r = sd_bus_message_read(m, "b", &interactive);
         if (r < 0)
@@ -657,7 +662,7 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_append_array(reply, 'y', &c->uuid, sizeof(c->uuid));
+        r = sd_bus_message_append_array(reply, 'y', &uuid, sizeof(uuid));
         if (r < 0)
                 return r;
 
@@ -673,9 +678,9 @@ static const sd_bus_vtable hostname_vtable[] = {
         SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("KernelVersion", "s", property_get_uname_field, offsetof(struct utsname, version), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("HomeURL", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -780,7 +785,7 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
 }
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(context_clear) Context context = {};
+        _cleanup_(context_destroy) Context context = {};
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
index 5bfe5acadcebc54159089ff342c84597c5feb4d5..da37b3416ff4ce1b4bee2daf122cfc8b9e5f3819 100644 (file)
@@ -410,5 +410,5 @@ int open_journal_for_upload(Uploader *u,
                                                cursor);
         }
 
-        return process_journal_input(u, 1 + !!after_cursor);
+        return process_journal_input(u, !!after_cursor);
 }
index 2f9585df564220f51a59d224ba72198ce550690b..bf656ac670617c7ff740450c73ef30d168b1ee0d 100644 (file)
@@ -720,7 +720,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_FILE:
-                        r = glob_extend(&arg_file, optarg);
+                        r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to add paths: %m");
                         break;
index bd5363586077bda3a7d3b724a70ccbe098807935..b1e092224f789cfa227c7fdc1d3f1f10a44ebaf3 100644 (file)
@@ -533,7 +533,7 @@ static int journal_file_verify_header(JournalFile *f) {
         if (f->header->state >= _STATE_MAX)
                 return -EBADMSG;
 
-        header_size = le64toh(f->header->header_size);
+        header_size = le64toh(READ_NOW(f->header->header_size));
 
         /* The first addition was n_data, so check that we are at least this large */
         if (header_size < HEADER_SIZE_MIN)
@@ -542,7 +542,7 @@ static int journal_file_verify_header(JournalFile *f) {
         if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
                 return -EBADMSG;
 
-        arena_size = le64toh(f->header->arena_size);
+        arena_size = le64toh(READ_NOW(f->header->arena_size));
 
         if (UINT64_MAX - header_size < arena_size || header_size + arena_size > (uint64_t) f->last_stat.st_size)
                 return -ENODATA;
@@ -625,26 +625,29 @@ int journal_file_fstat(JournalFile *f) {
 }
 
 static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
-        uint64_t old_size, new_size;
+        uint64_t old_size, new_size, old_header_size, old_arena_size;
         int r;
 
         assert(f);
         assert(f->header);
 
-        /* We assume that this file is not sparse, and we know that
-         * for sure, since we always call posix_fallocate()
-         * ourselves */
+        /* We assume that this file is not sparse, and we know that for sure, since we always call
+         * posix_fallocate() ourselves */
+
+        if (size > PAGE_ALIGN_DOWN(UINT64_MAX) - offset)
+                return -EINVAL;
 
         if (mmap_cache_got_sigbus(f->mmap, f->cache_fd))
                 return -EIO;
 
-        old_size =
-                le64toh(f->header->header_size) +
-                le64toh(f->header->arena_size);
+        old_header_size = le64toh(READ_NOW(f->header->header_size));
+        old_arena_size = le64toh(READ_NOW(f->header->arena_size));
+        if (old_arena_size > PAGE_ALIGN_DOWN(UINT64_MAX) - old_header_size)
+                return -EBADMSG;
+
+        old_size = old_header_size + old_arena_size;
 
-        new_size = PAGE_ALIGN(offset + size);
-        if (new_size < le64toh(f->header->header_size))
-                new_size = le64toh(f->header->header_size);
+        new_size = MAX(PAGE_ALIGN(offset + size), old_header_size);
 
         if (new_size <= old_size) {
 
@@ -690,7 +693,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
         if (r != 0)
                 return -r;
 
-        f->header->arena_size = htole64(new_size - le64toh(f->header->header_size));
+        f->header->arena_size = htole64(new_size - old_header_size);
 
         return journal_file_fstat(f);
 }
@@ -702,7 +705,15 @@ static unsigned type_to_context(ObjectType type) {
         return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0;
 }
 
-static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret, size_t *ret_size) {
+static int journal_file_move_to(
+                JournalFile *f,
+                ObjectType type,
+                bool keep_always,
+                uint64_t offset,
+                uint64_t size,
+                void **ret,
+                size_t *ret_size) {
+
         int r;
 
         assert(f);
@@ -711,6 +722,9 @@ static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_alway
         if (size <= 0)
                 return -EINVAL;
 
+        if (size > UINT64_MAX - offset)
+                return -EBADMSG;
+
         /* Avoid SIGBUS on invalid accesses */
         if (offset + size > (uint64_t) f->last_stat.st_size) {
                 /* Hmm, out of range? Let's refresh the fstat() data
@@ -760,7 +774,7 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                                                le64toh(o->data.n_entries),
                                                offset);
 
-                if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
+                if (le64toh(o->object.size) <= offsetof(DataObject, payload))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64,
                                                offsetof(DataObject, payload),
@@ -782,7 +796,7 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                 break;
 
         case OBJECT_FIELD:
-                if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
+                if (le64toh(o->object.size) <= offsetof(FieldObject, payload))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Bad field size (<= %zu): %" PRIu64 ": %" PRIu64,
                                                offsetof(FieldObject, payload),
@@ -798,18 +812,22 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                                                offset);
                 break;
 
-        case OBJECT_ENTRY:
-                if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
+        case OBJECT_ENTRY: {
+                uint64_t sz;
+
+                sz = le64toh(READ_NOW(o->object.size));
+                if (sz < offsetof(EntryObject, items) ||
+                    (sz - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64,
                                                offsetof(EntryObject, items),
-                                               le64toh(o->object.size),
+                                               sz,
                                                offset);
 
-                if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
+                if ((sz - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid number items in entry: %" PRIu64 ": %" PRIu64,
-                                               (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem),
+                                               (sz - offsetof(EntryObject, items)) / sizeof(EntryItem),
                                                offset);
 
                 if (le64toh(o->entry.seqnum) <= 0)
@@ -831,25 +849,35 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                                                offset);
 
                 break;
+        }
 
         case OBJECT_DATA_HASH_TABLE:
-        case OBJECT_FIELD_HASH_TABLE:
-                if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
-                    (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
+        case OBJECT_FIELD_HASH_TABLE: {
+                uint64_t sz;
+
+                sz = le64toh(READ_NOW(o->object.size));
+                if (sz < offsetof(HashTableObject, items) ||
+                    (sz - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
+                    (sz - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid %s hash table size: %" PRIu64 ": %" PRIu64,
                                                o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
-                                               le64toh(o->object.size),
+                                               sz,
                                                offset);
 
                 break;
+        }
+
+        case OBJECT_ENTRY_ARRAY: {
+                uint64_t sz;
 
-        case OBJECT_ENTRY_ARRAY:
-                if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
-                    (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
+                sz = le64toh(READ_NOW(o->object.size));
+                if (sz < offsetof(EntryArrayObject, items) ||
+                    (sz - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
+                    (sz - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid object entry array size: %" PRIu64 ": %" PRIu64,
-                                               le64toh(o->object.size),
+                                               sz,
                                                offset);
 
                 if (!VALID64(le64toh(o->entry_array.next_entry_array_offset)))
@@ -859,6 +887,7 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                                                offset);
 
                 break;
+        }
 
         case OBJECT_TAG:
                 if (le64toh(o->object.size) != sizeof(TagObject))
@@ -905,7 +934,7 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
                 return r;
 
         o = (Object*) t;
-        s = le64toh(o->object.size);
+        s = le64toh(READ_NOW(o->object.size));
 
         if (s == 0)
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
@@ -995,11 +1024,21 @@ int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, O
         if (p == 0)
                 p = le64toh(f->header->header_size);
         else {
+                uint64_t sz;
+
                 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail);
                 if (r < 0)
                         return r;
 
-                p += ALIGN64(le64toh(tail->object.size));
+                sz = le64toh(READ_NOW(tail->object.size));
+                if (sz > UINT64_MAX - sizeof(uint64_t) + 1)
+                        return -EBADMSG;
+
+                sz = ALIGN64(sz);
+                if (p > UINT64_MAX - sz)
+                        return -EBADMSG;
+
+                p += sz;
         }
 
         r = journal_file_allocate(f, p, size);
@@ -1011,10 +1050,10 @@ int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, O
                 return r;
 
         o = (Object*) t;
-
-        zero(o->object);
-        o->object.type = type;
-        o->object.size = htole64(size);
+        o->object = (ObjectHeader) {
+                .type = type,
+                .size = htole64(size),
+        };
 
         f->header->tail_object_offset = htole64(p);
         f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1);
@@ -1156,7 +1195,7 @@ static int journal_file_link_field(
         if (o->object.type != OBJECT_FIELD)
                 return -EINVAL;
 
-        m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
+        m = le64toh(READ_NOW(f->header->field_hash_table_size)) / sizeof(HashItem);
         if (m <= 0)
                 return -EBADMSG;
 
@@ -1201,7 +1240,7 @@ static int journal_file_link_data(
         if (o->object.type != OBJECT_DATA)
                 return -EINVAL;
 
-        m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
+        m = le64toh(READ_NOW(f->header->data_hash_table_size)) / sizeof(HashItem);
         if (m <= 0)
                 return -EBADMSG;
 
@@ -1257,7 +1296,7 @@ int journal_file_find_field_object_with_hash(
 
         osize = offsetof(Object, field.payload) + size;
 
-        m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
+        m = le64toh(READ_NOW(f->header->field_hash_table_size)) / sizeof(HashItem);
         if (m <= 0)
                 return -EBADMSG;
 
@@ -1329,7 +1368,7 @@ int journal_file_find_data_object_with_hash(
 
         osize = offsetof(Object, data.payload) + size;
 
-        m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
+        m = le64toh(READ_NOW(f->header->data_hash_table_size)) / sizeof(HashItem);
         if (m <= 0)
                 return -EBADMSG;
 
@@ -1351,7 +1390,7 @@ int journal_file_find_data_object_with_hash(
                         uint64_t l;
                         size_t rsize = 0;
 
-                        l = le64toh(o->object.size);
+                        l = le64toh(READ_NOW(o->object.size));
                         if (l <= offsetof(Object, data.payload))
                                 return -EBADMSG;
 
@@ -1576,30 +1615,47 @@ static int journal_file_append_data(
 }
 
 uint64_t journal_file_entry_n_items(Object *o) {
+        uint64_t sz;
         assert(o);
 
         if (o->object.type != OBJECT_ENTRY)
                 return 0;
 
-        return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem);
+        sz = le64toh(READ_NOW(o->object.size));
+        if (sz < offsetof(Object, entry.items))
+                return 0;
+
+        return (sz - offsetof(Object, entry.items)) / sizeof(EntryItem);
 }
 
 uint64_t journal_file_entry_array_n_items(Object *o) {
+        uint64_t sz;
+
         assert(o);
 
         if (o->object.type != OBJECT_ENTRY_ARRAY)
                 return 0;
 
-        return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
+        sz = le64toh(READ_NOW(o->object.size));
+        if (sz < offsetof(Object, entry_array.items))
+                return 0;
+
+        return (sz - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
 }
 
 uint64_t journal_file_hash_table_n_items(Object *o) {
+        uint64_t sz;
+
         assert(o);
 
         if (!IN_SET(o->object.type, OBJECT_DATA_HASH_TABLE, OBJECT_FIELD_HASH_TABLE))
                 return 0;
 
-        return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem);
+        sz = le64toh(READ_NOW(o->object.size));
+        if (sz < offsetof(Object, hash_table.items))
+                return 0;
+
+        return (sz - offsetof(Object, hash_table.items)) / sizeof(HashItem);
 }
 
 static int link_entry_into_array(JournalFile *f,
@@ -1617,7 +1673,7 @@ static int link_entry_into_array(JournalFile *f,
         assert(p > 0);
 
         a = le64toh(*first);
-        i = hidx = le64toh(*idx);
+        i = hidx = le64toh(READ_NOW(*idx));
         while (a > 0) {
 
                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
@@ -1682,6 +1738,7 @@ static int link_entry_into_array_plus_one(JournalFile *f,
                                           le64_t *idx,
                                           uint64_t p) {
 
+        uint64_t hidx;
         int r;
 
         assert(f);
@@ -1690,32 +1747,33 @@ static int link_entry_into_array_plus_one(JournalFile *f,
         assert(idx);
         assert(p > 0);
 
-        if (*idx == 0)
+        hidx = le64toh(READ_NOW(*idx));
+        if (hidx == UINT64_MAX)
+                return -EBADMSG;
+        if (hidx == 0)
                 *extra = htole64(p);
         else {
                 le64_t i;
 
-                i = htole64(le64toh(*idx) - 1);
+                i = htole64(hidx - 1);
                 r = link_entry_into_array(f, first, &i, p);
                 if (r < 0)
                         return r;
         }
 
-        *idx = htole64(le64toh(*idx) + 1);
+        *idx = htole64(hidx + 1);
         return 0;
 }
 
 static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
         uint64_t p;
         int r;
+
         assert(f);
         assert(o);
         assert(offset > 0);
 
         p = le64toh(o->entry.items[i].object_offset);
-        if (p == 0)
-                return -EINVAL;
-
         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
         if (r < 0)
                 return r;
@@ -2435,6 +2493,7 @@ _pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle
 }
 
 static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
+        uint64_t sq;
         Object *o;
         int r;
 
@@ -2445,9 +2504,10 @@ static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
         if (r < 0)
                 return r;
 
-        if (le64toh(o->entry.seqnum) == needle)
+        sq = le64toh(READ_NOW(o->entry.seqnum));
+        if (sq == needle)
                 return TEST_FOUND;
-        else if (le64toh(o->entry.seqnum) < needle)
+        else if (sq < needle)
                 return TEST_LEFT;
         else
                 return TEST_RIGHT;
@@ -2473,6 +2533,7 @@ int journal_file_move_to_entry_by_seqnum(
 
 static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
         Object *o;
+        uint64_t rt;
         int r;
 
         assert(f);
@@ -2482,9 +2543,10 @@ static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
         if (r < 0)
                 return r;
 
-        if (le64toh(o->entry.realtime) == needle)
+        rt = le64toh(READ_NOW(o->entry.realtime));
+        if (rt == needle)
                 return TEST_FOUND;
-        else if (le64toh(o->entry.realtime) < needle)
+        else if (rt < needle)
                 return TEST_LEFT;
         else
                 return TEST_RIGHT;
@@ -2510,6 +2572,7 @@ int journal_file_move_to_entry_by_realtime(
 
 static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
         Object *o;
+        uint64_t m;
         int r;
 
         assert(f);
@@ -2519,9 +2582,10 @@ static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
         if (r < 0)
                 return r;
 
-        if (le64toh(o->entry.monotonic) == needle)
+        m = le64toh(READ_NOW(o->entry.monotonic));
+        if (m == needle)
                 return TEST_FOUND;
-        else if (le64toh(o->entry.monotonic) < needle)
+        else if (m < needle)
                 return TEST_LEFT;
         else
                 return TEST_RIGHT;
@@ -2679,7 +2743,7 @@ int journal_file_next_entry(
         assert(f);
         assert(f->header);
 
-        n = le64toh(f->header->n_entries);
+        n = le64toh(READ_NOW(f->header->n_entries));
         if (n <= 0)
                 return 0;
 
@@ -2752,7 +2816,7 @@ int journal_file_next_entry_for_data(
         if (r < 0)
                 return r;
 
-        n = le64toh(d->data.n_entries);
+        n = le64toh(READ_NOW(d->data.n_entries));
         if (n <= 0)
                 return n;
 
@@ -2981,7 +3045,7 @@ void journal_file_dump(JournalFile *f) {
 
         journal_file_print_header(f);
 
-        p = le64toh(f->header->header_size);
+        p = le64toh(READ_NOW(f->header->header_size));
         while (p != 0) {
                 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
                 if (r < 0)
@@ -3038,7 +3102,7 @@ void journal_file_dump(JournalFile *f) {
                 if (p == le64toh(f->header->tail_object_offset))
                         p = 0;
                 else
-                        p = p + ALIGN64(le64toh(o->object.size));
+                        p += ALIGN64(le64toh(o->object.size));
         }
 
         return;
@@ -3659,7 +3723,11 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
                 if (le_hash != o->data.hash)
                         return -EBADMSG;
 
-                l = le64toh(o->object.size) - offsetof(Object, data.payload);
+                l = le64toh(READ_NOW(o->object.size));
+                if (l < offsetof(Object, data.payload))
+                        return -EBADMSG;
+
+                l -= offsetof(Object, data.payload);
                 t = (size_t) l;
 
                 /* We hit the limit on 32bit machines */
index 1454df602b8594ad8466d8f2121c59c74328d3b9..e1fb01a6e38ab823c77b740292566e3264c17272 100644 (file)
@@ -41,10 +41,10 @@ struct Match {
 struct Location {
         LocationType type;
 
-        bool seqnum_set;
-        bool realtime_set;
-        bool monotonic_set;
-        bool xor_hash_set;
+        bool seqnum_set:1;
+        bool realtime_set:1;
+        bool monotonic_set:1;
+        bool xor_hash_set:1;
 
         uint64_t seqnum;
         sd_id128_t seqnum_id;
index b07bb592add44df9e485c9c74a7df374fdc99cbd..bbb1c206c5d15f321074a41ae3ae27062dd06801 100644 (file)
@@ -94,7 +94,7 @@ _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
         if (len >= (int)LONG_LINE_MAX - 8)
                 return -ENOBUFS;
 
-        /* Allocate large buffer to accomodate big message */
+        /* Allocate large buffer to accommodate big message */
         if (len >= LINE_MAX) {
                 int rlen;
                 buffer = alloca(len + 9);
@@ -472,7 +472,7 @@ _public_ int sd_journal_printv_with_location(int priority, const char *file, con
         if (len >= (int)LONG_LINE_MAX - 8)
                 return -ENOBUFS;
 
-        /* Allocate large buffer to accomodate big message */
+        /* Allocate large buffer to accommodate big message */
         if (len >= LINE_MAX) {
                 int rlen;
                 buffer = alloca(len + 9);
index 01d75b0e24d0f5687d7eac87b8108777f6f98c45..a082995498f993e07b269a0fb26291ed22e9e5b9 100644 (file)
@@ -705,7 +705,7 @@ static int parse_argv(int argc, char *argv[]) {
                                  * STDIN. To avoid confusion we hence don't document this feature. */
                                 arg_file_stdin = true;
                         else {
-                                r = glob_extend(&arg_file, optarg);
+                                r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to add paths: %m");
                         }
index 5c13134b3086906a3cf99f5c804201c1d09e3a67..9b6c425285e3825ff5c20f1a4a7be458ff375168 100644 (file)
@@ -115,28 +115,24 @@ static void detach_location(sd_journal *j) {
                 journal_file_reset_location(f);
 }
 
-static void reset_location(sd_journal *j) {
-        assert(j);
-
-        detach_location(j);
-        zero(j->current_location);
-}
-
 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
         assert(l);
         assert(IN_SET(type, LOCATION_DISCRETE, LOCATION_SEEK));
         assert(f);
-        assert(o->object.type == OBJECT_ENTRY);
-
-        l->type = type;
-        l->seqnum = le64toh(o->entry.seqnum);
-        l->seqnum_id = f->header->seqnum_id;
-        l->realtime = le64toh(o->entry.realtime);
-        l->monotonic = le64toh(o->entry.monotonic);
-        l->boot_id = o->entry.boot_id;
-        l->xor_hash = le64toh(o->entry.xor_hash);
 
-        l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
+        *l = (Location) {
+                .type = type,
+                .seqnum = le64toh(o->entry.seqnum),
+                .seqnum_id = f->header->seqnum_id,
+                .realtime = le64toh(o->entry.realtime),
+                .monotonic = le64toh(o->entry.monotonic),
+                .boot_id = o->entry.boot_id,
+                .xor_hash = le64toh(o->entry.xor_hash),
+                .seqnum_set = true,
+                .realtime_set = true,
+                .monotonic_set = true,
+                .xor_hash_set = true,
+        };
 }
 
 static void set_location(sd_journal *j, JournalFile *f, Object *o) {
@@ -1014,9 +1010,10 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
             !realtime_set)
                 return -EINVAL;
 
-        reset_location(j);
-
-        j->current_location.type = LOCATION_SEEK;
+        detach_location(j);
+        j->current_location = (Location) {
+                .type = LOCATION_SEEK,
+        };
 
         if (realtime_set) {
                 j->current_location.realtime = (uint64_t) realtime;
@@ -1129,11 +1126,14 @@ _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, u
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
 
-        reset_location(j);
-        j->current_location.type = LOCATION_SEEK;
-        j->current_location.boot_id = boot_id;
-        j->current_location.monotonic = usec;
-        j->current_location.monotonic_set = true;
+        detach_location(j);
+
+        j->current_location = (Location) {
+                .type = LOCATION_SEEK,
+                .boot_id = boot_id,
+                .monotonic = usec,
+                .monotonic_set = true,
+        };
 
         return 0;
 }
@@ -1142,10 +1142,13 @@ _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
 
-        reset_location(j);
-        j->current_location.type = LOCATION_SEEK;
-        j->current_location.realtime = usec;
-        j->current_location.realtime_set = true;
+        detach_location(j);
+
+        j->current_location = (Location) {
+                .type = LOCATION_SEEK,
+                .realtime = usec,
+                .realtime_set = true,
+        };
 
         return 0;
 }
@@ -1154,8 +1157,11 @@ _public_ int sd_journal_seek_head(sd_journal *j) {
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
 
-        reset_location(j);
-        j->current_location.type = LOCATION_HEAD;
+        detach_location(j);
+
+        j->current_location = (Location) {
+                .type = LOCATION_HEAD,
+        };
 
         return 0;
 }
@@ -1164,8 +1170,11 @@ _public_ int sd_journal_seek_tail(sd_journal *j) {
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
 
-        reset_location(j);
-        j->current_location.type = LOCATION_TAIL;
+        detach_location(j);
+
+        j->current_location = (Location) {
+                .type = LOCATION_TAIL,
+        };
 
         return 0;
 }
@@ -2357,7 +2366,10 @@ static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **da
         uint64_t l;
         int compression;
 
-        l = le64toh(o->object.size) - offsetof(Object, data.payload);
+        l = le64toh(READ_NOW(o->object.size));
+        if (l < offsetof(Object, data.payload))
+                return -EBADMSG;
+        l -= offsetof(Object, data.payload);
         t = (size_t) l;
 
         /* We can't read objects larger than 4G on a 32bit machine */
index c01c1cf6d6f50b5177ccfe65b175896075e8c020..d0610a32e23d5defcca83889ea716bfb2a4e491e 100644 (file)
@@ -15,7 +15,6 @@
 #include "udev-util.h"
 #include "virt.h"
 
-#define SYSTEMD_PEN    43793
 #define HASH_KEY       SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
 #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
 #define USEC_2000       ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
index b3115125d9ab1ae59043388e9355ab32763afc54..76abd6583e3a3905ed2cd3349a6040f56e96d5b2 100644 (file)
@@ -8,6 +8,8 @@
 #include "time-util.h"
 #include "unaligned.h"
 
+#define SYSTEMD_PEN    43793
+
 typedef enum DUIDType {
         DUID_TYPE_LLT       = 1,
         DUID_TYPE_EN        = 2,
index db80585a22c310c12da72a428df5bd641873ee79..ced85fd3afe7a0a26b3baeb68a81654cc4774eb3 100644 (file)
@@ -97,6 +97,8 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
 int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix);
 int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class);
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
 int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
index 3b7c89edd7b2eeab719d78cdedf49bb79a427833..0ec36507cd46cae97004c42c83e56bb86613f6e9 100644 (file)
@@ -9,6 +9,7 @@
 #include "sd-dhcp6-client.h"
 
 #include "alloc-util.h"
+#include "dhcp-identifier.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
 #include "dhcp6-protocol.h"
@@ -167,6 +168,75 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
         return r;
 }
 
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) {
+        _cleanup_free_ uint8_t *p = NULL;
+        size_t total = 0, offset = 0;
+        char **s;
+
+        assert_return(buf && *buf && buflen && user_class, -EINVAL);
+
+        STRV_FOREACH(s, user_class) {
+                size_t len = strlen(*s);
+                uint8_t *q;
+
+                if (len > 0xffff)
+                        return -ENAMETOOLONG;
+                q = realloc(p, total + len + 2);
+                if (!q)
+                        return -ENOMEM;
+
+                p = q;
+
+                unaligned_write_be16(&p[offset], len);
+                memcpy(&p[offset + 2], *s, len);
+
+                offset += 2 + len;
+                total += 2 + len;
+        }
+
+        return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p);
+}
+
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendor_class) {
+        _cleanup_free_ uint8_t *p = NULL;
+        uint32_t enterprise_identifier;
+        size_t total, offset;
+        char **s;
+
+        assert(buf);
+        assert(*buf);
+        assert(buflen);
+        assert(vendor_class);
+
+        enterprise_identifier = htobe32(SYSTEMD_PEN);
+
+        p = memdup(&enterprise_identifier, sizeof(enterprise_identifier));
+        if (!p)
+                return -ENOMEM;
+
+        total = sizeof(enterprise_identifier);
+        offset = total;
+
+        STRV_FOREACH(s, vendor_class) {
+                size_t len = strlen(*s);
+                uint8_t *q;
+
+                q = realloc(p, total + len + 2);
+                if (!q)
+                        return -ENOMEM;
+
+                p = q;
+
+                unaligned_write_be16(&p[offset], len);
+                memcpy(&p[offset + 2], *s, len);
+
+                offset += 2 + len;
+                total += 2 + len;
+        }
+
+        return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p);
+}
+
 int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) {
         DHCP6Option *option = (DHCP6Option *)buf;
         size_t i = sizeof(*option) + sizeof(pd->ia_pd);
index ee0e63e113c161c488a938347aa47ee90f00daf5..ce83a86aa9a7e916d7bb0c74ee721376748b028b 100644 (file)
@@ -87,6 +87,7 @@ struct sd_dhcp_client {
         char *mudurl;
         char **user_class;
         uint32_t mtu;
+        uint32_t fallback_lease_lifetime;
         uint32_t xid;
         usec_t start_time;
         uint64_t attempt;
@@ -612,6 +613,15 @@ int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
         return 0;
 }
 
+int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
+        assert_return(client, -EINVAL);
+        assert_return(fallback_lease_lifetime > 0, -EINVAL);
+
+        client->fallback_lease_lifetime = fallback_lease_lifetime;
+
+        return 0;
+}
+
 static int client_notify(sd_dhcp_client *client, int event) {
         assert(client);
 
@@ -850,36 +860,12 @@ static int dhcp_client_send_raw(
                                             packet, len);
 }
 
-static int client_send_discover(sd_dhcp_client *client) {
-        _cleanup_free_ DHCPPacket *discover = NULL;
-        size_t optoffset, optlen;
+static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) {
         sd_dhcp_option *j;
         Iterator i;
         int r;
 
         assert(client);
-        assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
-
-        r = client_message_init(client, &discover, DHCP_DISCOVER,
-                                &optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        /* the client may suggest values for the network address
-           and lease time in the DHCPDISCOVER message. The client may include
-           the ’requested IP address’ option to suggest that a particular IP
-           address be assigned, and may include the ’IP address lease time’
-           option to suggest the lease time it would like.
-         */
-        /* RFC7844 section 3:
-           SHOULD NOT contain any other option. */
-        if (!client->anonymize && client->last_addr != INADDR_ANY) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
-                                       4, &client->last_addr);
-                if (r < 0)
-                        return r;
-        }
 
         if (client->hostname) {
                 /* According to RFC 4702 "clients that send the Client FQDN option in
@@ -890,18 +876,18 @@ static int client_send_discover(sd_dhcp_client *client) {
                         /* it is unclear from RFC 2131 if client should send hostname in
                            DHCPDISCOVER but dhclient does and so we do as well
                         */
-                        r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                        r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                                SD_DHCP_OPTION_HOST_NAME,
                                                strlen(client->hostname), client->hostname);
                 } else
-                        r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
+                        r = client_append_fqdn_option(&packet->dhcp, optlen, optoffset,
                                                       client->hostname);
                 if (r < 0)
                         return r;
         }
 
         if (client->vendor_class_identifier) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
                                        strlen(client->vendor_class_identifier),
                                        client->vendor_class_identifier);
@@ -910,7 +896,7 @@ static int client_send_discover(sd_dhcp_client *client) {
         }
 
         if (client->mudurl) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        SD_DHCP_OPTION_MUD_URL,
                                        strlen(client->mudurl),
                                        client->mudurl);
@@ -919,7 +905,7 @@ static int client_send_discover(sd_dhcp_client *client) {
         }
 
         if (client->user_class) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        SD_DHCP_OPTION_USER_CLASS,
                                        strv_length(client->user_class),
                                        client->user_class);
@@ -928,7 +914,7 @@ static int client_send_discover(sd_dhcp_client *client) {
         }
 
         ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        j->option, j->length, j->data);
                 if (r < 0)
                         return r;
@@ -936,13 +922,50 @@ static int client_send_discover(sd_dhcp_client *client) {
 
         if (!ordered_hashmap_isempty(client->vendor_options)) {
                 r = dhcp_option_append(
-                                &discover->dhcp, optlen, &optoffset, 0,
+                                &packet->dhcp, optlen, optoffset, 0,
                                 SD_DHCP_OPTION_VENDOR_SPECIFIC,
                                 ordered_hashmap_size(client->vendor_options), client->vendor_options);
                 if (r < 0)
                         return r;
         }
 
+
+        return 0;
+}
+
+static int client_send_discover(sd_dhcp_client *client) {
+        _cleanup_free_ DHCPPacket *discover = NULL;
+        size_t optoffset, optlen;
+        int r;
+
+        assert(client);
+        assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
+
+        r = client_message_init(client, &discover, DHCP_DISCOVER,
+                                &optlen, &optoffset);
+        if (r < 0)
+                return r;
+
+        /* the client may suggest values for the network address
+           and lease time in the DHCPDISCOVER message. The client may include
+           the ’requested IP address’ option to suggest that a particular IP
+           address be assigned, and may include the ’IP address lease time’
+           option to suggest the lease time it would like.
+         */
+        /* RFC7844 section 3:
+           SHOULD NOT contain any other option. */
+        if (!client->anonymize && client->last_addr != INADDR_ANY) {
+                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                       4, &client->last_addr);
+                if (r < 0)
+                        return r;
+        }
+
+        r = client_append_common_discover_request_options(client, discover, &optoffset, optlen);
+        if (r < 0)
+                return r;
+
         r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
         if (r < 0)
@@ -1034,36 +1057,9 @@ static int client_send_request(sd_dhcp_client *client) {
                 return -EINVAL;
         }
 
-        if (client->hostname) {
-                if (dns_name_is_single_label(client->hostname))
-                        r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                               SD_DHCP_OPTION_HOST_NAME,
-                                               strlen(client->hostname), client->hostname);
-                else
-                        r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
-                                                      client->hostname);
-                if (r < 0)
-                        return r;
-        }
-
-        if (client->vendor_class_identifier) {
-                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
-                                       strlen(client->vendor_class_identifier),
-                                       client->vendor_class_identifier);
-                if (r < 0)
-                        return r;
-        }
-
-        if (client->mudurl) {
-                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_MUD_URL,
-                                       strlen(client->mudurl),
-                                       client->mudurl);
-                if (r < 0)
-                        return r;
-        }
-
+        r = client_append_common_discover_request_options(client, request, &optoffset, optlen);
+        if (r < 0)
+                return r;
 
         r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
@@ -1419,6 +1415,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_
         lease->next_server = offer->siaddr;
         lease->address = offer->yiaddr;
 
+        if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
+                lease->lifetime = client->fallback_lease_lifetime;
+
         if (lease->address == 0 ||
             lease->server_address == 0 ||
             lease->lifetime == 0) {
index d04cb869a30e0a3919b0889cd7f968e53945ca98..13d8f652888a1ce9c116bf4231eae31f31b6fa8b 100644 (file)
@@ -67,6 +67,8 @@ struct sd_dhcp6_client {
         size_t req_opts_len;
         char *fqdn;
         char *mudurl;
+        char **user_class;
+        char **vendor_class;
         sd_event_source *receive_message;
         usec_t retransmit_time;
         uint8_t retransmit_count;
@@ -253,7 +255,8 @@ static int dhcp6_client_set_duid_internal(
                         r = dhcp_validate_duid_len(duid_type, duid_len, false);
                         if (r < 0)
                                 return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
-                        log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
+
+                        log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type);
                 }
 
                 client->duid.type = htobe16(duid_type);
@@ -357,7 +360,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
         assert_return(client, -EINVAL);
         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
 
-        if (option <= 0 || option >= 255)
+        if (option <= 0 || option >= UINT8_MAX)
                 return -EINVAL;
 
         for (t = 0; t < client->req_opts_len; t++)
@@ -378,12 +381,55 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud
         assert_return(client, -EINVAL);
         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
         assert_return(mudurl, -EINVAL);
-        assert_return(strlen(mudurl) <= 255, -EINVAL);
+        assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
         assert_return(http_url_is_valid(mudurl), -EINVAL);
 
         return free_and_strdup(&client->mudurl, mudurl);
 }
 
+int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) {
+        _cleanup_strv_free_ char **s = NULL;
+        char **p;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+
+        assert_return(user_class, -EINVAL);
+
+        STRV_FOREACH(p, user_class)
+                if (strlen(*p) > UINT16_MAX)
+                        return -ENAMETOOLONG;
+
+        s = strv_copy(user_class);
+        if (!s)
+                return -ENOMEM;
+
+        client->user_class = TAKE_PTR(s);
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) {
+        _cleanup_strv_free_ char **s = NULL;
+        char **p;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+        assert_return(vendor_class, -EINVAL);
+
+        STRV_FOREACH(p, vendor_class)
+                if (strlen(*p) > UINT8_MAX)
+                        return -ENAMETOOLONG;
+
+        s = strv_copy(vendor_class);
+        if (!s)
+                return -ENOMEM;
+
+        client->vendor_class = TAKE_PTR(s);
+
+        return 0;
+}
+
 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
         assert_return(client, -EINVAL);
         assert_return(delegation, -EINVAL);
@@ -564,6 +610,18 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (client->user_class) {
+                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->vendor_class) {
+                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
                         if (r < 0)
@@ -610,6 +668,18 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (client->user_class) {
+                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->vendor_class) {
+                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
@@ -644,6 +714,18 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (client->user_class) {
+                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->vendor_class) {
+                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
@@ -1601,7 +1683,11 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
         free(client->req_opts);
         free(client->fqdn);
         free(client->mudurl);
+
         ordered_hashmap_free(client->extra_options);
+        strv_free(client->user_class);
+        strv_free(client->vendor_class);
+
         return mfree(client);
 }
 
index 174f1228af29fdc35cdbd2c14322de7543fb105f..28f98cebce97472e9455d1844a317414a68bdfb8 100644 (file)
@@ -134,6 +134,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_LOCKED,              ENOEXEC),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOO_MANY_OPERATIONS,          ENOBUFS),
         SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT,     ETOOMANYREFS),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE,       EKEYREVOKED),
 
         SD_BUS_ERROR_MAP_END
 };
index dc58f88bbd5cc667712e295dea9d2bf170e4f87c..68ecbd65ddddb5425a1e05b7ccda27d741ac6363 100644 (file)
 #define BUS_ERROR_NO_DISK_SPACE "org.freedesktop.home1.NoDiskSpace"
 #define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations"
 #define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit"
+#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate"
 
 BUS_ERROR_MAP_ELF_USE(bus_common_errors);
index 0af9cedf5a14183be46ece70794305f33cf0c34e..97b732ae425274585e4b5bb0cbd05698b58a987b 100644 (file)
@@ -451,7 +451,7 @@ int bus_message_from_header(
         if (!IN_SET(h->version, 1, 2))
                 return -EBADMSG;
 
-        if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID)
+        if (h->type <= _SD_BUS_MESSAGE_TYPE_INVALID || h->type >= _SD_BUS_MESSAGE_TYPE_MAX)
                 return -EBADMSG;
 
         if (!IN_SET(h->endian, BUS_LITTLE_ENDIAN, BUS_BIG_ENDIAN))
@@ -589,7 +589,7 @@ _public_ int sd_bus_message_new(
         assert_return(bus = bus_resolve(bus), -ENOPKG);
         assert_return(bus->state != BUS_UNSET, -ENOTCONN);
         assert_return(m, -EINVAL);
-        assert_return(type < _SD_BUS_MESSAGE_TYPE_MAX, -EINVAL);
+        assert_return(type > _SD_BUS_MESSAGE_TYPE_INVALID && type < _SD_BUS_MESSAGE_TYPE_MAX, -EINVAL);
 
         sd_bus_message *t = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header));
         if (!t)
@@ -5202,29 +5202,34 @@ int bus_message_parse_fields(sd_bus_message *m) {
                  * table */
                 m->user_body_size = m->body_size - ((char*) m->footer + m->footer_accessible - p);
 
-                /* Pull out the offset table for the fields array */
-                sz = bus_gvariant_determine_word_size(m->fields_size, 0);
-                if (sz > 0) {
-                        size_t framing;
-                        void *q;
+                /* Pull out the offset table for the fields array, if any */
+                if (m->fields_size > 0) {
+                        sz = bus_gvariant_determine_word_size(m->fields_size, 0);
+                        if (sz > 0) {
+                                size_t framing;
+                                void *q;
 
-                        ri = m->fields_size - sz;
-                        r = message_peek_fields(m, &ri, 1, sz, &q);
-                        if (r < 0)
-                                return r;
+                                if (m->fields_size < sz)
+                                        return -EBADMSG;
 
-                        framing = bus_gvariant_read_word_le(q, sz);
-                        if (framing >= m->fields_size - sz)
-                                return -EBADMSG;
-                        if ((m->fields_size - framing) % sz != 0)
-                                return -EBADMSG;
+                                ri = m->fields_size - sz;
+                                r = message_peek_fields(m, &ri, 1, sz, &q);
+                                if (r < 0)
+                                        return r;
 
-                        ri = framing;
-                        r = message_peek_fields(m, &ri, 1, m->fields_size - framing, &offsets);
-                        if (r < 0)
-                                return r;
+                                framing = bus_gvariant_read_word_le(q, sz);
+                                if (framing >= m->fields_size - sz)
+                                        return -EBADMSG;
+                                if ((m->fields_size - framing) % sz != 0)
+                                        return -EBADMSG;
 
-                        n_offsets = (m->fields_size - framing) / sz;
+                                ri = framing;
+                                r = message_peek_fields(m, &ri, 1, m->fields_size - framing, &offsets);
+                                if (r < 0)
+                                        return r;
+
+                                n_offsets = (m->fields_size - framing) / sz;
+                        }
                 }
         } else
                 m->user_body_size = m->body_size;
@@ -5492,6 +5497,9 @@ int bus_message_parse_fields(sd_bus_message *m) {
                 if (m->reply_cookie == 0 || !m->error.name)
                         return -EBADMSG;
                 break;
+
+        default:
+                assert_not_reached("Bad message type");
         }
 
         /* Refuse non-local messages that claim they are local */
index 2a9c04a84d5b50c8bf6bee549b48416c9835466e..9f8397cd37c95bbe801c0a77a2048350af77cab7 100644 (file)
@@ -624,7 +624,7 @@ static int get_search(uint64_t type, char ***list) {
         case SD_PATH_SYSTEMD_SYSTEM_GENERATOR_PATH:
         case SD_PATH_SYSTEMD_USER_GENERATOR_PATH: {
                 char **t;
-                const UnitFileScope scope = type == SD_PATH_SYSTEMD_SYSTEM_UNIT_PATH ?
+                const UnitFileScope scope = type == SD_PATH_SYSTEMD_SYSTEM_GENERATOR_PATH ?
                                                     UNIT_FILE_SYSTEM : UNIT_FILE_USER;
 
                 t = generator_binary_paths(scope);
index a9006d746a0df4615d7ac232ef383852abab844c..c160f546bca7cc95e96eb5d0505d0781c0fdac3e 100644 (file)
@@ -55,6 +55,7 @@ void manager_reset_config(Manager *m) {
         m->idle_action = HANDLE_IGNORE;
 
         m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
+        m->runtime_dir_inodes = DIV_ROUND_UP(m->runtime_dir_size, 4096); /* 4k per inode */
         m->sessions_max = 8192;
         m->inhibitors_max = 8192;
 
index 451fe2875487a04b19a6ac5f7116cfe57aae0b89..a88605232c40428c8441604e681ab38719184059 100644 (file)
@@ -3359,6 +3359,7 @@ static const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("OnExternalPower", "b", property_get_on_external_power, 0, 0),
         SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RuntimeDirectorySize", "t", NULL, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RuntimeDirectoryInodesMax", "t", NULL, offsetof(Manager, runtime_dir_inodes), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_hashmap_size, offsetof(Manager, inhibitors), 0),
         SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST),
index 983795da40d598a93f8ce33aac902c77a56e4bb6..73d96ff4367c6a7c4e739c8af94e20c68e37bed6 100644 (file)
@@ -38,6 +38,7 @@ Login.HoldoffTimeoutSec,            config_parse_sec,                   0, offse
 Login.IdleAction,                   config_parse_handle_action,         0, offsetof(Manager, idle_action)
 Login.IdleActionSec,                config_parse_sec,                   0, offsetof(Manager, idle_action_usec)
 Login.RuntimeDirectorySize,         config_parse_tmpfs_size,            0, offsetof(Manager, runtime_dir_size)
+Login.RuntimeDirectoryInodesMax,    config_parse_uint64,                0, offsetof(Manager, runtime_dir_inodes)
 Login.RemoveIPC,                    config_parse_bool,                  0, offsetof(Manager, remove_ipc)
 Login.InhibitorsMax,                config_parse_uint64,                0, offsetof(Manager, inhibitors_max)
 Login.SessionsMax,                  config_parse_uint64,                0, offsetof(Manager, sessions_max)
index 1029e29bc73565f064fc521b0caf63051fbff354..ed1084b06e295578a628fda374716cbc73b1f80b 100644 (file)
@@ -32,6 +32,7 @@
 #IdleAction=ignore
 #IdleActionSec=30min
 #RuntimeDirectorySize=10%
+#RuntimeDirectoryInodes=400k
 #RemoveIPC=yes
 #InhibitorsMax=8192
 #SessionsMax=8192
index d3f5b28078e83d271e3db433df495eac56b5c06a..e1d57277fa1bd20adea1fc90a3b79d292119d924 100644 (file)
@@ -120,6 +120,7 @@ struct Manager {
         sd_event_source *lid_switch_ignore_event_source;
 
         uint64_t runtime_dir_size;
+        uint64_t runtime_dir_inodes;
         uint64_t sessions_max;
         uint64_t inhibitors_max;
 };
index b6f5562707c738f56d425f987ea9fa6d918c2faf..64771153cd960d2ba471805cf4ce4a5267bd7f01 100644 (file)
@@ -99,6 +99,7 @@ static int acquire_user_record(
 
         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
         const char *username = NULL, *json = NULL;
+        _cleanup_free_ char *field = NULL;
         int r;
 
         assert(handle);
@@ -114,39 +115,18 @@ static int acquire_user_record(
                 return PAM_SERVICE_ERR;
         }
 
-        /* If pam_systemd_homed (or some other module) already acqired the user record we can reuse it
+        /* If pam_systemd_homed (or some other module) already acquired the user record we can reuse it
          * here. */
-        r = pam_get_data(handle, "systemd-user-record", (const void**) &json);
-        if (r != PAM_SUCCESS || !json) {
-                _cleanup_free_ char *formatted = NULL;
-
-                if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
-                        pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
-                        return r;
-                }
-
-                /* Request the record ourselves */
-                r = userdb_by_name(username, 0, &ur);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to get user record: %s", strerror_safe(r));
-                        return PAM_USER_UNKNOWN;
-                }
-
-                r = json_variant_format(ur->json, 0, &formatted);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to format user JSON: %s", strerror_safe(r));
-                        return PAM_SERVICE_ERR;
-                }
-
-                /* And cache it for everyone else */
-                r = pam_set_data(handle, "systemd-user-record", formatted, pam_cleanup_free);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r));
-                        return r;
-                }
+        field = strjoin("systemd-user-record-", username);
+        if (!field)
+                return pam_log_oom(handle);
 
-                TAKE_PTR(formatted);
-        } else {
+        r = pam_get_data(handle, field, (const void**) &json);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
+                return r;
+        }
+        if (r == PAM_SUCCESS && json) {
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
 
                 /* Parse cached record */
@@ -171,6 +151,31 @@ static int acquire_user_record(
                         pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name.");
                         return PAM_SERVICE_ERR;
                 }
+        } else {
+                _cleanup_free_ char *formatted = NULL;
+
+                /* Request the record ourselves */
+                r = userdb_by_name(username, 0, &ur);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to get user record: %s", strerror_safe(r));
+                        return PAM_USER_UNKNOWN;
+                }
+
+                r = json_variant_format(ur->json, 0, &formatted);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to format user JSON: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+
+                /* And cache it for everyone else */
+                r = pam_set_data(handle, field, formatted, pam_cleanup_free);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s",
+                                   field, pam_strerror(handle, r));
+                        return r;
+                }
+
+                TAKE_PTR(formatted);
         }
 
         if (!uid_is_valid(ur->uid)) {
@@ -642,10 +647,6 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         assert(handle);
 
-        /* Make this a NOP on non-logind systems */
-        if (!logind_running())
-                return PAM_SUCCESS;
-
         if (parse_argv(handle,
                        argc, argv,
                        &class_pam,
@@ -661,6 +662,10 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         if (r != PAM_SUCCESS)
                 return r;
 
+        /* Make most of this a NOP on non-logind systems */
+        if (!logind_running())
+                goto success;
+
         /* Make sure we don't enter a loop by talking to
          * systemd-logind when it is actually waiting for the
          * background to finish start-up. If the service is
@@ -684,11 +689,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 if (r != PAM_SUCCESS)
                         return r;
 
-                r = apply_user_record_settings(handle, ur, debug);
-                if (r != PAM_SUCCESS)
-                        return r;
-
-                return PAM_SUCCESS;
+                goto success;
         }
 
         /* Otherwise, we ask logind to create a session for us */
@@ -842,7 +843,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
                         if (debug)
                                 pam_syslog(handle, LOG_DEBUG, "Not creating session: %s", bus_error_message(&error, r));
-                        return PAM_SUCCESS;
+
+                        /* We are already in a session, don't do anything */
+                        goto success;
                 } else {
                         pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
                         return PAM_SESSION_ERR;
@@ -939,6 +942,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 }
         }
 
+success:
         r = apply_user_record_settings(handle, ur, debug);
         if (r != PAM_SUCCESS)
                 return r;
index 1f98898b695e778a3bfe206741aa6a5d04a83e2d..4055c910c2d468fe840b35f1cfb5ec46eaa067c0 100644 (file)
@@ -22,7 +22,7 @@
 #include "strv.h"
 #include "user-util.h"
 
-static int acquire_runtime_dir_size(uint64_t *ret) {
+static int acquire_runtime_dir_properties(uint64_t *size, uint64_t *inodes) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
@@ -31,10 +31,14 @@ static int acquire_runtime_dir_size(uint64_t *ret) {
         if (r < 0)
                 return log_error_errno(r, "Failed to connect to system bus: %m");
 
-        r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', ret);
+        r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', size);
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire runtime directory size: %s", bus_error_message(&error, r));
 
+        r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectoryInodesMax", &error, 't', inodes);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire number of inodes for runtime directory: %s", bus_error_message(&error, r));
+
         return 0;
 }
 
@@ -42,7 +46,8 @@ static int user_mkdir_runtime_path(
                 const char *runtime_path,
                 uid_t uid,
                 gid_t gid,
-                uint64_t runtime_dir_size) {
+                uint64_t runtime_dir_size,
+                uint64_t runtime_dir_inodes) {
 
         int r;
 
@@ -58,14 +63,15 @@ static int user_mkdir_runtime_path(
         if (path_is_mount_point(runtime_path, NULL, 0) >= 0)
                 log_debug("%s is already a mount point", runtime_path);
         else {
-                char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*")
+                char options[sizeof("mode=0700,uid=,gid=,size=,nr_inodes=,smackfsroot=*")
                              + DECIMAL_STR_MAX(uid_t)
                              + DECIMAL_STR_MAX(gid_t)
+                             + DECIMAL_STR_MAX(uint64_t)
                              + DECIMAL_STR_MAX(uint64_t)];
 
                 xsprintf(options,
-                         "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 "%s",
-                         uid, gid, runtime_dir_size,
+                         "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 ",nr_inodes=%" PRIu64 "%s",
+                         uid, gid, runtime_dir_size, runtime_dir_inodes,
                          mac_smack_use() ? ",smackfsroot=*" : "");
 
                 (void) mkdir_label(runtime_path, 0700);
@@ -127,7 +133,7 @@ static int user_remove_runtime_path(const char *runtime_path) {
 
 static int do_mount(const char *user) {
         char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
-        uint64_t runtime_dir_size;
+        uint64_t runtime_dir_size, runtime_dir_inodes;
         uid_t uid;
         gid_t gid;
         int r;
@@ -140,14 +146,14 @@ static int do_mount(const char *user) {
                                                     : "Failed to look up user \"%s\": %m",
                                        user);
 
-        r = acquire_runtime_dir_size(&runtime_dir_size);
+        r = acquire_runtime_dir_properties(&runtime_dir_size, &runtime_dir_inodes);
         if (r < 0)
                 return r;
 
         xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
 
         log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid);
-        return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size);
+        return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size, runtime_dir_inodes);
 }
 
 static int do_umount(const char *user) {
index 1d87cfa865a13f853a35e491e4beea3ad87e7ea1..bd14f625deaa0a95842f75abd203c2a32dcd1d81 100644 (file)
@@ -4,25 +4,11 @@
 
 #include "conf-parser.h"
 #include "ipvlan.h"
+#include "ipvlan-util.h"
 #include "networkd-link.h"
-#include "string-table.h"
+#include "string-util.h"
 
-static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = {
-        [NETDEV_IPVLAN_MODE_L2] = "L2",
-        [NETDEV_IPVLAN_MODE_L3] = "L3",
-        [NETDEV_IPVLAN_MODE_L3S] = "L3S",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(ipvlan_mode, IPVlanMode);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_mode, ipvlan_mode, IPVlanMode, "Failed to parse ipvlan mode");
-
-static const char* const ipvlan_flags_table[_NETDEV_IPVLAN_FLAGS_MAX] = {
-        [NETDEV_IPVLAN_FLAGS_BRIGDE] = "bridge",
-        [NETDEV_IPVLAN_FLAGS_PRIVATE] = "private",
-        [NETDEV_IPVLAN_FLAGS_VEPA] = "vepa",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(ipvlan_flags, IPVlanFlags);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_flags, ipvlan_flags, IPVlanFlags, "Failed to parse ipvlan flags");
 
 static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
index 171407b7c4bfb17c3cd526c3ec2aed7bde5e8e26..8e658184ffce2c7fc3e7a06fadac38de32032687 100644 (file)
@@ -4,24 +4,9 @@
 #include <netinet/in.h>
 #include <linux/if_link.h>
 
+#include "ipvlan-util.h"
 #include "netdev.h"
 
-typedef enum IPVlanMode {
-        NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2,
-        NETDEV_IPVLAN_MODE_L3 = IPVLAN_MODE_L3,
-        NETDEV_IPVLAN_MODE_L3S = IPVLAN_MODE_L3S,
-        _NETDEV_IPVLAN_MODE_MAX,
-        _NETDEV_IPVLAN_MODE_INVALID = -1
-} IPVlanMode;
-
-typedef enum IPVlanFlags {
-        NETDEV_IPVLAN_FLAGS_BRIGDE,
-        NETDEV_IPVLAN_FLAGS_PRIVATE = IPVLAN_F_PRIVATE,
-        NETDEV_IPVLAN_FLAGS_VEPA = IPVLAN_F_VEPA,
-        _NETDEV_IPVLAN_FLAGS_MAX,
-        _NETDEV_IPVLAN_FLAGS_INVALID = -1
-} IPVlanFlags;
-
 typedef struct IPVlan {
         NetDev meta;
 
@@ -34,12 +19,6 @@ DEFINE_NETDEV_CAST(IPVTAP, IPVlan);
 extern const NetDevVTable ipvlan_vtable;
 extern const NetDevVTable ipvtap_vtable;
 
-const char *ipvlan_mode_to_string(IPVlanMode d) _const_;
-IPVlanMode ipvlan_mode_from_string(const char *d) _pure_;
-
-const char *ipvlan_flags_to_string(IPVlanFlags d) _const_;
-IPVlanFlags ipvlan_flags_from_string(const char *d) _pure_;
-
 CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_flags);
 
index 91c1de1507b2edcbba50083e6724e939b3645cc4..c725baeedb18fdbb4afb7d0a31f3fb2dfb266dab 100644 (file)
@@ -33,6 +33,7 @@
 #include "geneve-util.h"
 #include "glob-util.h"
 #include "hwdb-util.h"
+#include "ipvlan-util.h"
 #include "local-addresses.h"
 #include "locale-util.h"
 #include "logs-show.h"
@@ -192,6 +193,10 @@ typedef struct LinkInfo {
         /* macvlan and macvtap info */
         uint32_t macvlan_mode;
 
+        /* ipvlan info */
+        uint16_t ipvlan_mode;
+        uint16_t ipvlan_flags;
+
         /* ethtool info */
         int autonegotiation;
         uint64_t speed;
@@ -335,6 +340,10 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
                 (void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_REMOTE, &info->remote.in6);
         } else if (STR_IN_SET(received_kind, "macvlan", "macvtap"))
                 (void) sd_netlink_message_read_u32(m, IFLA_MACVLAN_MODE, &info->macvlan_mode);
+        else if (streq(received_kind, "ipvlan")) {
+                (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_MODE, &info->ipvlan_mode);
+                (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_FLAGS, &info->ipvlan_flags);
+        }
 
         strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
 
@@ -444,7 +453,11 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b
                 r = sd_netlink_message_enter_container(m, AF_INET6);
                 if (r >= 0) {
                         r = sd_netlink_message_read_u8(m, IFLA_INET6_ADDR_GEN_MODE, &info->addr_gen_mode);
-                        if (r >= 0)
+                        if (r >= 0 && IN_SET(info->addr_gen_mode,
+                                             IN6_ADDR_GEN_MODE_EUI64,
+                                             IN6_ADDR_GEN_MODE_NONE,
+                                             IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+                                             IN6_ADDR_GEN_MODE_RANDOM))
                                 info->has_ipv6_address_generation_mode = true;
 
                         (void) sd_netlink_message_exit_container(m);
@@ -1798,6 +1811,28 @@ static int link_status_one(
                                    TABLE_STRING, macvlan_mode_to_string(info->macvlan_mode));
                 if (r < 0)
                         return table_log_add_error(r);
+        } else if (streq_ptr(info->netdev_kind, "ipvlan")) {
+                _cleanup_free_ char *p = NULL, *s = NULL;
+
+                if (info->ipvlan_flags & IPVLAN_F_PRIVATE)
+                        p = strdup("private");
+                else if (info->ipvlan_flags & IPVLAN_F_VEPA)
+                        p = strdup("vepa");
+                else
+                        p = strdup("bridge");
+                if (!p)
+                        log_oom();
+
+                s = strjoin(ipvlan_mode_to_string(info->ipvlan_mode), " (", p, ")");
+                if (!s)
+                        return log_oom();
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Mode:",
+                                   TABLE_STRING, s);
+                if (r < 0)
+                        return table_log_add_error(r);
         }
 
         if (info->has_wlan_link_info) {
index 1567bd7ee9112b7db9d5c4e4f73514cc3d80da93..9b78530334a08f0cb2adececb5d98f962badd5fe 100644 (file)
@@ -954,7 +954,7 @@ int config_parse_lifetime(const char *unit,
                           void *userdata) {
         Network *network = userdata;
         _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
-        unsigned k;
+        uint32_t k;
         int r;
 
         assert(filename);
@@ -967,8 +967,8 @@ int config_parse_lifetime(const char *unit,
         if (r < 0)
                 return r;
 
-        /* We accept only "forever", "infinity", or "0". */
-        if (STR_IN_SET(rvalue, "forever", "infinity"))
+        /* We accept only "forever", "infinity", empty, or "0". */
+        if (STR_IN_SET(rvalue, "forever", "infinity", ""))
                 k = CACHE_INFO_INFINITY_LIFE_TIME;
         else if (streq(rvalue, "0"))
                 k = 0;
@@ -979,7 +979,7 @@ int config_parse_lifetime(const char *unit,
         }
 
         n->cinfo.ifa_prefered = k;
-        n = NULL;
+        TAKE_PTR(n);
 
         return 0;
 }
index ae1c4493c774cf5a1ba1ee937644992b0639e843..3d9dab7e3c1abaa5bf31d5240396c90e1fc9cf7f 100644 (file)
@@ -267,7 +267,69 @@ int config_parse_dhcp6_pd_hint(
         return 0;
 }
 
-int config_parse_dhcp6_mud_url(
+int config_parse_dhcp_user_class(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char ***l = data;
+        int r;
+
+        assert(l);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *l = strv_free(*l);
+                return 0;
+        }
+
+        for (;;) {
+                _cleanup_free_ char *w = NULL;
+
+                r = extract_first_word(&rvalue, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to split user classes option, ignoring: %s", rvalue);
+                        break;
+                }
+                if (r == 0)
+                        break;
+
+                if (ltype == AF_INET) {
+                        if (strlen(w) > UINT8_MAX) {
+                                log_syntax(unit, LOG_ERR, filename, line, 0,
+                                           "%s length is not in the range 1-255, ignoring.", w);
+                                continue;
+                        }
+                } else {
+                        if (strlen(w) > UINT16_MAX) {
+                                log_syntax(unit, LOG_ERR, filename, line, 0,
+                                           "%s length is not in the range 1-65535, ignoring.", w);
+                                continue;
+                        }
+                }
+
+                r = strv_push(l, w);
+                if (r < 0)
+                        return log_oom();
+
+                w = NULL;
+        }
+
+        return 0;
+}
+
+int config_parse_dhcp_vendor_class(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -278,7 +340,59 @@ int config_parse_dhcp6_mud_url(
                 const char *rvalue,
                 void *data,
                 void *userdata) {
+        char ***l = data;
+        int r;
 
+        assert(l);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *l = strv_free(*l);
+                return 0;
+        }
+
+        for (;;) {
+                _cleanup_free_ char *w = NULL;
+
+                r = extract_first_word(&rvalue, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to split vendor classes option, ignoring: %s", rvalue);
+                        break;
+                }
+                if (r == 0)
+                        break;
+
+                if (strlen(w) > UINT8_MAX) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                                   "%s length is not in the range 1-255, ignoring.", w);
+                        continue;
+                }
+
+                r = strv_push(l, w);
+                if (r < 0)
+                        return log_oom();
+
+                w = NULL;
+        }
+
+        return 0;
+}
+
+int config_parse_dhcp6_mud_url(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
         _cleanup_free_ char *unescaped = NULL;
         Network *network = data;
         int r;
@@ -299,7 +413,7 @@ int config_parse_dhcp6_mud_url(
                 return 0;
         }
 
-        if (!http_url_is_valid(unescaped) || strlen(unescaped) > 255) {
+        if (!http_url_is_valid(unescaped) || strlen(unescaped) > UINT8_MAX) {
                 log_syntax(unit, LOG_ERR, filename, line, 0,
                            "Failed to parse MUD URL '%s', ignoring: %m", rvalue);
 
@@ -362,7 +476,7 @@ int config_parse_dhcp_send_option(
                                    "Invalid DHCP option, ignoring assignment: %s", rvalue);
                          return 0;
                 }
-                if (u16 < 1 || u16 >= 65535) {
+                if (u16 < 1 || u16 >= UINT16_MAX) {
                         log_syntax(unit, LOG_ERR, filename, line, 0,
                                    "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue);
                         return 0;
@@ -374,7 +488,7 @@ int config_parse_dhcp_send_option(
                                    "Invalid DHCP option, ignoring assignment: %s", rvalue);
                          return 0;
                 }
-                if (u8 < 1 || u8 >= 255) {
+                if (u8 < 1 || u8 >= UINT8_MAX) {
                         log_syntax(unit, LOG_ERR, filename, line, 0,
                                    "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue);
                         return 0;
@@ -385,7 +499,7 @@ int config_parse_dhcp_send_option(
         r = extract_first_word(&p, &word, ":", 0);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r <= 0) {
+        if (r <= 0 || isempty(p)) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
                            "Invalid DHCP option, ignoring assignment: %s", rvalue);
                 return 0;
@@ -570,7 +684,7 @@ int config_parse_dhcp_request_options(
                         continue;
                 }
 
-                if (i < 1 || i >= 255) {
+                if (i < 1 || i >= UINT8_MAX) {
                         log_syntax(unit, LOG_ERR, filename, line, r,
                                    "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n);
                         continue;
index efadcebe21e83c4a8871fe089756f2de80352413..d837f89c25865e00719aaeb6520c12a0808d6501 100644 (file)
@@ -50,5 +50,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
 CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_vendor_class);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_option);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_request_options);
index 2e9cef590fffb7e81c058bdb7f0b140360603397..3427c6642aec38531c3ebbbcdab98853c71fcba9 100644 (file)
@@ -21,24 +21,15 @@ static Address* link_find_dhcp_server_address(Link *link) {
         assert(link->network);
 
         /* The first statically configured address if there is any */
-        LIST_FOREACH(addresses, address, link->network->static_addresses) {
-
-                if (address->family != AF_INET)
-                        continue;
-
-                if (in_addr_is_null(address->family, &address->in_addr))
-                        continue;
-
-                return address;
-        }
+        LIST_FOREACH(addresses, address, link->network->static_addresses)
+                if (address->family == AF_INET &&
+                    !in_addr_is_null(address->family, &address->in_addr))
+                        return address;
 
         /* If that didn't work, find a suitable address we got from the pool */
-        LIST_FOREACH(addresses, address, link->pool_addresses) {
-                if (address->family != AF_INET)
-                        continue;
-
-                return address;
-        }
+        LIST_FOREACH(addresses, address, link->pool_addresses)
+                if (address->family == AF_INET)
+                        return address;
 
         return NULL;
 }
@@ -46,9 +37,8 @@ static Address* link_find_dhcp_server_address(Link *link) {
 static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
         _cleanup_free_ struct in_addr *addresses = NULL;
         size_t n_addresses = 0, n_allocated = 0;
-        unsigned i;
 
-        for (i = 0; i < link->network->n_dns; i++) {
+        for (unsigned i = 0; i < link->network->n_dns; i++) {
                 struct in_addr ia;
 
                 /* Only look for IPv4 addresses */
@@ -68,16 +58,14 @@ static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
         }
 
         if (link->network->dhcp_use_dns && link->dhcp_lease) {
-                const struct in_addr *da = NULL;
-                int j, n;
+                const struct in_addr *da;
 
-                n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
+                int n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
                 if (n > 0) {
-
                         if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
                                 return log_oom();
 
-                        for (j = 0; j < n; j++)
+                        for (int j = 0; j < n; j++)
                                 if (in4_addr_is_non_local(&da[j]))
                                         addresses[n_addresses++] = da[j];
                 }
@@ -160,12 +148,12 @@ static int link_push_uplink_to_dhcp_server(
         if (lease_condition && link->dhcp_lease) {
                 const struct in_addr *da;
 
-                size_t n = sd_dhcp_lease_get_servers(link->dhcp_lease, what, &da);
+                int n = sd_dhcp_lease_get_servers(link->dhcp_lease, what, &da);
                 if (n > 0) {
                         if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
                                 return log_oom();
 
-                        for (unsigned i = 0; i < n; i++)
+                        for (int i = 0; i < n; i++)
                                 if (in4_addr_is_non_local(&da[i]))
                                         addresses[n_addresses++] = da[i];
                 }
index 7d52a3a6a1c635e2a83509b5970c591754475af2..8e2c775fcf420418faa0d9c383c7f49fda091d66 100644 (file)
@@ -1488,6 +1488,12 @@ int dhcp4_configure(Link *link) {
                         return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set ip service type: %m");
         }
 
+        if (link->network->dhcp_fallback_lease_lifetime > 0) {
+                r = sd_dhcp_client_set_fallback_lease_lifetime(link->dhcp_client, link->network->dhcp_fallback_lease_lifetime);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed set to lease lifetime: %m");
+        }
+
         if (link->network->dhcp_send_decline) {
                 r = configure_dhcpv4_duplicate_address_detection(link);
                 if (r < 0)
@@ -1605,60 +1611,6 @@ int config_parse_dhcp_black_listed_ip_address(
         return 0;
 }
 
-int config_parse_dhcp_user_class(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        char ***l = data;
-        int r;
-
-        assert(l);
-        assert(lvalue);
-        assert(rvalue);
-
-        if (isempty(rvalue)) {
-                *l = strv_free(*l);
-                return 0;
-        }
-
-        for (;;) {
-                _cleanup_free_ char *w = NULL;
-
-                r = extract_first_word(&rvalue, &w, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to split user classes option, ignoring: %s", rvalue);
-                        break;
-                }
-                if (r == 0)
-                        break;
-
-                if (strlen(w) > 255) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
-                                   "%s length is not in the range 1-255, ignoring.", w);
-                        continue;
-                }
-
-                r = strv_push(l, w);
-                if (r < 0)
-                        return log_oom();
-
-                w = NULL;
-        }
-
-        return 0;
-}
-
 int config_parse_dhcp_ip_service_type(
                 const char *unit,
                 const char *filename,
@@ -1728,6 +1680,44 @@ int config_parse_dhcp_mud_url(
         return free_and_strdup_warn(&network->dhcp_mudurl, unescaped);
 }
 
+int config_parse_dhcp_fallback_lease_lifetime(const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+        Network *network = userdata;
+        uint32_t k;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                network->dhcp_fallback_lease_lifetime = 0;
+                return 0;
+        }
+
+        /* We accept only "forever" or "infinity". */
+        if (STR_IN_SET(rvalue, "forever", "infinity"))
+                k = CACHE_INFO_INFINITY_LIFE_TIME;
+        else {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Invalid LeaseLifetime= value, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        network->dhcp_fallback_lease_lifetime = k;
+
+        return 0;
+}
+
 static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
         [DHCP_CLIENT_ID_MAC] = "mac",
         [DHCP_CLIENT_ID_DUID] = "duid",
index 78e10d9299050d1f9b7560ca082dc87e40b6370d..a6e24be78d73744dbccaa84b71ac5e10d1450b99 100644 (file)
@@ -25,6 +25,6 @@ int dhcp4_set_promote_secondaries(Link *link);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_ip_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_ip_service_type);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_mud_url);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_fallback_lease_lifetime);
index 05df41cd3a7bb448633fd38fce51dbebe9e71fcc..29226b64bf8404593282cb2e5517b8ddc8368611 100644 (file)
@@ -706,6 +706,18 @@ int dhcp6_configure(Link *link) {
                         return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for '%u': %m", option);
         }
 
+        if (link->network->dhcp6_user_class) {
+                r = sd_dhcp6_client_set_request_user_class(client, link->network->dhcp6_user_class);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set user class: %m");
+        }
+
+        if (link->network->dhcp6_vendor_class) {
+                r = sd_dhcp6_client_set_request_vendor_class(client, link->network->dhcp6_vendor_class);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor class: %m");
+        }
+
         r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
         if (r < 0)
                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
index 32fe86045d6172cdaae813b1eaf7aff2ad3e09b2..59e37baa6bf1b7bab94185f97f6bcca93ca732b9 100644 (file)
@@ -1692,12 +1692,18 @@ static int link_configure_addrgen_mode(Link *link) {
 
         if (!link_ipv6ll_enabled(link))
                 ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE;
-        else if (sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL) < 0)
-                /* The file may not exist. And even if it exists, when stable_secret is unset,
-                 * reading the file fails with EIO. */
-                ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64;
-        else
-                ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+        else if (link->network->ipv6_address_gen_mode < 0) {
+                r = sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL);
+                if (r < 0) {
+                        /* The file may not exist. And even if it exists, when stable_secret is unset,
+                         * reading the file fails with EIO. */
+                        log_link_debug_errno(link, r, "Failed to read sysctl property stable_secret: %m");
+
+                        ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64;
+                } else
+                        ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+        } else
+                ipv6ll_mode = link->network->ipv6_address_gen_mode;
 
         r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode);
         if (r < 0)
@@ -4434,6 +4440,16 @@ void link_clean(Link *link) {
         link_unref(set_remove(link->manager->dirty_links, link));
 }
 
+static const char* const link_ipv6_address_gen_mode_table[_LINK_IPV6_ADDRESS_GEN_MODE_MAX] = {
+        [LINK_IPV6_ADDRESSS_GEN_MODE_EUI64] = "eui64",
+        [LINK_IPV6_ADDRESSS_GEN_MODE_NONE] = "none",
+        [LINK_IPV6_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy",
+        [LINK_IPV6_ADDRESSS_GEN_MODE_RANDOM] = "random",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(link_ipv6_address_gen_mode, LinkIPv6AddressGenMode);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_link_ipv6_address_gen_mode, link_ipv6_address_gen_mode, LinkIPv6AddressGenMode, "Failed to parse link IPv6 address generation mode");
+
 static const char* const link_state_table[_LINK_STATE_MAX] = {
         [LINK_STATE_PENDING] = "pending",
         [LINK_STATE_INITIALIZED] = "initialized",
index 3d07d882cda14a4d9cc6269a85aadbf142781657..e8d67e0d64fc817a604fcdf3ec21ccbae4e8143a 100644 (file)
@@ -35,6 +35,15 @@ typedef enum LinkState {
         _LINK_STATE_INVALID = -1
 } LinkState;
 
+typedef enum LinkIPv6AddressGenMode {
+       LINK_IPV6_ADDRESSS_GEN_MODE_EUI64          = IN6_ADDR_GEN_MODE_EUI64,
+       LINK_IPV6_ADDRESSS_GEN_MODE_NONE           = IN6_ADDR_GEN_MODE_NONE,
+       LINK_IPV6_ADDRESSS_GEN_MODE_STABLE_PRIVACY = IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+       LINK_IPV6_ADDRESSS_GEN_MODE_RANDOM         = IN6_ADDR_GEN_MODE_RANDOM,
+       _LINK_IPV6_ADDRESS_GEN_MODE_MAX,
+       _LINK_IPV6_ADDRESS_GEN_MODE_INVALID        = -1
+} LinkIPv6AddressGenMode;
+
 typedef struct Manager Manager;
 typedef struct Network Network;
 typedef struct Address Address;
@@ -209,6 +218,9 @@ int link_stop_clients(Link *link, bool may_keep_dhcp);
 const char* link_state_to_string(LinkState s) _const_;
 LinkState link_state_from_string(const char *s) _pure_;
 
+const char* link_ipv6_address_gen_mode_to_string(LinkIPv6AddressGenMode s) _const_;
+LinkIPv6AddressGenMode link_ipv6_address_gen_mode_from_string(const char *s) _pure_;
+
 uint32_t link_get_vrf_table(Link *link);
 uint32_t link_get_dhcp_route_table(Link *link);
 uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
@@ -217,6 +229,8 @@ int link_request_set_nexthop(Link *link);
 
 int link_reconfigure(Link *link, bool force);
 
+CONFIG_PARSER_PROTOTYPE(config_parse_link_ipv6_address_gen_mode);
+
 int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg);
 #define log_link_message_error_errno(link, m, err, msg)   log_link_message_full_errno(link, m, LOG_ERR, err, msg)
 #define log_link_message_warning_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_WARNING, err, msg)
index 711bec7588e2317ad89e0efba54c1e4c4e4e54d6..a2dd5479b2173467cf8eb6710b16ea87a1a216ab 100644 (file)
@@ -51,6 +51,7 @@ Link.ARP,                                    config_parse_tristate,
 Link.Multicast,                              config_parse_tristate,                                    0,                             offsetof(Network, multicast)
 Link.AllMulticast,                           config_parse_tristate,                                    0,                             offsetof(Network, allmulticast)
 Link.Unmanaged,                              config_parse_bool,                                        0,                             offsetof(Network, unmanaged)
+Link.IPv6LinkLocalAddressGenerationMode,     config_parse_link_ipv6_address_gen_mode,                  0,                             offsetof(Network, ipv6_address_gen_mode)
 Link.RequiredForOnline,                      config_parse_required_for_online,                         0,                             0
 Network.Description,                         config_parse_string,                                      0,                             offsetof(Network, description)
 Network.Bridge,                              config_parse_ifname,                                      0,                             offsetof(Network, bridge_name)
@@ -174,7 +175,7 @@ DHCPv4.RequestBroadcast,                     config_parse_bool,
 DHCPv4.VendorClassIdentifier,                config_parse_string,                                      0,                             offsetof(Network, dhcp_vendor_class_identifier)
 DHCPv4.MUDURL,                               config_parse_dhcp_mud_url,                                0,                             0
 DHCPv4.MaxAttempts,                          config_parse_dhcp_max_attempts,                           0,                             0
-DHCPv4.UserClass,                            config_parse_dhcp_user_class,                             0,                             offsetof(Network, dhcp_user_class)
+DHCPv4.UserClass,                            config_parse_dhcp_user_class,                             AF_INET,                       offsetof(Network, dhcp_user_class)
 DHCPv4.DUIDType,                             config_parse_duid_type,                                   0,                             offsetof(Network, duid)
 DHCPv4.DUIDRawData,                          config_parse_duid_rawdata,                                0,                             offsetof(Network, duid)
 DHCPv4.RouteMetric,                          config_parse_unsigned,                                    0,                             offsetof(Network, dhcp_route_metric)
@@ -189,11 +190,14 @@ DHCPv4.IPServiceType,                        config_parse_dhcp_ip_service_type,
 DHCPv4.SendOption,                           config_parse_dhcp_send_option,                            AF_INET,                       offsetof(Network, dhcp_client_send_options)
 DHCPv4.SendVendorOption,                     config_parse_dhcp_send_option,                            0,                             offsetof(Network, dhcp_client_send_vendor_options)
 DHCPv4.RouteMTUBytes,                        config_parse_mtu,                                         AF_INET,                       offsetof(Network, dhcp_route_mtu)
+DHCPv4.FallbackLeaseLifetimeSec,             config_parse_dhcp_fallback_lease_lifetime,                0,                             0
 DHCPv6.UseDNS,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_dns)
 DHCPv6.UseNTP,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_ntp)
 DHCPv6.RapidCommit,                          config_parse_bool,                                        0,                             offsetof(Network, rapid_commit)
 DHCPv6.MUDURL,                               config_parse_dhcp6_mud_url,                               0,                             0
 DHCPv6.RequestOptions,                       config_parse_dhcp_request_options,                        AF_INET6,                      0
+DHCPv6.UserClass,                            config_parse_dhcp_user_class,                             AF_INET6,                      offsetof(Network, dhcp6_user_class)
+DHCPv6.VendorClass,                          config_parse_dhcp_vendor_class,                           0,                             offsetof(Network, dhcp6_vendor_class)
 DHCPv6.ForceDHCPv6PDOtherInformation,        config_parse_bool,                                        0,                             offsetof(Network, dhcp6_force_pd_other_information)
 DHCPv6.PrefixDelegationHint,                 config_parse_dhcp6_pd_hint,                               0,                             0
 DHCPv6.WithoutRA,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp6_without_ra)
@@ -384,7 +388,7 @@ DHCP.Hostname,                               config_parse_hostname,
 DHCP.RequestBroadcast,                       config_parse_bool,                                        0,                             offsetof(Network, dhcp_broadcast)
 DHCP.CriticalConnection,                     config_parse_tristate,                                    0,                             offsetof(Network, dhcp_critical)
 DHCP.VendorClassIdentifier,                  config_parse_string,                                      0,                             offsetof(Network, dhcp_vendor_class_identifier)
-DHCP.UserClass,                              config_parse_dhcp_user_class,                             0,                             offsetof(Network, dhcp_user_class)
+DHCP.UserClass,                              config_parse_dhcp_user_class,                             AF_INET,                       offsetof(Network, dhcp_user_class)
 DHCP.DUIDType,                               config_parse_duid_type,                                   0,                             offsetof(Network, duid)
 DHCP.DUIDRawData,                            config_parse_duid_rawdata,                                0,                             offsetof(Network, duid)
 DHCP.RouteMetric,                            config_parse_unsigned,                                    0,                             offsetof(Network, dhcp_route_metric)
index a15f884ab080a74716c221013d533938f77a6c9b..1b4083febf23432d7b32093b28a7b10ef97ac1b0 100644 (file)
@@ -461,7 +461,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .ipv6_accept_ra_start_dhcp6_client = true,
 
                 .keep_configuration = _KEEP_CONFIGURATION_INVALID,
-
+                .ipv6_address_gen_mode = _LINK_IPV6_ADDRESS_GEN_MODE_INVALID,
                 .can_triple_sampling = -1,
                 .can_termination = -1,
                 .ip_service_type = -1,
@@ -653,6 +653,8 @@ static Network *network_free(Network *network) {
         set_free(network->dhcp6_request_options);
         free(network->mac);
         free(network->dhcp6_mudurl);
+        strv_free(network->dhcp6_user_class);
+        strv_free(network->dhcp6_vendor_class);
 
         if (network->dhcp_acd)
                 sd_ipv4acd_unref(network->dhcp_acd);
index 019bd7676b18256cd11a49b305d6554ab3a6ec75..362ef6508f7f468d453bad7ead50a70ce0dc5c33 100644 (file)
@@ -97,6 +97,7 @@ struct Network {
         uint64_t dhcp_max_attempts;
         unsigned dhcp_route_metric;
         uint32_t dhcp_route_table;
+        uint32_t dhcp_fallback_lease_lifetime;
         uint32_t dhcp_route_mtu;
         uint16_t dhcp_client_port;
         int dhcp_critical;
@@ -132,6 +133,8 @@ struct Network {
         bool dhcp6_without_ra;
         uint8_t dhcp6_pd_length;
         char *dhcp6_mudurl;
+        char **dhcp6_user_class;
+        char **dhcp6_vendor_class;
         struct in6_addr dhcp6_pd_address;
         OrderedHashmap *dhcp6_client_send_options;
         Set *dhcp6_request_options;
@@ -256,6 +259,7 @@ struct Network {
         bool configure_without_carrier;
         bool ignore_carrier_loss;
         KeepConfiguration keep_configuration;
+        LinkIPv6AddressGenMode ipv6_address_gen_mode;
         uint32_t iaid;
         DUID duid;
 
index 4b1115b6e852394bca3fae259bc768ae4f602be2..4e1cb3835c67b38c5b5c363ef095bdd15f2b117e 100644 (file)
@@ -295,35 +295,6 @@ int config_parse_capability(
         return 0;
 }
 
-int config_parse_id128(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        sd_id128_t t, *result = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        r = sd_id128_from_string(rvalue, &t);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
-                return 0;
-        }
-
-        *result = t;
-        return 0;
-}
-
 int config_parse_pivot_root(
                 const char *unit,
                 const char *filename,
index 6f2c1141e63e6799c1a2a736e54c689ce85ca2a9..24f98fd7ef7b084870c84b4b1b87b07fe8453756 100644 (file)
@@ -234,7 +234,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Settings*, settings_free);
 const struct ConfigPerfItem* nspawn_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_capability);
-CONFIG_PARSER_PROTOTYPE(config_parse_id128);
 CONFIG_PARSER_PROTOTYPE(config_parse_expose_port);
 CONFIG_PARSER_PROTOTYPE(config_parse_volatile_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_pivot_root);
index d73d67c4e8a4d80e457af13794630c84af0e3970..97f50c90335bcd426ef4158b4fa5ad533e0bd495 100644 (file)
@@ -7,7 +7,9 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "blockdev-util.h"
 #include "dissect-image.h"
+#include "fd-util.h"
 #include "main-func.h"
 #include "process-util.h"
 #include "signal-util.h"
@@ -42,6 +44,7 @@ static int makefs(const char *type, const char *device) {
 
 static int run(int argc, char *argv[]) {
         _cleanup_free_ char *device = NULL, *type = NULL, *detected = NULL;
+        _cleanup_close_ int lock_fd = -1;
         struct stat st;
         int r;
 
@@ -54,28 +57,31 @@ static int run(int argc, char *argv[]) {
         /* type and device must be copied because makefs calls safe_fork, which clears argv[] */
         type = strdup(argv[1]);
         if (!type)
-                return -ENOMEM;
+                return log_oom();
 
         device = strdup(argv[2]);
         if (!device)
-                return -ENOMEM;
+                return log_oom();
 
         if (stat(device, &st) < 0)
                 return log_error_errno(errno, "Failed to stat \"%s\": %m", device);
 
-        if (!S_ISBLK(st.st_mode))
+        if (S_ISBLK(st.st_mode)) {
+                /* Lock the device so that udev doesn't interfere with our work */
+
+                lock_fd = lock_whole_block_device(st.st_rdev, LOCK_EX);
+                if (lock_fd < 0)
+                        return log_error_errno(lock_fd, "Failed to lock whole block device of \"%s\": %m", device);
+        } else
                 log_info("%s is not a block device.", device);
 
         r = probe_filesystem(device, &detected);
+        if (r == -EUCLEAN)
+                return log_error_errno(r, "Ambiguous results of probing for file system on \"%s\", refusing to proceed.", device);
         if (r < 0)
-                return log_warning_errno(r,
-                                         r == -EUCLEAN ?
-                                         "Cannot reliably determine probe \"%s\", refusing to proceed." :
-                                         "Failed to probe \"%s\": %m",
-                                         device);
-
+                return log_error_errno(r, "Failed to probe \"%s\": %m", device);
         if (detected) {
-                log_info("%s is not empty (type %s), exiting", device, detected);
+                log_info("'%s' is not empty (contains file system of type %s), exiting.", device, detected);
                 return 0;
         }
 
index 4c738920abcb1fbae0979a7a6bf7ffc888f06274..16cb5e45c451e6df3c090d9b1637c0f97c550f82 100644 (file)
@@ -963,6 +963,7 @@ static int partition_read_definition(Partition *p, const char *path) {
         ConfigTableItem table[] = {
                 { "Partition", "Type",            config_parse_type,     0,  &p->type_uuid      },
                 { "Partition", "Label",           config_parse_label,    0,  &p->new_label      },
+                { "Partition", "UUID",            config_parse_id128,    0,  &p->new_uuid       },
                 { "Partition", "Priority",        config_parse_int32,    0,  &p->priority       },
                 { "Partition", "Weight",          config_parse_weight,   0,  &p->weight         },
                 { "Partition", "PaddingWeight",   config_parse_weight,   0,  &p->padding_weight },
@@ -2232,14 +2233,12 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
         assert(context);
 
         LIST_FOREACH(partitions, p, context->partitions) {
-                assert(sd_id128_is_null(p->new_uuid));
-                assert(!p->new_label);
-
                 /* Never touch foreign partitions */
                 if (PARTITION_IS_FOREIGN(p)) {
                         p->new_uuid = p->current_uuid;
 
                         if (p->current_label) {
+                                free(p->new_label);
                                 p->new_label = strdup(p->current_label);
                                 if (!p->new_label)
                                         return log_oom();
@@ -2250,17 +2249,21 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
 
                 if (!sd_id128_is_null(p->current_uuid))
                         p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */
-                else {
+                else if (sd_id128_is_null(p->new_uuid)) {
+                        /* Not explicitly set by user! */
                         r = partition_acquire_uuid(context, p, &p->new_uuid);
                         if (r < 0)
                                 return r;
                 }
 
                 if (!isempty(p->current_label)) {
+                        free(p->new_label);
                         p->new_label = strdup(p->current_label); /* never change initialized labels */
                         if (!p->new_label)
                                 return log_oom();
-                } else {
+                } else if (!p->new_label) {
+                        /* Not explicitly set by user! */
+
                         r = partition_acquire_label(context, p, &p->new_label);
                         if (r < 0)
                                 return r;
index 58fcc797c43e72843710f369d371fee3446485ef..e1ff357929d6d932e45cfe0a8dd9f0dbb512c1d5 100755 (executable)
@@ -68,8 +68,13 @@ EOF
 cat >$D/definitions/extra.conf <<EOF
 [Partition]
 Type=linux-generic
+Label=custom_label
+UUID=a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
 EOF
 
+echo "Label=ignored_label" >> $D/definitions/home.conf
+echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >> $D/definitions/home.conf
+
 $repart $D/zzz --dry-run=no --seed=$SEED --definitions=$D/definitions
 
 sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated2
@@ -85,7 +90,7 @@ $D/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E
 $D/zzz2 : start=      593904, size=      591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
 $D/zzz3 : start=     1185760, size=      591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
 $D/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
-$D/zzz5 : start=     1908696, size=      188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=03477476-06AD-44E8-9EF4-BC2BD7771289, name="linux-generic"
+$D/zzz5 : start=     1908696, size=      188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
 EOF
 
 $repart $D/zzz --size=2G --dry-run=no --seed=$SEED --definitions=$D/definitions
@@ -103,5 +108,5 @@ $D/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E
 $D/zzz2 : start=      593904, size=      591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
 $D/zzz3 : start=     1185760, size=      591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
 $D/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
-$D/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=03477476-06AD-44E8-9EF4-BC2BD7771289, name="linux-generic"
+$D/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
 EOF
index cbaef5e49c65f986b6530774c419df1ef094249f..6fa5e734bbb024c06bdd5d4e9d8c0b681bf31db8 100644 (file)
@@ -167,7 +167,7 @@ void manager_verify_all(Manager *m);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 
-/* For some reason we need some extra cmsg space on some kernels/archs. One of those days we ned to figure out why */
+/* For some reason we need some extra cmsg space on some kernels/archs. One of those days we need to figure out why */
 #define EXTRA_CMSG_SPACE 1024
 
 int manager_is_own_hostname(Manager *m, const char *name);
index 463a0ddb716086bb83eff8d235c127b33f7fb6f9..3be75e6b4d291459334abf640be0328a8e4a7edc 100644 (file)
@@ -1436,7 +1436,8 @@ static int bus_append_mount_property(sd_bus_message *m, const char *field, const
 
         if (STR_IN_SET(field, "SloppyOptions",
                               "LazyUnmount",
-                              "ForceUnmount"))
+                              "ForceUnmount",
+                              "ReadwriteOnly"))
                 return bus_append_parse_boolean(m, field, eq);
 
         return 0;
index c7611a6e851db1db5293463169e67689f0ba7388..379aecf73078697bbbcdfbc9a9f52cd4d5dd2107 100644 (file)
@@ -1591,6 +1591,12 @@ int bus_add_implementation(sd_bus *bus, const BusObjectImplementation *impl, voi
                                                impl->path);
         }
 
+        if (impl->manager) {
+                r = sd_bus_add_object_manager(bus, NULL, impl->path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add object manager for %s: %m", impl->path);
+        }
+
         for (size_t i = 0; impl->children && impl->children[i]; i++) {
                 r = bus_add_implementation(bus, impl->children[i], userdata);
                 if (r < 0)
index c83a4ba0eb8a86a297e3a78e18d58a1ea53b595d..bf3b5fa1622800a26edfedaf75720bb03d61600c 100644 (file)
@@ -546,30 +546,47 @@ static int condition_test_capability(Condition *c, char **env) {
 }
 
 static int condition_test_needs_update(Condition *c, char **env) {
-        const char *p;
         struct stat usr, other;
+        const char *p;
+        bool b;
+        int r;
 
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_NEEDS_UPDATE);
 
+        r = proc_cmdline_get_bool("systemd.condition-needs-update", &b);
+        if (r < 0)
+                log_debug_errno(r, "Failed to parse systemd.condition-needs-update= kernel command line argument, ignoring: %m");
+        if (r > 0)
+                return b;
+
+        if (!path_is_absolute(c->parameter)) {
+                log_debug("Specified condition parameter '%s' is not absolute, assuming an update is needed.", c->parameter);
+                return true;
+        }
+
         /* If the file system is read-only we shouldn't suggest an update */
-        if (path_is_read_only_fs(c->parameter) > 0)
+        r = path_is_read_only_fs(c->parameter);
+        if (r < 0)
+                log_debug_errno(r, "Failed to determine if '%s' is read-only, ignoring: %m", c->parameter);
+        if (r > 0)
                 return false;
 
-        /* Any other failure means we should allow the condition to be true,
-         * so that we rather invoke too many update tools than too
-         * few. */
-
-        if (!path_is_absolute(c->parameter))
-                return true;
+        /* Any other failure means we should allow the condition to be true, so that we rather invoke too
+         * many update tools than too few. */
 
         p = strjoina(c->parameter, "/.updated");
-        if (lstat(p, &other) < 0)
+        if (lstat(p, &other) < 0) {
+                if (errno != ENOENT)
+                        log_debug_errno(errno, "Failed to stat() '%s', assuming an update is needed: %m", p);
                 return true;
+        }
 
-        if (lstat("/usr/", &usr) < 0)
+        if (lstat("/usr/", &usr) < 0) {
+                log_debug_errno(errno, "Failed to stat() /usr/, assuming an update is needed: %m");
                 return true;
+        }
 
         /*
          * First, compare seconds as they are always accurate...
@@ -585,44 +602,52 @@ static int condition_test_needs_update(Condition *c, char **env) {
          * AND the target file's nanoseconds == 0
          * (otherwise the filesystem supports nsec timestamps, see stat(2)).
          */
-        if (usr.st_mtim.tv_nsec > 0 && other.st_mtim.tv_nsec == 0) {
-                _cleanup_free_ char *timestamp_str = NULL;
-                uint64_t timestamp;
-                int r;
-
-                r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", &timestamp_str);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
-                        return true;
-                } else if (r == 0) {
-                        log_debug("No data in timestamp file '%s', using mtime", p);
-                        return true;
-                }
+        if (usr.st_mtim.tv_nsec == 0 || other.st_mtim.tv_nsec > 0)
+                return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
 
-                r = safe_atou64(timestamp_str, &timestamp);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p);
-                        return true;
-                }
+        _cleanup_free_ char *timestamp_str = NULL;
+        r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", &timestamp_str);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
+                return true;
+        } else if (r == 0) {
+                log_debug("No data in timestamp file '%s', using mtime.", p);
+                return true;
+        }
 
-                timespec_store(&other.st_mtim, timestamp);
+        uint64_t timestamp;
+        r = safe_atou64(timestamp_str, &timestamp);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p);
+                return true;
         }
 
-        return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
+        return timespec_load_nsec(&usr.st_mtim) > timestamp;
 }
 
 static int condition_test_first_boot(Condition *c, char **env) {
-        int r;
+        int r, q;
+        bool b;
 
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_FIRST_BOOT);
 
+        r = proc_cmdline_get_bool("systemd.condition-first-boot", &b);
+        if (r < 0)
+                log_debug_errno(r, "Failed to parse systemd.condition-first-boot= kernel command line argument, ignoring: %m");
+        if (r > 0)
+                return b == !!r;
+
         r = parse_boolean(c->parameter);
         if (r < 0)
                 return r;
 
-        return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
+        q = access("/run/systemd/first-boot", F_OK);
+        if (q < 0 && errno != ENOENT)
+                log_debug_errno(errno, "Failed to check if /run/systemd/first-boot exists, ignoring: %m");
+
+        return (q >= 0) == !!r;
 }
 
 static int condition_test_environment(Condition *c, char **env) {
index 23cb3b65b67678c84727a1c680d1a194d70ae72e..e685ecbff7af8779c7a42926dc14488fd0e4dc69 100644 (file)
@@ -23,6 +23,7 @@
 #include "path-util.h"
 #include "process-util.h"
 #include "rlimit-util.h"
+#include "sd-id128.h"
 #include "signal-util.h"
 #include "socket-util.h"
 #include "string-util.h"
@@ -648,6 +649,35 @@ int config_parse_bool(const char* unit,
         return 0;
 }
 
+int config_parse_id128(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        sd_id128_t t, *result = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = sd_id128_from_string(rvalue, &t);
+        if (r < 0)
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
+        else if (sd_id128_is_null(t))
+                log_syntax(unit, LOG_ERR, filename, line, 0, "128bit ID/UUID is all 0, ignoring: %s", rvalue);
+
+        *result = t;
+        return 0;
+}
+
 int config_parse_tristate(
                 const char* unit,
                 const char *filename,
index 59e74590cabff84870530ee816a5c1817b03c180..82c692af10c3a91366dd0f4aaf5876ab181788ed 100644 (file)
@@ -122,6 +122,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
 CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
 CONFIG_PARSER_PROTOTYPE(config_parse_bool);
+CONFIG_PARSER_PROTOTYPE(config_parse_id128);
 CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
 CONFIG_PARSER_PROTOTYPE(config_parse_string);
 CONFIG_PARSER_PROTOTYPE(config_parse_path);
index 23ad6b06cfa9f06ef307fc1798ff9c21d41765fd..8decac94b2238fd74011a57a66ae1357aa4c77f9 100644 (file)
@@ -75,10 +75,9 @@ int probe_filesystem(const char *node, char **ret_fstype) {
                 log_debug("No type detected on partition %s", node);
                 goto not_found;
         }
-        if (r == -2) {
-                log_debug("Results ambiguous for partition %s", node);
-                return -EUCLEAN;
-        }
+        if (r == -2)
+                return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
+                                       "Results ambiguous for partition %s", node);
         if (r != 0)
                 return errno_or_else(EIO);
 
index 21ad5aab36a6bc0485efd26f601bf521c911e13c..60005967e79feb614e2f036671b36889f2e86414 100644 (file)
@@ -227,7 +227,7 @@ static int path_is_runtime(const LookupPaths *p, const char *path, bool check_pa
                path_equal_ptr(path, p->runtime_control);
 }
 
-static int path_is_vendor(const LookupPaths *p, const char *path) {
+static int path_is_vendor_or_generator(const LookupPaths *p, const char *path) {
         const char *rpath;
 
         assert(p);
@@ -245,6 +245,9 @@ static int path_is_vendor(const LookupPaths *p, const char *path) {
                 return true;
 #endif
 
+        if (path_is_generator(p, rpath))
+                return true;
+
         return path_equal(rpath, SYSTEM_DATA_UNIT_PATH);
 }
 
@@ -2374,7 +2377,7 @@ int unit_file_revert(
                                         return -errno;
                         } else if (S_ISREG(st.st_mode)) {
                                 /* Check if there's a vendor version */
-                                r = path_is_vendor(&paths, path);
+                                r = path_is_vendor_or_generator(&paths, path);
                                 if (r < 0)
                                         return r;
                                 if (r > 0)
diff --git a/src/shared/ipvlan-util.c b/src/shared/ipvlan-util.c
new file mode 100644 (file)
index 0000000..da6be76
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <net/if.h>
+
+#include "ipvlan-util.h"
+#include "string-table.h"
+
+static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = {
+        [NETDEV_IPVLAN_MODE_L2] = "L2",
+        [NETDEV_IPVLAN_MODE_L3] = "L3",
+        [NETDEV_IPVLAN_MODE_L3S] = "L3S",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ipvlan_mode, IPVlanMode);
+
+static const char* const ipvlan_flags_table[_NETDEV_IPVLAN_FLAGS_MAX] = {
+        [NETDEV_IPVLAN_FLAGS_BRIGDE] = "bridge",
+        [NETDEV_IPVLAN_FLAGS_PRIVATE] = "private",
+        [NETDEV_IPVLAN_FLAGS_VEPA] = "vepa",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ipvlan_flags, IPVlanFlags);
diff --git a/src/shared/ipvlan-util.h b/src/shared/ipvlan-util.h
new file mode 100644 (file)
index 0000000..dda659d
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <netinet/in.h>
+#include <linux/if_link.h>
+
+#include "macro.h"
+
+typedef enum IPVlanMode {
+        NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2,
+        NETDEV_IPVLAN_MODE_L3 = IPVLAN_MODE_L3,
+        NETDEV_IPVLAN_MODE_L3S = IPVLAN_MODE_L3S,
+        _NETDEV_IPVLAN_MODE_MAX,
+        _NETDEV_IPVLAN_MODE_INVALID = -1
+} IPVlanMode;
+
+typedef enum IPVlanFlags {
+        NETDEV_IPVLAN_FLAGS_BRIGDE,
+        NETDEV_IPVLAN_FLAGS_PRIVATE = IPVLAN_F_PRIVATE,
+        NETDEV_IPVLAN_FLAGS_VEPA = IPVLAN_F_VEPA,
+        _NETDEV_IPVLAN_FLAGS_MAX,
+        _NETDEV_IPVLAN_FLAGS_INVALID = -1
+} IPVlanFlags;
+
+const char *ipvlan_mode_to_string(IPVlanMode d) _const_;
+IPVlanMode ipvlan_mode_from_string(const char *d) _pure_;
+
+const char *ipvlan_flags_to_string(IPVlanFlags d) _const_;
+IPVlanFlags ipvlan_flags_from_string(const char *d) _pure_;
index c92b5b4267d067e115f98d2e1bd9366ecf27dcd0..e3aee57ac816e7ae9e96c1ec1d4fcb32e52606d6 100644 (file)
@@ -120,6 +120,8 @@ shared_sources = files('''
         install-printf.h
         install.c
         install.h
+        ipvlan-util.c
+        ipvlan-util.h
         ip-protocol-list.c
         ip-protocol-list.h
         journal-importer.c
index 3b3305d7f68a849f1b6bf49b9cf6b0abaccb04c6..5274cd24b38704f22d0e2de18d113d2e557f8c54 100644 (file)
@@ -138,7 +138,7 @@ static int apply_all(OrderedHashmap *sysctl_options) {
                         if (!pattern)
                                 return log_oom();
 
-                        k = glob_extend(&paths, pattern);
+                        k = glob_extend(&paths, pattern, GLOB_NOCHECK);
                         if (k < 0) {
                                 if (option->ignore_failure || ERRNO_IS_PRIVILEGE(k))
                                         log_debug_errno(k, "Failed to resolve glob '%s', ignoring: %m",
index 83d0e397389057e1781b996df90e18da754e5cc0..460e7f69b9ae3e34a26f1500e314bd1cf219151e 100644 (file)
@@ -4192,7 +4192,7 @@ static void print_status_info(
 
         fs = !isempty(i->freezer_state) && !streq(i->freezer_state, "running") ? i->freezer_state : NULL;
         if (fs)
-                printf(" %s(%s)%s", ansi_highlight_yellow(), fs, active_off);
+                printf(" %s(%s)%s", ansi_highlight_yellow(), fs, ansi_normal());
 
         if (!isempty(i->result) && !streq(i->result, "success"))
                 printf(" (Result: %s)", i->result);
index 032afc9f127511e1f1531ff165d22a8289acca90..85b49bae74b872a867579b64cf251fbf311387ea 100644 (file)
@@ -185,6 +185,9 @@ int sd_dhcp_client_get_lease(
 int sd_dhcp_client_set_service_type(
                 sd_dhcp_client *client,
                 int type);
+int sd_dhcp_client_set_fallback_lease_lifetime(
+                sd_dhcp_client *client,
+                uint32_t fallback_lease_lifetime);
 
 int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
 int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);
index d365fc763805c6d7803a8e1a4535ffc9984fce2e..c342be4b294b7ef972908c64e5fff5e8a21e764c 100644 (file)
@@ -125,6 +125,12 @@ int sd_dhcp6_client_set_request_option(
 int sd_dhcp6_client_set_request_mud_url(
                 sd_dhcp6_client *client,
                 const char *mudurl);
+int sd_dhcp6_client_set_request_user_class(
+                sd_dhcp6_client *client,
+                char** user_class);
+int sd_dhcp6_client_set_request_vendor_class(
+                sd_dhcp6_client *client,
+                char** vendor_class);
 int sd_dhcp6_client_set_prefix_delegation_hint(
                 sd_dhcp6_client *client,
                 uint8_t prefixlen,
index d005b3e8e5f640d564091b6789669fa69f2c09cd..80c4c209b3f5727c7c3ff82f299e6e39fe19ec81 100644 (file)
@@ -670,7 +670,7 @@ static void test_unlinkat_deallocate(void) {
         assert_se(st.st_blocks > 0);
         assert_se(st.st_nlink == 1);
 
-        assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
+        assert_se(unlinkat_deallocate(AT_FDCWD, p, UNLINK_ERASE) >= 0);
 
         assert_se(fstat(fd, &st) >= 0);
         assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6
@@ -850,6 +850,12 @@ static void test_path_is_encrypted_one(const char *p, int expect) {
         int r;
 
         r = path_is_encrypted(p);
+        if (r == -ENOENT) /* This might fail, if btrfs is used and we run in a container. In that case we
+                           * cannot resolve the device node paths that BTRFS_IOC_DEV_INFO returns, because
+                           * the device nodes are unlikely to exist in the container. But if we can't stat()
+                           * them we cannot determine the dev_t of them, and thus cannot figure out if they
+                           * are enrypted. Hence let's just ignore ENOENT here. */
+                return;
         assert_se(r >= 0);
 
         printf("%s encrypted: %s\n", p, yes_no(r));
index 4d7293fd7d847018750757e5088151b25d9f0eb1..75c2aec491ee1e7c42a545bbb71d5347df9b5db3 100644 (file)
@@ -328,7 +328,11 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr
 
         if (want_random) {
                 log_device_debug(device, "Using random bytes to generate MAC");
-                random_bytes(mac->ether_addr_octet, ETH_ALEN);
+
+                /* We require genuine randomness here, since we want to make sure we won't collide with other
+                 * systems booting up at the very same time. We do allow RDRAND however, since this is not
+                 * cryptographic key material. */
+                genuine_random_bytes(mac->ether_addr_octet, ETH_ALEN, RANDOM_ALLOW_RDRAND);
         } else {
                 uint64_t result;
 
index ca38f56087911f283165d6207ca69e67ec616c1c..48e95005483b853290b24e2cedda03c250d9fbec 100644 (file)
@@ -253,14 +253,20 @@ static sd_device *handle_scsi_iscsi(sd_device *parent, char **path) {
         return parent;
 }
 
-static sd_device *handle_scsi_ata(sd_device *parent, char **path) {
+static sd_device *handle_scsi_ata(sd_device *parent, char **path, char **compat_path) {
         sd_device *targetdev, *target_parent;
         _cleanup_(sd_device_unrefp) sd_device *atadev = NULL;
-        const char *port_no, *sysname;
+        const char *port_no, *sysname, *name;
+        unsigned host, bus, target, lun;
 
         assert(parent);
         assert(path);
 
+        if (sd_device_get_sysname(parent, &name) < 0)
+                return NULL;
+        if (sscanf(name, "%u:%u:%u:%u", &host, &bus, &target, &lun) != 4)
+                return NULL;
+
         if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host", &targetdev) < 0)
                 return NULL;
 
@@ -275,7 +281,17 @@ static sd_device *handle_scsi_ata(sd_device *parent, char **path) {
         if (sd_device_get_sysattr_value(atadev, "port_no", &port_no) < 0)
                 return NULL;
 
-        path_prepend(path, "ata-%s", port_no);
+        if (bus != 0)
+                /* Devices behind port multiplier have a bus != 0*/
+                path_prepend(path, "ata-%s.%u.0", port_no, bus);
+        else
+                /* Master/slave are distinguished by target id */
+                path_prepend(path, "ata-%s.%u", port_no, target);
+
+        /* old compatible persistent link for ATA devices */
+        if (compat_path)
+                path_prepend(compat_path, "ata-%s", port_no);
+
         return parent;
 }
 
@@ -392,7 +408,7 @@ static sd_device *handle_scsi_hyperv(sd_device *parent, char **path, size_t guid
         return parent;
 }
 
-static sd_device *handle_scsi(sd_device *parent, char **path, bool *supported_parent) {
+static sd_device *handle_scsi(sd_device *parent, char **path, char **compat_path, bool *supported_parent) {
         const char *devtype, *id, *name;
 
         if (sd_device_get_devtype(parent, &devtype) < 0 ||
@@ -426,7 +442,7 @@ static sd_device *handle_scsi(sd_device *parent, char **path, bool *supported_pa
         }
 
         if (strstr(name, "/ata"))
-                return handle_scsi_ata(parent, path);
+                return handle_scsi_ata(parent, path, compat_path);
 
         if (strstr(name, "/vmbus_"))
                 return handle_scsi_hyperv(parent, path, 37);
@@ -520,6 +536,7 @@ static sd_device *handle_ap(sd_device *parent, char **path) {
 static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
         sd_device *parent;
         _cleanup_free_ char *path = NULL;
+        _cleanup_free_ char *compat_path = NULL;
         bool supported_transport = false;
         bool supported_parent = false;
         const char *subsystem;
@@ -537,7 +554,7 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                 } else if (streq(subsys, "scsi_tape")) {
                         handle_scsi_tape(parent, &path);
                 } else if (streq(subsys, "scsi")) {
-                        parent = handle_scsi(parent, &path, &supported_parent);
+                        parent = handle_scsi(parent, &path, &compat_path, &supported_parent);
                         supported_transport = true;
                 } else if (streq(subsys, "cciss")) {
                         parent = handle_cciss(parent, &path);
@@ -557,19 +574,27 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                         }
                 } else if (streq(subsys, "pci")) {
                         path_prepend(&path, "pci-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "pci-%s", sysname);
                         parent = skip_subsystem(parent, "pci");
                         supported_parent = true;
                 } else if (streq(subsys, "platform")) {
                         path_prepend(&path, "platform-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "platform-%s", sysname);
                         parent = skip_subsystem(parent, "platform");
                         supported_transport = true;
                         supported_parent = true;
                 } else if (streq(subsys, "acpi")) {
                         path_prepend(&path, "acpi-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "acpi-%s", sysname);
                         parent = skip_subsystem(parent, "acpi");
                         supported_parent = true;
                 } else if (streq(subsys, "xen")) {
                         path_prepend(&path, "xen-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "xen-%s", sysname);
                         parent = skip_subsystem(parent, "xen");
                         supported_parent = true;
                 } else if (streq(subsys, "virtio")) {
@@ -577,16 +602,22 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                         supported_transport = true;
                 } else if (streq(subsys, "scm")) {
                         path_prepend(&path, "scm-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "scm-%s", sysname);
                         parent = skip_subsystem(parent, "scm");
                         supported_transport = true;
                         supported_parent = true;
                 } else if (streq(subsys, "ccw")) {
                         path_prepend(&path, "ccw-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "ccw-%s", sysname);
                         parent = skip_subsystem(parent, "ccw");
                         supported_transport = true;
                         supported_parent = true;
                 } else if (streq(subsys, "ccwgroup")) {
                         path_prepend(&path, "ccwgroup-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "ccwgroup-%s", sysname);
                         parent = skip_subsystem(parent, "ccwgroup");
                         supported_transport = true;
                         supported_parent = true;
@@ -596,6 +627,8 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                         supported_parent = true;
                 } else if (streq(subsys, "iucv")) {
                         path_prepend(&path, "iucv-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "iucv-%s", sysname);
                         parent = skip_subsystem(parent, "iucv");
                         supported_transport = true;
                         supported_parent = true;
@@ -604,6 +637,8 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
 
                         if (sd_device_get_sysattr_value(dev, "nsid", &nsid) >= 0) {
                                 path_prepend(&path, "nvme-%s", nsid);
+                                if (compat_path)
+                                        path_prepend(&compat_path, "nvme-%s", nsid);
                                 parent = skip_subsystem(parent, "nvme");
                                 supported_parent = true;
                                 supported_transport = true;
@@ -671,6 +706,14 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                 udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
         }
 
+        /*
+         * Compatible link generation for ATA devices
+         * we assign compat_link to the env variable
+         * ID_PATH_ATA_COMPAT
+         */
+        if (compat_path)
+                udev_builtin_add_property(dev, test, "ID_PATH_ATA_COMPAT", compat_path);
+
         return 0;
 }
 
index 4c955b0c079c3ee0a67db3a3001d681800ba871f..e9975030aae3cf518ed44a7cb2d54150b432efe1 100644 (file)
@@ -1814,7 +1814,7 @@ static int udev_rule_apply_token_to_event(
         case TK_M_IMPORT_CMDLINE: {
                 _cleanup_free_ char *value = NULL;
 
-                r = proc_cmdline_get_key(token->value, PROC_CMDLINE_VALUE_OPTIONAL, &value);
+                r = proc_cmdline_get_key(token->value, PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_IGNORE_EFI_OPTIONS, &value);
                 if (r < 0)
                         return log_rule_error_errno(dev, rules, r,
                                                     "Failed to read '%s' option from /proc/cmdline: %m",
index 33a1fb6fd09a480bd9fad341e3531eb681084cf4..221a18682a9f90820db8357ee837716c7b13bdbe 100755 (executable)
@@ -2,6 +2,7 @@
 set -e
 TEST_DESCRIPTION="Job-related tests"
 TEST_NO_QEMU=1
+IMAGE_NAME="default"
 
 . $TEST_BASE_DIR/test-functions
 
index 4a08cfd4ec0a5e74883877dd4d43227bece1be72..fbe52296cec9e955eeec756d7d4a627614a5e0cb 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="systemd-nspawn smoke test"
-IMAGE_NAME=nspawn
+IMAGE_NAME="nspawn"
 TEST_NO_NSPAWN=1
 
 . $TEST_BASE_DIR/test-functions
index d1486f0aae0ae9aeb7c4918dfc7cfb317d8d2e24..e8dbf23c580c954de3306d4a37ef9d12e9f61cc9 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="/etc/machine-id testing"
-IMAGE_NAME=badid
+IMAGE_NAME="badid"
 TEST_NO_NSPAWN=1
 
 . $TEST_BASE_DIR/test-functions
index f7dbbbf47b236cb7574efd3af179a25e5f39c96a..527db85b072a9e6e0c242bf8a685d573e1fa58b1 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Sysuser-related tests"
-IMAGE_NAME=sysusers
+IMAGE_NAME="sysusers"
 . $TEST_BASE_DIR/test-functions
 
 test_setup() {
diff --git a/test/fuzz/fuzz-bus-message/oss-fuzz-19446 b/test/fuzz/fuzz-bus-message/oss-fuzz-19446
new file mode 100644 (file)
index 0000000..f937ef8
Binary files /dev/null and b/test/fuzz/fuzz-bus-message/oss-fuzz-19446 differ
index 743ab903ea8cff9c5d14956e6d630bce69d6d9a8..2b95192672507acb3c1006eee44741ab554cf3b2 100644 (file)
@@ -38,6 +38,7 @@ MTUBytes=
 Multicast=
 MACAddress=
 Group=
+IPv6LinkLocalAddressGenerationMode=
 [BridgeFDB]
 VLANId=
 MACAddress=
@@ -105,6 +106,7 @@ SendVendorOption=
 SendDecline=
 MUDURL=
 RouteMTUBytes=
+FallbackLeaseLifetimeSec=
 [DHCPv6]
 UseNTP=
 UseDNS=
@@ -115,6 +117,8 @@ WithoutRA=
 MUDURL=
 SendOption=
 RequestOptions=
+UserClass=
+VendorClass=
 [Route]
 Destination=
 Protocol=
diff --git a/test/fuzz/fuzz-network-parser/github-15885 b/test/fuzz/fuzz-network-parser/github-15885
new file mode 100644 (file)
index 0000000..9bbdcb2
--- /dev/null
@@ -0,0 +1,9 @@
+[DHCPv4]
+SendOption=1:string:
+SendOption=1:uint8:
+SendOption=1:uint16:
+SendOption=1:uint32:
+SendOption=1:ipv4address:
+SendOption=1:ipv4address:127.0.0.1
+SendOption=1:ipv6address:
+SendOption=1:ipv6address:52:54:00:b9:b5:61
index 98cddad3494addaaa333ee33d364c32894820309..048bd34e9e2b05894508ad1cd6ffbfc8c6b100a1 100644 (file)
@@ -856,6 +856,7 @@ RateLimitIntervalSec=
 ReadKMsg=
 ReadOnly=
 ReadOnlyPaths=
+ReadWriteOnly=
 ReadWritePaths=
 RemoveIPC=
 ReserveVT=
@@ -864,6 +865,7 @@ RestrictNamespaces=
 RestrictRealtime=
 RestrictSUIDSGID=
 RuntimeDirectory=
+RuntimeDirectoryInodesMax=
 RuntimeDirectoryMode=
 RuntimeDirectoryPreserve=
 RuntimeDirectorySize=
index c514f57fe3d1618667bd65122f0efb01983239a5..b0305bdd0ec05457cb58851ce25943d552f27ea9 100644 (file)
@@ -1,17 +1,18 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
-sanitize_address = custom_target(
-        'sanitize-address-fuzzers',
-        output : 'sanitize-address-fuzzers',
+sanitize_address_undefined = custom_target(
+        'sanitize-address-undefined-fuzzers',
+        output : 'sanitize-address-undefined-fuzzers',
         command : [meson_build_sh,
                    project_source_root,
                    '@OUTPUT@',
                    'fuzzers',
-                   '-Db_lundef=false -Db_sanitize=address',
+                   '-Db_lundef=false -Db_sanitize=address,undefined ' +
+                   '--optimization=@0@'.format(get_option('optimization')),
                    ' '.join(cc.cmd_array()),
                    cxx_cmd])
 
-sanitizers = [['address', sanitize_address]]
+sanitizers = [['address,undefined', sanitize_address_undefined]]
 
 if git.found()
         out = run_command(
index 79130c3984dd1af7709171c01d874c99e001fe9c..f5a798b4b83cad560a216069873dfa8961cbe979 100644 (file)
@@ -18,6 +18,7 @@ EFI_MOUNT="${EFI_MOUNT:-$(bootctl -x 2>/dev/null || echo /boot)}"
 QEMU_MEM="${QEMU_MEM:-512M}"
 IMAGE_NAME=${IMAGE_NAME:-default}
 TEST_REQUIRE_INSTALL_TESTS="${TEST_REQUIRE_INSTALL_TESTS:-1}"
+TEST_PARALLELIZE="${TEST_PARALLELIZE:-0}"
 LOOPDEV=
 
 # Decide if we can (and want to) run QEMU with KVM acceleration.
@@ -329,12 +330,13 @@ systemd.wants=testsuite-$1.service ${_end} \
 $KERNEL_APPEND \
 "
 
+    [ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC"
     QEMU_OPTIONS="-smp $QEMU_SMP \
 -net none \
 -m $QEMU_MEM \
 -nographic \
 -kernel $KERNEL_BIN \
--drive format=raw,cache=unsafe,file=${IMAGESTATEDIR}/${IMAGE_NAME}.img \
+-drive format=raw,cache=unsafe,file=$image \
 $QEMU_OPTIONS \
 "
 
@@ -692,16 +694,14 @@ create_empty_image() {
         _size=$((4*_size))
     fi
 
-    image="${TESTDIR}/${IMAGE_NAME}.img"
-    public="$IMAGESTATEDIR/${IMAGE_NAME}.img"
-    echo "Setting up $public (${_size} MB)"
-    rm -f "$image" "$public"
+    echo "Setting up $IMAGE_PUBLIC (${_size} MB)"
+    rm -f "$IMAGE_PRIVATE" "$IMAGE_PUBLIC"
 
     # Create the blank file to use as a root filesystem
-    truncate -s "${_size}M" "$image"
-    ln -vs "$(realpath $image)" "$public"
+    truncate -s "${_size}M" "$IMAGE_PRIVATE"
+    ln -vs "$(realpath $IMAGE_PRIVATE)" "$IMAGE_PUBLIC"
 
-    LOOPDEV=$(losetup --show -P -f "$public")
+    LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC")
     [ -b "$LOOPDEV" ] || return 1
     sfdisk "$LOOPDEV" <<EOF
 ,$((_size-50))M
@@ -722,7 +722,7 @@ EOF
 
 mount_initdir() {
     if [ -z "${LOOPDEV}" ]; then
-        image="${IMAGESTATEDIR}/${IMAGE_NAME}.img"
+        [ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC"
         LOOPDEV=$(losetup --show -P -f "$image")
         [ -b "$LOOPDEV" ] || return 1
 
@@ -744,8 +744,7 @@ cleanup_initdir() {
 umount_loopback() {
     # unmount the loopback device from all places. Otherwise we risk file
     # system corruption.
-    image="${IMAGESTATEDIR}/${IMAGE_NAME}.img"
-    for device in $(losetup -l | awk '$6=="'"$image"'" {print $1}'); do
+    for device in $(losetup -l | awk '$6=="'"$IMAGE_PUBLIC"'" {print $1}'); do
         ddebug "Unmounting all uses of $device"
         mount | awk '/^'"${device}"'p/{print $1}' | xargs --no-run-if-empty umount -v
     done
@@ -1215,6 +1214,9 @@ TESTDIR="$TESTDIR"
 EOF
         export TESTDIR
     fi
+
+    IMAGE_PRIVATE="${TESTDIR}/${IMAGE_NAME}.img"
+    IMAGE_PUBLIC="${IMAGESTATEDIR}/${IMAGE_NAME}.img"
 }
 
 import_initdir() {
@@ -1964,7 +1966,7 @@ _test_cleanup() {
     (
         set +e
         _umount_dir $initdir
-        rm -vf "${IMAGESTATEDIR}/${IMAGE_NAME}.img"
+        rm -vf "$IMAGE_PUBLIC"
         rm -vfr "$TESTDIR"
         rm -vf "$STATEFILE"
     ) || :
@@ -2000,14 +2002,16 @@ test_setup() {
         exit 1
     fi
 
-    image="${TESTDIR}/${IMAGE_NAME}.img"
-    public="${IMAGESTATEDIR}/${IMAGE_NAME}.img"
-    if [ -e "$image" ]; then
-        echo "Reusing existing image $PWD/$image → $(realpath $image)"
+    if [ -e "$IMAGE_PRIVATE" ]; then
+        echo "Reusing existing image $IMAGE_PRIVATE → $(realpath $IMAGE_PRIVATE)"
         mount_initdir
-    elif [ -e "$public" ]; then
-        echo "Reusing existing cached image $PWD/$public → $(realpath $public)"
-        ln -s "$(realpath $public)" "$image"
+    elif [ -e "$IMAGE_PUBLIC" ]; then
+        echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath $IMAGE_PUBLIC)"
+        if [ ${TEST_PARALLELIZE} -ne 0 ]; then
+            cp -v "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
+        else
+            ln -sv "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
+        fi
         mount_initdir
     else
         test_create_image
index 79846f87879cfadc7be758e29fbf669ce0983cec..5b8690b687fe199d8e8dd121aab3f06129068f43 100755 (executable)
@@ -27,9 +27,15 @@ build=$WORK/build
 rm -rf $build
 mkdir -p $build
 
-fuzzflag="oss-fuzz=true"
 if [ -z "$FUZZING_ENGINE" ]; then
     fuzzflag="llvm-fuzz=true"
+else
+    fuzzflag="oss-fuzz=true"
+    if [[ "$SANITIZER" == undefined ]]; then
+        UBSAN_FLAGS="-fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow"
+        CFLAGS="$CFLAGS $UBSAN_FLAGS"
+        CXXFLAGS="$CXXFLAGS $UBSAN_FLAGS"
+    fi
 fi
 
 meson $build -D$fuzzflag -Db_lundef=false
index 2851d9229da57a416829f33781517c22d06f1e16..60f6237606f6269d8edc7bdb3254ae6ab731820a 100755 (executable)
@@ -65,7 +65,10 @@ for phase in "${PHASES[@]}"; do
         RUN_ASAN|RUN_CLANG_ASAN)
             if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then
                 ENV_VARS="-e CC=clang -e CXX=clang++"
-                MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764
+                # Build fuzzer regression tests only with clang (for now),
+                # see: https://github.com/systemd/systemd/pull/15886#issuecomment-632689604
+                # -Db_lundef=false: See https://github.com/mesonbuild/meson/issues/764
+                MESON_ARGS="-Db_lundef=false -Dfuzz-tests=true --optimization=1"
             fi
             docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Db_sanitize=address,undefined -Dsplit-usr=true $MESON_ARGS build
             $DOCKER_EXEC ninja -v -C build
index a4bb5b143fa67b6f843c0a02e9dd0c0c69825f9e..ca7fa6aab0666f1d0e00834c3a788dd6c32fdffb 100755 (executable)
@@ -22,11 +22,10 @@ cd $REPO_ROOT
 export PATH="$HOME/.local/bin/:$PATH"
 
 # We use a subset of https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks instead of "undefined"
-# because our fuzzers crash with "pointer-overflow" and "float-cast-overflow":
-# https://github.com/systemd/systemd/pull/12771#issuecomment-502139157
+# because our fuzzers crash with "float-cast-overflow":
 # https://github.com/systemd/systemd/pull/12812#issuecomment-502780455
 # TODO: figure out what to do about unsigned-integer-overflow: https://github.com/google/oss-fuzz/issues/910
-export SANITIZER="address -fsanitize=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,unsigned-integer-overflow,vla-bound,vptr -fno-sanitize-recover=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,vla-bound,vptr"
+export SANITIZER="address -fsanitize=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,unsigned-integer-overflow,vla-bound,vptr -fno-sanitize-recover=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,vla-bound,vptr"
 tools/oss-fuzz.sh
 
 FUZZING_TYPE=${1:-regression}
index ba3abc29ea535017ce49f8cb4437c0db984fb6d0..a0677196deae0f15fbbadc0b2e0ace3f91199563 100644 (file)
@@ -24,15 +24,15 @@ units = [
         ['hibernate.target',                    'ENABLE_HIBERNATE'],
         ['hybrid-sleep.target',                 'ENABLE_HIBERNATE'],
         ['suspend-then-hibernate.target',       'ENABLE_HIBERNATE'],
-        ['initrd-cleanup.service',              ''],
-        ['initrd-fs.target',                    ''],
-        ['initrd-parse-etc.service',            ''],
-        ['initrd-root-device.target',           ''],
-        ['initrd-root-fs.target',               ''],
-        ['initrd-switch-root.service',          ''],
-        ['initrd-switch-root.target',           ''],
-        ['initrd-udevadm-cleanup-db.service',   ''],
-        ['initrd.target',                       ''],
+        ['initrd-cleanup.service',              'ENABLE_INITRD'],
+        ['initrd-fs.target',                    'ENABLE_INITRD'],
+        ['initrd-parse-etc.service',            'ENABLE_INITRD'],
+        ['initrd-root-device.target',           'ENABLE_INITRD'],
+        ['initrd-root-fs.target',               'ENABLE_INITRD'],
+        ['initrd-switch-root.service',          'ENABLE_INITRD'],
+        ['initrd-switch-root.target',           'ENABLE_INITRD'],
+        ['initrd-udevadm-cleanup-db.service',   'ENABLE_INITRD'],
+        ['initrd.target',                       'ENABLE_INITRD'],
         ['kexec.target',                        ''],
         ['ldconfig.service',                    'ENABLE_LDCONFIG',
          'sysinit.target.wants/'],
@@ -225,7 +225,7 @@ in_units = [
         ['systemd-user-sessions.service',        'HAVE_PAM',
          'multi-user.target.wants/'],
         ['systemd-vconsole-setup.service',       'ENABLE_VCONSOLE'],
-        ['systemd-volatile-root.service',        ''],
+        ['systemd-volatile-root.service',        'ENABLE_INITRD'],
         ['systemd-repart.service',               'ENABLE_REPART',
          'sysinit.target.wants/ initrd-root-fs.target.wants/'],
         ['user-runtime-dir@.service',            ''],