jobs:
Fuzzing:
runs-on: ubuntu-latest
+ if: github.repository == 'systemd/systemd'
steps:
- name: Build Fuzzers
id: build
* 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.
[![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/>
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.
* 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
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
* 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)
- 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
- 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
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.
✓ SloppyOptions=
✓ LazyUnmount=
✓ ForceUnmount=
+✓ ReadWriteOnly=
```
## Automount Unit Settings
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
#########################################
# 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.
('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'))),
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>
<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
<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>
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>
@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 = ...;
<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"/>
<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
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>
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>
<funcsynopsis>
<funcsynopsisinfo>#include <systemd/sd-bus.h></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>
<funcsynopsis>
<funcsynopsisinfo>#include <systemd/sd-bus-vtable.h></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>
<funcsynopsis>
<funcsynopsisinfo>#include <systemd/sd-bus.h></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>.
<funcsynopsis>
<funcsynopsisinfo>#include <systemd/sd-bus.h></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>
<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>
<funcsynopsis>
<funcsynopsisinfo>#include <systemd/sd-bus.h></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>
<funcsynopsis>
<funcsynopsisinfo>#include <systemd/sd-bus.h></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>
<funcsynopsis>
<funcsynopsisinfo>#include <systemd/sd-bus.h></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>
<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
<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>
<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>
<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>
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>
<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>
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
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'
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)
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)
'smack',
'gshadow',
'idn',
+ 'initrd',
'nss-myhostname',
'nss-systemd']
have = get_option(term)
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
include_directories : includes,
link_with : [libshared],
install_rpath : rootlibexecdir,
- install : true,
+ install : conf.get('ENABLE_INITRD') == 1,
install_dir : rootlibexecdir)
executable(
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,
'@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
['openssl'],
['libcurl'],
['idn'],
+ ['initrd'],
['libidn2'],
['libidn'],
['libiptc'],
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')
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')
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}"
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"
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"
)
'--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'
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <sys/file.h>
#include <unistd.h>
#include "alloc-util.h"
*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);
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);
+}
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);
return -errno;
if (!S_ISBLK(st.st_mode))
- return -ENODEV;
+ return -ENOTBLK;
if (major(st.st_rdev) == 0)
return -ENODEV;
* 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;
}
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:
break;
}
- if (errno != -EINTR)
+ if (errno != EINTR)
return -errno;
}
#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"
(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
* 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) {
}
}
- 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 ☹️ */
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
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);
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;
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)
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"
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) {
if (!path)
return -ENOMEM;
- r = glob_extend(&names, path);
+ r = glob_extend(&names, path, 0);
if (r == -ENOENT)
continue;
if (r < 0)
/* 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;
}
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.
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;
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;
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;
/* 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 (;;) {
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);
#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);
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);
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;
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)
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;
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)
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;
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)
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;
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) {
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),
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),
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;
}
#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;
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
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;
}
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;
(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)
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;
}
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;
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)
(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);
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)
/* 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;
(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)
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);
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);
}
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),
}
}
-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) {
.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,
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);
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)) {
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;
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,
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;
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;
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)
return MANAGER_RUNNING;
}
-#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
-
static void manager_unref_uid_internal(
Manager *m,
Hashmap **uid_refs,
}
}
-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) {
_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"
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];
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);
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);
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_;
"%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),
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)
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))
"-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
bool lazy_unmount;
bool force_unmount;
+ bool read_write_only;
+
MountResult result;
MountResult reload_result;
MountResult clean_result;
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;
/* 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;
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);
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)
#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;
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(
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) {
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)
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);
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) {
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
#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"
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;
/* 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) {
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);
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();
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"))
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;
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) {
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 */
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, ¶ms);
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 */
}
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) {
_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);
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)
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 */
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) {
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;
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];
/* 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);
}
/* 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);
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)
} 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]);
AUTOMOUNT = 1 << 2,
MAKEFS = 1 << 3,
GROWFS = 1 << 4,
+ RWONLY = 1 << 5,
} MountpointFlags;
static const char *arg_dest = NULL;
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);
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))
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");
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);
}
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;
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;
case HOME_UNFIXATED:
case HOME_ABSENT:
case HOME_INACTIVE:
- r = 0; /* done */
+ r = 1; /* done */
break;
case HOME_LOCKED:
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);
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:
_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;
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) {
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;
}
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");
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");
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;
}
#
# See homed.conf(5) for details
-[Resolve]
+[Home]
#DefaultStorage=
#DefaultFileSystemType=ext4
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;
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;
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;
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;
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;
/* 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;
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");
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;
} 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;
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)
(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;
}
#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);
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;
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 */
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;
}
}
if (r < 0)
return log_oom();
- return 0;
+ return 1;
}
#else
need_token = true;
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) {
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:
*
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;
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 {
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)
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)
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;
/* 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;
* 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"))
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);
#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,
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
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)
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);
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;
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;
}
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;
* 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;
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;
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;
/* 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;
}
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));
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,
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,
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. */
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;
usec_t t;
int r;
+ if (parse_env(handle, &please_suspend) < 0)
+ return PAM_AUTH_ERR;
+
if (parse_argv(handle,
argc, argv,
&please_suspend,
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;
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;
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,
typedef struct Context {
char *data[_PROP_MAX];
Hashmap *polkit_registry;
- sd_id128_t uuid;
- bool has_uuid;
} Context;
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);
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;
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;
}
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;
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)
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;
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),
}
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;
cursor);
}
- return process_journal_input(u, 1 + !!after_cursor);
+ return process_journal_input(u, !!after_cursor);
}
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;
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)
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;
}
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) {
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);
}
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);
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
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),
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),
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)
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)))
offset);
break;
+ }
case OBJECT_TAG:
if (le64toh(o->object.size) != sizeof(TagObject))
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),
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);
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);
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;
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;
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;
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;
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;
}
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,
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);
le64_t *idx,
uint64_t p) {
+ uint64_t hidx;
int r;
assert(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;
}
static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
+ uint64_t sq;
Object *o;
int r;
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;
static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
Object *o;
+ uint64_t rt;
int r;
assert(f);
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;
static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
Object *o;
+ uint64_t m;
int r;
assert(f);
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;
assert(f);
assert(f->header);
- n = le64toh(f->header->n_entries);
+ n = le64toh(READ_NOW(f->header->n_entries));
if (n <= 0)
return 0;
if (r < 0)
return r;
- n = le64toh(d->data.n_entries);
+ n = le64toh(READ_NOW(d->data.n_entries));
if (n <= 0)
return n;
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)
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;
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 */
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;
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);
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);
* 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");
}
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) {
!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;
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;
}
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;
}
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;
}
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;
}
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 */
#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 */
#include "time-util.h"
#include "unaligned.h"
+#define SYSTEMD_PEN 43793
+
typedef enum DUIDType {
DUID_TYPE_LLT = 1,
DUID_TYPE_EN = 2,
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);
#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"
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);
char *mudurl;
char **user_class;
uint32_t mtu;
+ uint32_t fallback_lease_lifetime;
uint32_t xid;
usec_t start_time;
uint64_t attempt;
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);
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
/* 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);
}
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);
}
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);
}
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;
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)
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);
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) {
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;
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);
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++)
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);
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)
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)
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)
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);
}
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
};
#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);
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))
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)
* 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;
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 */
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);
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;
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),
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)
#IdleAction=ignore
#IdleActionSec=30min
#RuntimeDirectorySize=10%
+#RuntimeDirectoryInodes=400k
#RemoveIPC=yes
#InhibitorsMax=8192
#SessionsMax=8192
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;
};
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
const char *username = NULL, *json = NULL;
+ _cleanup_free_ char *field = NULL;
int r;
assert(handle);
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 */
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)) {
assert(handle);
- /* Make this a NOP on non-logind systems */
- if (!logind_running())
- return PAM_SUCCESS;
-
if (parse_argv(handle,
argc, argv,
&class_pam,
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
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 */
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;
}
}
+success:
r = apply_user_record_settings(handle, ur, debug);
if (r != PAM_SUCCESS)
return r;
#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;
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;
}
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;
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);
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;
: "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) {
#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) {
#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;
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);
#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"
/* 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;
(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);
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);
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) {
void *userdata) {
Network *network = userdata;
_cleanup_(address_free_or_set_invalidp) Address *n = NULL;
- unsigned k;
+ uint32_t k;
int r;
assert(filename);
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;
}
n->cinfo.ifa_prefered = k;
- n = NULL;
+ TAKE_PTR(n);
return 0;
}
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,
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;
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);
"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;
"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;
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;
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;
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);
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;
}
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 */
}
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];
}
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];
}
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)
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,
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",
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);
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");
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)
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",
_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;
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);
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)
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)
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)
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)
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)
.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,
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);
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;
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;
bool configure_without_carrier;
bool ignore_carrier_loss;
KeepConfiguration keep_configuration;
+ LinkIPv6AddressGenMode ipv6_address_gen_mode;
uint32_t iaid;
DUID duid;
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,
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);
#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"
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;
/* 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;
}
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 },
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();
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;
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
$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
$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
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);
if (STR_IN_SET(field, "SloppyOptions",
"LazyUnmount",
- "ForceUnmount"))
+ "ForceUnmount",
+ "ReadwriteOnly"))
return bus_append_parse_boolean(m, field, eq);
return 0;
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)
}
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...
* 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", ×tamp_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, ×tamp);
- 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", ×tamp_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, ×tamp);
+ 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) {
#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"
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,
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);
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);
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);
return true;
#endif
+ if (path_is_generator(p, rpath))
+ return true;
+
return path_equal(rpath, SYSTEM_DATA_UNIT_PATH);
}
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)
--- /dev/null
+/* 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);
--- /dev/null
+/* 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_;
install-printf.h
install.c
install.h
+ ipvlan-util.c
+ ipvlan-util.h
ip-protocol-list.c
ip-protocol-list.h
journal-importer.c
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",
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);
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);
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,
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
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));
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;
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;
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;
}
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 ||
}
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);
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;
} 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);
}
} 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")) {
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;
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;
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;
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;
}
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",
set -e
TEST_DESCRIPTION="Job-related tests"
TEST_NO_QEMU=1
+IMAGE_NAME="default"
. $TEST_BASE_DIR/test-functions
#!/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
#!/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
#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Sysuser-related tests"
-IMAGE_NAME=sysusers
+IMAGE_NAME="sysusers"
. $TEST_BASE_DIR/test-functions
test_setup() {
Multicast=
MACAddress=
Group=
+IPv6LinkLocalAddressGenerationMode=
[BridgeFDB]
VLANId=
MACAddress=
SendDecline=
MUDURL=
RouteMTUBytes=
+FallbackLeaseLifetimeSec=
[DHCPv6]
UseNTP=
UseDNS=
MUDURL=
SendOption=
RequestOptions=
+UserClass=
+VendorClass=
[Route]
Destination=
Protocol=
--- /dev/null
+[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
ReadKMsg=
ReadOnly=
ReadOnlyPaths=
+ReadWriteOnly=
ReadWritePaths=
RemoveIPC=
ReserveVT=
RestrictRealtime=
RestrictSUIDSGID=
RuntimeDirectory=
+RuntimeDirectoryInodesMax=
RuntimeDirectoryMode=
RuntimeDirectoryPreserve=
RuntimeDirectorySize=
# 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(
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.
$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 \
"
_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
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
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
EOF
export TESTDIR
fi
+
+ IMAGE_PRIVATE="${TESTDIR}/${IMAGE_NAME}.img"
+ IMAGE_PUBLIC="${IMAGESTATEDIR}/${IMAGE_NAME}.img"
}
import_initdir() {
(
set +e
_umount_dir $initdir
- rm -vf "${IMAGESTATEDIR}/${IMAGE_NAME}.img"
+ rm -vf "$IMAGE_PUBLIC"
rm -vfr "$TESTDIR"
rm -vf "$STATEFILE"
) || :
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
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
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
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}
['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/'],
['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', ''],