kernel-install: add support for KERNEL_INSTALL_INITRD_GENERATOR and KERNEL_INSTALL_STAGING_AREA
--- /dev/null
+FROM gcr.io/oss-fuzz-base/base-builder@sha256:14b332de0e18683f37386eaedbf735bc6e8d81f9c0e1138d620f2178e20cd30a
+ENV MERGE_WITH_OSS_FUZZ_CORPORA=yes
+COPY . $SRC/systemd
+WORKDIR $SRC/systemd
+COPY tools/oss-fuzz.sh $SRC/build.sh
<!-- **NOTE:** Do not submit bug reports about anything but the two most recently released *major* systemd versions upstream! -->
<!-- If there have been multiple stable releases for that major version, please consider updating to a recent one before reporting an issue. -->
<!-- When using a distro package, please make sure that the version reported is meaningful for upstream. -->
-<!-- See https://github.com/systemd/systemd-stable/releases for the list of most recent releases. -->
+<!-- See https://github.com/systemd/systemd-stable/tags for the list of most recent releases. -->
<!-- For older version please use distribution trackers (see https://systemd.io/CONTRIBUTING#filing-issues). -->
**Used distribution**
- package-ecosystem: "github-actions"
directory: "/"
schedule:
- interval: "weekly"
+ interval: "monthly"
open-pull-requests-limit: 2
- package-ecosystem: "pip"
directory: "/.github/workflows"
schedule:
interval: "monthly"
open-pull-requests-limit: 2
+ - package-ecosystem: "docker"
+ directory: "/.clusterfuzzlite"
+ schedule:
+ interval: "monthly"
+ open-pull-requests-limit: 2
--- /dev/null
+---
+# vi: ts=2 sw=2 et:
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+name: ClusterFuzzLite PR fuzzing
+on:
+ pull_request:
+ branches:
+ - main
+ - v[0-9]+-stable
+
+permissions: read-all
+
+jobs:
+ PR:
+ runs-on: ubuntu-latest
+ if: github.repository != 'systemd/systemd' || github.event.pull_request.user.login == 'dependabot[bot]'
+ concurrency:
+ group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }}
+ cancel-in-progress: true
+ strategy:
+ fail-fast: false
+ matrix:
+ sanitizer: [address, undefined, memory]
+ steps:
+ - name: Build Fuzzers (${{ matrix.sanitizer }})
+ id: build
+ uses: google/clusterfuzzlite/actions/build_fuzzers@41dccd0566905e2a7d1724e7883edbfa66d78877
+ with:
+ sanitizer: ${{ matrix.sanitizer }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: Run Fuzzers (${{ matrix.sanitizer }})
+ id: run
+ uses: google/clusterfuzzlite/actions/run_fuzzers@41dccd0566905e2a7d1724e7883edbfa66d78877
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ fuzz-seconds: 1200
+ mode: 'code-change'
+ sanitizer: ${{ matrix.sanitizer }}
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Initialize CodeQL
- uses: github/codeql-action/init@cd783c8a29bdcf5a5c79c5137889e24651fa626c
+ uses: github/codeql-action/init@384cfc42b2131df01c009d3d2eed7b78d8e8556e
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql-config.yml
- run: sudo -E .github/workflows/unit_tests.sh SETUP
- name: Autobuild
- uses: github/codeql-action/autobuild@cd783c8a29bdcf5a5c79c5137889e24651fa626c
+ uses: github/codeql-action/autobuild@384cfc42b2131df01c009d3d2eed7b78d8e8556e
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@cd783c8a29bdcf5a5c79c5137889e24651fa626c
+ uses: github/codeql-action/analyze@384cfc42b2131df01c009d3d2eed7b78d8e8556e
trigger: pull_request
metadata:
targets:
- - fedora-rawhide-aarch64
- - fedora-rawhide-i386
- - fedora-rawhide-ppc64le
- - fedora-rawhide-x86_64
+ - fedora-35-aarch64
+ - fedora-35-i386
+ - fedora-35-ppc64le
+ - fedora-35-x86_64
systemd System and Service Manager
+CHANGES WITH 251:
+ * Incompatibility and Regression note:
+ In v250, the feature that automatically configures routes to addresses
+ specified in AllowedIPs= was added and enabled by default. However,
+ this feature causes network connectivity issues on many existing
+ setups. Hence, this is disabled by default since v250.3. The feature
+ can still be used by explicitly configuring RouteTable= setting in
+ .netdev files.
+
+ * Services with Restart=always and a failing ExecCondition= will no longer
+ be restarted, to bring ExecCondition= in line with Condition*= settings.
+
+ * In v250 systemd-homed started making use of UID mapped mounts for the
+ home areas if the kernel and used file system support it. Files are
+ now internally owned by the "nobody" user (i.e. the user typically
+ used for indicating "this ownership is not mapped"), and dynamically
+ mapped to the UID used locally on the system via the UID mapping
+ mount logic of recent kernels.
+ In the current implementation systemd-homed only maps a limited
+ number of UIDs and GIDs making it impossible to run unprivileged
+ containers that want to map a full POSIX compliant UID and GID range
+ with their rootfs located within the systemd-homed managed home area.
+ This will be fixed in subsequent releases. See
+ https://github.com/systemd/systemd/pull/22239 for a proposal.
+
CHANGES WITH 250:
* Support for encrypted and authenticated credentials has been added.
Features:
+* automatically ignore threaded cgroups in cg_xyz().
+
* add linker script that implicitly adds symbol for build ID and new coredump
json package metadata, and use that when logging
support_sed = 's~%SUPPORT_URL%~@0@~'.format(support_url)
foreach file : in_files
- custom_target(
+ catalogs += custom_target(
file,
input : file + '.in',
output: file,
care. Replicating the cgroup hierarchies in those unsupported controllers would
mean replicating the full cgroup paths in them, and hence the prefixing
`.slice` components too, otherwise the hierarchies will start being orthogonal
-after all, and that's not really desirable. On more thing: systemd will clean
+after all, and that's not really desirable. One more thing: systemd will clean
up after you in the hierarchies it manages: if your daemon goes down, its
cgroups will be removed too. You basically get the guarantee that you start
with a pristine cgroup sub-tree for your service or scope whenever it is
- For every function you add, think about whether it is a "logging" function or
a "non-logging" function. "Logging" functions do (non-debug) logging on their
- own, "non-logging" function never log on their own (except at debug level)
+ own, "non-logging" functions never log on their own (except at debug level)
and expect their callers to log. All functions in "library" code, i.e. in
`src/shared/` and suchlike must be "non-logging". Every time a "logging"
function calls a "non-logging" function, it should log about the resulting
1. Do not drop `CAP_MKNOD` from the container. `PrivateDevices=` is a commonly
used service setting that provides a service with its own, private, minimal
version of `/dev/`. To set this up systemd in the container needs this
- capability. If you take away the capability than all services that set this
+ capability. If you take away the capability, then all services that set this
flag will cease to work. Use `BPF_PROG_TYPE_CGROUP_DEVICE` BPF programs — on
cgroupv2 — or the `devices` controller — on cgroupv1 — to restrict what
device nodes the container can create instead of taking away the capability
* `$SYSTEMD_FUZZ_RUNS` — The number of times execution should be repeated in
manual invocations.
-Note that is may be also useful to set `$SYSTEMD_LOG_LEVEL`, since all logging
+Note that it may be also useful to set `$SYSTEMD_LOG_LEVEL`, since all logging
is suppressed by default.
`systemd-importd`:
# GVariant D-Bus Message Serialization
-We stay close to the original dbus1 framing as possible, but make
+We stay as close to the original dbus1 framing as possible, but make
certain changes to adapt for GVariant. dbus1 has the following
framing:
file system archive that is unpacked by the kernel and contains the first
userspace code that runs. It typically finds and transitions into the actual
root file system to use. systemd supports both initrd and initrd-less boots. If
-an initrd is used it is a good idea to pass a few bits of runtime information
+an initrd is used, it is a good idea to pass a few bits of runtime information
from the initrd to systemd in order to avoid duplicate work and to provide
performance data to the administrator. In this page we attempt to roughly
describe the interfaces that exist between the initrd and systemd. These
STATE_OFFLINE it should immediately rotate the file and start a new one,
without changing the file.
-After and before the state field is changed `fdatasync()` should be executed on
+After and before the state field is changed, `fdatasync()` should be executed on
the file to ensure the dirty state hits disk.
The latter is what this document is about: if you are developing a program and
want to pass structured log data to `journald`, it's the Journal's native
-protocol what you want to use. The systemd project provides the
+protocol that you want to use. The systemd project provides the
[`sd_journal_print(3)`](https://www.freedesktop.org/software/systemd/man/sd_journal_print.html)
API that implements the client side of this protocol. This document explains
what this interface does behind the scenes, in case you'd like to implement a
Again, it is essential that you stop showing the password box/notification/status icon if the `ask.xxx` file is removed or when `NotAfter=` elapses (if it is set `!= 0`)!
-It may happen that multiple password entries are pending at the same time. Your agent needs to be able to deal with that. Depending on your environment you may either choose to show all outstanding passwords at the same time or instead only one and as soon as the user replied to that one go on to the next one.
+It may happen that multiple password entries are pending at the same time. Your agent needs to be able to deal with that. Depending on your environment you may either choose to show all outstanding passwords at the same time or instead only one and as soon as the user has replied to that one go on to the next one.
You may test this all with manually invoking the "`systemd-ask-password`" tool on the command line. Pass `--no-tty` to ensure the password is asked via the agent system. Note that only privileged users may use this tool (after all this is intended purely for system-level passwords).
4. The upper extension(s) image(s) must at least contain one matching unit file each,
with the right name prefix and suffix (see above).
+5. As with the base/OS image, the upper extension(s) image(s) must be a plain
+ sub-directory, a btrfs subvolume or a raw disk image.
+
```
# portablectl attach --extension foobar_0.7.23.raw debian-runtime_11.1.raw foobar
-# portablectl attach --extension barbaz_7.0.23.raw debian-runtime_11.1.raw barbaz
+# portablectl attach --extension barbaz_7.0.23/ debian-runtime_11.1.raw barbaz
```
## Execution Environment
differentiate them. i.e. `~foo.com` is a configured routing domain, while
`foo.com` would be a configured search domain.
- One routing domain is particular interesting: `~.` — the catch-all routing
+ One routing domain is particularly interesting: `~.` — the catch-all routing
domain. (The *dot* domain `.` is how DNS denotes the "root" domain, i.e. the
parent domain of all domains, but itself.) When used on an interface any DNS
traffic is preferably routed to its DNS servers. (A search domain – i.e. `.`
recommended. It will nevertheless query `/etc/login.defs` at runtime, when
compiled with `-Dcompat-mutable-uid-boundaries=true` and that file is present.
Support for this is considered only a compatibility feature and should not be
-used except when upgrading systems which were creating with different defaults.
+used except when upgrading systems which were created with different defaults.
## Considerations for container managers
| 2147483648…4294967294 | HIC SVNT LEONES | | |
| 4294967295 | 32bit `(uid_t) -1` | Linux | |
-Note that "Unused" in the table above doesn't meant that these ranges are
+Note that "Unused" in the table above doesn't mean that these ranges are
really unused. It just means that these ranges have no well-established
pre-defined purposes between Linux, generic low-level distributions and
`systemd`. There might very well be other packages that allocate from these
record at all. (The `service` field is defined in order to allow implementation
of daemons that provide multiple distinct user/group services over the same
`AF_UNIX` socket: in order to correctly determine which service a client wants
-to talk to the client needs to provide the name in each request.)
+to talk to, the client needs to provide the name in each request.)
The `GetGroupRecord` method call works analogously but for groups.
there are multiple members per group and also multiple groups a user is
member of). As with `GetUserRecord` and `GetGroupRecord` the `service`
parameter needs to contain the name of the service being talked to, in order to
-allow implementation of multiple service within the same IPC socket. In case no
+allow implementation of multiple services within the same IPC socket. In case no
matching membership is known `NoRecordFound` is returned. The other two errors
are also generated in the same cases as for `GetUserRecord` and
`GetGroupRecord`.
Note that only the `GetMemberships` call is authoritative about memberships of
users in groups. i.e. it should not be considered sufficient to check the
`memberOf` field of user records and the `members` field of group records to
-acquire the full list of memberships. The full list can only bet determined by
+acquire the full list of memberships. The full list can only be determined by
`GetMemberships`, and as mentioned requires merging of these lists of all local
services. Result of this is that it can be one service that defines a user A,
and another service that defines a group B, and a third service that declares
[`setrlimit()`](http://man7.org/linux/man-pages/man2/setrlimit.2.html) for more
information.
-`locked` → A boolean value. If true the user account is locked, the user may
+`locked` → A boolean value. If true, the user account is locked, the user may
not log in. If this field is missing it should be assumed to be false,
i.e. logins are permitted. This field corresponds to the `sp_expire` field of
`struct spwd` (i.e. the `/etc/shadow` data for a user) being set to zero or
LUKS volume that is mounted as home directory, and `cifs` a home directory
mounted from a Windows File Share. The five latter types are primarily used by
`systemd-homed` when managing home directories, but may be used if other
-managers are used too. If this is not set `classic` is the implied default.
+managers are used too. If this is not set, `classic` is the implied default.
`diskSize` → An unsigned 64bit integer, indicating the intended home directory
disk space in bytes to assign to the user. Depending on the selected storage
-type this might be implement differently: for `luks` this is the intended size
+type this might be implemented differently: for `luks` this is the intended size
of the file system and LUKS volume, while for the others this likely translates
to classic file system quota settings.
`imagePath` → A string with an absolute file system path to the file, directory
or block device to use for storage backing the home directory. If the `luks`
-storage is used this refers to the loopback file or block device node to store
+storage is used, this refers to the loopback file or block device node to store
the LUKS volume on. For `fscrypt`, `directory`, `subvolume` this refers to the
directory to bind mount as home directory on login. Not defined for `classic`
or `cifs`.
referencing the file system UUID the home directory is located in. This is
primarily relevant when the storage mechanism used is `luks`.
-`luksDiscard` → A boolean. If true and `luks` storage is used controls whether
+`luksDiscard` → A boolean. If true and `luks` storage is used, controls whether
the loopback block devices, LUKS and the file system on top shall be used in
`discard` mode, i.e. erased sectors should always be returned to the underlying
storage. If false and `luks` storage is used turns this behavior off. In
private key found with it it is used.
`fido2HmacCredential` → An array of strings, each with a Base64-encoded FIDO2
-credential ID that shell be used for authentication with FIDO2 devices that
+credential ID that shall be used for authentication with FIDO2 devices that
implement the `hmac-secret` extension. The salt to pass to the FIDO2 device is
found in `fido2HmacSalt`.
dmi:bvnLENOVO*
ID_SYSFS_ATTRIBUTE_MODEL=product_version
ID_VENDOR_FROM_DATABASE=Lenovo
+
+# Microsoft Surface 1's chassis type
+dmi:bvnMicrosoft Corporation*:pvrSurface with Windows 8 Pro*
+ ID_CHASSIS=tablet
EVDEV_ABS_35=::31
EVDEV_ABS_36=::30
+#########################################
+# Microsoft
+#########################################
+
+# Surface Laptop 2 (13")
+evdev:name:Microsoft Surface 045E:0933 Touchpad:dmi:*svnMicrosoftCorporation:*pnSurfaceLaptop2**
+ EVDEV_ABS_00=::38
+ EVDEV_ABS_01=::38
+ EVDEV_ABS_35=::38
+ EVDEV_ABS_36=::38
+
+# Surface Laptop 3 (15")
+evdev:name:Microsoft Surface 045E:09AF Touchpad:dmi:*svnMicrosoftCorporation:*pnSurfaceLaptop3**
+ EVDEV_ABS_00=::39
+ EVDEV_ABS_01=::37
+ EVDEV_ABS_35=::39
+ EVDEV_ABS_36=::37
+
#########################################
# NEWYES
#########################################
# Logitech MX Keys
id-input:modalias:input:b0003v046Dp408Ae0111*
- ID_INPUT_MOUSE=0
+ ID_INPUT_MOUSE=0
+
+# Logitech Craft Keyboard
+id-input:modalias:input:b0003v046Dp4066e0111*
+ ID_INPUT_MOUSE=0
+
+# CH Products Pro Pedals
+id-input:modalias:input:b0003v068Ep00F2e0100*
+ ID_INPUT_ACCELEROMETER=0
+ ID_INPUT_JOYSTICK=1
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/25/2017:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
ACCEL_LOCATION=base
+# GPD Pocket 3
+sensor:modalias:acpi:MXC6655*:dmi:*:svnGPD:pnG1621-02:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+
#########################################
# Hometech
########################################
mouse:usb:v056ep010d:name:ELECOM TrackBall Mouse HUGE TrackBall:*
MOUSE_DPI=500@125 *1000@125 1500@125
+# Elecom DEFT Pro TrackBall (M-DPT1MR)
+mouse:usb:v056ep0131:name:ELECOM TrackBall Mouse DEFT Pro TrackBall Mouse:*
+ MOUSE_DPI=*500 1000 1500
+ MOUSE_WHEEL_CLICK_ANGLE=10
+
+# Elecom Relacon (M-RT1DR)
+mouse:usb:v056ep0155:name:ELECOM ELECOM Relacon:*
+ ID_INPUT_TRACKBALL=1
+ MOUSE_DPI=*500 1000 1500
+ MOUSE_WHEEL_CLICK_ANGLE=30
+
##########################################
# Fujitsu Siemens
##########################################
--- /dev/null
+# This file is part of systemd.
+#
+# Database for handhelds (PDAs, calculators, etc.) that should be accessible
+# the seat owner.
+#
+# Permitted keys:
+# Specify if a device is a signal analyzer
+# ID_PDA=1|0
+
+###########################################################
+# Texas Instruments
+###########################################################
+# SilverLink
+usb:v0451pE001*
+ ID_PDA=1
+
+# TI-84 Plus DirectLink
+usb:v0451pE003*
+ ID_PDA=1
+
+# TI-89 Titanium DirectLink
+usb:v0451pE004*
+ ID_PDA=1
+
+# TI-84 Plus Silver Edition DirectLink
+usb:v0451pE008*
+ ID_PDA=1
+
+# TI-Nspire DirectLink
+usb:v0451pE012*
+ ID_PDA=1
+
+# TI-Nspire Lab Cradle
+usb:v0451pE01C*
+ ID_PDA=1
+
+# TI-Nspire CX II DirectLink
+usb:v0451pE022*
+ ID_PDA=1
70-cameras.hwdb
70-joystick.hwdb
70-mouse.hwdb
+ 70-pda.hwdb
70-pointingstick.hwdb
70-touchpad.hwdb
80-ieee1394-unit-function.hwdb
def property_grammar():
ParserElement.setDefaultWhitespaceChars(' ')
- dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ'))('SETTINGS*')
+ dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Optional(Suppress('@') + INTEGER('HZ')))('SETTINGS*')
mount_matrix_row = SIGNED_REAL + ',' + SIGNED_REAL + ',' + SIGNED_REAL
mount_matrix = Group(mount_matrix_row + ';' + mount_matrix_row + ';' + mount_matrix_row)('MOUNT_MATRIX')
xkb_setting = Optional(Word(alphanums + '+-/@._'))
('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER),
('ID_AUTOSUSPEND', Or((Literal('0'), Literal('1')))),
('ID_PERSIST', Or((Literal('0'), Literal('1')))),
+ ('ID_PDA', Or((Literal('0'), Literal('1')))),
('ID_INPUT', Or((Literal('0'), Literal('1')))),
('ID_INPUT_ACCELEROMETER', Or((Literal('0'), Literal('1')))),
('ID_INPUT_JOYSTICK', Or((Literal('0'), Literal('1')))),
<citerefentry project='man-pages'><refentrytitle>less</refentrytitle><manvolnum>1</manvolnum></citerefentry> and
<citerefentry project='man-pages'><refentrytitle>more</refentrytitle><manvolnum>1</manvolnum></citerefentry>, until one is found. If
no pager implementation is discovered no pager is invoked. Setting this environment variable to an empty string
- or the value <literal>cat</literal> is equivalent to passing <option>--no-pager</option>.</para></listitem>
+ or the value <literal>cat</literal> is equivalent to passing <option>--no-pager</option>.</para>
+
+ <para>Note: if <varname>$SYSTEMD_PAGERSECURE</varname> is not set, <varname>$SYSTEMD_PAGER</varname>
+ (as well as <varname>$PAGER</varname>) will be silently ignored.</para></listitem>
</varlistentry>
<varlistentry id='less'>
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
will be used.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>VENDOR=</varname></term>
+
+ <listitem><para>Specifies the hardware vendor. If unspecified, the hardware vendor set in DMI
+ or hwdb will be used.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MODEL=</varname></term>
+
+ <listitem><para>Specifies the hardware model. If unspecified, the hardware model set in DMI or
+ hwdb will be used.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
if conf.get('BUILD_MODE_DEVELOPER') == 1
test('dbus-docs-fresh',
update_dbus_docs_py,
- args : ['--build-dir', project_build_root, '--test', dbus_docs])
+ args : ['--build-dir', project_build_root, '--test', dbus_docs],
+ depends : dbus_programs)
endif
update_man_rules = custom_target(
in b interactive);
GetProductUUID(in b interactive,
out ay uuid);
+ GetHardwareSerial(in b interactive,
+ out s serial);
Describe(out s json);
properties:
readonly s Hostname = '...';
};
</programlisting>
+ <!--method GetHardwareSerial is not documented!-->
+
<!--property HardwareVendor is not documented!-->
<!--property HardwareModel is not documented!-->
<variablelist class="dbus-method" generated="True" extra-ref="GetProductUUID()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="GetHardwareSerial()"/>
+
<variablelist class="dbus-method" generated="True" extra-ref="Describe()"/>
<variablelist class="dbus-property" generated="True" extra-ref="Hostname"/>
out a{say} units);
GetImageState(in s image,
out s state);
+ GetImageStateWithExtensions(in s image,
+ in as extensions,
+ in t flags,
+ out s state);
AttachImage(in s image,
in as matches,
in s profile,
<variablelist class="dbus-method" generated="True" extra-ref="GetImageState()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="GetImageStateWithExtensions()"/>
+
<variablelist class="dbus-method" generated="True" extra-ref="AttachImage()"/>
<variablelist class="dbus-method" generated="True" extra-ref="AttachImageWithExtensions()"/>
This method is a superset of <function>GetImageMetadata()</function> with the addition of
a list of extensions as input parameter, which were overlaid on top of the main
image via <function>AttachImageWithExtensions()</function>.
- The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
+ The <varname>flag</varname> parameter can be used to request that, before the units, the path of
+ each extension and an array of bytes with the content of the respective extension-release file
+ are sent. One such structure will be sent for each extension named in the input arguments. The
+ flag value to enable this functionality is defined as follows:</para>
+
+ <programlisting>
+#define PORTABLE_INSPECT_EXTENSION_RELEASES (UINT64_C(1) << 1)
+ </programlisting>
+
<para><function>GetImageState()</function> retrieves the image state as one of the following
strings:
<listitem><para>running-runtime</para></listitem>
</itemizedlist></para>
+ <para><function>GetImageStateWithExtensions()</function> is a superset of
+ <function>GetImageState()</function>, with additional support for a list of extensions
+ as input parameters, which is necessary to query the state in case the image was attached
+ in that particular way. The <varname>flag</varname> parameter is currently unused and
+ reserved for future purposes.</para>
+
<para><function>AttachImage()</function> attaches a portable image to the system.
This method takes an image path or name, a list of strings that will be used to search for
unit files inside the image (partial or complete matches), a string indicating which
out ay os_release,
out a{say} units);
GetState(out s state);
+ GetStateWithExtensions(in as extensions,
+ in t flags,
+ out s state);
Attach(in as matches,
in s profile,
in b runtime,
<!--method GetState is not documented!-->
+ <!--method GetStateWithExtensions is not documented!-->
+
<!--method Attach is not documented!-->
<!--method AttachWithExtensions is not documented!-->
<variablelist class="dbus-method" generated="True" extra-ref="GetState()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="GetStateWithExtensions()"/>
+
<variablelist class="dbus-method" generated="True" extra-ref="Attach()"/>
<variablelist class="dbus-method" generated="True" extra-ref="AttachWithExtensions()"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s RootVerity = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ExtensionDirectories = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sba(ss)) ExtensionImages = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ssba(ss)) MountImages = [...];
<variablelist class="dbus-property" generated="True" extra-ref="RootVerity"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
<variablelist class="dbus-property" generated="True" extra-ref="MountImages"/>
<varname>RootHashSignature</varname>
<varname>MountImages</varname>
<varname>ExtensionImages</varname>
+ <varname>ExtensionDirectories</varname>
see systemd.exec(5) for their meaning.</para>
<para><varname>MemoryAvailable</varname> indicates how much unused memory is available to the unit before
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s RootVerity = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ExtensionDirectories = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sba(ss)) ExtensionImages = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ssba(ss)) MountImages = [...];
<variablelist class="dbus-property" generated="True" extra-ref="RootVerity"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
<variablelist class="dbus-property" generated="True" extra-ref="MountImages"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s RootVerity = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ExtensionDirectories = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sba(ss)) ExtensionImages = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ssba(ss)) MountImages = [...];
<variablelist class="dbus-property" generated="True" extra-ref="RootVerity"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
<variablelist class="dbus-property" generated="True" extra-ref="MountImages"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s RootVerity = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ExtensionDirectories = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sba(ss)) ExtensionImages = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ssba(ss)) MountImages = [...];
<variablelist class="dbus-property" generated="True" extra-ref="RootVerity"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
<variablelist class="dbus-property" generated="True" extra-ref="MountImages"/>
The image(s) must contain an <filename>extension-release</filename> file with metadata that matches
what is defined in the <filename>os-release</filename> of <replaceable>IMAGE</replaceable>. See:
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- For more information on portable services with extensions, see the <literal>Extension Images</literal>
- paragraph on <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>.
+ Images can be block images, btrfs subvolumes or directories. For more information on portable
+ services with extensions, see the <literal>Extension Images</literal> paragraph on
+ <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>.
</para>
<para>Note that the same extensions have to be specified, in the same order, when attaching
['systemd-network-generator.service', '8', ['systemd-network-generator'], ''],
['systemd-networkd-wait-online.service',
'8',
- ['systemd-networkd-wait-online'],
+ ['systemd-networkd-wait-online', 'systemd-networkd-wait-online@.service'],
'ENABLE_NETWORKD'],
['systemd-networkd.service', '8', ['systemd-networkd'], 'ENABLE_NETWORKD'],
['systemd-notify', '1', [], ''],
<refnamediv>
<refname>systemd-networkd-wait-online.service</refname>
+ <refname>systemd-networkd-wait-online@.service</refname>
<refname>systemd-networkd-wait-online</refname>
<refpurpose>Wait for network to come online</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-networkd-wait-online.service</filename></para>
+ <para><filename>systemd-networkd-wait-online@.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-networkd-wait-online</filename></para>
</refsynopsisdiv>
to be fully configured or failed, and for at least one link to be online. Here, online means that
the link's operational state is equal or higher than <literal>degraded</literal>. The threshold
can be configured by <option>--operational-state=</option> option.</para>
+
+ <para>The service <filename>systemd-networkd-wait-online.service</filename> invokes
+ <command>systemd-networkd-wait-online</command> without any options. Thus, it waits for all managed
+ interfaces to be configured or failed, and for at least one to be online.</para>
+
+ <para>The service <filename>systemd-networkd-wait-online@.service</filename> takes an interface
+ name, and invokes <command>systemd-networkd-wait-online</command> with <option>-i</option> and the
+ specified interface name. Thus, wait for the specified interface to be configured and online. For
+ example, <filename>systemd-networkd-wait-online@eth0.service</filename> waits for
+ <filename>eth0</filename> to be configured by <command>systemd-networkd</command> and online.
+ </para>
</refsect1>
<refsect1>
<xi:include href="system-only.xml" xpointer="singular"/></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>ExtensionDirectories=</varname></term>
+
+ <listitem><para>This setting is similar to <varname>BindReadOnlyPaths=</varname> in that it mounts a file
+ system hierarchy from a directory, but instead of providing a destination path, an overlay will be set
+ up. This option expects a whitespace separated list of source directories.</para>
+
+ <para>A read-only OverlayFS will be set up on top of <filename>/usr/</filename> and
+ <filename>/opt/</filename> hierarchies. The order in which the directories are listed will determine
+ the order in which the overlay is laid down: directories specified first to last will result in overlayfs
+ layers bottom to top.</para>
+
+ <para>Each directory listed in <varname>ExtensionDirectories=</varname> may be prefixed with <literal>-</literal>,
+ in which case it will be ignored when its source path does not exist. Any mounts created with this option are
+ specific to the unit, and are not visible in the host's mount table.</para>
+
+ <para>These settings may be used more than once, each usage appends to the unit's list of directories
+ paths. If the empty string is assigned, the entire list of mount paths defined prior to this is
+ reset.</para>
+
+ <para>Each directory must contain a <filename>/usr/lib/extension-release.d/extension-release.IMAGE</filename>
+ file, with the appropriate metadata which matches <varname>RootImage=</varname>/<varname>RootDirectory=</varname>
+ or the host. See:
+ <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+ <xi:include href="system-only.xml" xpointer="singular"/></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<varlistentry>
<term><varname>EnvironmentFile=</varname></term>
- <listitem><para>Similar to <varname>Environment=</varname> but reads the environment variables from a text
- file. The text file should contain new-line-separated variable assignments. Empty lines, lines without an
- <literal>=</literal> separator, or lines starting with ; or # will be ignored, which may be used for
- commenting. A line ending with a backslash will be concatenated with the following one, allowing multiline
- variable definitions. The parser strips leading and trailing whitespace from the values of assignments, unless
- you use double quotes (").</para>
-
- <para><ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C escapes</ulink>
- are supported, but not
- <ulink url="https://en.wikipedia.org/wiki/Control_character#In_ASCII">most control characters</ulink>.
- <literal>\t</literal> and <literal>\n</literal> can be used to insert tabs and newlines within
- <varname>EnvironmentFile=</varname>.</para>
+ <listitem><para>Similar to <varname>Environment=</varname> but reads the environment variables from a text file.
+ The text file should contain newline-separated variable assignments. Empty lines, lines without an
+ <literal>=</literal> separator, or lines starting with <literal>;</literal> or <literal>#</literal> will be
+ ignored, which may be used for commenting. The file must be UTF-8 encoded. Valid characters are <ulink
+ url="https://www.unicode.org/glossary/#unicode_scalar_value">unicode scalar values</ulink> other than <ulink
+ url="https://www.unicode.org/glossary/#noncharacter">noncharacters</ulink>, U+0000 NUL, and U+FEFF <ulink
+ url="https://www.unicode.org/glossary/#byte_order_mark">byte order mark</ulink>. Control codes other than NUL
+ are allowed.</para>
+
+ <para>In the file, an unquoted value after the <literal>=</literal> is parsed with the same backslash-escape
+ rules as <ulink
+ url="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01">unquoted
+ text</ulink> in a POSIX shell, but unlike in a shell, interior whitespace is preserved and quotes after the
+ first non-whitespace character are preserved. Leading and trailing whitespace (space, tab, carriage return) is
+ discarded, but interior whitespace within the line is preserved verbatim. A line ending with a backslash will be
+ continued to the following one, with the newline itself discarded. A backslash
+ <literal>\</literal> followed by any character other than newline will preserve the following character, so that
+ <literal>\\</literal> will become the value <literal>\</literal>.</para>
+
+ <para>In the file, a <literal>'</literal>-quoted value after the <literal>=</literal> can span multiple lines
+ and contain any character verbatim other than single quote, like <ulink
+ url="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_02">single-quoted
+ text</ulink> in a POSIX shell. No backslash-escape sequences are recognized. Leading and trailing whitespace
+ outside of the single quotes is discarded.</para>
+
+ <para>In the file, a <literal>"</literal>-quoted value after the <literal>=</literal> can span multiple lines,
+ and the same escape sequences are recognized as in <ulink
+ url="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_03">double-quoted
+ text</ulink> of a POSIX shell. Backslash (<literal>\</literal>) followed by any of <literal>"\`$</literal> will
+ preserve that character. A backslash followed by newline is a line continuation, and the newline itself is
+ discarded. A backslash followed by any other character is ignored; both the backslash and the following
+ character are preserved verbatim. Leading and trailing whitespace outside of the double quotes is
+ discarded.</para>
<para>The argument passed should be an absolute filename or wildcard expression, optionally prefixed with
<literal>-</literal>, which indicates that if the file does not exist, it will not be read and no error or
<para>Variables set for invoked processes due to this setting are subject to being overridden by those
configured with <varname>Environment=</varname> or <varname>EnvironmentFile=</varname>.</para>
- <para><ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C escapes</ulink>
- are supported, but not
- <ulink url="https://en.wikipedia.org/wiki/Control_character#In_ASCII">most control characters</ulink>.
- <literal>\t</literal> and <literal>\n</literal> can be used to insert tabs and newlines within
- <varname>EnvironmentFile=</varname>.</para>
-
<para>Example:
<programlisting>PassEnvironment=VAR1 VAR2 VAR3</programlisting>
passes three variables <literal>VAR1</literal>,
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>SR-IOVVirtualFunctions=</varname></term>
+ <listitem>
+ <para>Specifies the number of SR-IOV virtual functions. Takes an integer in the range
+ 0…2147483647. Defaults to unset, and automatically determined from the values specified in
+ the <varname>VirtualFunction=</varname> settings in the [SR-IOV] sections.</para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id='sr-iov'>
+ <title>[SR-IOV] Section Options</title>
+ <para>The [SR-IOV] section accepts the following keys. Specify several [SR-IOV] sections to
+ configure several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource
+ into virtual PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV
+ improves north-south network performance (that is, traffic with endpoints outside the host machine)
+ by allowing traffic to bypass the host machine’s network stack.</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>VirtualFunction=</varname></term>
+ <listitem>
+ <para>Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move
+ data in and out. Takes an integer in the range 0…2147483646. This option is compulsory.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>VLANId=</varname></term>
+ <listitem>
+ <para>Specifies VLAN ID of the virtual function. Takes an integer in the range 1…4095.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>QualityOfService=</varname></term>
+ <listitem>
+ <para>Specifies quality of service of the virtual function. Takes an integer in the range
+ 1…4294967294.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>VLANProtocol=</varname></term>
+ <listitem>
+ <para>Specifies VLAN protocol of the virtual function. Takes <literal>802.1Q</literal> or
+ <literal>802.1ad</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MACSpoofCheck=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will
+ be used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>QueryReceiveSideScaling=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Toggle the ability of querying the receive side scaling (RSS)
+ configuration of the virtual function (VF). The VF RSS information like RSS hash key may be
+ considered sensitive on some devices where this information is shared between VF and the
+ physical function (PF). When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Trust=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Allows one to set trust mode of the virtual function (VF). When set,
+ VF users can set a specific feature which may impact security and/or performance. When unset,
+ the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LinkState=</varname></term>
+ <listitem>
+ <para>Allows one to set the link state of the virtual function (VF). Takes a boolean or a
+ special value <literal>auto</literal>. Setting to <literal>auto</literal> means a
+ reflection of the physical function (PF) link state, <literal>yes</literal> lets the VF to
+ communicate with other VFs on this host even if the PF link state is down,
+ <literal>no</literal> causes the hardware to drop any packets sent by the VF. When unset,
+ the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MACAddress=</varname></term>
+ <listitem>
+ <para>Specifies the MAC address for the virtual function.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<entry>A dummy device drops all packets sent to it.</entry></row>
<row><entry><varname>gre</varname></entry>
- <entry>A Level 3 GRE tunnel over IPv4. See <ulink url="https://tools.ietf.org/html/rfc2784">RFC 2784</ulink> for details.</entry></row>
+ <entry>A Level 3 GRE tunnel over IPv4. See <ulink url="https://tools.ietf.org/html/rfc2784">RFC 2784</ulink> for details. Name <literal>gre0</literal> should not be used, as the kernel creates a device with this name when the corresponding kernel module is loaded.</entry></row>
<row><entry><varname>gretap</varname></entry>
- <entry>A Level 2 GRE tunnel over IPv4.</entry></row>
+ <entry>A Level 2 GRE tunnel over IPv4. Name <literal>gretap0</literal> should not be used, as the kernel creates a device with this name when the corresponding kernel module is loaded.</entry></row>
<row><entry><varname>erspan</varname></entry>
- <entry>ERSPAN mirrors traffic on one or more source ports and delivers the mirrored traffic to one or more destination ports on another switch. The traffic is encapsulated in generic routing encapsulation (GRE) and is therefore routable across a layer 3 network between the source switch and the destination switch.</entry></row>
+ <entry>ERSPAN mirrors traffic on one or more source ports and delivers the mirrored traffic to one or more destination ports on another switch. The traffic is encapsulated in generic routing encapsulation (GRE) and is therefore routable across a layer 3 network between the source switch and the destination switch. Name <literal>erspan0</literal> should not be used, as the kernel creates a device with this name when the corresponding kernel module is loaded.</entry></row>
<row><entry><varname>ip6gre</varname></entry>
<entry>A Level 3 GRE tunnel over IPv6.</entry></row>
</variablelist>
</refsect1>
- <refsect1>
- <title>[SR-IOV] Section Options</title>
- <para>The [SR-IOV] section accepts the following keys. Specify several [SR-IOV] sections to
- configure several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource
- into virtual PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV
- improves north-south network performance (that is, traffic with endpoints outside the host machine)
- by allowing traffic to bypass the host machine’s network stack.</para>
-
- <variablelist class='network-directives'>
- <varlistentry>
- <term><varname>VirtualFunction=</varname></term>
- <listitem>
- <para>Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move
- data in and out. Takes an integer in the range 0…2147483646. This option is compulsory.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>VLANId=</varname></term>
- <listitem>
- <para>Specifies VLAN ID of the virtual function. Takes an integer in the range 1…4095.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>QualityOfService=</varname></term>
- <listitem>
- <para>Specifies quality of service of the virtual function. Takes an integer in the range
- 1…4294967294.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>VLANProtocol=</varname></term>
- <listitem>
- <para>Specifies VLAN protocol of the virtual function. Takes <literal>802.1Q</literal> or
- <literal>802.1ad</literal>.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>MACSpoofCheck=</varname></term>
- <listitem>
- <para>Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will
- be used.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>QueryReceiveSideScaling=</varname></term>
- <listitem>
- <para>Takes a boolean. Toggle the ability of querying the receive side scaling (RSS)
- configuration of the virtual function (VF). The VF RSS information like RSS hash key may be
- considered sensitive on some devices where this information is shared between VF and the
- physical function (PF). When unset, the kernel's default will be used.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>Trust=</varname></term>
- <listitem>
- <para>Takes a boolean. Allows one to set trust mode of the virtual function (VF). When set, VF
- users can set a specific feature which may impact security and/or performance. When unset,
- the kernel's default will be used.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>LinkState=</varname></term>
- <listitem>
- <para>Allows one to set the link state of the virtual function (VF). Takes a boolean or a
- special value <literal>auto</literal>. Setting to <literal>auto</literal> means a
- reflection of the physical function (PF) link state, <literal>yes</literal> lets the VF to
- communicate with other VFs on this host even if the PF link state is down,
- <literal>no</literal> causes the hardware to drop any packets sent by the VF. When unset,
- the kernel's default will be used.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>MACAddress=</varname></term>
- <listitem>
- <para>Specifies the MAC address for the virtual function.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
+ <xi:include href="systemd.link.xml" xpointer="sr-iov" />
<refsect1>
<title>[Network] Section Options</title>
</example>
<example>
- <title>IPv6 Prefix Delegation</title>
+ <title>IPv6 Prefix Delegation (DHCPv6 PD)</title>
- <programlisting># /etc/systemd/network/55-ipv6-pd-upstream.network
+ <programlisting># /etc/systemd/network/55-dhcpv6-pd-upstream.network
[Match]
Name=enp1s0
[Network]
-DHCP=ipv6</programlisting>
+DHCP=ipv6
+
+# The below setting is optional, to also assign an address in the delegated prefix
+# to the upstream interface. If not necessary, then comment out the line below and
+# the [DHCPPrefixDelegation] section.
+DHCPPrefixDelegation=yes
+
+# If the upstream network provides Router Advertisement with Managed bit set,
+# then comment out the line below and WithoutRA= setting in the [DHCPv6] section.
+IPv6AcceptRA=no
- <programlisting># /etc/systemd/network/56-ipv6-pd-downstream.network
+[DHCPv6]
+WithoutRA=solicit
+
+[DHCPPrefixDelegation]
+UplinkInterface=:self
+SubnetId=0
+Announce=no</programlisting>
+
+ <programlisting># /etc/systemd/network/55-dhcpv6-pd-downstream.network
[Match]
Name=enp2s0
[Network]
+DHCPPrefixDelegation=yes
IPv6SendRA=yes
-DHCPPrefixDelegation=yes</programlisting>
+
+# It is expected that the host is acting as a router. So, usually it is not
+# necessary to receive Router Advertisement from other hosts in the downstream network.
+IPv6AcceptRA=no
+
+[DHCPPrefixDelegation]
+UplinkInterface=enp1s0
+SubnetId=1
+Announce=yes</programlisting>
<para>This will enable DHCPv6-PD on the interface enp1s0 as an upstream interface where the
DHCPv6 client is running and enp2s0 as a downstream interface where the prefix is delegated to.
</para>
</example>
+ <example>
+ <title>IPv6 Prefix Delegation (DHCPv4 6RD)</title>
+
+ <programlisting># /etc/systemd/network/55-dhcpv4-6rd-upstream.network
+[Match]
+Name=enp1s0
+
+[Network]
+DHCP=ipv4
+
+# When DHCPv4-6RD is used, the upstream network does not support IPv6.
+# Hence, it is not necessary to wait for Router Advertisement, which is enabled by default.
+IPv6AcceptRA=no
+
+[DHCPv4]
+Use6RD=yes</programlisting>
+
+ <programlisting># /etc/systemd/network/55-dhcpv4-6rd-downstream.network
+[Match]
+Name=enp2s0
+
+[Network]
+DHCPPrefixDelegation=yes
+IPv6SendRA=yes
+
+# It is expected that the host is acting as a router. So, usually it is not
+# necessary to receive Router Advertisement from other hosts in the downstream network.
+IPv6AcceptRA=no
+
+[DHCPPrefixDelegation]
+UplinkInterface=enp1s0
+SubnetId=1
+Announce=yes</programlisting>
+
+ <para>This will enable DHCPv4-6RD on the interface enp1s0 as an upstream interface where the
+ DHCPv4 client is running and enp2s0 as a downstream interface where the prefix is delegated to.
+ The delegated prefixes are distributed by IPv6 Router Advertisement on the downstream network.
+ </para>
+ </example>
+
<example>
<title>A bridge with two enslaved links</title>
<entry><filename>$XDG_RUNTIME_DIR/systemd/user.control</filename></entry>
</row>
<row>
- <entry><filename>/run/systemd/transient</filename></entry>
+ <entry><filename>$XDG_RUNTIME_DIR/systemd/transient</filename></entry>
<entry>Dynamic configuration for transient units</entry>
</row>
<row>
- <entry><filename>/run/systemd/generator.early</filename></entry>
+ <entry><filename>$XDG_RUNTIME_DIR/systemd/generator.early</filename></entry>
<entry>Generated units with high priority (see <replaceable>early-dir</replaceable> in <citerefentry
><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
</row>
<xi:include href="standard-specifiers.xml" xpointer="V"/>
<xi:include href="standard-specifiers.xml" xpointer="w"/>
<xi:include href="standard-specifiers.xml" xpointer="W"/>
+ <row>
+ <entry><literal>%y</literal></entry>
+ <entry>The path to the fragment</entry>
+ <entry>This is the path where the main part of the unit file is located. For linked unit files, the real path outside of the unit search directories is used. For units that don't have a fragment file, this specifier will raise an error.</entry>
+ </row>
+ <row>
+ <entry><literal>%Y</literal></entry>
+ <entry>The directory of the fragment</entry>
+ <entry>This is the directory part of <literal>%y</literal>.</entry>
+ </row>
<xi:include href="standard-specifiers.xml" xpointer="percent"/>
</tbody>
</tgroup>
perl = find_program('perl', required : false)
rsync = find_program('rsync', required : false)
meson_make_symlink = project_source_root + '/tools/meson-make-symlink.sh'
-test_efi_create_disk_sh = find_program('test/test-efi-create-disk.sh')
mkdir_p = 'mkdir -p $DESTDIR/@0@'
-splash_bmp = files('test/splash.bmp')
# If -Dxxx-path option is found, use that. Otherwise, check in $PATH,
# /usr/sbin, /sbin, and fall back to the default from middle column.
libbpf = dependency('libbpf', required : bpf_framework_required, version : '>= 0.2')
conf.set10('HAVE_LIBBPF', libbpf.found())
-if want_bpf_framework == 'false'
+if want_bpf_framework == 'false' or not libbpf.found()
conf.set10('BPF_FRAMEWORK', 0)
else
# Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu
# (like clang-10/llvm-strip-10)
if meson.is_cross_build() or cc.get_id() != 'clang' or cc.cmd_array()[0].contains('afl-clang') or cc.cmd_array()[0].contains('hfuzz-clang')
- r = find_program('clang', required : bpf_framework_required)
+ r = find_program('clang', required : bpf_framework_required, version : '>= 10.0.0')
clang_found = r.found()
if clang_found
if meson.version().version_compare('>= 0.55')
clang_supports_bpf = false
endif
- if not meson.is_cross_build() and clang_found
- llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
- check : true).stdout().strip()
- else
- llvm_strip_bin = 'llvm-strip'
- endif
- llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required)
-
# Debian installs this in /usr/sbin/ which is not in $PATH.
# We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
# We use 'bpftool gen' subcommand, it was added by 985ead416df39d6fe8e89580cc1db6aa273e0175 (v5.6).
bpftool = find_program('bpftool',
'/usr/sbin/bpftool',
- required : bpf_framework_required,
- version : '>= 5.6')
+ required : false,
+ version : '>= 5.13.0')
- deps_found = libbpf.found() and clang_found and clang_supports_bpf and clang_supports_flags and llvm_strip.found() and bpftool.found()
+ if bpftool.found()
+ bpftool_strip = true
+ else
+ bpftool_strip = false
+ bpftool = find_program('bpftool',
+ '/usr/sbin/bpftool',
+ required : bpf_framework_required,
+ version : '>= 5.6.0')
+ endif
+
+ if not bpftool_strip
+ if not meson.is_cross_build() and clang_found
+ llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
+ check : true).stdout().strip()
+ else
+ llvm_strip_bin = 'llvm-strip'
+ endif
+ llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required, version : '>= 10.0.0')
+ endif
+
+ deps_found = clang_found and clang_supports_bpf and clang_supports_flags and (bpftool_strip or llvm_strip.found()) and bpftool.found()
# Can build BPF program from source code in restricted C
conf.set10('BPF_FRAMEWORK', deps_found)
tests = []
fuzzers = []
+catalogs = []
############################################################
test('dlopen-nss_' + module,
test_dlopen,
# path to dlopen must include a slash
- args : nss.full_path())
+ args : nss.full_path(),
+ depends : nss)
endif
endif
endforeach
test('test-fstab-generator',
test_fstab_generator_sh,
# https://github.com/mesonbuild/meson/issues/2681
- args : exe.full_path())
+ args : exe.full_path(),
+ depends : exe)
endif
if conf.get('ENABLE_ENVIRONMENT_D') == 1
test('dlopen-pam_systemd',
test_dlopen,
# path to dlopen must include a slash
- args : pam_systemd.full_path())
+ args : pam_systemd.full_path(),
+ depends : pam_systemd)
endif
endif
endif
if conf.get('HAVE_SYSV_COMPAT') == 1
- executable(
+ exe = executable(
'systemd-sysv-generator',
'src/sysv-generator/sysv-generator.c',
include_directories : includes,
install : true,
install_dir : systemgeneratordir)
+ sysv_generator_test_py = find_program('test/sysv-generator-test.py')
+ if want_tests != 'false'
+ test('sysv-generator-test',
+ sysv_generator_test_py,
+ depends : exe)
+ endif
+
executable(
'systemd-rc-local-generator',
'src/rc-local-generator/rc-local-generator.c',
test('test-network-generator-conversion',
test_network_generator_conversion_sh,
# https://github.com/mesonbuild/meson/issues/2681
- args : exe.full_path())
+ args : exe.full_path(),
+ depends : exe)
endif
executable(
############################################################
-custom_target(
+runtest_env = custom_target(
'systemd-runtest.env',
output : 'systemd-runtest.env',
command : [sh, '-c',
'{ echo SYSTEMD_TEST_DATA=@0@; echo SYSTEMD_CATALOG_DIR=@1@; } >@OUTPUT@'.format(
project_source_root / 'test',
project_build_root / 'catalog')],
+ depends : catalogs,
build_by_default : true)
test_cflags = ['-DTEST_CODE=1']
build_by_default : want_tests != 'false',
install_rpath : rootlibexecdir,
install : install_tests,
- install_dir : testsdir / type)
+ install_dir : testsdir / type,
+ link_depends : runtest_env)
if type == 'manual'
message('@0@ is a manual test'.format(name))
if want_tests != 'false'
test('check-help-' + name,
check_help,
- args : exec.full_path())
+ args : exec.full_path(),
+ depends: exec)
endif
endforeach
SUBSYSTEM=="ptp", ATTR{clock_name}=="hyperv", SYMLINK += "ptp_hyperv"
-SUBSYSTEM=="dmi", ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="", ENV{ID_VENDOR}="$attr{sys_vendor}", ENV{ID_MODEL}="$attr{product_name}"
-SUBSYSTEM=="dmi", ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="product_version", ENV{ID_VENDOR}="$attr{sys_vendor}", ENV{ID_MODEL}="$attr{product_version}"
+SUBSYSTEM!="dmi", GOTO="dmi_end"
+ENV{ID_VENDOR}="$attr{sys_vendor}"
+ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="|product_name", ENV{ID_MODEL}="$attr{product_name}"
+ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="product_version", ENV{ID_MODEL}="$attr{product_version}"
+# fallback to board information
+ENV{ID_VENDOR}=="", ENV{ID_VENDOR}="$attr{board_vendor}"
+ENV{ID_MODEL}=="", ENV{ID_MODEL}="$attr{board_name}"
+LABEL="dmi_end"
LABEL="default_end"
cgroup2, {CGROUP2_SUPER_MAGIC}
# note that the cgroupfs magic got reassigned from cpuset
cgroup, {CGROUP_SUPER_MAGIC}
-cifs, {CIFS_MAGIC_NUMBER}
+cifs, {CIFS_SUPER_MAGIC, SMB2_SUPER_MAGIC}
coda, {CODA_SUPER_MAGIC}
configfs, {CONFIGFS_MAGIC}
cramfs, {CRAMFS_MAGIC}
shiftfs, {SHIFTFS_MAGIC}
smackfs, {SMACK_MAGIC}
# smb3 is an alias for cifs
-smb3, {CIFS_MAGIC_NUMBER}
+smb3, {CIFS_SUPER_MAGIC}
# smbfs was removed from the kernel in 2010, the magic remains
smbfs, {SMB_SUPER_MAGIC}
sockfs, {SOCKFS_MAGIC}
})
#if LOG_TRACE
-# define log_trace(...) log_debug(__VA_ARGS__)
+# define log_trace(...) log_debug(__VA_ARGS__)
+# define log_trace_errno(...) log_debug_errno(__VA_ARGS__)
#else
-# define log_trace(...) do {} while (0)
+# define log_trace(...) do {} while (0)
+# define log_trace_errno(e, ...) (-ERRNO_VALUE(e))
#endif
/* Structured logging */
#define XFS_SB_MAGIC 0x58465342
#endif
-/* Not exposed yet. Defined at fs/cifs/cifsglob.h */
-#ifndef CIFS_MAGIC_NUMBER
-#define CIFS_MAGIC_NUMBER 0xFF534D42
+/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
+#ifndef CIFS_SUPER_MAGIC
+#define CIFS_SUPER_MAGIC 0xFF534D42
+#endif
+
+/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
+#ifndef SMB2_SUPER_MAGIC
+#define SMB2_SUPER_MAGIC 0xFE534D42
#endif
/* 257f871993474e2bde6c497b54022c362cf398e1 (4.5) */
char **generator_early,
char **generator_late) {
- _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
+ _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *p = NULL;
const char *prefix;
assert(generator);
if (!e)
return -ENXIO;
- prefix = strjoina(e, "/systemd");
+ p = path_join(e, "/systemd");
+ if (!p)
+ return -ENOMEM;
+
+ prefix = p;
}
x = path_join(prefix, "generator");
if (isempty(s)) {
r = sd_id128_get_machine(&arg_machine_id);
- if (r < 0)
+ if (r < 0 && !IN_SET(r, -ENOENT, -ENOMEDIUM))
return log_error_errno(r, "Failed to get machine-id: %m");
} else {
r = sd_id128_from_string(s, &arg_machine_id);
bool layout_non_bls = arg_install_layout && !streq(arg_install_layout, "bls");
if (arg_make_machine_id_directory < 0) {
- if (layout_non_bls)
+ if (layout_non_bls || sd_id128_is_null(arg_machine_id))
arg_make_machine_id_directory = 0;
else {
r = path_is_temporary_fs("/etc/machine-id");
}
}
+ if (arg_make_machine_id_directory > 0 && sd_id128_is_null(arg_machine_id))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Machine ID not found, but bls directory creation was requested.");
+
if (arg_make_machine_id_directory > 0 && layout_non_bls)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"KERNEL_INSTALL_LAYOUT=%s is configured, but bls directory creation was requested.",
static int remove_machine_id_directory(const char *root) {
assert(root);
assert(arg_make_machine_id_directory >= 0);
- assert(!sd_id128_is_null(arg_machine_id));
- if (!arg_make_machine_id_directory)
+ if (!arg_make_machine_id_directory || sd_id128_is_null(arg_machine_id))
return 0;
return rmdir_one(root, SD_ID128_TO_STRING(arg_machine_id));
else if (r < 0)
log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
else
- (void) boot_entries_augment_from_loader(&config, efi_entries, false);
+ (void) boot_entries_augment_from_loader(&config, efi_entries);
if (config.n_entries == 0)
log_info("No boot loader entries found.");
CHAR16 *options;
CHAR16 key;
EFI_STATUS (*call)(void);
- BOOLEAN non_unique;
UINTN tries_done;
UINTN tries_left;
CHAR16 *path;
static void print_status(Config *config, CHAR16 *loaded_image_path) {
UINT64 key;
UINTN x_max, y_max;
+ UINT32 screen_width = 0, screen_height = 0;
SecureBootMode secure;
_cleanup_freepool_ CHAR16 *device_part_uuid = NULL;
clear_screen(COLOR_NORMAL);
console_query_mode(&x_max, &y_max);
+ query_screen_resolution(&screen_width, &screen_height);
secure = secure_boot_mode();
(void) efivar_get(LOADER_GUID, L"LoaderDevicePartUUID", &device_part_uuid);
Print(L" secure boot: %s (%s)\n", yes_no(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)), secure_boot_mode_to_string(secure));
ps_bool(L" shim: %s\n", shim_loaded());
ps_bool(L" TPM: %s\n", tpm_present());
- Print(L" console mode: %d/%d (%lu x %lu)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - 1LL, x_max, y_max);
+ Print(L" console mode: %d/%d (%lux%lu @%ux%u)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - 1LL, x_max, y_max, screen_width, screen_height);
Print(L"\n--- Press any key to continue. ---\n\n");
console_key_read(&key, UINT64_MAX);
config->timeout_sec = 10;
}
-static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
- BOOLEAN non_unique = FALSE;
+static BOOLEAN entries_unique(ConfigEntry **entries, BOOLEAN *unique, UINTN entry_count) {
+ BOOLEAN is_unique = TRUE;
assert(entries);
+ assert(unique);
for (UINTN i = 0; i < entry_count; i++)
- entries[i]->non_unique = FALSE;
-
- for (UINTN i = 0; i < entry_count; i++)
- for (UINTN k = 0; k < entry_count; k++) {
- if (i == k)
- continue;
+ for (UINTN k = i + 1; k < entry_count; k++) {
if (StrCmp(entries[i]->title_show, entries[k]->title_show) != 0)
continue;
- non_unique = entries[i]->non_unique = entries[k]->non_unique = TRUE;
+ is_unique = unique[i] = unique[k] = FALSE;
}
- return non_unique;
+ return is_unique;
}
/* generate a unique title, avoiding non-distinguishable menu entries */
static void config_title_generate(Config *config) {
assert(config);
+ BOOLEAN unique[config->entry_count];
+
/* set title */
for (UINTN i = 0; i < config->entry_count; i++) {
- FreePool(config->entries[i]->title_show);
- config->entries[i]->title_show = xstrdup(
- config->entries[i]->title ?: config->entries[i]->id);
+ assert(!config->entries[i]->title_show);
+ unique[i] = TRUE;
+ config->entries[i]->title_show = xstrdup(config->entries[i]->title ?: config->entries[i]->id);
}
- if (!find_nonunique(config->entries, config->entry_count))
+ if (entries_unique(config->entries, unique, config->entry_count))
return;
/* add version to non-unique titles */
for (UINTN i = 0; i < config->entry_count; i++) {
- CHAR16 *s;
-
- if (!config->entries[i]->non_unique)
+ if (unique[i])
continue;
+
+ unique[i] = TRUE;
+
if (!config->entries[i]->version)
continue;
- s = xpool_print(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
- FreePool(config->entries[i]->title_show);
- config->entries[i]->title_show = s;
+ _cleanup_freepool_ CHAR16 *t = config->entries[i]->title_show;
+ config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->version);
}
- if (!find_nonunique(config->entries, config->entry_count))
+ if (entries_unique(config->entries, unique, config->entry_count))
return;
/* add machine-id to non-unique titles */
for (UINTN i = 0; i < config->entry_count; i++) {
- CHAR16 *s;
- _cleanup_freepool_ CHAR16 *m = NULL;
-
- if (!config->entries[i]->non_unique)
+ if (unique[i])
continue;
+
+ unique[i] = TRUE;
+
if (!config->entries[i]->machine_id)
continue;
- m = xstrdup(config->entries[i]->machine_id);
- m[8] = '\0';
- s = xpool_print(L"%s (%s)", config->entries[i]->title_show, m);
- FreePool(config->entries[i]->title_show);
- config->entries[i]->title_show = s;
+ _cleanup_freepool_ CHAR16 *t = config->entries[i]->title_show;
+ config->entries[i]->title_show = xpool_print(
+ L"%s (%.*s)",
+ t,
+ StrnLen(config->entries[i]->machine_id, 8),
+ config->entries[i]->machine_id);
}
- if (!find_nonunique(config->entries, config->entry_count))
+ if (entries_unique(config->entries, unique, config->entry_count))
return;
/* add file name to non-unique titles */
for (UINTN i = 0; i < config->entry_count; i++) {
- CHAR16 *s;
-
- if (!config->entries[i]->non_unique)
+ if (unique[i])
continue;
- s = xpool_print(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->id);
- FreePool(config->entries[i]->title_show);
- config->entries[i]->title_show = s;
- config->entries[i]->non_unique = FALSE;
+
+ _cleanup_freepool_ CHAR16 *t = config->entries[i]->title_show;
+ config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->id);
}
}
return err;
}
-static INT64 get_auto_mode(void) {
- EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
+EFI_STATUS query_screen_resolution(UINT32 *ret_w, UINT32 *ret_h) {
EFI_STATUS err;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *go;
+
+ err = LibLocateProtocol(&GraphicsOutputProtocol, (void **) &go);
+ if (EFI_ERROR(err))
+ return err;
+
+ if (!go->Mode || !go->Mode->Info)
+ return EFI_DEVICE_ERROR;
+
+ *ret_w = go->Mode->Info->HorizontalResolution;
+ *ret_h = go->Mode->Info->VerticalResolution;
+ return EFI_SUCCESS;
+}
+
+static INT64 get_auto_mode(void) {
+ UINT32 screen_width, screen_height;
- err = LibLocateProtocol(&GraphicsOutputProtocol, (void **)&GraphicsOutput);
- if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
- EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info = GraphicsOutput->Mode->Info;
+ if (!EFI_ERROR(query_screen_resolution(&screen_width, &screen_height))) {
BOOLEAN keep = FALSE;
/* Start verifying if we are in a resolution larger than Full HD
* (1920x1080). If we're not, assume we're in a good mode and do not
* try to change it. */
- if (Info->HorizontalResolution <= HORIZONTAL_MAX_OK && Info->VerticalResolution <= VERTICAL_MAX_OK)
+ if (screen_width <= HORIZONTAL_MAX_OK && screen_height <= VERTICAL_MAX_OK)
keep = TRUE;
/* For larger resolutions, calculate the ratio of the total screen
* area to the text viewport area. If it's less than 10 times bigger,
else {
UINT64 text_area;
UINTN x_max, y_max;
- UINT64 screen_area = (UINT64)Info->HorizontalResolution * (UINT64)Info->VerticalResolution;
+ UINT64 screen_area = (UINT64)screen_width * (UINT64)screen_height;
console_query_mode(&x_max, &y_max);
text_area = SYSTEM_FONT_WIDTH * SYSTEM_FONT_HEIGHT * (UINT64)x_max * (UINT64)y_max;
EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec);
EFI_STATUS console_set_mode(INT64 mode);
EFI_STATUS console_query_mode(UINTN *x_max, UINTN *y_max);
+EFI_STATUS query_screen_resolution(UINT32 *ret_width, UINT32 *ret_height);
if get_option('werror')
efi_cflags += ['-Werror']
endif
-if get_option('debug')
+if get_option('debug') and get_option('mode') == 'developer'
efi_cflags += ['-ggdb', '-DEFI_DEBUG']
endif
if get_option('optimization') != '0'
endif
foreach arg : get_option('c_args')
- if arg in ['-Werror', '-g', '-ggdb', '-O1', '-O2', '-O3', '-Og', '-Os', '-DNDEBUG', '-flto', '-fno-lto']
+ if arg in [
+ '-DNDEBUG',
+ '-fno-lto',
+ '-O1', '-O2', '-O3', '-Og', '-Os',
+ '-Werror',
+ ] or arg.split('=')[0] in [
+ '-ffile-prefix-map',
+ '-flto',
+ ] or (get_option('mode') == 'developer' and arg in [
+ '-DEFI_DEBUG',
+ '-g', '-ggdb',
+ ])
+
message('Using "@0@" from c_args for EFI compiler'.format(arg))
efi_cflags += arg
endif
endif
if efi_arch[1] == 'arm'
- # On arm, the compiler (correctly) gives us the following warning:
- # libgcc.a(_popcountsi2.o) uses 4-byte wchar_t yet the output is to
- # use 2-byte wchar_t; use of wchar_t values across objects may fail
- #
- # libgcc does not have any occurrences of wchar_t in its sources or the
- # documentation, so it's safe to assume that we can ignore this warning.
- #
- # So far, this only happens with arm due to popcount even though x86 and
- # x86_64 also have to rely on libgcc's popcount. Therefore, we only disable
- # this for arm to make sure this doesn't mask other issues in the future.
- efi_ldflags += ['-Wl,--no-warn-mismatch']
+ # On arm, the compiler (correctly) warns about wchar_t size mismatch. This
+ # is because libgcc is not compiled with -fshort-wchar, but it does not
+ # have any occurrences of wchar_t in its sources or the documentation, so
+ # it is safe to assume that we can ignore this warning.
+ efi_ldflags += ['-Wl,--no-wchar-size-warning']
endif
if run_command('grep', '-q', '__CTOR_LIST__', efi_lds, check: false).returncode() == 0
'disk.h',
'drivers.h',
'graphics.h',
+ 'initrd.h',
'linux.h',
'measure.h',
'missing_efi.h',
'pe.h',
'random-seed.h',
+ 'secure-boot.h',
'shim.h',
'splash.h',
'util.h',
endif
endforeach
-systemd_boot_efi_name = 'systemd-boot@0@.efi'.format(efi_arch[0])
-stub_elf_name = 'linux@0@.elf.stub'.format(efi_arch[0])
-stub_efi_name = 'linux@0@.efi.stub'.format(efi_arch[0])
-
-efi_stubs = []
-foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects, false],
- [stub_elf_name, stub_efi_name, stub_objects, true]]
- so = custom_target(
- tuple[0],
- input : tuple[2],
- output : tuple[0],
- command : [cc.cmd_array(), '-o', '@OUTPUT@', efi_ldflags, efi_cflags, tuple[2], '-lefi', '-lgnuefi', '-lgcc'],
- install : tuple[3],
+foreach tuple : [['systemd-boot@0@.@1@', systemd_boot_objects, false],
+ ['linux@0@.@1@.stub', stub_objects, true]]
+ elf = custom_target(
+ tuple[0].format(efi_arch[0], 'elf'),
+ input : tuple[1],
+ output : tuple[0].format(efi_arch[0], 'elf'),
+ command : [cc.cmd_array(),
+ '-o', '@OUTPUT@',
+ efi_cflags,
+ efi_ldflags,
+ '@INPUT@',
+ '-lefi',
+ '-lgnuefi',
+ '-lgcc'],
+ install : tuple[2],
install_dir : bootlibdir)
- stub = custom_target(
- tuple[1],
- input : so,
- output : tuple[1],
+ custom_target(
+ tuple[0].format(efi_arch[0], 'efi'),
+ input : elf,
+ output : tuple[0].format(efi_arch[0], 'efi'),
command : [objcopy,
'-j', '.bss*',
'-j', '.data',
'@INPUT@', '@OUTPUT@'],
install : true,
install_dir : bootlibdir)
-
- efi_stubs += [[so, stub]]
endforeach
-
-############################################################
-
-test_efi_disk_img = custom_target(
- 'test-efi-disk.img',
- input : [efi_stubs[0][0], efi_stubs[1][1]],
- output : 'test-efi-disk.img',
- command : [test_efi_create_disk_sh, '@OUTPUT@','@INPUT@', splash_bmp])
if (EFI_ERROR(err))
return err;
- size = info->FileSize+1;
+ size = info->FileSize;
}
if (off > 0) {
return err;
}
- buf = xallocate_pool(size + 1);
+ /* Allocate some extra bytes to guarantee the result is NUL-terminated for CHAR8 and CHAR16 strings. */
+ UINTN extra = size % sizeof(CHAR16) + sizeof(CHAR16);
+
+ buf = xallocate_pool(size + extra);
err = handle->Read(handle, &size, buf);
if (EFI_ERROR(err))
return err;
- buf[size] = '\0';
+ /* Note that handle->Read() changes size to reflect the actually bytes read. */
+ ZeroMem(buf + size, extra);
*ret = TAKE_PTR(buf);
if (ret_size)
#define UINT64_MAX ((UINT64) -1)
#endif
-#define assert_alloc_ret(p) \
- ({ \
- void *_p = (p); \
- assert(_p); \
- _p; \
- })
-
#define xnew_alloc(type, n, alloc) \
({ \
UINTN _alloc_size; \
(type *) alloc(_alloc_size); \
})
-#define xallocate_pool(size) assert_alloc_ret(AllocatePool(size))
-#define xallocate_zero_pool(size) assert_alloc_ret(AllocateZeroPool(size))
-#define xreallocate_pool(p, old_size, new_size) assert_alloc_ret(ReallocatePool((p), (old_size), (new_size)))
-#define xpool_print(fmt, ...) ((CHAR16 *) assert_alloc_ret(PoolPrint((fmt), ##__VA_ARGS__)))
-#define xstrdup(str) ((CHAR16 *) assert_alloc_ret(StrDuplicate(str)))
+#define xallocate_pool(size) ASSERT_PTR(AllocatePool(size))
+#define xallocate_zero_pool(size) ASSERT_PTR(AllocateZeroPool(size))
+#define xreallocate_pool(p, old_size, new_size) ASSERT_PTR(ReallocatePool((p), (old_size), (new_size)))
+#define xpool_print(fmt, ...) ((CHAR16 *) ASSERT_PTR(PoolPrint((fmt), ##__VA_ARGS__)))
+#define xstrdup(str) ((CHAR16 *) ASSERT_PTR(StrDuplicate(str)))
#define xnew(type, n) xnew_alloc(type, (n), xallocate_pool)
#define xnew0(type, n) xnew_alloc(type, (n), xallocate_zero_pool)
uint8_t space[CONST_ALIGN_TO(sizeof(EFI_PARTITION_TABLE_HEADER), 512)];
};
-static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
- EFI_DEVICE_PATH *parent;
- UINTN len;
-
+static EFI_DEVICE_PATH *path_chop(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
assert(path);
assert(node);
- len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
- parent = (EFI_DEVICE_PATH*) xallocate_pool(len + sizeof(EFI_DEVICE_PATH));
+ UINTN len = (UINT8 *) node - (UINT8 *) path;
+ EFI_DEVICE_PATH *chopped = xallocate_pool(len + END_DEVICE_PATH_LENGTH);
- CopyMem(parent, path, len);
- CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH));
+ CopyMem(chopped, path, len);
+ SetDevicePathEndNode((EFI_DEVICE_PATH *) ((UINT8 *) chopped + len));
- return parent;
+ return chopped;
}
static BOOLEAN verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_expected) {
EFI_BLOCK_IO *block_io,
EFI_LBA lba,
EFI_LBA *ret_backup_lba, /* May be changed even on error! */
- UINT32 *ret_part_number,
- UINT64 *ret_part_start,
- UINT64 *ret_part_size,
- EFI_GUID *ret_part_uuid) {
+ HARDDRIVE_DEVICE_PATH *ret_hd) {
_cleanup_freepool_ EFI_PARTITION_ENTRY *entries = NULL;
union GptHeaderBuffer gpt;
UINTN size;
assert(block_io);
- assert(ret_part_number);
- assert(ret_part_start);
- assert(ret_part_size);
- assert(ret_part_uuid);
+ assert(ret_hd);
/* Read the GPT header */
err = block_io->ReadBlocks(
if (end < start) /* Bogus? */
continue;
- *ret_part_number = i + 1;
- *ret_part_start = start;
- *ret_part_size = end - start + 1;
- CopyMem(ret_part_uuid, &entry->UniquePartitionGUID, sizeof(*ret_part_uuid));
+ ret_hd->PartitionNumber = i + 1;
+ ret_hd->PartitionStart = start;
+ ret_hd->PartitionSize = end - start + 1;
+ ret_hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
+ ret_hd->SignatureType = SIGNATURE_TYPE_GUID;
+ CopyMem(ret_hd->Signature, &entry->UniquePartitionGUID, sizeof(ret_hd->Signature));
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}
-static EFI_STATUS find_device(
- EFI_HANDLE *device,
- EFI_DEVICE_PATH **ret_device_path,
- UINT32 *ret_part_number,
- UINT64 *ret_part_start,
- UINT64 *ret_part_size,
- EFI_GUID *ret_part_uuid) {
-
- EFI_DEVICE_PATH *partition_path;
+static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_path) {
EFI_STATUS err;
assert(device);
assert(ret_device_path);
- assert(ret_part_number);
- assert(ret_part_start);
- assert(ret_part_size);
- assert(ret_part_uuid);
- partition_path = DevicePathFromHandle(device);
+ EFI_DEVICE_PATH *partition_path = DevicePathFromHandle(device);
if (!partition_path)
return EFI_NOT_FOUND;
+ /* Find the (last) partition node itself. */
+ EFI_DEVICE_PATH *part_node = NULL;
for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
- _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL;
- EFI_HANDLE disk_handle;
- EFI_BLOCK_IO *block_io;
- EFI_DEVICE_PATH *p;
-
- /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media
- * devices */
- if (DevicePathType(node) != MESSAGING_DEVICE_PATH)
+ if (DevicePathType(node) != MEDIA_DEVICE_PATH)
continue;
- /* Determine the device path one level up */
- disk_path = p = path_parent(partition_path, node);
- if (!disk_path)
+ if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)
continue;
- err = BS->LocateDevicePath(&BlockIoProtocol, &p, &disk_handle);
- if (EFI_ERROR(err))
- continue;
+ part_node = node;
+ }
- err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io);
- if (EFI_ERROR(err))
- continue;
+ if (!part_node)
+ return EFI_NOT_FOUND;
- /* Filter out some block devices early. (We only care about block devices that aren't
- * partitions themselves — we look for GPT partition tables to parse after all —, and only
- * those which contain a medium and have at least 2 blocks.) */
- if (block_io->Media->LogicalPartition ||
- !block_io->Media->MediaPresent ||
- block_io->Media->LastBlock <= 1)
- continue;
+ /* Chop off the partition part, leaving us with the full path to the disk itself. */
+ _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL;
+ EFI_DEVICE_PATH *p = disk_path = path_chop(partition_path, part_node);
+
+ EFI_HANDLE disk_handle;
+ EFI_BLOCK_IO *block_io;
+ err = BS->LocateDevicePath(&BlockIoProtocol, &p, &disk_handle);
+ if (EFI_ERROR(err))
+ return err;
- /* Try several copies of the GPT header, in case one is corrupted */
- EFI_LBA backup_lba = 0;
- for (UINTN nr = 0; nr < 3; nr++) {
- EFI_LBA lba;
-
- /* Read the first copy at LBA 1 and then try the backup GPT header pointed
- * to by the first header if that one was corrupted. As a last resort,
- * try the very last LBA of this block device. */
- if (nr == 0)
- lba = 1;
- else if (nr == 1 && backup_lba != 0)
- lba = backup_lba;
- else if (nr == 2 && backup_lba != block_io->Media->LastBlock)
- lba = block_io->Media->LastBlock;
- else
- continue;
-
- err = try_gpt(
- block_io, lba,
- nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */
- ret_part_number,
- ret_part_start,
- ret_part_size,
- ret_part_uuid);
- if (!EFI_ERROR(err)) {
- *ret_device_path = DuplicateDevicePath(partition_path);
- if (!*ret_device_path)
- return EFI_OUT_OF_RESOURCES;
- return EFI_SUCCESS;
- }
+ err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io);
+ if (EFI_ERROR(err))
+ return err;
+
+ /* Filter out some block devices early. (We only care about block devices that aren't
+ * partitions themselves — we look for GPT partition tables to parse after all —, and only
+ * those which contain a medium and have at least 2 blocks.) */
+ if (block_io->Media->LogicalPartition ||
+ !block_io->Media->MediaPresent ||
+ block_io->Media->LastBlock <= 1)
+ return EFI_NOT_FOUND;
+ /* Try several copies of the GPT header, in case one is corrupted */
+ EFI_LBA backup_lba = 0;
+ HARDDRIVE_DEVICE_PATH hd = *((HARDDRIVE_DEVICE_PATH *) part_node);
+ for (UINTN nr = 0; nr < 3; nr++) {
+ EFI_LBA lba;
+
+ /* Read the first copy at LBA 1 and then try the backup GPT header pointed
+ * to by the first header if that one was corrupted. As a last resort,
+ * try the very last LBA of this block device. */
+ if (nr == 0)
+ lba = 1;
+ else if (nr == 1 && backup_lba != 0)
+ lba = backup_lba;
+ else if (nr == 2 && backup_lba != block_io->Media->LastBlock)
+ lba = block_io->Media->LastBlock;
+ else
+ continue;
+
+ err = try_gpt(
+ block_io, lba,
+ nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */
+ &hd);
+ if (EFI_ERROR(err)) {
/* GPT was valid but no XBOOT loader partition found. */
if (err == EFI_NOT_FOUND)
break;
+ /* Bad GPT, try next one. */
+ continue;
}
+
+ /* Patch in the data we found */
+ EFI_DEVICE_PATH *xboot_path = ASSERT_PTR(DuplicateDevicePath(partition_path));
+ CopyMem((UINT8 *) xboot_path + ((UINT8 *) part_node - (UINT8 *) partition_path), &hd, sizeof(hd));
+ *ret_device_path = xboot_path;
+ return EFI_SUCCESS;
}
/* No xbootloader partition found */
EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir) {
_cleanup_freepool_ EFI_DEVICE_PATH *partition_path = NULL;
- UINT32 part_number = UINT32_MAX;
- UINT64 part_start = UINT64_MAX, part_size = UINT64_MAX;
EFI_HANDLE new_device;
EFI_FILE *root_dir;
- EFI_GUID part_uuid;
EFI_STATUS err;
assert(device);
assert(ret_device);
assert(ret_root_dir);
- err = find_device(device, &partition_path, &part_number, &part_start, &part_size, &part_uuid);
+ err = find_device(device, &partition_path);
if (EFI_ERROR(err))
return err;
- /* Patch in the data we found */
- for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
- HARDDRIVE_DEVICE_PATH *hd;
-
- if (DevicePathType(node) != MEDIA_DEVICE_PATH)
- continue;
-
- if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)
- continue;
-
- hd = (HARDDRIVE_DEVICE_PATH*) node;
- hd->PartitionNumber = part_number;
- hd->PartitionStart = part_start;
- hd->PartitionSize = part_size;
- CopyMem(hd->Signature, &part_uuid, sizeof(hd->Signature));
- hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
- hd->SignatureType = SIGNATURE_TYPE_GUID;
- }
-
EFI_DEVICE_PATH *dp = partition_path;
err = BS->LocateDevicePath(&BlockIoProtocol, &dp, &new_device);
if (EFI_ERROR(err))
if (policy == CGROUP_DEVICE_POLICY_AUTO && !allow_list)
return 0;
- r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog);
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, "sd_devices", &prog);
if (r < 0)
return log_error_errno(r, "Loading device control BPF program failed: %m");
return supported = 0;
}
- r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &program);
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, "sd_devices", &program);
if (r < 0) {
log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
return supported = 0;
static int bpf_firewall_compile_bpf(
Unit *u,
+ const char *prog_name,
bool is_ingress,
BPFProgram **ret,
bool ip_allow_any,
return 0;
}
- r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p);
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, prog_name, &p);
if (r < 0)
return r;
}
int bpf_firewall_compile(Unit *u) {
+ const char *ingress_name = NULL, *egress_name = NULL;
+ bool ip_allow_any = false, ip_deny_any = false;
CGroupContext *cc;
int r, supported;
- bool ip_allow_any = false, ip_deny_any = false;
assert(u);
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
+ /* If BPF_F_ALLOW_MULTI flag is supported program name is also supported (both were added to v4.15
+ * kernel). */
+ if (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
+ ingress_name = "sd_fw_ingress";
+ egress_name = "sd_fw_egress";
+ }
+
/* Note that when we compile a new firewall we first flush out the access maps and the BPF programs themselves,
* but we reuse the accounting maps. That way the firewall in effect always maps to the actual
* configuration, but we don't flush out the accounting unnecessarily */
if (r < 0)
return log_unit_error_errno(u, r, "Preparation of eBPF accounting maps failed: %m");
- r = bpf_firewall_compile_bpf(u, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
+ r = bpf_firewall_compile_bpf(u, ingress_name, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "Compilation for ingress BPF program failed: %m");
- r = bpf_firewall_compile_bpf(u, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
+ r = bpf_firewall_compile_bpf(u, egress_name, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "Compilation for egress BPF program failed: %m");
_cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
int r;
- r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &prog);
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &prog);
if (r < 0)
return log_unit_error_errno(u, r, "Can't allocate CGROUP SKB BPF program: %m");
return supported = BPF_FIREWALL_UNSUPPORTED;
}
- r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &program);
+ /* prog_name is NULL since it is supported only starting from v4.15 kernel. */
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &program);
if (r < 0) {
bpf_firewall_unsupported_reason =
log_debug_errno(r, "Can't allocate CGROUP SKB BPF program, BPF firewalling is not supported: %m");
/* So now we know that the BPF program is generally available, let's see if BPF_F_ALLOW_MULTI is also supported
* (which was added in kernel 4.15). We use a similar logic as before, but this time we use the BPF_PROG_ATTACH
* bpf() call and the BPF_F_ALLOW_MULTI flags value. Since the flags are checked early in the system call we'll
- * get EINVAL if it's not supported, and EBADF as before if it is available. */
+ * get EINVAL if it's not supported, and EBADF as before if it is available.
+ * Use probe result as the indicator that program name is also supported since they both were
+ * added in kernel 4.15. */
zero(attr);
attr.attach_type = BPF_CGROUP_INET_EGRESS;
'@OUTPUT@'
]
-bpf_o_cmd = [
- llvm_strip,
- '-g',
- '@INPUT@',
- '-o',
- '@OUTPUT@'
-]
+if bpftool_strip
+ bpf_o_cmd = [
+ bpftool,
+ 'g',
+ 'o',
+ '@OUTPUT@',
+ '@INPUT@'
+ ]
+else
+ bpf_o_cmd = [
+ llvm_strip,
+ '-g',
+ '@INPUT@',
+ '-o',
+ '@OUTPUT@'
+ ]
+endif
skel_h_cmd = [
bpftool,
SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootHashSignaturePath", "s", NULL, offsetof(ExecContext, root_hash_sig_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ExtensionDirectories", "as", NULL, offsetof(ExecContext, extension_directories), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ExtensionImages", "a(sba(ss))", property_get_extension_images, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MountImages", "a(ssba(ss))", property_get_mount_images, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
return 1;
} else if (STR_IN_SET(name, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
- "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths", "ExecPaths", "NoExecPaths")) {
+ "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths", "ExecPaths", "NoExecPaths",
+ "ExtensionDirectories")) {
_cleanup_strv_free_ char **l = NULL;
char ***dirs;
char **p;
dirs = &c->exec_paths;
else if (streq(name, "NoExecPaths"))
dirs = &c->no_exec_paths;
+ else if (streq(name, "ExtensionDirectories"))
+ dirs = &c->extension_directories;
else /* "InaccessiblePaths" */
dirs = &c->inaccessible_paths;
r = sockaddr_un_set_path(&sa.un, "/run/systemd/private");
} else {
- const char *e, *joined;
+ _cleanup_free_ char *joined = NULL;
+ const char *e;
e = secure_getenv("XDG_RUNTIME_DIR");
if (!e)
return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
"XDG_RUNTIME_DIR is not set, refusing.");
- joined = strjoina(e, "/systemd/private");
+ joined = path_join(e, "/systemd/private");
+ if (!joined)
+ return log_oom();
+
r = sockaddr_un_set_path(&sa.un, joined);
}
if (r < 0)
if (context->n_extension_images > 0)
return true;
+ if (!strv_isempty(context->extension_directories))
+ return true;
+
if (!IN_SET(context->mount_flags, 0, MS_SHARED))
return true;
return r;
}
- if (!exec_directory_is_private(context, dt))
+ if (!exec_directory_is_private(context, dt) || exec_context_with_rootfs(context))
continue;
private_path = path_join(params->prefix[dt], "private", context->directories[dt].items[i].path);
context->root_verity,
context->extension_images,
context->n_extension_images,
+ context->extension_directories,
propagate_dir,
incoming_dir,
root_dir || root_image ? params->notify_socket : NULL,
assert(params);
assert(exit_status);
+ /* Explicitly test for CVE-2021-4034 inspired invocations */
+ assert(command->path);
+ assert(!strv_isempty(command->argv));
+
rename_process_from_path(command->path);
/* We reset exactly these signals, since they are the only ones we set to SIG_IGN in the main
c->root_hash_sig_path = mfree(c->root_hash_sig_path);
c->root_verity = mfree(c->root_verity);
c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
+ c->extension_directories = strv_free(c->extension_directories);
c->tty_path = mfree(c->tty_path);
c->syslog_identifier = mfree(c->syslog_identifier);
c->user = mfree(c->user);
return targets == 0 ? 0 : -ENOENT;
}
-static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l) {
- char **i, **r = NULL;
+static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
+ _cleanup_strv_free_ char **v = NULL;
+ char **i;
+ int r;
assert(c);
- assert(l);
+ assert(ret);
STRV_FOREACH(i, c->environment_files) {
- char *fn;
- int k;
- bool ignore = false;
- char **p;
_cleanup_globfree_ glob_t pglob = {};
-
- fn = *i;
+ bool ignore = false;
+ char *fn = *i;
if (fn[0] == '-') {
ignore = true;
if (!path_is_absolute(fn)) {
if (ignore)
continue;
-
- strv_free(r);
return -EINVAL;
}
/* Filename supports globbing, take all matching files */
- k = safe_glob(fn, 0, &pglob);
- if (k < 0) {
+ r = safe_glob(fn, 0, &pglob);
+ if (r < 0) {
if (ignore)
continue;
-
- strv_free(r);
- return k;
+ return r;
}
/* When we don't match anything, -ENOENT should be returned */
assert(pglob.gl_pathc > 0);
for (unsigned n = 0; n < pglob.gl_pathc; n++) {
- k = load_env_file(NULL, pglob.gl_pathv[n], &p);
- if (k < 0) {
+ _cleanup_strv_free_ char **p = NULL;
+
+ r = load_env_file(NULL, pglob.gl_pathv[n], &p);
+ if (r < 0) {
if (ignore)
continue;
-
- strv_free(r);
- return k;
+ return r;
}
+
/* Log invalid environment variables with filename */
if (p) {
InvalidEnvInfo info = {
p = strv_env_clean_with_callback(p, invalid_env, &info);
}
- if (!r)
- r = p;
+ if (!v)
+ v = TAKE_PTR(p);
else {
- char **m;
-
- m = strv_env_merge(r, p);
- strv_free(r);
- strv_free(p);
+ char **m = strv_env_merge(v, p);
if (!m)
return -ENOMEM;
- r = m;
+ strv_free_and_replace(v, m);
}
}
}
- *l = r;
+ *ret = TAKE_PTR(v);
return 0;
}
strempty(o->options));
fprintf(f, "\n");
}
+
+ strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
}
bool exec_context_maintains_privileges(const ExecContext *c) {
size_t n_mount_images;
MountImage *extension_images;
size_t n_extension_images;
+ char **extension_directories;
uint64_t capability_bounding_set;
uint64_t capability_ambient_set;
{{type}}.RootHash, config_parse_exec_root_hash, 0, offsetof({{type}}, exec_context)
{{type}}.RootHashSignature, config_parse_exec_root_hash_sig, 0, offsetof({{type}}, exec_context)
{{type}}.RootVerity, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_verity)
+{{type}}.ExtensionDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.extension_directories)
{{type}}.ExtensionImages, config_parse_extension_images, 0, offsetof({{type}}, exec_context)
{{type}}.MountImages, config_parse_mount_images, 0, offsetof({{type}}, exec_context)
{{type}}.User, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.user)
Manager *m = NULL;
FDSet *fds = NULL;
+ assert_se(argc > 0 && !isempty(argv[0]));
+
/* SysV compatibility: redirect init → telinit */
redirect_telinit(argc, argv);
EXEC,
TMPFS,
RUN,
+ EXTENSION_DIRECTORIES, /* Bind-mounted outside the root directory, and used by subsequent mounts */
EXTENSION_IMAGES, /* Mounted outside the root directory, and used by subsequent mounts */
MQUEUEFS,
READWRITE_IMPLICIT, /* Should have the lowest priority. */
return 0;
}
-static int append_extension_images(
+static int append_extensions(
MountEntry **p,
const char *root,
const char *extension_dir,
char **hierarchies,
const MountImage *mount_images,
- size_t n) {
+ size_t n,
+ char **extension_directories) {
_cleanup_strv_free_ char **overlays = NULL;
- char **hierarchy;
+ char **hierarchy, **extension_directory;
int r;
assert(p);
assert(extension_dir);
- if (n == 0)
+ if (n == 0 && strv_isempty(extension_directories))
return 0;
/* Prepare a list of overlays, that will have as each element a string suitable for being
};
}
+ /* Secondly, extend the lowerdir= parameters with each ExtensionDirectory.
+ * Bind mount them in the same location as the ExtensionImages, so that we
+ * can check that they are valid trees (extension-release.d). */
+ STRV_FOREACH(extension_directory, extension_directories) {
+ _cleanup_free_ char *mount_point = NULL, *source = NULL;
+ const char *e = *extension_directory;
+ bool ignore_enoent = false;
+
+ /* Pick up the counter where the ExtensionImages left it. */
+ r = asprintf(&mount_point, "%s/%zu", extension_dir, n++);
+ if (r < 0)
+ return -ENOMEM;
+
+ /* Look for any prefixes */
+ if (startswith(e, "-")) {
+ e++;
+ ignore_enoent = true;
+ }
+ /* Ignore this for now */
+ if (startswith(e, "+"))
+ e++;
+
+ source = strdup(e);
+ if (!source)
+ return -ENOMEM;
+
+ for (size_t j = 0; hierarchies && hierarchies[j]; ++j) {
+ _cleanup_free_ char *prefixed_hierarchy = NULL, *escaped = NULL, *lowerdir = NULL;
+
+ prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
+ if (!prefixed_hierarchy)
+ return -ENOMEM;
+
+ escaped = shell_escape(prefixed_hierarchy, ",:");
+ if (!escaped)
+ return -ENOMEM;
+
+ /* Note that lowerdir= parameters are in 'reverse' order, so the
+ * top-most directory in the overlay comes first in the list. */
+ lowerdir = strjoin(escaped, ":", overlays[j]);
+ if (!lowerdir)
+ return -ENOMEM;
+
+ free_and_replace(overlays[j], lowerdir);
+ }
+
+ *((*p)++) = (MountEntry) {
+ .path_malloc = TAKE_PTR(mount_point),
+ .source_const = TAKE_PTR(source),
+ .mode = EXTENSION_DIRECTORIES,
+ .ignore = ignore_enoent,
+ .has_prefix = true,
+ .read_only = true,
+ };
+ }
+
/* Then, for each hierarchy, prepare an overlay with the list of lowerdir= strings
* set up earlier. */
for (size_t i = 0; hierarchies && hierarchies[i]; ++i) {
static int mount_path_compare(const MountEntry *a, const MountEntry *b) {
int d;
- /* EXTENSION_IMAGES will be used by other mounts as a base, so sort them first
+ /* ExtensionImages/Directories will be used by other mounts as a base, so sort them first
* regardless of the prefix - they are set up in the propagate directory anyway */
d = -CMP(a->mode == EXTENSION_IMAGES, b->mode == EXTENSION_IMAGES);
+ if (d != 0)
+ return d;
+ d = -CMP(a->mode == EXTENSION_DIRECTORIES, b->mode == EXTENSION_DIRECTORIES);
if (d != 0)
return d;
for (f = m, t = m; f < m + *n; f++) {
- /* ExtensionImages bases are opened in /run/systemd/unit-extensions on the host */
- if (f->mode != EXTENSION_IMAGES && !path_startswith(mount_entry_path(f), root_directory)) {
+ /* ExtensionImages/Directories bases are opened in /run/systemd/unit-extensions on the host */
+ if (!IN_SET(f->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) && !path_startswith(mount_entry_path(f), root_directory)) {
log_debug("%s is outside of root directory.", mount_entry_path(f));
mount_entry_done(f);
continue;
NULL);
if (r < 0)
return log_debug_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
+ if (isempty(host_os_release_id))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
}
r = verity_dissect_and_mount(
what = mount_entry_path(m);
break;
+ case EXTENSION_DIRECTORIES: {
+ _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL,
+ *host_os_release_sysext_level = NULL, *extension_name = NULL;
+ _cleanup_strv_free_ char **extension_release = NULL;
+
+ r = path_extract_filename(mount_entry_source(m), &extension_name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract extension name from %s: %m", mount_entry_source(m));
+
+ r = parse_os_release(
+ empty_to_root(root_directory),
+ "ID", &host_os_release_id,
+ "VERSION_ID", &host_os_release_version_id,
+ "SYSEXT_LEVEL", &host_os_release_sysext_level,
+ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
+ if (isempty(host_os_release_id))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
+
+ r = load_extension_release_pairs(mount_entry_source(m), extension_name, &extension_release);
+ if (r == -ENOENT && m->ignore)
+ return 0;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse directory %s extension-release metadata: %m", extension_name);
+
+ r = extension_release_validate(
+ extension_name,
+ host_os_release_id,
+ host_os_release_version_id,
+ host_os_release_sysext_level,
+ /* host_sysext_scope */ NULL, /* Leave empty, we need to accept both system and portable */
+ extension_release);
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Directory %s extension-release metadata does not match the root's", extension_name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to compare directory %s extension-release metadata with the root's os-release: %m", extension_name);
+
+ _fallthrough_;
+ }
+
case BIND_MOUNT:
rbind = false;
(void) mkdir_parents(mount_entry_path(m), 0755);
q = make_mount_point_inode_from_path(what, mount_entry_path(m), 0755);
- if (q < 0)
+ if (q < 0 && q != -EEXIST)
log_error_errno(q, "Failed to create destination mount point node '%s': %m",
mount_entry_path(m));
else
size_t n_temporary_filesystems,
size_t n_mount_images,
size_t n_extension_images,
+ size_t n_extension_directories,
size_t n_hierarchies,
const char* tmp_dir,
const char* var_tmp_dir,
strv_length(empty_directories) +
n_bind_mounts +
n_mount_images +
- (n_extension_images > 0 ? n_hierarchies + n_extension_images : 0) + /* Mount each image plus an overlay per hierarchy */
+ (n_extension_images > 0 || n_extension_directories > 0 ? /* Mount each image and directory plus an overlay per hierarchy */
+ n_hierarchies + n_extension_images + n_extension_directories: 0) +
n_temporary_filesystems +
ns_info->private_dev +
(ns_info->protect_kernel_tunables ?
ns_info->private_ipc; /* /dev/mqueue */
}
-static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
+/* Walk all mount entries and dropping any unused mounts. This affects all
+ * mounts:
+ * - that are implicitly protected by a path that has been rendered inaccessible
+ * - whose immediate parent requests the same protection mode as the mount itself
+ * - that are outside of the relevant root directory
+ * - which are duplicates
+ */
+static void drop_unused_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
assert(root_directory);
assert(n_mounts);
assert(mounts || *n_mounts == 0);
if (m->applied)
continue;
- /* ExtensionImages are first opened in the propagate directory, not in the root_directory */
- r = follow_symlink(m->mode != EXTENSION_IMAGES ? root : NULL, m);
+ /* ExtensionImages/Directories are first opened in the propagate directory, not in the root_directory */
+ r = follow_symlink(!IN_SET(m->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) ? root : NULL, m);
if (r < 0) {
if (error_path && mount_entry_path(m))
*error_path = strdup(mount_entry_path(m));
if (!again)
break;
- normalize_mounts(root, mounts, n_mounts);
+ drop_unused_mounts(root, mounts, n_mounts);
}
/* Now that all filesystems have been set up, but before the
* exist, which means this will be a no-op. */
r = create_symlinks_from_tuples(root, exec_dir_symlinks);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to set up ExecDirectories symlinks inside mount namespace: %m");
/* Create a deny list we can pass to bind_mount_recursive() */
deny_list = new(char*, (*n_mounts)+1);
const char *verity_data_path,
const MountImage *extension_images,
size_t n_extension_images,
+ char **extension_directories,
const char *propagate_dir,
const char *incoming_dir,
const char *notify_socket,
require_prefix = true;
}
- if (n_extension_images > 0) {
+ if (n_extension_images > 0 || !strv_isempty(extension_directories)) {
r = parse_env_extension_hierarchies(&hierarchies);
if (r < 0)
return r;
n_temporary_filesystems,
n_mount_images,
n_extension_images,
+ strv_length(extension_directories),
strv_length(hierarchies),
tmp_dir, var_tmp_dir,
creds_path,
if (r < 0)
goto finish;
- r = append_extension_images(&m, root, extension_dir, hierarchies, extension_images, n_extension_images);
+ r = append_extensions(&m, root, extension_dir, hierarchies, extension_images, n_extension_images, extension_directories);
if (r < 0)
goto finish;
goto finish;
}
+ /* Note, if proc is mounted with subset=pid then neither of the
+ * two paths will exist, i.e. they are implicitly protected by
+ * the mount option. */
if (ns_info->protect_hostname) {
*(m++) = (MountEntry) {
.path_const = "/proc/sys/kernel/hostname",
.mode = READONLY,
+ .ignore = ignore_protect_proc,
};
*(m++) = (MountEntry) {
.path_const = "/proc/sys/kernel/domainname",
.mode = READONLY,
+ .ignore = ignore_protect_proc,
};
}
if (r < 0)
goto finish;
- normalize_mounts(root, mounts, &n_mounts);
+ drop_unused_mounts(root, mounts, &n_mounts);
}
/* All above is just preparation, figuring out what to do. Let's now actually start doing something. */
const char *root_verity,
const MountImage *extension_images,
size_t n_extension_images,
+ char **extension_directories,
const char *propagate_dir,
const char *incoming_dir,
const char *notify_socket,
return false;
case SERVICE_RESTART_ALWAYS:
- return true;
+ return s->result != SERVICE_SKIP_CONDITION;
case SERVICE_RESTART_ON_SUCCESS:
return s->result == SERVICE_SUCCESS;
#include "user-util.h"
static int specifier_prefix_and_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const Unit *u = userdata;
-
- assert(u);
+ const Unit *u = ASSERT_PTR(userdata);
return unit_name_to_prefix_and_instance(u->id, ret);
}
static int specifier_prefix(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const Unit *u = userdata;
-
- assert(u);
+ const Unit *u = ASSERT_PTR(userdata);
return unit_name_to_prefix(u->id, ret);
}
static int specifier_prefix_unescaped(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
_cleanup_free_ char *p = NULL;
- const Unit *u = userdata;
+ const Unit *u = ASSERT_PTR(userdata);
int r;
- assert(u);
-
r = unit_name_to_prefix(u->id, &p);
if (r < 0)
return r;
}
static int specifier_instance_unescaped(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const Unit *u = userdata;
-
- assert(u);
+ const Unit *u = ASSERT_PTR(userdata);
return unit_name_unescape(strempty(u->instance), ret);
}
static int specifier_last_component(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const Unit *u = userdata;
+ const Unit *u = ASSERT_PTR(userdata);
_cleanup_free_ char *prefix = NULL;
char *dash;
int r;
- assert(u);
-
r = unit_name_to_prefix(u->id, &prefix);
if (r < 0)
return r;
}
static int specifier_filename(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const Unit *u = userdata;
-
- assert(u);
+ const Unit *u = ASSERT_PTR(userdata);
if (u->instance)
return unit_name_path_unescape(u->instance, ret);
}
static int specifier_cgroup(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const Unit *u = userdata;
+ const Unit *u = ASSERT_PTR(userdata);
char *n;
- assert(u);
-
bad_specifier(u, specifier);
if (u->cgroup_path)
}
static int specifier_cgroup_root(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const Unit *u = userdata;
+ const Unit *u = ASSERT_PTR(userdata);
char *n;
- assert(u);
-
bad_specifier(u, specifier);
n = strdup(u->manager->cgroup_root);
}
static int specifier_cgroup_slice(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const Unit *u = userdata, *slice;
+ const Unit *u = ASSERT_PTR(userdata), *slice;
char *n;
- assert(u);
-
bad_specifier(u, specifier);
slice = UNIT_GET_SLICE(u);
}
static int specifier_special_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const Unit *u = userdata;
+ const Unit *u = ASSERT_PTR(userdata);
char *n = NULL;
- assert(u);
-
n = strdup(u->manager->prefix[PTR_TO_UINT(data)]);
if (!n)
return -ENOMEM;
}
int unit_name_printf(const Unit *u, const char* format, char **ret) {
-
/*
* This will use the passed string as format string and replace the following specifiers (which should all be
* safe for inclusion in unit names):
{ 'P', specifier_prefix_unescaped, NULL },
{ 'f', specifier_filename, NULL },
+ { 'y', specifier_real_path, u->fragment_path },
+ { 'Y', specifier_real_directory, u->fragment_path },
{ 'c', specifier_cgroup, NULL },
{ 'r', specifier_cgroup_slice, NULL },
e = sd_bus_message_get_error(message);
if (e) {
- if (!sd_bus_error_has_name(e, "org.freedesktop.DBus.Error.NameHasNoOwner"))
- log_unit_error(u, "Unexpected error response from GetNameOwner(): %s", e->message);
+ if (!sd_bus_error_has_name(e, "org.freedesktop.DBus.Error.NameHasNoOwner")) {
+ r = sd_bus_error_get_errno(e);
+ log_unit_error_errno(u, r,
+ "Unexpected error response from GetNameOwner(): %s",
+ bus_error_message(e, r));
+ }
new_owner = NULL;
} else {
if (!sep)
return -EINVAL;
- a = strndupa(arg_root_what + 1, sep - arg_root_what - 1);
+ a = strndupa_safe(arg_root_what + 1, sep - arg_root_what - 1);
r = in_addr_from_string(AF_INET6, a, &u);
if (r < 0)
/* IPv4 address */
sep = strchr(arg_root_what, ':');
if (sep) {
- a = strndupa(arg_root_what, sep - arg_root_what);
+ a = strndupa_safe(arg_root_what, sep - arg_root_what);
if (in_addr_from_string(AF_INET, a, &u) >= 0)
return true;
if (r < 0)
return r;
if (r == 0) {
+ _cleanup_free_ char *joined = NULL;
const char *homework, *suffix, *unix_path;
/* Child */
suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
- if (suffix)
- unix_path = strjoina("/run/systemd/home/notify.", suffix);
- else
+ if (suffix) {
+ joined = strjoin("/run/systemd/home/notify.", suffix);
+ if (!joined)
+ return log_oom();
+ unix_path = joined;
+ } else
unix_path = "/run/systemd/home/notify";
if (setenv("NOTIFY_SOCKET", unix_path, 1) < 0) {
}
static int manager_connect_bus(Manager *m) {
+ _cleanup_free_ char *b = NULL;
const char *suffix, *busname;
int r;
return r;
suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
- if (suffix)
- busname = strjoina("org.freedesktop.home1.", suffix);
- else
+ if (suffix) {
+ b = strjoin("org.freedesktop.home1.", suffix);
+ if (!b)
+ return log_oom();
+ busname = b;
+ } else
busname = "org.freedesktop.home1";
r = sd_bus_request_name_async(m->bus, NULL, busname, 0, NULL, NULL);
}
static int manager_bind_varlink(Manager *m) {
+ _cleanup_free_ char *p = NULL;
const char *suffix, *socket_path;
int r;
/* To make things easier to debug, when working from a homed managed home directory, let's optionally
* use a different varlink socket name */
suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
- if (suffix)
- socket_path = strjoina("/run/systemd/userdb/io.systemd.Home.", suffix);
- else
+ if (suffix) {
+ p = strjoin("/run/systemd/userdb/io.systemd.Home.", suffix);
+ if (!p)
+ return log_oom();
+ socket_path = p;
+ } else
socket_path = "/run/systemd/userdb/io.systemd.Home";
r = varlink_server_listen_address(m->varlink_server, socket_path, 0666);
suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
if (suffix) {
- const char *unix_path;
+ _cleanup_free_ char *unix_path = NULL;
- unix_path = strjoina("/run/systemd/home/notify.", suffix);
+ unix_path = strjoin("/run/systemd/home/notify.", suffix);
+ if (!unix_path)
+ return log_oom();
r = sockaddr_un_set_path(&sa.un, unix_path);
if (r < 0)
return log_error_errno(r, "Socket path %s does not fit in sockaddr_un: %m", unix_path);
/* Properties we cache are indexed by an enum, to make invalidation easy and systematic (as we can iterate
* through them all, and they are uniformly strings). */
-enum {
+typedef enum {
/* Read from /etc/hostname */
PROP_STATIC_HOSTNAME,
PROP_CHASSIS,
PROP_DEPLOYMENT,
PROP_LOCATION,
+ PROP_VENDOR,
+ PROP_MODEL,
/* Read from /etc/os-release (or /usr/lib/os-release) */
PROP_OS_PRETTY_NAME,
PROP_OS_HOME_URL,
_PROP_MAX,
_PROP_INVALID = -EINVAL,
-};
+} HostProperty;
typedef struct Context {
char *data[_PROP_MAX];
"ICON_NAME", &c->data[PROP_ICON_NAME],
"CHASSIS", &c->data[PROP_CHASSIS],
"DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
- "LOCATION", &c->data[PROP_LOCATION]);
+ "LOCATION", &c->data[PROP_LOCATION],
+ "VENDOR", &c->data[PROP_VENDOR],
+ "MODEL", &c->data[PROP_MODEL]);
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
c->etc_os_release_stat = current_stat;
}
+static int get_dmi_data(const char *database_key, const char *regular_key, char **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ _cleanup_free_ char *b = NULL;
+ const char *s = NULL;
+ int r;
+
+ r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id");
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m");
+
+ if (database_key)
+ (void) sd_device_get_property_value(device, database_key, &s);
+ if (!s && regular_key)
+ (void) sd_device_get_property_value(device, regular_key, &s);
+
+ if (s) {
+ b = strdup(s);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ if (ret)
+ *ret = TAKE_PTR(b);
+
+ return !!s;
+}
+
+static int get_hardware_vendor(char **ret) {
+ return get_dmi_data("ID_VENDOR_FROM_DATABASE", "ID_VENDOR", ret);
+}
+
+static int get_hardware_model(char **ret) {
+ return get_dmi_data("ID_MODEL_FROM_DATABASE", "ID_MODEL", ret);
+}
+
+static int get_hardware_serial(char **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ _cleanup_free_ char *b = NULL;
+ const char *s = NULL;
+ int r;
+
+ r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id");
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m");
+
+ (void) sd_device_get_sysattr_value(device, "product_serial", &s);
+ if (isempty(s))
+ /* Fallback to board serial */
+ (void) sd_device_get_sysattr_value(device, "board_serial", &s);
+
+ if (!isempty(s)) {
+ b = strdup(s);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ if (ret)
+ *ret = TAKE_PTR(b);
+
+ return !isempty(s);
+}
+
static const char* valid_chassis(const char *chassis) {
assert(chassis);
return chassis;
}
-static char* context_fallback_icon_name(Context *c) {
- const char *chassis;
+static char* context_get_chassis(Context *c) {
+ const char *fallback;
+ char *dmi;
assert(c);
if (!isempty(c->data[PROP_CHASSIS]))
- return strjoin("computer-", c->data[PROP_CHASSIS]);
+ return strdup(c->data[PROP_CHASSIS]);
+
+ if (get_dmi_data("ID_CHASSIS", NULL, &dmi) >= 0)
+ return dmi;
+
+ fallback = fallback_chassis();
+ if (fallback)
+ return strdup(fallback);
+
+ return NULL;
+}
+
+static char* context_fallback_icon_name(Context *c) {
+ _cleanup_free_ char *chassis = NULL;
- chassis = fallback_chassis();
+ assert(c);
+
+ chassis = context_get_chassis(c);
if (chassis)
return strjoin("computer-", chassis);
return 0;
}
-static int get_dmi_data(const char *database_key, const char *regular_key, char **ret) {
- _cleanup_(sd_device_unrefp) sd_device *device = NULL;
- _cleanup_free_ char *b = NULL;
- const char *s = NULL;
- int r;
-
- r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id");
- if (r < 0)
- return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m");
-
- if (database_key)
- (void) sd_device_get_property_value(device, database_key, &s);
- if (!s && regular_key)
- (void) sd_device_get_property_value(device, regular_key, &s);
+static int property_get_hardware_property(
+ sd_bus_message *reply,
+ Context *c,
+ HostProperty prop,
+ int (*getter)(char **)) {
- if (s) {
- b = strdup(s);
- if (!b)
- return -ENOMEM;
- }
+ _cleanup_free_ char *from_dmi = NULL;
- if (ret)
- *ret = TAKE_PTR(b);
+ assert(reply);
+ assert(c);
+ assert(IN_SET(prop, PROP_VENDOR, PROP_MODEL));
+ assert(getter);
- return !!s;
-}
+ context_read_machine_info(c);
-static int get_hardware_vendor(char **ret) {
- return get_dmi_data("ID_VENDOR_FROM_DATABASE", "ID_VENDOR", ret);
-}
+ if (isempty(c->data[prop]))
+ (void) getter(&from_dmi);
-static int get_hardware_model(char **ret) {
- return get_dmi_data("ID_MODEL_FROM_DATABASE", "ID_MODEL", ret);
+ return sd_bus_message_append(reply, "s", from_dmi ?: c->data[prop]);
}
static int property_get_hardware_vendor(
void *userdata,
sd_bus_error *error) {
- _cleanup_free_ char *vendor = NULL;
-
- (void) get_hardware_vendor(&vendor);
- return sd_bus_message_append(reply, "s", vendor);
+ return property_get_hardware_property(reply, userdata, PROP_VENDOR, get_hardware_vendor);
}
static int property_get_hardware_model(
void *userdata,
sd_bus_error *error) {
- _cleanup_free_ char *model = NULL;
-
- (void) get_hardware_model(&model);
- return sd_bus_message_append(reply, "s", model);
+ return property_get_hardware_property(reply, userdata, PROP_MODEL, get_hardware_model);
}
static int property_get_hostname(
void *userdata,
sd_bus_error *error) {
+ _cleanup_free_ char *chassis = NULL;
Context *c = userdata;
- const char *name;
context_read_machine_info(c);
- if (isempty(c->data[PROP_CHASSIS]))
- name = fallback_chassis();
- else
- name = c->data[PROP_CHASSIS];
+ chassis = context_get_chassis(c);
- return sd_bus_message_append(reply, "s", name);
+ return sd_bus_message_append(reply, "s", chassis);
}
static int property_get_uname_field(
return sd_bus_send(NULL, reply, NULL);
}
+static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ char *serial = NULL;
+ Context *c = userdata;
+ int interactive, r;
+
+ assert(m);
+ assert(c);
+
+ r = sd_bus_message_read(m, "b", &interactive);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.hostname1.get-hardware-serial",
+ NULL,
+ interactive,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = get_hardware_serial(&serial);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", serial);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL, *vendor = NULL, *model = NULL;
+ _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
+ *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
sd_id128_t product_uuid = SD_ID128_NULL;
- const char *chassis = NULL;
Context *c = userdata;
bool privileged;
struct utsname u;
r = bus_verify_polkit_async(
m,
CAP_SYS_ADMIN,
- "org.freedesktop.hostname1.get-product-uuid",
+ "org.freedesktop.hostname1.get-description",
NULL,
false,
UID_INVALID,
if (isempty(c->data[PROP_ICON_NAME]))
in = context_fallback_icon_name(c);
- if (isempty(c->data[PROP_CHASSIS]))
- chassis = fallback_chassis();
+ chassis = context_get_chassis(c);
assert_se(uname(&u) >= 0);
- (void) get_hardware_vendor(&vendor);
- (void) get_hardware_model(&model);
+ if (isempty(c->data[PROP_VENDOR]))
+ (void) get_hardware_vendor(&vendor);
+ if (isempty(c->data[PROP_MODEL]))
+ (void) get_hardware_model(&model);
- if (privileged) /* The product UUID is only available to privileged clients */
- id128_get_product(&product_uuid);
+ if (privileged) {
+ /* The product UUID and hardware serial is only available to privileged clients */
+ (void) id128_get_product(&product_uuid);
+ (void) get_hardware_serial(&serial);
+ }
r = json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)),
JSON_BUILD_PAIR("DefaultHostname", JSON_BUILD_STRING(dhn)),
JSON_BUILD_PAIR("HostnameSource", JSON_BUILD_STRING(hostname_source_to_string(c->hostname_source))),
JSON_BUILD_PAIR("IconName", JSON_BUILD_STRING(in ?: c->data[PROP_ICON_NAME])),
- JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis ?: c->data[PROP_CHASSIS])),
+ JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis)),
JSON_BUILD_PAIR("Deployment", JSON_BUILD_STRING(c->data[PROP_DEPLOYMENT])),
JSON_BUILD_PAIR("Location", JSON_BUILD_STRING(c->data[PROP_LOCATION])),
JSON_BUILD_PAIR("KernelName", JSON_BUILD_STRING(u.sysname)),
JSON_BUILD_PAIR("OperatingSystemPrettyName", JSON_BUILD_STRING(c->data[PROP_OS_PRETTY_NAME])),
JSON_BUILD_PAIR("OperatingSystemCPEName", JSON_BUILD_STRING(c->data[PROP_OS_CPE_NAME])),
JSON_BUILD_PAIR("OperatingSystemHomeURL", JSON_BUILD_STRING(c->data[PROP_OS_HOME_URL])),
- JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor)),
- JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model)),
+ JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor ?: c->data[PROP_VENDOR])),
+ JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model ?: c->data[PROP_MODEL])),
+ JSON_BUILD_PAIR("HardwareSerial", JSON_BUILD_STRING(serial)),
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL)));
SD_BUS_PARAM(uuid),
method_get_product_uuid,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_NAMES("GetHardwareSerial",
+ "b",
+ SD_BUS_PARAM(interactive),
+ "s",
+ SD_BUS_PARAM(serial),
+ method_get_hardware_serial,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("Describe",
SD_BUS_NO_ARGS,
SD_BUS_RESULT("s", json),
</defaults>
</action>
+ <action id="org.freedesktop.hostname1.get-hardware-serial">
+ <description gettext-domain="systemd">Get hardware serial number</description>
+ <message gettext-domain="systemd">Authentication is required to get hardware serial number.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.hostname1.get-description">
+ <description gettext-domain="systemd">Get system description</description>
+ <message gettext-domain="systemd">Authentication is required to get system description.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
</policyconfig>
_cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-journal-remote.XXXXXX.journal";
_cleanup_close_ int fdout = -1;
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
- RemoteServer s = {};
+ _cleanup_(journal_remote_server_destroy) RemoteServer s = {};
int r;
if (size <= 2)
while (s.active)
assert_se(journal_remote_handle_raw_source(NULL, fdin, 0, &s) >= 0);
- journal_remote_server_destroy(&s);
assert_se(close(fdin) < 0 && errno == EBADF); /* Check that the fd is closed already */
/* Out */
z = n;
- map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, iovec, &n, ELEMENTSOF(iovec));
+ map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, iovec, &n, n + N_IOVEC_AUDIT_FIELDS);
server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, LOG_NOTICE, 0);
f->header->arena_size = htole64(p - le64toh(f->header->header_size));
if (ftruncate(f->fd, p) < 0)
- log_debug_errno(errno, "Failed to truncate %s: %m", f->path);
+ return log_debug_errno(errno, "Failed to truncate %s: %m", f->path);
- return 0;
+ return journal_file_fstat(f);
}
static int journald_file_entry_array_punch_hole(JournalFile *f, uint64_t p, uint64_t n_entries) {
if (sz < MINIMUM_HOLE_SIZE)
return 0;
+ if (p == le64toh(f->header->tail_object_offset) && !f->seal) {
+ o.object.size = htole64(offset - p);
+ if (pwrite(f->fd, &o, sizeof(EntryArrayObject), p) < 0)
+ return log_debug_errno(errno, "Failed to modify entry array object size: %m");
+
+ f->header->arena_size = htole64(ALIGN64(offset) - le64toh(f->header->header_size));
+
+ if (ftruncate(f->fd, ALIGN64(offset)) < 0)
+ return log_debug_errno(errno, "Failed to truncate %s: %m", f->path);
+
+ return 0;
+ }
+
if (fallocate(f->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, sz) < 0)
return log_debug_errno(errno, "Failed to punch hole in entry array of %s: %m", f->path);
JournaldFile *f;
struct iovec iovec;
static const char test[] = "TEST1=1", test2[] = "TEST2=2";
- Object *o;
+ Object *o, *d;
uint64_t p;
sd_id128_t fake_boot_id;
char t[] = "/var/tmp/journal-XXXXXX";
assert_se(journal_file_next_entry(f->file, 0, DIRECTION_DOWN, &o, &p) == 1);
assert_se(le64toh(o->entry.seqnum) == 1);
- assert_se(journal_file_find_data_object(f->file, test, strlen(test), NULL, &p) == 1);
- assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_DOWN, &o, NULL) == 1);
+ assert_se(journal_file_find_data_object(f->file, test, strlen(test), &d, NULL) == 1);
+ assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_DOWN, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 1);
- assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_UP, &o, NULL) == 1);
+ assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_UP, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 3);
- assert_se(journal_file_find_data_object(f->file, test2, strlen(test2), NULL, &p) == 1);
- assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_UP, &o, NULL) == 1);
+ assert_se(journal_file_find_data_object(f->file, test2, strlen(test2), &d, NULL) == 1);
+ assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_UP, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 2);
- assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_DOWN, &o, NULL) == 1);
+ assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_DOWN, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 2);
- assert_se(journal_file_find_data_object(f->file, "quux", 4, NULL, &p) == 0);
+ assert_se(journal_file_find_data_object(f->file, "quux", 4, &d, NULL) == 0);
assert_se(journal_file_move_to_entry_by_seqnum(f->file, 1, DIRECTION_DOWN, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 1);
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
- uint8_t type, uint16_t arp_type, size_t optlen,
- size_t *optoffset);
+ uint8_t type, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr,
+ size_t optlen, size_t *optoffset);
uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);
#include "dhcp-internal.h"
#include "dhcp-protocol.h"
+#include "memory-util.h"
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
-int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
- uint8_t type, uint16_t arp_type, size_t optlen,
- size_t *optoffset) {
+int dhcp_message_init(
+ DHCPMessage *message,
+ uint8_t op,
+ uint32_t xid,
+ uint8_t type,
+ uint16_t arp_type,
+ uint8_t hlen,
+ const uint8_t *chaddr,
+ size_t optlen,
+ size_t *optoffset) {
+
size_t offset = 0;
int r;
assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
- assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
+ assert(chaddr || hlen == 0);
message->op = op;
message->htype = arp_type;
- message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
+
+ /* RFC2131 section 4.1.1:
+ The client MUST include its hardware address in the ’chaddr’ field, if
+ necessary for delivery of DHCP reply messages.
+
+ RFC 4390 section 2.1:
+ A DHCP client, when working over an IPoIB interface, MUST follow the
+ following rules:
+ "htype" (hardware address type) MUST be 32 [ARPPARAM].
+ "hlen" (hardware address length) MUST be 0.
+ "chaddr" (client hardware address) field MUST be zeroed.
+ */
+ message->hlen = (arp_type == ARPHRD_INFINIBAND) ? 0 : hlen;
+ memcpy_safe(message->chaddr, chaddr, message->hlen);
+
message->xid = htobe32(xid);
message->magic = htobe32(DHCP_MAGIC_COOKIE);
typedef struct DHCPClientId {
size_t length;
- void *data;
+ uint8_t *data;
} DHCPClientId;
typedef struct DHCPLease {
DHCPClientId client_id;
+ uint8_t htype; /* e.g. ARPHRD_ETHER */
+ uint8_t hlen; /* e.g. ETH_ALEN */
be32_t address;
be32_t gateway;
uint8_t chaddr[16];
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fuzz.h"
+#include "sd-event.h"
+
+#include "sd-dhcp-client.c"
+
+int dhcp_network_bind_raw_socket(
+ int ifindex,
+ union sockaddr_union *link,
+ uint32_t id,
+ const uint8_t *addr, size_t addr_len,
+ const uint8_t *bcaddr, size_t bcaddr_len,
+ uint16_t arp_type, uint16_t port) {
+
+ int fd;
+ fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) {
+ return len;
+}
+
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
+ int fd;
+
+ fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) {
+ return len;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'};
+ uint8_t bcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ int res, r;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ r = sd_dhcp_client_new(&client, false);
+ assert_se(r >= 0);
+ assert_se(client);
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ r = sd_dhcp_client_attach_event(client, e, 0);
+ assert_se(r >= 0);
+
+ assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
+ assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0);
+ dhcp_client_set_test_mode(client, true);
+
+ res = sd_dhcp_client_start(client);
+ assert_se(IN_SET(res, 0, -EINPROGRESS));
+ client->xid = 2;
+
+ (void) client_handle_offer(client, (DHCPMessage*) data, size);
+
+ assert_se(sd_dhcp_client_stop(client) >= 0);
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "fuzz.h"
+
+#include "sd-dhcp-server.c"
+
+ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) {
+ return len;
+}
+
+ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
+ struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))};
+ union in_addr_union relay_address;
+ _cleanup_free_ uint8_t *message = NULL;
+
+ if (size < sizeof(DHCPMessage))
+ return 0;
+
+ assert_se(sd_dhcp_server_new(&server, 1) >= 0);
+ assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
+ assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0);
+ assert_se(in_addr_from_string(AF_INET, "192.168.5.1", &relay_address) >= 0);
+ assert_se(sd_dhcp_server_set_relay_target(server, &relay_address.in) >= 0);
+ assert_se(sd_dhcp_server_set_bind_to_interface(server, false) >= 0);
+ assert_se(sd_dhcp_server_set_relay_agent_information(server, "string:sample_circuit_id", "string:sample_remote_id") >= 0);
+
+ size_t buflen = size;
+ buflen += relay_agent_information_length(server->agent_circuit_id, server->agent_remote_id) + 2;
+ assert_se(message = malloc(buflen));
+ memcpy(message, data, size);
+
+ server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY);
+ assert_se(server->fd >= 0);
+
+ (void) dhcp_server_relay_message(server, (DHCPMessage *) message, size - sizeof(DHCPMessage), buflen);
+ return 0;
+}
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))};
static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
+ _cleanup_free_ uint8_t *duped = NULL;
uint8_t *client_id;
DHCPLease *lease;
if (size < sizeof(DHCPMessage))
return 0;
+ assert_se(duped = memdup(data, size));
+
assert_se(sd_dhcp_server_new(&server, 1) >= 0);
+ assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY);
assert_se(server->fd >= 0);
assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0);
lease->address = htobe32(UINT32_C(10) << 24 | UINT32_C(2));
lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1));
lease->expiration = UINT64_MAX;
- memcpy(lease->chaddr, chaddr, 16);
+ lease->htype = ARPHRD_ETHER;
+ lease->hlen = ETH_ALEN;
+ memcpy(lease->chaddr, chaddr, ETH_ALEN);
assert_se(hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease) >= 0);
assert_se(hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease) >= 0);
lease->server = server;
- (void) dhcp_server_handle_message(server, (DHCPMessage*)data, size);
+ (void) dhcp_server_handle_message(server, (DHCPMessage*) duped, size);
return 0;
}
]
fuzzers += [
+ [files('fuzz-dhcp-client.c'),
+ [libshared,
+ libsystemd_network]],
+
[files('fuzz-dhcp6-client.c'),
[libshared,
libsystemd_network]],
[libsystemd_network,
libshared]],
+ [files('fuzz-dhcp-server-relay-message.c'),
+ [libsystemd_network,
+ libshared]],
+
[files('fuzz-lldp-rx.c'),
[libshared,
libsystemd_network]],
return -ENOMEM;
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
- client->arp_type, optlen, &optoffset);
+ client->arp_type, client->mac_addr_len, client->mac_addr,
+ optlen, &optoffset);
if (r < 0)
return r;
secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
packet->dhcp.secs = htobe16(secs);
- /* RFC2132 section 4.1
+ /* RFC2131 section 4.1
A client that cannot receive unicast IP datagrams until its protocol
software has been configured with an IP address SHOULD set the
BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
packet->dhcp.flags = htobe16(0x8000);
- /* RFC2132 section 4.1.1:
- The client MUST include its hardware address in the ’chaddr’ field, if
- necessary for delivery of DHCP reply messages. Non-Ethernet
- interfaces will leave 'chaddr' empty and use the client identifier
- instead (eg, RFC 4390 section 2.1).
- */
- if (client->arp_type == ARPHRD_ETHER)
- memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
-
/* If no client identifier exists, construct an RFC 4361-compliant one */
if (client->client_id_len == 0) {
size_t duid_len;
route->option = SD_DHCP_OPTION_STATIC_ROUTE;
r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
- if (r < 0) {
- log_debug("Failed to determine destination prefix length from class based IP, ignoring");
- continue;
- }
+ if (r < 0)
+ return -EINVAL;
assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
pos = next_chunk;
}
- *domains = TAKE_PTR(names);
+ strv_free_and_replace(*domains, names);
return cnt;
}
#include "fd-util.h"
#include "in-addr-util.h"
#include "io-util.h"
+#include "memory-util.h"
#include "network-common.h"
#include "ordered-set.h"
#include "siphash24.h"
return 0;
}
-static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
- DHCPPacket *packet, size_t len) {
+static int dhcp_server_send_unicast_raw(
+ sd_dhcp_server *server,
+ uint8_t hlen,
+ const uint8_t *chaddr,
+ DHCPPacket *packet,
+ size_t len) {
+
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETH_P_IP),
.ll.sll_ifindex = server->ifindex,
- .ll.sll_halen = ETH_ALEN,
+ .ll.sll_halen = hlen,
};
assert(server);
assert(server->ifindex > 0);
assert(server->address);
+ assert(hlen > 0);
+ assert(chaddr);
assert(packet);
assert(len > sizeof(DHCPPacket));
- memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
+ memcpy(link.ll.sll_addr, chaddr, hlen);
+
+ if (len > UINT16_MAX)
+ return -EOVERFLOW;
dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
packet->dhcp.yiaddr,
return message->flags & htobe16(0x8000);
}
-static int dhcp_server_send(sd_dhcp_server *server, be32_t destination, uint16_t destination_port,
- DHCPPacket *packet, size_t optoffset, bool l2_broadcast) {
+static int dhcp_server_send(
+ sd_dhcp_server *server,
+ uint8_t hlen,
+ const uint8_t *chaddr,
+ be32_t destination,
+ uint16_t destination_port,
+ DHCPPacket *packet,
+ size_t optoffset,
+ bool l2_broadcast) {
+
if (destination != INADDR_ANY)
return dhcp_server_send_udp(server, destination,
destination_port, &packet->dhcp,
/* we cannot send UDP packet to specific MAC address when the
address is not yet configured, so must fall back to raw
packets */
- return dhcp_server_send_unicast_raw(server, packet,
+ return dhcp_server_send_unicast_raw(server, hlen, chaddr, packet,
sizeof(DHCPPacket) + optoffset);
}
destination = req->message->ciaddr;
bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK;
- return dhcp_server_send(server, destination, destination_port, packet, optoffset, l2_broadcast);
+ return dhcp_server_send(server, req->message->hlen, req->message->chaddr,
+ destination, destination_port, packet, optoffset, l2_broadcast);
}
static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
return -ENOMEM;
r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
- be32toh(req->message->xid), type, ARPHRD_ETHER,
+ be32toh(req->message->xid), type,
+ req->message->htype, req->message->hlen, req->message->chaddr,
req->max_optlen, &optoffset);
if (r < 0)
return r;
packet->dhcp.flags = req->message->flags;
packet->dhcp.giaddr = req->message->giaddr;
- memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
*_optoffset = optoffset;
*ret = TAKE_PTR(packet);
return r;
}
- r = dhcp_server_send_packet(server, req, packet, type, offset);
- if (r < 0)
- return r;
-
- return 0;
+ return dhcp_server_send_packet(server, req, packet, type, offset);
}
static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
}
-static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
- be32_t gateway, const uint8_t chaddr[]) {
+static int server_send_forcerenew(
+ sd_dhcp_server *server,
+ be32_t address,
+ be32_t gateway,
+ uint8_t htype,
+ uint8_t hlen,
+ const uint8_t *chaddr) {
+
_cleanup_free_ DHCPPacket *packet = NULL;
size_t optoffset = 0;
int r;
return -ENOMEM;
r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
- DHCP_FORCERENEW, ARPHRD_ETHER,
+ DHCP_FORCERENEW, htype, hlen, chaddr,
DHCP_MIN_OPTIONS_SIZE, &optoffset);
if (r < 0)
return r;
if (r < 0)
return r;
- memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
-
- r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
- &packet->dhcp,
- sizeof(DHCPMessage) + optoffset);
- if (r < 0)
- return r;
-
- return 0;
+ return dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
+ &packet->dhcp,
+ sizeof(DHCPMessage) + optoffset);
}
static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
if (!data)
return -ENOMEM;
- free(req->client_id.data);
- req->client_id.data = data;
+ free_and_replace(req->client_id.data, data);
req->client_id.length = len;
}
req->message = message;
- /* set client id based on MAC address if client did not send an explicit
- one */
+ if (message->hlen > sizeof(message->chaddr))
+ return -EBADMSG;
+
+ /* set client id based on MAC address if client did not send an explicit one */
if (!req->client_id.data) {
- void *data;
+ uint8_t *data;
- data = malloc0(ETH_ALEN + 1);
+ if (message->hlen == 0)
+ return -EBADMSG;
+
+ data = new0(uint8_t, message->hlen + 1);
if (!data)
return -ENOMEM;
- ((uint8_t*) data)[0] = 0x01;
- memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
+ data[0] = 0x01;
+ memcpy(data + 1, message->chaddr, message->hlen);
- req->client_id.length = ETH_ALEN + 1;
+ req->client_id.length = message->hlen + 1;
req->client_id.data = data;
}
+ if (message->hlen == 0 || memeqzero(message->chaddr, message->hlen)) {
+ /* See RFC2131 section 4.1.1.
+ * hlen and chaddr may not be set for non-ethernet interface.
+ * Let's try to retrieve it from the client ID. */
+
+ if (!req->client_id.data)
+ return -EBADMSG;
+
+ if (req->client_id.length <= 1 || req->client_id.length > sizeof(message->chaddr) + 1)
+ return -EBADMSG;
+
+ if (req->client_id.data[0] != 0x01)
+ return -EBADMSG;
+
+ message->hlen = req->client_id.length - 1;
+ memcpy(message->chaddr, req->client_id.data + 1, message->hlen);
+ }
+
if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
assert(message);
assert(sd_dhcp_server_is_in_relay_mode(server));
+ if (message->hlen == 0 || message->hlen > sizeof(message->chaddr) || memeqzero(message->chaddr, message->hlen))
+ return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG),
+ "(relay agent) received message without/invalid hardware address, discarding.");
+
if (message->op == BOOTREQUEST) {
log_dhcp_server(server, "(relay agent) BOOTREQUEST (0x%x)", be32toh(message->xid));
if (message->hops >= 16)
bool l2_broadcast = requested_broadcast(message) || message_type == DHCP_NAK;
const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message->ciaddr;
- return dhcp_server_send(server, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast);
+ return dhcp_server_send(server, message->hlen, message->chaddr, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast);
}
return -EBADMSG;
}
DHCPLease **ret_lease,
be32_t address,
const DHCPClientId *client_id,
- const uint8_t chaddr[static ETH_ALEN],
+ uint8_t htype,
+ uint8_t hlen,
+ const uint8_t *chaddr,
be32_t gateway,
usec_t expiration) {
*lease = (DHCPLease) {
.address = address,
.client_id.length = client_id->length,
+ .htype = htype,
+ .hlen = hlen,
.gateway = gateway,
.expiration = expiration,
};
if (!lease->client_id.data)
return -ENOMEM;
- memcpy(&lease->chaddr, chaddr, ETH_ALEN);
+ memcpy(lease->chaddr, chaddr, hlen);
*ret_lease = TAKE_PTR(lease);
if (r < 0)
return r;
- HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) {
+ HASHMAP_FOREACH(lease, server->bound_leases_by_client_id)
if (lease->expiration < time_now) {
log_dhcp_server(server, "CLEAN (0x%x)", be32toh(lease->address));
dhcp_lease_free(lease);
}
- }
return 0;
}
assert(server);
assert(message);
- if (message->op != BOOTREQUEST ||
- message->htype != ARPHRD_ETHER ||
- message->hlen != ETHER_ADDR_LEN)
+ if (message->op != BOOTREQUEST)
return 0;
req = new0(DHCPRequest, 1);
r = ensure_sane_request(server, req, message);
if (r < 0)
- /* this only fails on critical errors */
return r;
r = dhcp_server_cleanup_expired_leases(server);
expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now);
r = prepare_new_lease(&lease, static_lease->address, &req->client_id,
+ req->message->htype, req->message->hlen,
req->message->chaddr, req->message->giaddr, expiration);
if (r < 0)
return r;
if (!existing_lease) {
r = prepare_new_lease(&new_lease, address, &req->client_id,
+ req->message->htype, req->message->hlen,
req->message->chaddr, req->message->giaddr, expiration);
if (r < 0)
return r;
if ((size_t) len < sizeof(DHCPMessage))
return 0;
- CMSG_FOREACH(cmsg, &msg) {
+ CMSG_FOREACH(cmsg, &msg)
if (cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_PKTINFO &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
break;
}
- }
if (sd_dhcp_server_is_in_relay_mode(server)) {
r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage), buflen);
log_dhcp_server(server, "FORCERENEW");
HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) {
- k = server_send_forcerenew(server, lease->address, lease->gateway, lease->chaddr);
+ k = server_send_forcerenew(server, lease->address, lease->gateway,
+ lease->htype, lease->hlen, lease->chaddr);
if (k < 0)
r = k;
}
assert_return(server, -EINVAL);
assert_return(client_id, -EINVAL);
- assert_return(client_id_size == ETH_ALEN + 1, -EINVAL);
assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
/* Static lease with an empty or omitted address is a valid entry,
- * the server removes any static lease with the specified mac address. */
+ * the server removes any static lease with the specified mac address. */
if (!address || address->s_addr == 0) {
- _cleanup_free_ void *data = NULL;
DHCPClientId c;
- data = memdup(client_id, client_id_size);
- if (!data)
- return -ENOMEM;
-
c = (DHCPClientId) {
.length = client_id_size,
- .data = data,
+ .data = client_id,
};
dhcp_lease_free(hashmap_remove(server->static_leases_by_client_id, &c));
message = malloc0(len);
assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678,
- DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0);
+ DHCP_DISCOVER, ARPHRD_ETHER, ETH_ALEN, (uint8_t[16]){},
+ optlen, &optoffset) >= 0);
assert_se(message->xid == htobe32(0x12345678));
assert_se(message->op == BOOTREQUEST);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
test.message.htype = 0;
- assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
+ assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
test.message.htype = ARPHRD_ETHER;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
test.message.hlen = 0;
- assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
+ assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == -EBADMSG);
test.message.hlen = ETHER_ADDR_LEN;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
return free_and_strdup(&intro->interface_name, interface_name);
}
-int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) {
+int introspect_write_child_nodes(struct introspect *i, OrderedSet *s, const char *prefix) {
char *node;
assert(i);
assert_se(set_interface_name(i, NULL) >= 0);
- while ((node = set_steal_first(s))) {
+ while ((node = ordered_set_steal_first(s))) {
const char *e;
e = object_path_startswith(node, prefix);
#include "sd-bus.h"
-#include "set.h"
+#include "ordered-set.h"
struct introspect {
FILE *f;
int introspect_begin(struct introspect *i, bool trusted);
int introspect_write_default_interfaces(struct introspect *i, bool object_manager);
-int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix);
+int introspect_write_child_nodes(struct introspect *i, OrderedSet *s, const char *prefix);
int introspect_write_interface(
struct introspect *i,
const char *interface_name,
#include "bus-slot.h"
#include "bus-type.h"
#include "missing_capability.h"
-#include "set.h"
#include "string-util.h"
#include "strv.h"
sd_bus *bus,
const char *prefix,
struct node_enumerator *first,
- Set *s,
+ OrderedSet *s,
sd_bus_error *error) {
struct node_enumerator *c;
continue;
}
- r = set_consume(s, *k);
+ r = ordered_set_consume(s, *k);
if (r == -EEXIST)
r = 0;
}
const char *prefix,
struct node *n,
unsigned flags,
- Set *s,
+ OrderedSet *s,
sd_bus_error *error) {
struct node *i;
if (!t)
return -ENOMEM;
- r = set_consume(s, t);
+ r = ordered_set_consume(s, t);
if (r < 0 && r != -EEXIST)
return r;
const char *prefix,
struct node *n,
unsigned flags,
- Set **_s,
+ OrderedSet **_s,
sd_bus_error *error) {
- Set *s = NULL;
+ OrderedSet *s = NULL;
int r;
assert(bus);
assert(n);
assert(_s);
- s = set_new(&string_hash_ops);
+ s = ordered_set_new(&string_hash_ops);
if (!s)
return -ENOMEM;
r = add_subtree_to_set(bus, prefix, n, flags, s, error);
if (r < 0) {
- set_free_free(s);
+ ordered_set_free_free(s);
return r;
}
char **ret,
sd_bus_error *error) {
- _cleanup_set_free_free_ Set *s = NULL;
+ _cleanup_ordered_set_free_ OrderedSet *s = NULL;
_cleanup_(introspect_free) struct introspect intro = {};
struct node_vtable *c;
bool empty;
if (r < 0)
return r;
- empty = set_isempty(s);
+ empty = ordered_set_isempty(s);
LIST_FOREACH(vtables, c, n->vtables) {
if (require_fallback && !c->is_fallback)
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_set_free_free_ Set *s = NULL;
+ _cleanup_ordered_set_free_free_ OrderedSet *s = NULL;
char *path;
int r;
if (r < 0)
return r;
- SET_FOREACH(path, s) {
+ ORDERED_SET_FOREACH(path, s) {
r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
if (r < 0)
return bus_maybe_reply_error(m, r, &error);
static int object_added_append_all_prefix(
sd_bus *bus,
sd_bus_message *m,
- Set *s,
+ OrderedSet *s,
const char *prefix,
const char *path,
bool require_fallback) {
* skip it on any of its parents. The child vtables
* always fully override any conflicting vtables of
* any parent node. */
- if (set_get(s, c->interface))
+ if (ordered_set_get(s, c->interface))
continue;
- r = set_put(s, c->interface);
+ r = ordered_set_put(s, c->interface);
if (r < 0)
return r;
}
static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
- _cleanup_set_free_ Set *s = NULL;
+ _cleanup_ordered_set_free_ OrderedSet *s = NULL;
_cleanup_free_ char *prefix = NULL;
size_t pl;
int r;
* a parent that were overwritten by a child.
*/
- s = set_new(&string_hash_ops);
+ s = ordered_set_new(&string_hash_ops);
if (!s)
return -ENOMEM;
static int object_removed_append_all_prefix(
sd_bus *bus,
sd_bus_message *m,
- Set *s,
+ OrderedSet *s,
const char *prefix,
const char *path,
bool require_fallback) {
* skip it on any of its parents. The child vtables
* always fully override any conflicting vtables of
* any parent node. */
- if (set_get(s, c->interface))
+ if (ordered_set_get(s, c->interface))
continue;
r = node_vtable_get_userdata(bus, path, c, &u, &error);
if (r == 0)
continue;
- r = set_put(s, c->interface);
+ r = ordered_set_put(s, c->interface);
if (r < 0)
return r;
}
static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
- _cleanup_set_free_ Set *s = NULL;
+ _cleanup_ordered_set_free_ OrderedSet *s = NULL;
_cleanup_free_ char *prefix = NULL;
size_t pl;
int r;
/* see sd_bus_emit_object_added() for details */
- s = set_new(&string_hash_ops);
+ s = ordered_set_new(&string_hash_ops);
if (!s)
return -ENOMEM;
if (verify) {
r = chase_symlinks(_syspath, NULL, 0, &syspath, NULL);
if (r == -ENOENT)
- return -ENODEV; /* the device does not exist (any more?) */
+ /* the device does not exist (any more?) */
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+ "sd-device: Failed to chase symlinks in \"%s\".", _syspath);
if (r < 0)
return log_debug_errno(r, "sd-device: Failed to get target of '%s': %m", _syspath);
new_syspath = path_join("/sys", p);
if (!new_syspath)
- return -ENOMEM;
+ return log_oom_debug();
free_and_replace(syspath, new_syspath);
path_simplify(syspath);
path = strjoina(syspath, "/uevent");
if (access(path, F_OK) < 0) {
if (errno == ENOENT)
- /* this is not a valid device */
- return -ENODEV;
+ /* This is not a valid device.
+ * Note, this condition is quite often satisfied when
+ * enumerating devices or finding a parent device.
+ * Hence, use log_trace_errno() here. */
+ return log_trace_errno(SYNTHETIC_ERRNO(ENODEV),
+ "sd-device: the uevent file \"%s\" does not exist.", path);
return log_debug_errno(errno, "sd-device: cannot access uevent file for %s: %m", syspath);
}
} else {
/* everything else just needs to be a directory */
if (!is_dir(syspath, false))
- return -ENODEV;
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+ "sd-device: the syspath \"%s\" is not a directory.", syspath);
}
} else {
syspath = strdup(_syspath);
if (!syspath)
- return -ENOMEM;
+ return log_oom_debug();
}
devpath = syspath + STRLEN("/sys");
if (devpath[0] != '/')
- /* '/sys' alone is not a valid device path */
- return -ENODEV;
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "sd-device: \"/sys\" alone is not a valid device path.");
r = device_add_property_internal(device, "DEVPATH", devpath);
if (r < 0)
- return r;
+ return log_debug_errno(r, "sd-device: Failed to add \"DEVPATH\" property for device \"%s\": %m", syspath);
free_and_replace(device->syspath, syspath);
device->devpath = devpath;
sd_event_inotify_handler_t callback,
void *userdata) {
- sd_event_source *s;
+ sd_event_source *s = NULL; /* avoid false maybe-uninitialized warning */
int fd, r;
assert_return(path, -EINVAL);
case OBJECT_DATA:
/* All but hash and payload are mutable */
gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
- gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
+ gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
break;
case OBJECT_FIELD:
/* Same here */
gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
- gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload));
+ gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
break;
case OBJECT_ENTRY:
/* All */
- gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
+ gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
break;
case OBJECT_FIELD_HASH_TABLE:
le64toh(o->data.n_entries),
offset);
- if (le64toh(o->object.size) <= offsetof(DataObject, payload))
+ if (le64toh(o->object.size) <= offsetof(Object, data.payload))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"Bad object size (<= %zu): %" PRIu64 ": %" PRIu64,
- offsetof(DataObject, payload),
+ offsetof(Object, data.payload),
le64toh(o->object.size),
offset);
break;
case OBJECT_FIELD:
- if (le64toh(o->object.size) <= offsetof(FieldObject, payload))
+ if (le64toh(o->object.size) <= offsetof(Object, field.payload))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"Bad field size (<= %zu): %" PRIu64 ": %" PRIu64,
- offsetof(FieldObject, payload),
+ offsetof(Object, field.payload),
le64toh(o->object.size),
offset);
uint64_t sz;
sz = le64toh(READ_NOW(o->object.size));
- if (sz < offsetof(EntryObject, items) ||
- (sz - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
+ if (sz < offsetof(Object, entry.items) ||
+ (sz - offsetof(Object, entry.items)) % sizeof(EntryItem) != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64,
- offsetof(EntryObject, items),
+ offsetof(Object, entry.items),
sz,
offset);
- if ((sz - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
+ if ((sz - offsetof(Object, entry.items)) / sizeof(EntryItem) <= 0)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"Invalid number items in entry: %" PRIu64 ": %" PRIu64,
- (sz - offsetof(EntryObject, items)) / sizeof(EntryItem),
+ (sz - offsetof(Object, entry.items)) / sizeof(EntryItem),
offset);
if (le64toh(o->entry.seqnum) <= 0)
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)
+ if (sz < offsetof(Object, hash_table.items) ||
+ (sz - offsetof(Object, hash_table.items)) % sizeof(HashItem) != 0 ||
+ (sz - offsetof(Object, hash_table.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",
uint64_t sz;
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)
+ if (sz < offsetof(Object, entry_array.items) ||
+ (sz - offsetof(Object, entry_array.items)) % sizeof(le64_t) != 0 ||
+ (sz - offsetof(Object, entry_array.items)) / sizeof(le64_t) <= 0)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"Invalid object entry array size: %" PRIu64 ": %" PRIu64,
sz,
uint64_t s;
assert(f);
- assert(ret);
/* Objects may only be located at multiple of 64 bit */
if (!VALID64(offset))
if (r < 0)
return r;
- *ret = o;
+ if (ret)
+ *ret = o;
+
return 0;
}
uint64_t s;
assert(f);
- assert(ret);
/* Objects may only be located at multiple of 64 bit */
if (!VALID64(offset))
if (r < 0)
return r;
- *ret = o;
+ if (ret)
+ *ret = o;
+
return 0;
}
hash = journal_file_hash_data(f, field, size);
- r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p);
+ r = journal_file_find_field_object_with_hash(f, field, size, hash, ret, ret_offset);
if (r < 0)
return r;
- if (r > 0) {
-
- if (ret)
- *ret = o;
-
- if (ret_offset)
- *ret_offset = p;
-
+ if (r > 0)
return 0;
- }
osize = offsetof(Object, field.payload) + size;
r = journal_file_append_object(f, OBJECT_FIELD, osize, &o, &p);
if (r < 0)
return r;
- /* The linking might have altered the window, so let's
- * refresh our pointer */
- r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o);
- if (r < 0)
- return r;
+ /* The linking might have altered the window, so let's only pass the offset to hmac which will
+ * move to the object again if needed. */
#if HAVE_GCRYPT
- r = journal_file_hmac_put_object(f, OBJECT_FIELD, o, p);
+ r = journal_file_hmac_put_object(f, OBJECT_FIELD, NULL, p);
if (r < 0)
return r;
#endif
- if (ret)
- *ret = o;
+ if (ret) {
+ r = journal_file_move_to_object(f, OBJECT_FIELD, p, ret);
+ if (r < 0)
+ return r;
+ }
if (ret_offset)
*ret_offset = p;
hash = journal_file_hash_data(f, data, size);
- r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
+ r = journal_file_find_data_object_with_hash(f, data, size, hash, ret, ret_offset);
if (r < 0)
return r;
- if (r > 0) {
-
- if (ret)
- *ret = o;
-
- if (ret_offset)
- *ret_offset = p;
-
+ if (r > 0)
return 0;
- }
eq = memchr(data, '=', size);
if (!eq)
if (r < 0)
return r;
-#if HAVE_GCRYPT
- r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
+ /* The linking might have altered the window, so let's refresh our pointer. */
+ r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
if (r < 0)
return r;
-#endif
- /* The linking might have altered the window, so let's
- * refresh our pointer */
- r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
+#if HAVE_GCRYPT
+ r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
if (r < 0)
return r;
+#endif
/* Create field object ... */
r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, &fp);
/* Link up the items */
n = journal_file_entry_n_items(o);
for (uint64_t i = 0; i < n; i++) {
- r = journal_file_link_entry_item(f, o, offset, i);
- if (r < 0)
- return r;
+ int k;
+
+ /* If we fail to link an entry item because we can't allocate a new entry array, don't fail
+ * immediately but try to link the other entry items since it might still be possible to link
+ * those if they don't require a new entry array to be allocated. */
+
+ k = journal_file_link_entry_item(f, o, offset, i);
+ if (k == -E2BIG)
+ r = k;
+ else if (k < 0)
+ return k;
}
- return 0;
+ return r;
}
static int journal_file_append_entry_internal(
direction_t direction,
Object **ret, uint64_t *ret_offset) {
- Object *o, *e;
+ Object *o;
uint64_t p = 0, a, t = 0, k;
int r;
ChainCacheItem *ci;
do {
p = le64toh(o->entry_array.items[i]);
- r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &e);
- if (r >= 0)
- goto found;
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, p, ret);
+ if (r >= 0) {
+ /* Let's cache this item for the next invocation */
+ chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i);
+
+ if (ret_offset)
+ *ret_offset = p;
+
+ return 1;
+ }
if (!IN_SET(r, -EADDRNOTAVAIL, -EBADMSG))
return r;
}
return 0;
-
-found:
- /* Let's cache this item for the next invocation */
- chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i);
-
- if (ret)
- *ret = e;
-
- if (ret_offset)
- *ret_offset = p;
-
- return 1;
}
static int generic_array_get_plus_one(
direction_t direction,
Object **ret, uint64_t *ret_offset) {
- Object *o;
int r;
assert(f);
if (i == 0) {
- r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, ret);
if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG))
return generic_array_get(f, first, 0, direction, ret, ret_offset);
if (r < 0)
return r;
- if (ret)
- *ret = o;
-
if (ret_offset)
*ret_offset = extra;
uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = UINT64_MAX;
bool subtract_one = false;
- Object *o, *array = NULL;
+ Object *array = NULL;
int r;
ChainCacheItem *ci;
else
p = le64toh(array->entry_array.items[i]);
- r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
- if (r < 0)
- return r;
-
- if (ret)
- *ret = o;
+ if (ret) {
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, p, ret);
+ if (r < 0)
+ return r;
+ }
if (ret_offset)
*ret_offset = p;
int r;
bool step_back = false;
- Object *o;
assert(f);
assert(test_object);
return r;
found:
- r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
- if (r < 0)
- return r;
-
- if (ret)
- *ret = o;
+ if (ret) {
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, ret);
+ if (r < 0)
+ return r;
+ }
if (ret_offset)
*ret_offset = extra;
return TEST_RIGHT;
}
+int journal_file_move_to_entry_by_offset(
+ JournalFile *f,
+ uint64_t p,
+ direction_t direction,
+ Object **ret,
+ uint64_t *ret_offset) {
+
+ assert(f);
+ assert(f->header);
+
+ return generic_array_bisect(
+ f,
+ le64toh(f->header->entry_array_offset),
+ le64toh(f->header->n_entries),
+ p,
+ test_object_offset,
+ direction,
+ ret, ret_offset, NULL);
+}
+
static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
uint64_t sq;
Object *o;
int journal_file_next_entry_for_data(
JournalFile *f,
- uint64_t data_offset,
+ Object *d,
direction_t direction,
Object **ret, uint64_t *ret_offset) {
uint64_t i, n, ofs;
- Object *d;
int r;
assert(f);
-
- r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
- if (r < 0)
- return r;
+ assert(d);
+ assert(d->object.type == OBJECT_DATA);
n = le64toh(READ_NOW(d->data.n_entries));
if (n <= 0)
int journal_file_move_to_entry_by_offset_for_data(
JournalFile *f,
- uint64_t data_offset,
+ Object *d,
uint64_t p,
direction_t direction,
Object **ret, uint64_t *ret_offset) {
- int r;
- Object *d;
-
assert(f);
-
- r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
- if (r < 0)
- return r;
+ assert(d);
+ assert(d->object.type == OBJECT_DATA);
return generic_array_bisect_plus_one(
f,
int journal_file_move_to_entry_by_monotonic_for_data(
JournalFile *f,
- uint64_t data_offset,
+ Object *d,
sd_id128_t boot_id,
uint64_t monotonic,
direction_t direction,
Object **ret, uint64_t *ret_offset) {
- Object *o, *d;
+ Object *o;
int r;
- uint64_t b, z;
+ uint64_t b, z, entry_offset, entry_array_offset, n_entries;
assert(f);
+ assert(d);
+ assert(d->object.type == OBJECT_DATA);
+
+ /* Save all the required data before the data object gets invalidated. */
+ entry_offset = le64toh(READ_NOW(d->data.entry_offset));
+ entry_array_offset = le64toh(READ_NOW(d->data.entry_array_offset));
+ n_entries = le64toh(READ_NOW(d->data.n_entries));
/* First, seek by time */
r = find_data_object_by_boot_id(f, boot_id, &o, &b);
/* And now, continue seeking until we find an entry that
* exists in both bisection arrays */
+ r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
+ if (r < 0)
+ return r;
+
for (;;) {
- Object *qo;
uint64_t p, q;
- r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
- if (r < 0)
- return r;
-
r = generic_array_bisect_plus_one(f,
- le64toh(d->data.entry_offset),
- le64toh(d->data.entry_array_offset),
- le64toh(d->data.n_entries),
+ entry_offset,
+ entry_array_offset,
+ n_entries,
z,
test_object_offset,
direction,
if (r <= 0)
return r;
- r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
- if (r < 0)
- return r;
-
r = generic_array_bisect_plus_one(f,
le64toh(o->data.entry_offset),
le64toh(o->data.entry_array_offset),
p,
test_object_offset,
direction,
- &qo, &q, NULL);
+ NULL, &q, NULL);
if (r <= 0)
return r;
if (p == q) {
- if (ret)
- *ret = qo;
+ if (ret) {
+ r = journal_file_move_to_object(f, OBJECT_ENTRY, q, ret);
+ if (r < 0)
+ return r;
+ }
+
if (ret_offset)
*ret_offset = q;
int journal_file_move_to_entry_by_seqnum_for_data(
JournalFile *f,
- uint64_t data_offset,
+ Object *d,
uint64_t seqnum,
direction_t direction,
Object **ret, uint64_t *ret_offset) {
- Object *d;
- int r;
-
assert(f);
-
- r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
- if (r < 0)
- return r;
+ assert(d);
+ assert(d->object.type == OBJECT_DATA);
return generic_array_bisect_plus_one(
f,
int journal_file_move_to_entry_by_realtime_for_data(
JournalFile *f,
- uint64_t data_offset,
+ Object *d,
uint64_t realtime,
direction_t direction,
Object **ret, uint64_t *ret_offset) {
- Object *d;
- int r;
-
assert(f);
-
- r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
- if (r < 0)
- return r;
+ assert(d);
+ assert(d->object.type == OBJECT_DATA);
return generic_array_bisect_plus_one(
f,
r = getenv_bool("SYSTEMD_JOURNAL_KEYED_HASH");
if (r < 0) {
if (r != -ENXIO)
- log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring.");
+ log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring: %m");
f->keyed_hash = true;
} else
f->keyed_hash = r;
for (uint64_t i = 0; i < n; i++) {
uint64_t l, h;
- le64_t le_hash;
size_t t;
void *data;
Object *u;
q = le64toh(o->entry.items[i].object_offset);
- le_hash = o->entry.items[i].hash;
r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
if (r < 0)
return r;
- if (le_hash != o->data.hash)
- return -EBADMSG;
-
l = le64toh(READ_NOW(o->object.size));
if (l < offsetof(Object, data.payload))
return -EBADMSG;
uint64_t journal_file_entry_array_n_items(Object *o) _pure_;
uint64_t journal_file_hash_table_n_items(Object *o) _pure_;
-int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset);
+int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *ret_offset);
int journal_file_append_entry(
JournalFile *f,
const dual_timestamp *ts,
const struct iovec iovec[], unsigned n_iovec,
uint64_t *seqno,
Object **ret,
- uint64_t *offset);
+ uint64_t *ret_offset);
-int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset);
-int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset);
+int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *ret_offset);
+int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *ret_offset);
-int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *offset);
-int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset);
+int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *ret_offset);
+int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *ret_offset);
void journal_file_reset_location(JournalFile *f);
void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset);
int journal_file_compare_locations(JournalFile *af, JournalFile *bf);
-int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *offset);
+int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset);
-int journal_file_next_entry_for_data(JournalFile *f, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset);
+int journal_file_next_entry_for_data(JournalFile *f, Object *d, direction_t direction, Object **ret, uint64_t *ret_offset);
-int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset);
+int journal_file_move_to_entry_by_offset(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *ret_offset);
-int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, uint64_t data_offset, uint64_t p, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, uint64_t data_offset, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, uint64_t data_offset, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, uint64_t data_offset, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset);
+int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, Object *d, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, Object *d, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, Object *d, uint64_t realtime, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, Object *d, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *ret_offset);
int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p);
}
static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
- uint64_t i;
-
assert(f);
assert(offset);
assert(o);
return -EBADMSG;
}
- if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
+ if (le64toh(o->object.size) - offsetof(Object, data.payload) <= 0) {
error(offset, "Bad object size (<= %zu): %"PRIu64,
- offsetof(DataObject, payload),
+ offsetof(Object, data.payload),
le64toh(o->object.size));
return -EBADMSG;
}
uint64_t h1, h2;
int r;
- if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
+ if (le64toh(o->object.size) - offsetof(Object, field.payload) <= 0) {
error(offset,
"Bad field size (<= %zu): %"PRIu64,
- offsetof(FieldObject, payload),
+ offsetof(Object, field.payload),
le64toh(o->object.size));
return -EBADMSG;
}
}
case OBJECT_ENTRY:
- if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
+ if ((le64toh(o->object.size) - offsetof(Object, entry.items)) % sizeof(EntryItem) != 0) {
error(offset,
"Bad entry size (<= %zu): %"PRIu64,
- offsetof(EntryObject, items),
+ offsetof(Object, entry.items),
le64toh(o->object.size));
return -EBADMSG;
}
- if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
+ if ((le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem) <= 0) {
error(offset,
"Invalid number items in entry: %"PRIu64,
- (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
+ (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem));
return -EBADMSG;
}
return -EBADMSG;
}
- for (i = 0; i < journal_file_entry_n_items(o); i++) {
+ for (uint64_t i = 0; i < journal_file_entry_n_items(o); i++) {
if (le64toh(o->entry.items[i].object_offset) == 0 ||
!VALID64(le64toh(o->entry.items[i].object_offset))) {
error(offset,
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) {
+ if ((le64toh(o->object.size) - offsetof(Object, hash_table.items)) % sizeof(HashItem) != 0 ||
+ (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem) <= 0) {
error(offset,
"Invalid %s size: %"PRIu64,
journal_object_type_to_string(o->object.type),
return -EBADMSG;
}
- for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
+ for (uint64_t i = 0; i < journal_file_hash_table_n_items(o); i++) {
if (o->hash_table.items[i].head_hash_offset != 0 &&
!VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
error(offset,
break;
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) {
+ if ((le64toh(o->object.size) - offsetof(Object, entry_array.items)) % sizeof(le64_t) != 0 ||
+ (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(le64_t) <= 0) {
error(offset,
"Invalid object entry array size: %"PRIu64,
le64toh(o->object.size));
return -EBADMSG;
}
- for (i = 0; i < journal_file_entry_array_n_items(o); i++)
+ for (uint64_t i = 0; i < journal_file_entry_array_n_items(o); i++)
if (le64toh(o->entry_array.items[i]) != 0 &&
!VALID64(le64toh(o->entry_array.items[i]))) {
error(offset,
return 0;
}
-static int entry_points_to_data(
- JournalFile *f,
- MMapFileDescriptor *cache_entry_fd,
- uint64_t n_entries,
- uint64_t entry_p,
- uint64_t data_p) {
-
- int r;
- uint64_t i, n, a;
- Object *o;
- bool found = false;
-
- assert(f);
- assert(cache_entry_fd);
-
- if (!contains_uint64(cache_entry_fd, n_entries, entry_p)) {
- error(data_p, "Data object references invalid entry at "OFSfmt, entry_p);
- return -EBADMSG;
- }
-
- r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
- if (r < 0)
- return r;
-
- n = journal_file_entry_n_items(o);
- for (i = 0; i < n; i++)
- if (le64toh(o->entry.items[i].object_offset) == data_p) {
- found = true;
- break;
- }
-
- if (!found) {
- error(entry_p, "Data object at "OFSfmt" not referenced by linked entry", data_p);
- return -EBADMSG;
- }
-
- /* Check if this entry is also in main entry array. Since the
- * main entry array has already been verified we can rely on
- * its consistency. */
-
- i = 0;
- n = le64toh(f->header->n_entries);
- a = le64toh(f->header->entry_array_offset);
-
- while (i < n) {
- uint64_t m, u;
-
- r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
- if (r < 0)
- return r;
-
- m = journal_file_entry_array_n_items(o);
- u = MIN(n - i, m);
-
- if (entry_p <= le64toh(o->entry_array.items[u-1])) {
- uint64_t x, y, z;
-
- x = 0;
- y = u;
-
- while (x < y) {
- z = (x + y) / 2;
-
- if (le64toh(o->entry_array.items[z]) == entry_p)
- return 0;
-
- if (x + 1 >= y)
- break;
-
- if (entry_p < le64toh(o->entry_array.items[z]))
- y = z;
- else
- x = z;
- }
-
- error(entry_p, "Entry object doesn't exist in main entry array");
- return -EBADMSG;
- }
-
- i += u;
- a = le64toh(o->entry_array.next_entry_array_offset);
- }
-
- return 0;
-}
-
static int verify_data(
JournalFile *f,
Object *o, uint64_t p,
assert(o->data.entry_offset);
last = q = le64toh(o->data.entry_offset);
- r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p);
+ if (!contains_uint64(cache_entry_fd, n_entries, q)) {
+ error(p, "Data object references invalid entry at "OFSfmt, q);
+ return -EBADMSG;
+ }
+
+ r = journal_file_move_to_entry_by_offset(f, q, DIRECTION_DOWN, NULL, NULL);
if (r < 0)
return r;
+ if (r == 0) {
+ error(q, "Entry object doesn't exist in the main entry array");
+ return -EBADMSG;
+ }
i = 1;
while (i < n) {
}
last = q;
- r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p);
+ if (!contains_uint64(cache_entry_fd, n_entries, q)) {
+ error(p, "Data object references invalid entry at "OFSfmt, q);
+ return -EBADMSG;
+ }
+
+ r = journal_file_move_to_entry_by_offset(f, q, DIRECTION_DOWN, NULL, NULL);
if (r < 0)
return r;
+ if (r == 0) {
+ error(q, "Entry object doesn't exist in the main entry array");
+ return -EBADMSG;
+ }
/* Pointer might have moved, reposition */
r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
static int verify_entry(
JournalFile *f,
Object *o, uint64_t p,
- MMapFileDescriptor *cache_data_fd, uint64_t n_data) {
+ MMapFileDescriptor *cache_data_fd, uint64_t n_data,
+ bool last) {
uint64_t i, n;
int r;
n = journal_file_entry_n_items(o);
for (i = 0; i < n; i++) {
- uint64_t q, h;
+ uint64_t q;
Object *u;
q = le64toh(o->entry.items[i].object_offset);
- h = le64toh(o->entry.items[i].hash);
if (!contains_uint64(cache_data_fd, n_data, q)) {
error(p, "Invalid data object of entry");
if (r < 0)
return r;
- if (le64toh(u->data.hash) != h) {
- error(p, "Hash mismatch for data object of entry");
+ r = data_object_in_hash_table(f, le64toh(u->data.hash), q);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ error(p, "Data object missing from hash table");
return -EBADMSG;
}
- r = data_object_in_hash_table(f, h, q);
+ r = journal_file_move_to_entry_by_offset_for_data(f, u, p, DIRECTION_DOWN, NULL, NULL);
if (r < 0)
return r;
- if (r == 0) {
- error(p, "Data object missing from hash table");
+
+ /* The last entry object has a very high chance of not being referenced as journal files
+ * almost always run out of space during linking of entry items when trying to add a new
+ * entry array so let's not error in that scenario. */
+ if (r == 0 && !last) {
+ error(p, "Entry object not referenced by linked data object at "OFSfmt, q);
return -EBADMSG;
}
}
if (r < 0)
return r;
- r = verify_entry(f, o, p, cache_data_fd, n_data);
+ r = verify_entry(f, o, p, cache_data_fd, n_data, /*last=*/ i + 1 == n);
if (r < 0)
return r;
return -EBADMSG;
}
- if (header_offset != p + offsetof(HashTableObject, items)) {
+ if (header_offset != p + offsetof(Object, hash_table.items)) {
error(p,
"Header offset for %s invalid (%" PRIu64 " != %" PRIu64 ")",
journal_object_type_to_string(o->object.type),
header_offset,
- p + offsetof(HashTableObject, items));
+ p + offsetof(Object, hash_table.items));
return -EBADMSG;
}
- if (header_size != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
+ if (header_size != le64toh(o->object.size) - offsetof(Object, hash_table.items)) {
error(p,
"Header size for %s invalid (%" PRIu64 " != %" PRIu64 ")",
journal_object_type_to_string(o->object.type),
header_size,
- le64toh(o->object.size) - offsetof(HashTableObject, items));
+ le64toh(o->object.size) - offsetof(Object, hash_table.items));
return -EBADMSG;
}
assert(f);
if (m->type == MATCH_DISCRETE) {
- uint64_t dp, hash;
+ Object *d;
+ uint64_t hash;
/* If the keyed hash logic is used, we need to calculate the hash fresh per file. Otherwise
* we can use what we pre-calculated. */
else
hash = m->hash;
- r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp);
+ r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, &d, NULL);
if (r <= 0)
return r;
- return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
+ return journal_file_move_to_entry_by_offset_for_data(f, d, after_offset, direction, ret, offset);
} else if (m->type == MATCH_OR_TERM) {
Match *i;
assert(f);
if (m->type == MATCH_DISCRETE) {
+ Object *d;
uint64_t dp, hash;
if (JOURNAL_HEADER_KEYED_HASH(f->header))
else
hash = m->hash;
- r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp);
+ r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, &d, &dp);
if (r <= 0)
return r;
/* FIXME: missing: find by monotonic */
if (j->current_location.type == LOCATION_HEAD)
- return journal_file_next_entry_for_data(f, dp, DIRECTION_DOWN, ret, offset);
+ return journal_file_next_entry_for_data(f, d, DIRECTION_DOWN, ret, offset);
if (j->current_location.type == LOCATION_TAIL)
- return journal_file_next_entry_for_data(f, dp, DIRECTION_UP, ret, offset);
+ return journal_file_next_entry_for_data(f, d, DIRECTION_UP, ret, offset);
if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
- return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
+ return journal_file_move_to_entry_by_seqnum_for_data(f, d, j->current_location.seqnum, direction, ret, offset);
if (j->current_location.monotonic_set) {
- r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
+ r = journal_file_move_to_entry_by_monotonic_for_data(f, d, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
if (r != -ENOENT)
return r;
+
+ /* The data object might have been invalidated. */
+ r = journal_file_move_to_object(f, OBJECT_DATA, dp, &d);
+ if (r < 0)
+ return r;
}
if (j->current_location.realtime_set)
- return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
+ return journal_file_move_to_entry_by_realtime_for_data(f, d, j->current_location.realtime, direction, ret, offset);
- return journal_file_next_entry_for_data(f, dp, direction, ret, offset);
+ return journal_file_next_entry_for_data(f, d, direction, ret, offset);
} else if (m->type == MATCH_OR_TERM) {
uint64_t np = 0;
for (i = 0; i < n; i++) {
Object *d;
uint64_t p, l;
- le64_t le_hash;
size_t t;
int compression;
p = le64toh(o->entry.items[i].object_offset);
- le_hash = o->entry.items[i].hash;
r = journal_file_move_to_object(f, OBJECT_DATA, p, &d);
if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) {
log_debug_errno(r, "Entry item %"PRIu64" data object is bad, skipping over it: %m", i);
if (r < 0)
return r;
- if (le_hash != d->data.hash) {
- log_debug("Entry item %"PRIu64" hash is bad, skipping over it.", i);
- continue;
- }
-
l = le64toh(d->object.size) - offsetof(Object, data.payload);
compression = d->object.flags & OBJECT_COMPRESSION_MASK;
for (uint64_t n = journal_file_entry_n_items(o); j->current_field < n; j->current_field++) {
uint64_t p;
- le64_t le_hash;
p = le64toh(o->entry.items[j->current_field].object_offset);
- le_hash = o->entry.items[j->current_field].hash;
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) {
log_debug_errno(r, "Entry item %"PRIu64" data object is bad, skipping over it: %m", j->current_field);
if (r < 0)
return r;
- if (le_hash != o->data.hash) {
- log_debug("Entry item %"PRIu64" hash is bad, skipping over it.", j->current_field);
- continue;
- }
-
r = return_data(j, f, o, data, size);
if (r == -EBADMSG) {
log_debug("Entry item %"PRIu64" data payload is bad, skipping over it.", j->current_field);
return 0;
}
+
+int network_link_get_operational_state(int ifindex, LinkOperationalState *ret) {
+ _cleanup_free_ char *str = NULL;
+ LinkOperationalState s;
+ int r;
+
+ assert(ifindex > 0);
+ assert(ret);
+
+ r = sd_network_link_get_operational_state(ifindex, &str);
+ if (r < 0)
+ return r;
+
+ s = link_operstate_from_string(str);
+ if (s < 0)
+ return s;
+
+ *ret = s;
+ return 0;
+}
LINK_OPERSTATE_ROUTABLE }
int parse_operational_state_range(const char *str, LinkOperationalStateRange *out);
+int network_link_get_operational_state(int ifindex, LinkOperationalState *ret);
#include <unistd.h>
+#include "sd-messages.h"
+
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
#include "logind-dbus.h"
#include "logind-session-dbus.h"
#include "process-util.h"
-#include "sleep-config.h"
#include "special.h"
#include "string-table.h"
#include "terminal-util.h"
#include "user-util.h"
+static const ActionTableItem action_table[_HANDLE_ACTION_MAX] = {
+ [HANDLE_POWEROFF] = {
+ SPECIAL_POWEROFF_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.power-off",
+ "org.freedesktop.login1.power-off-multiple-sessions",
+ "org.freedesktop.login1.power-off-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is powering down",
+ "power-off",
+ },
+ [HANDLE_REBOOT] = {
+ SPECIAL_REBOOT_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.reboot",
+ "org.freedesktop.login1.reboot-multiple-sessions",
+ "org.freedesktop.login1.reboot-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is rebooting",
+ "reboot",
+ },
+ [HANDLE_HALT] = {
+ SPECIAL_HALT_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.halt",
+ "org.freedesktop.login1.halt-multiple-sessions",
+ "org.freedesktop.login1.halt-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is halting",
+ "halt",
+ },
+ [HANDLE_KEXEC] = {
+ SPECIAL_KEXEC_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.reboot",
+ "org.freedesktop.login1.reboot-multiple-sessions",
+ "org.freedesktop.login1.reboot-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is rebooting with kexec",
+ "kexec",
+ },
+ [HANDLE_SUSPEND] = {
+ SPECIAL_SUSPEND_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.suspend",
+ "org.freedesktop.login1.suspend-multiple-sessions",
+ "org.freedesktop.login1.suspend-ignore-inhibit",
+ SLEEP_SUSPEND,
+ },
+ [HANDLE_HIBERNATE] = {
+ SPECIAL_HIBERNATE_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ SLEEP_HIBERNATE,
+ },
+ [HANDLE_HYBRID_SLEEP] = {
+ SPECIAL_HYBRID_SLEEP_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ SLEEP_HYBRID_SLEEP,
+ },
+ [HANDLE_SUSPEND_THEN_HIBERNATE] = {
+ SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ SLEEP_SUSPEND_THEN_HIBERNATE,
+ },
+ [HANDLE_FACTORY_RESET] = {
+ SPECIAL_FACTORY_RESET_TARGET,
+ _INHIBIT_WHAT_INVALID,
+ NULL,
+ NULL,
+ NULL,
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_FACTORY_RESET_STR,
+ "System is performing factory reset",
+ NULL
+ },
+};
+
const char* manager_target_for_action(HandleAction handle) {
- static const char * const target_table[_HANDLE_ACTION_MAX] = {
- [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
- [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
- [HANDLE_HALT] = SPECIAL_HALT_TARGET,
- [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
- [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
- [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
- [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
- [HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
- [HANDLE_FACTORY_RESET] = SPECIAL_FACTORY_RESET_TARGET,
- };
+ assert(handle >= 0);
+ assert(handle < (ssize_t) ELEMENTSOF(action_table));
+ return action_table[handle].target;
+}
+
+const ActionTableItem* manager_item_for_handle(HandleAction handle) {
assert(handle >= 0);
- if (handle < (ssize_t) ELEMENTSOF(target_table))
- return target_table[handle];
- return NULL;
+ assert(handle < (ssize_t) ELEMENTSOF(action_table));
+
+ return &action_table[handle];
+}
+
+HandleAction manager_handle_for_item(const ActionTableItem* a) {
+ if (a && a < action_table + ELEMENTSOF(action_table))
+ return a - action_table;
+ return _HANDLE_ACTION_INVALID;
}
int manager_handle_action(
InhibitWhat inhibit_operation;
Inhibitor *offending = NULL;
bool supported;
- const char *target;
int r;
assert(m);
return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Requested %s operation not supported, ignoring.", handle_action_to_string(handle));
- if (m->action_what > 0)
+ if (m->delayed_action)
return log_debug_errno(SYNTHETIC_ERRNO(EALREADY),
"Action already in progress (%s), ignoring requested %s operation.",
- inhibit_what_to_string(m->action_what),
+ inhibit_what_to_string(m->delayed_action->inhibit_what),
handle_action_to_string(handle));
- assert_se(target = manager_target_for_action(handle));
-
- inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE,
- HANDLE_HYBRID_SLEEP,
- HANDLE_SUSPEND_THEN_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
+ inhibit_operation = manager_item_for_handle(handle)->inhibit_what;
/* If the actual operation is inhibited, warn and fail */
if (!ignore_inhibited &&
log_info("%s", message_table[handle]);
- r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error);
+ r = bus_manager_shutdown_or_sleep_now_or_later(m, manager_item_for_handle(handle), &error);
if (r < 0)
return log_error_errno(r, "Failed to execute %s operation: %s",
handle_action_to_string(handle),
_HANDLE_ACTION_INVALID = -EINVAL,
} HandleAction;
+typedef struct ActionTableItem ActionTableItem;
+
+#define handle_action_valid(x) (x && (x < _HANDLE_ACTION_MAX))
+
#include "logind-inhibit.h"
#include "logind.h"
+#include "sleep-config.h"
+
+struct ActionTableItem {
+ const char *target;
+ InhibitWhat inhibit_what;
+ const char *polkit_action;
+ const char *polkit_action_multiple_sessions;
+ const char *polkit_action_ignore_inhibit;
+ SleepOperation sleep_operation;
+ const char* message_id;
+ const char* message;
+ const char* log_str;
+
+};
int manager_handle_action(
Manager *m,
HandleAction handle_action_from_string(const char *s) _pure_;
const char* manager_target_for_action(HandleAction handle);
+const ActionTableItem* manager_item_for_handle(HandleAction handle);
+HandleAction manager_handle_for_item(const ActionTableItem* a);
CONFIG_PARSER_PROTOTYPE(config_parse_handle_action);
* differently */
if (manager_is_docked_or_external_displays(manager))
handle_action = manager->handle_lid_switch_docked;
- else if (manager->handle_lid_switch_ep != _HANDLE_ACTION_INVALID &&
- manager_is_on_external_power())
+ else if (!handle_action_valid(manager->handle_lid_switch_ep) && manager_is_on_external_power())
handle_action = manager->handle_lid_switch_ep;
else
handle_action = manager->handle_lid_switch;
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
+#include "logind-action.h"
#include "logind-dbus.h"
#include "logind-polkit.h"
#include "logind-seat-dbus.h"
#include "utmp-wtmp.h"
#include "virt.h"
+static void reset_scheduled_shutdown(Manager *m);
+
static int get_sender_session(
Manager *m,
sd_bus_message *message,
sd_bus_error *error) {
Manager *m = userdata;
- bool b;
+ bool b = false;
assert(bus);
assert(reply);
assert(m);
- if (streq(property, "PreparingForShutdown"))
- b = m->action_what & INHIBIT_SHUTDOWN;
- else
- b = m->action_what & INHIBIT_SLEEP;
+ if (m->delayed_action) {
+ if (streq(property, "PreparingForShutdown"))
+ b = m->delayed_action->inhibit_what & INHIBIT_SHUTDOWN;
+ else
+ b = m->delayed_action->inhibit_what & INHIBIT_SLEEP;
+ }
return sd_bus_message_append(reply, "b", b);
}
if (r < 0)
return r;
- r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout);
+ r = sd_bus_message_append(reply, "st",
+ handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)),
+ m->scheduled_shutdown_timeout);
if (r < 0)
return r;
return false;
}
-_printf_(2, 0)
-static int log_with_wall_message(Manager *m, const char *d, const char *p, const char *q) {
+static int bus_manager_log_shutdown(
+ Manager *m,
+ const ActionTableItem *a) {
+
+ const char *message, *log_str;
+
assert(m);
+ assert(a);
+
+ message = a->message;
+ log_str = a->log_str;
+
+ if (message)
+ message = strjoina("MESSAGE=", message);
+ else
+ message = "MESSAGE=System is shutting down";
if (isempty(m->wall_message))
- p = strjoina(p, ".");
+ message = strjoina(message, ".");
else
- p = strjoina(p, " (", m->wall_message, ").");
+ message = strjoina(message, " (", m->wall_message, ").");
- return log_struct(LOG_NOTICE, d, p, q);
-}
+ if (log_str)
+ log_str = strjoina("SHUTDOWN=", log_str);
-static int bus_manager_log_shutdown(
- Manager *m,
- const char *unit_name) {
-
- assert(m);
- assert(unit_name);
-
- if (streq(unit_name, SPECIAL_POWEROFF_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is powering down",
- "SHUTDOWN=power-off");
-
- if (streq(unit_name, SPECIAL_REBOOT_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is rebooting",
- "SHUTDOWN=reboot");
-
- if (streq(unit_name, SPECIAL_HALT_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is halting",
- "SHUTDOWN=halt");
-
- if (streq(unit_name, SPECIAL_KEXEC_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is rebooting with kexec",
- "SHUTDOWN=kexec");
-
- if (streq(unit_name, SPECIAL_FACTORY_RESET_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_FACTORY_RESET_STR,
- "MESSAGE=System is performing factory reset",
- NULL);
-
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is shutting down",
- NULL);
+ return log_struct(LOG_NOTICE,
+ "MESSAGE_ID=%s", a->message_id ? a->message_id : SD_MESSAGE_SHUTDOWN_STR,
+ message,
+ log_str);
}
static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) {
static int execute_shutdown_or_sleep(
Manager *m,
- InhibitWhat w,
- const char *unit_name,
+ const ActionTableItem *a,
sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(m);
- assert(w > 0);
- assert(w < _INHIBIT_WHAT_MAX);
- assert(unit_name);
+ assert(a);
- if (w == INHIBIT_SHUTDOWN)
- bus_manager_log_shutdown(m, unit_name);
+ if (a->inhibit_what == INHIBIT_SHUTDOWN)
+ bus_manager_log_shutdown(m, a);
r = bus_call_method(
m->bus,
"StartUnit",
error,
&reply,
- "ss", unit_name, "replace-irreversibly");
+ "ss", a->target, "replace-irreversibly");
if (r < 0)
goto error;
if (r < 0)
goto error;
- m->action_unit = unit_name;
- m->action_what = w;
+ m->delayed_action = a;
/* Make sure the lid switch is ignored for a while */
manager_set_lid_switch_ignore(m, usec_add(now(CLOCK_MONOTONIC), m->holdoff_timeout_usec));
error:
/* Tell people that they now may take a lock again */
- (void) send_prepare_for(m, w, false);
+ (void) send_prepare_for(m, a->inhibit_what, false);
return r;
}
assert(manager);
- if (manager->action_what == 0 || manager->action_job)
+ if (!manager->delayed_action || manager->action_job)
return 0;
- if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
+ if (manager_is_inhibited(manager, manager->delayed_action->inhibit_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
_cleanup_free_ char *comm = NULL, *u = NULL;
if (!timeout)
}
/* Actually do the operation */
- r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
+ r = execute_shutdown_or_sleep(manager, manager->delayed_action, &error);
if (r < 0) {
log_warning("Error during inhibitor-delayed operation (already returned success to client): %s",
bus_error_message(&error, r));
- manager->action_unit = NULL;
- manager->action_what = 0;
+ manager->delayed_action = NULL;
}
return 1; /* We did some work. */
static int delay_shutdown_or_sleep(
Manager *m,
- InhibitWhat w,
- const char *unit_name) {
+ const ActionTableItem *a) {
int r;
assert(m);
- assert(w >= 0);
- assert(w < _INHIBIT_WHAT_MAX);
- assert(unit_name);
+ assert(a);
if (m->inhibit_timeout_source) {
r = sd_event_source_set_time_relative(m->inhibit_timeout_source, m->inhibit_delay_max);
return r;
}
- m->action_unit = unit_name;
- m->action_what = w;
+ m->delayed_action = a;
return 0;
}
int bus_manager_shutdown_or_sleep_now_or_later(
Manager *m,
- const char *unit_name,
- InhibitWhat w,
+ const ActionTableItem *a,
sd_bus_error *error) {
_cleanup_free_ char *load_state = NULL;
int r;
assert(m);
- assert(unit_name);
- assert(w > 0);
- assert(w < _INHIBIT_WHAT_MAX);
+ assert(a);
assert(!m->action_job);
- r = unit_load_state(m->bus, unit_name, &load_state);
+ r = unit_load_state(m->bus, a->target, &load_state);
if (r < 0)
return r;
if (!streq(load_state, "loaded"))
return log_notice_errno(SYNTHETIC_ERRNO(EACCES),
"Unit %s is %s, refusing operation.",
- unit_name, load_state);
+ a->target, load_state);
/* Tell everybody to prepare for shutdown/sleep */
- (void) send_prepare_for(m, w, true);
+ (void) send_prepare_for(m, a->inhibit_what, true);
delayed =
m->inhibit_delay_max > 0 &&
- manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL);
+ manager_is_inhibited(m, a->inhibit_what, INHIBIT_DELAY, NULL, false, false, 0, NULL);
if (delayed)
/* Shutdown is delayed, keep in mind what we
* want to do, and start a timeout */
- r = delay_shutdown_or_sleep(m, w, unit_name);
+ r = delay_shutdown_or_sleep(m, a);
else
/* Shutdown is not delayed, execute it
* immediately */
- r = execute_shutdown_or_sleep(m, w, unit_name, error);
+ r = execute_shutdown_or_sleep(m, a, error);
return r;
}
static int verify_shutdown_creds(
Manager *m,
sd_bus_message *message,
- InhibitWhat w,
- const char *action,
- const char *action_multiple_sessions,
- const char *action_ignore_inhibit,
+ const ActionTableItem *a,
uint64_t flags,
sd_bus_error *error) {
int r;
assert(m);
+ assert(a);
assert(message);
- assert(w >= 0);
- assert(w <= _INHIBIT_WHAT_MAX);
- assert(action);
- assert(action_multiple_sessions);
- assert(action_ignore_inhibit);
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
return r;
multiple_sessions = r > 0;
- blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
+ blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
interactive = flags & SD_LOGIND_INTERACTIVE;
if (multiple_sessions) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_BOOT,
+ a->polkit_action_multiple_sessions,
+ NULL,
+ interactive,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED,
"Access denied to root due to active block inhibitor");
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(message,
+ CAP_SYS_BOOT,
+ a->polkit_action_ignore_inhibit,
+ NULL,
+ interactive,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
}
if (!multiple_sessions && !blocked) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(message,
+ CAP_SYS_BOOT,
+ a->polkit_action,
+ NULL,
+ interactive,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return 0;
}
+static int setup_wall_message_timer(Manager *m, sd_bus_message* message) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ int r;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
+ if (r >= 0) {
+ const char *tty = NULL;
+
+ (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
+ (void) sd_bus_creds_get_tty(creds, &tty);
+
+ r = free_and_strdup(&m->scheduled_shutdown_tty, tty);
+ if (r < 0)
+ return log_oom();
+ }
+
+ r = manager_setup_wall_message_timer(m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int method_do_shutdown_or_sleep(
Manager *m,
sd_bus_message *message,
- const char *unit_name,
- InhibitWhat w,
- const char *action,
- const char *action_multiple_sessions,
- const char *action_ignore_inhibit,
- SleepOperation sleep_operation,
+ const ActionTableItem *a,
bool with_flags,
sd_bus_error *error) {
assert(m);
assert(message);
- assert(unit_name);
- assert(w >= 0);
- assert(w <= _INHIBIT_WHAT_MAX);
+ assert(a);
if (with_flags) {
/* New style method: with flags parameter (and interactive bool in the bus message header) */
return r;
if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
- if (!streq(unit_name, SPECIAL_REBOOT_TARGET) && (flags & SD_LOGIND_REBOOT_VIA_KEXEC))
+ if (manager_handle_for_item(a) != HANDLE_REBOOT && (flags & SD_LOGIND_REBOOT_VIA_KEXEC))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec is only applicable with reboot operations");
} else {
/* Old style method: no flags parameter, but interactive bool passed as boolean in
}
if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
- unit_name = SPECIAL_KEXEC_TARGET;
+ a = manager_item_for_handle(HANDLE_KEXEC);
/* Don't allow multiple jobs being executed at the same time */
- if (m->action_what > 0)
+ if (m->delayed_action)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
"There's already a shutdown or sleep operation in progress");
- if (sleep_operation >= 0) {
- r = can_sleep(sleep_operation);
+ if (a->sleep_operation >= 0) {
+ r = can_sleep(a->sleep_operation);
if (r == -ENOSPC)
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Not enough swap space for hibernation");
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
- "Sleep verb \"%s\" not supported", sleep_operation_to_string(sleep_operation));
+ "Sleep verb \"%s\" not supported", sleep_operation_to_string(a->sleep_operation));
if (r < 0)
return r;
}
- r = verify_shutdown_creds(m, message, w, action, action_multiple_sessions,
- action_ignore_inhibit, flags, error);
+ r = verify_shutdown_creds(m, message, a, flags, error);
if (r != 0)
return r;
- r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
+ /* reset case we're shorting a scheduled shutdown */
+ m->unlink_nologin = false;
+ reset_scheduled_shutdown(m);
+
+ m->scheduled_shutdown_timeout = 0;
+ m->scheduled_shutdown_type = a;
+
+ (void) setup_wall_message_timer(m, message);
+
+ r = bus_manager_shutdown_or_sleep_now_or_later(m, a, error);
if (r < 0)
return r;
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_POWEROFF_TARGET,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.power-off",
- "org.freedesktop.login1.power-off-multiple-sessions",
- "org.freedesktop.login1.power-off-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ manager_item_for_handle(HANDLE_POWEROFF),
sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_REBOOT_TARGET,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.reboot",
- "org.freedesktop.login1.reboot-multiple-sessions",
- "org.freedesktop.login1.reboot-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ manager_item_for_handle(HANDLE_REBOOT),
sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_HALT_TARGET,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.halt",
- "org.freedesktop.login1.halt-multiple-sessions",
- "org.freedesktop.login1.halt-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ manager_item_for_handle(HANDLE_HALT),
sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_SUSPEND_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.suspend",
- "org.freedesktop.login1.suspend-multiple-sessions",
- "org.freedesktop.login1.suspend-ignore-inhibit",
- SLEEP_SUSPEND,
+ manager_item_for_handle(HANDLE_SUSPEND),
sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_HIBERNATE_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HIBERNATE,
+ manager_item_for_handle(HANDLE_HIBERNATE),
sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_HYBRID_SLEEP_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HYBRID_SLEEP,
+ manager_item_for_handle(HANDLE_HYBRID_SLEEP),
sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_SUSPEND_THEN_HIBERNATE,
+ manager_item_for_handle(HANDLE_SUSPEND_THEN_HIBERNATE),
sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"),
error);
}
"MODE=%s\n",
m->scheduled_shutdown_timeout,
m->enable_wall_messages,
- m->scheduled_shutdown_type);
+ handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)));
if (!isempty(m->wall_message)) {
_cleanup_free_ char *t = NULL;
m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source);
- m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type);
- m->scheduled_shutdown_timeout = 0;
+ m->scheduled_shutdown_type = NULL;
m->shutdown_dry_run = false;
if (m->unlink_nologin) {
uint64_t usec,
void *userdata) {
+ const ActionTableItem *a = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Manager *m = userdata;
- const char *target;
int r;
assert(m);
- if (isempty(m->scheduled_shutdown_type))
- return 0;
-
- if (streq(m->scheduled_shutdown_type, "poweroff"))
- target = SPECIAL_POWEROFF_TARGET;
- else if (streq(m->scheduled_shutdown_type, "reboot"))
- target = SPECIAL_REBOOT_TARGET;
- else if (streq(m->scheduled_shutdown_type, "kexec"))
- target = SPECIAL_KEXEC_TARGET;
- else if (streq(m->scheduled_shutdown_type, "halt"))
- target = SPECIAL_HALT_TARGET;
- else
- assert_not_reached();
+ a = m->scheduled_shutdown_type;
+ assert(a);
/* Don't allow multiple jobs being executed at the same time */
- if (m->action_what > 0) {
+ if (m->delayed_action) {
r = -EALREADY;
- log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", target);
+ log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", a->target);
goto error;
}
* above) for some seconds after our admin has seen the final
* wall message. */
- bus_manager_log_shutdown(m, target);
+ bus_manager_log_shutdown(m, a);
log_info("Running in dry run, suppressing action.");
reset_scheduled_shutdown(m);
return 0;
}
- r = bus_manager_shutdown_or_sleep_now_or_later(m, target, INHIBIT_SHUTDOWN, &error);
+ r = bus_manager_shutdown_or_sleep_now_or_later(m, m->scheduled_shutdown_type, &error);
if (r < 0) {
- log_error_errno(r, "Scheduled shutdown to %s failed: %m", target);
+ log_error_errno(r, "Scheduled shutdown to %s failed: %m", a->target);
goto error;
}
static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- const char *action_multiple_sessions = NULL;
- const char *action_ignore_inhibit = NULL;
- const char *action = NULL;
+ HandleAction handle;
+ const ActionTableItem *a;
uint64_t elapse;
char *type;
int r;
dry_run = true;
}
- if (streq(type, "poweroff")) {
- action = "org.freedesktop.login1.power-off";
- action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions";
- action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit";
- } else if (STR_IN_SET(type, "reboot", "kexec")) {
- action = "org.freedesktop.login1.reboot";
- action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions";
- action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit";
- } else if (streq(type, "halt")) {
- action = "org.freedesktop.login1.halt";
- action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions";
- action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit";
- } else
+ handle = handle_action_from_string(type);
+ if (!IN_SET(handle, HANDLE_POWEROFF, HANDLE_REBOOT, HANDLE_HALT, HANDLE_KEXEC))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
- r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, action, action_multiple_sessions,
- action_ignore_inhibit, 0, error);
+ a = manager_item_for_handle(handle);
+ assert(a);
+ assert(a->polkit_action);
+
+ r = verify_shutdown_creds(m, message, a, 0, error);
if (r != 0)
return r;
return log_error_errno(r, "sd_event_add_time() failed: %m");
}
- r = free_and_strdup(&m->scheduled_shutdown_type, type);
- if (r < 0) {
- m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
- return log_oom();
- }
-
+ m->scheduled_shutdown_type = a;
m->shutdown_dry_run = dry_run;
if (m->nologin_timeout_source) {
m->scheduled_shutdown_timeout = elapse;
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
- if (r >= 0) {
- const char *tty = NULL;
-
- (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
- (void) sd_bus_creds_get_tty(creds, &tty);
-
- r = free_and_strdup(&m->scheduled_shutdown_tty, tty);
- if (r < 0) {
- m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
- return log_oom();
- }
- }
-
- r = manager_setup_wall_message_timer(m);
- if (r < 0)
+ r = setup_wall_message_timer(m, message);
+ if (r < 0) {
+ m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
return r;
+ }
r = update_schedule_file(m);
if (r < 0)
static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ const ActionTableItem *a;
bool cancelled;
+ int r;
assert(m);
assert(message);
- cancelled = m->scheduled_shutdown_type != NULL;
+ cancelled = !IN_SET(manager_handle_for_item(m->scheduled_shutdown_type), HANDLE_IGNORE, _HANDLE_ACTION_INVALID);
+ if (!cancelled)
+ goto done;
+
+ a = m->scheduled_shutdown_type;
+ if (!a->polkit_action)
+ return sd_bus_error_set(error, SD_BUS_ERROR_AUTH_FAILED, "Unsupported shutdown type");
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_BOOT,
+ a->polkit_action,
+ NULL,
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
reset_scheduled_shutdown(m);
- if (cancelled && m->enable_wall_messages) {
+ if (m->enable_wall_messages) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_free_ char *username = NULL;
const char *tty = NULL;
uid_t uid = 0;
- int r;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
if (r >= 0) {
username, tty, logind_wall_tty_filter, m);
}
+done:
return sd_bus_reply_method_return(message, "b", cancelled);
}
static int method_can_shutdown_or_sleep(
Manager *m,
sd_bus_message *message,
- InhibitWhat w,
- const char *action,
- const char *action_multiple_sessions,
- const char *action_ignore_inhibit,
- SleepOperation sleep_operation,
+ const ActionTableItem *a,
sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- HandleAction handle;
bool multiple_sessions, challenge, blocked;
const char *result = NULL;
uid_t uid;
assert(m);
assert(message);
- assert(w >= 0);
- assert(w <= _INHIBIT_WHAT_MAX);
- assert(action);
- assert(action_multiple_sessions);
- assert(action_ignore_inhibit);
-
- if (sleep_operation >= 0) {
- r = can_sleep(sleep_operation);
+ assert(a);
+
+ if (a->sleep_operation >= 0) {
+ r = can_sleep(a->sleep_operation);
if (IN_SET(r, 0, -ENOSPC))
return sd_bus_reply_method_return(message, "s", "na");
if (r < 0)
return r;
multiple_sessions = r > 0;
- blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
+ blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
- handle = handle_action_from_string(sleep_operation_to_string(sleep_operation));
+ HandleAction handle = handle_action_from_string(sleep_operation_to_string(a->sleep_operation));
if (handle >= 0) {
const char *target;
}
if (multiple_sessions) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
}
if (blocked) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
/* If neither inhibit nor multiple sessions
* apply then just check the normal policy */
- r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.power-off",
- "org.freedesktop.login1.power-off-multiple-sessions",
- "org.freedesktop.login1.power-off-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ m, message, manager_item_for_handle(HANDLE_POWEROFF),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.reboot",
- "org.freedesktop.login1.reboot-multiple-sessions",
- "org.freedesktop.login1.reboot-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ m, message, manager_item_for_handle(HANDLE_REBOOT),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.halt",
- "org.freedesktop.login1.halt-multiple-sessions",
- "org.freedesktop.login1.halt-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ m, message, manager_item_for_handle(HANDLE_HALT),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.suspend",
- "org.freedesktop.login1.suspend-multiple-sessions",
- "org.freedesktop.login1.suspend-ignore-inhibit",
- SLEEP_SUSPEND,
+ m, message, manager_item_for_handle(HANDLE_SUSPEND),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HIBERNATE,
+ m, message, manager_item_for_handle(HANDLE_HIBERNATE),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HYBRID_SLEEP,
+ m, message, manager_item_for_handle(HANDLE_HYBRID_SLEEP),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_SUSPEND_THEN_HIBERNATE,
+ m, message, manager_item_for_handle(HANDLE_SUSPEND_THEN_HIBERNATE),
error);
}
r = manager_read_efi_boot_loader_entries(m);
if (r >= 0)
- (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true);
+ (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries);
return boot_config_has_entry(&config, id);
}
r = manager_read_efi_boot_loader_entries(m);
if (r >= 0)
- (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true);
+ (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries);
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
if (r < 0)
return r;
+ /* sysvinit has a 252 (256-(strlen(" \r\n")+1)) character
+ * limit for the wall message. There is no real technical
+ * need for that but doesn't make sense to store arbitrary
+ * armounts either.
+ * https://git.savannah.nongnu.org/cgit/sysvinit.git/tree/src/shutdown.c#n72)
+ */
+ if (strlen(wall_message) > 252)
+ return -EMSGSIZE;
+
/* Short-circuit the operation if the desired state is already in place, to
* avoid an unnecessary polkit permission check. */
if (streq_ptr(m->wall_message, empty_to_null(wall_message)) &&
* executing the operation. We shouldn't create the impression
* that the lock was successful if the machine is about to go
* down/suspend any moment. */
- if (m->action_what & w)
+ if (m->delayed_action && m->delayed_action->inhibit_what & w)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
"The operation inhibition has been requested for is already running");
}
if (m->action_job && streq(m->action_job, path)) {
- log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what));
+ assert(m->delayed_action);
+ log_info("Operation '%s' finished.", inhibit_what_to_string(m->delayed_action->inhibit_what));
/* Tell people that they now may take a lock again */
- (void) send_prepare_for(m, m->action_what, false);
+ (void) send_prepare_for(m, m->delayed_action->inhibit_what, false);
m->action_job = mfree(m->action_job);
- m->action_unit = NULL;
- m->action_what = 0;
+ m->delayed_action = NULL;
return 0;
}
#include "sd-bus.h"
#include "bus-object.h"
+#include "logind-action.h"
#include "logind-session.h"
#include "logind-user.h"
#include "logind.h"
int manager_dispatch_delayed(Manager *manager, bool timeout);
-int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error);
+int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const ActionTableItem *a, sd_bus_error *error);
int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
static int update_slice_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
_cleanup_(user_record_unrefp) UserRecord *ur = userdata;
+ const sd_bus_error *e;
+ int r;
assert(m);
assert(ur);
- if (sd_bus_message_is_method_error(m, NULL)) {
- log_warning_errno(sd_bus_message_get_errno(m),
+ e = sd_bus_message_get_error(m);
+ if (e) {
+ r = sd_bus_error_get_errno(e);
+ log_warning_errno(r,
"Failed to update slice of %s, ignoring: %s",
ur->user_name,
- sd_bus_message_get_error(m)->message);
+ bus_error_message(e, r));
return 0;
}
r = asprintf(&l, "%s%sThe system is going down for %s %s%s!",
strempty(m->wall_message),
isempty(m->wall_message) ? "" : "\n",
- m->scheduled_shutdown_type,
+ handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)),
left ? "at " : "NOW",
left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : "");
if (r < 0) {
/* wall message handling */
- if (isempty(m->scheduled_shutdown_type)) {
- warn_wall(m, n);
+ if (!m->scheduled_shutdown_type)
return 0;
- }
- if (elapse < n)
+ if (elapse > 0 && elapse < n)
return 0;
/* Warn immediately if less than 15 minutes are left */
- if (elapse - n < 15 * USEC_PER_MINUTE) {
+ if (elapse == 0 || elapse - n < 15 * USEC_PER_MINUTE) {
r = warn_wall(m, n);
if (r == 0)
return 0;
*m = (Manager) {
.console_active_fd = -1,
.reserve_vt_fd = -1,
+ .enable_wall_messages = true,
.idle_action_not_before_usec = now(CLOCK_MONOTONIC),
};
strv_free(m->kill_only_users);
strv_free(m->kill_exclude_users);
- free(m->scheduled_shutdown_type);
free(m->scheduled_shutdown_tty);
free(m->wall_message);
free(m->action_job);
#IdleAction=ignore
#IdleActionSec=30min
#RuntimeDirectorySize=10%
-#RuntimeDirectoryInodes=400k
+#RuntimeDirectoryInodesMax=400k
#RemoveIPC=yes
#InhibitorsMax=8192
#SessionsMax=8192
usec_t inhibit_delay_max;
usec_t user_stop_delay;
- /* If an action is currently being executed or is delayed,
- * this is != 0 and encodes what is being done */
- InhibitWhat action_what;
-
/* If a shutdown/suspend was delayed due to an inhibitor this
- contains the unit name we are supposed to start after the
+ contains the action we are supposed to start after the
delay is over */
- const char *action_unit;
+ const ActionTableItem *delayed_action;
/* If a shutdown/suspend is currently executed, then this is
* the job of it */
char *action_job;
sd_event_source *inhibit_timeout_source;
- char *scheduled_shutdown_type;
+ const ActionTableItem *scheduled_shutdown_type;
usec_t scheduled_shutdown_timeout;
sd_event_source *scheduled_shutdown_timeout_source;
uid_t scheduled_shutdown_uid;
uid, gid, runtime_dir_size, runtime_dir_inodes,
mac_smack_use() ? ",smackfsroot=*" : "");
- (void) mkdir_label(runtime_path, 0700);
+ r = mkdir_label(runtime_path, 0700);
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Failed to create %s: %m", runtime_path);
r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options);
if (r < 0) {
if (m->n_operations >= OPERATIONS_MAX)
return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+ const char *details[] = {
+ "image", image->name,
+ "verb", "remove",
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
- NULL,
+ details,
false,
UID_INVALID,
&m->polkit_registry,
if (!image_name_is_valid(new_name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
+ const char *details[] = {
+ "image", image->name,
+ "verb", "rename",
+ "new_name", new_name,
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
- NULL,
+ details,
false,
UID_INVALID,
&m->polkit_registry,
if (!image_name_is_valid(new_name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
+ const char *details[] = {
+ "image", image->name,
+ "verb", "clone",
+ "new_name", new_name,
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
- NULL,
+ details,
false,
UID_INVALID,
&m->polkit_registry,
Image *image = userdata;
Manager *m = image->userdata;
- int r, read_only;
+ bool read_only;
+ int r;
assert(message);
if (r < 0)
return r;
+ const char *details[] = {
+ "image", image->name,
+ "verb", "mark_read_only",
+ "read_only", (read_only?"1":"0"),
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
- NULL,
+ details,
false,
UID_INVALID,
&m->polkit_registry,
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ const char *details[] = {
+ "machine", image->name,
+ "verb", "set_limit",
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
- NULL,
+ details,
false,
UID_INVALID,
&m->polkit_registry,
assert(message);
assert(m);
+ const char *details[] = {
+ "machine", m->name,
+ "verb", "unregister",
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_KILL,
"org.freedesktop.machine1.manage-machines",
- NULL,
+ details,
false,
UID_INVALID,
&m->manager->polkit_registry,
assert(message);
assert(m);
+ const char *details[] = {
+ "machine", m->name,
+ "verb", "terminate",
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_KILL,
"org.freedesktop.machine1.manage-machines",
- NULL,
+ details,
false,
UID_INVALID,
&m->manager->polkit_registry,
if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
+ const char *details[] = {
+ "machine", m->name,
+ "verb", "kill",
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_KILL,
"org.freedesktop.machine1.manage-machines",
- NULL,
+ details,
false,
UID_INVALID,
&m->manager->polkit_registry,
assert(message);
assert(m);
+ const char *details[] = {
+ "machine", m->name,
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
- NULL,
+ details,
false,
UID_INVALID,
&m->manager->polkit_registry,
assert(message);
assert(m);
+ const char *details[] = {
+ "machine", m->name,
+ "verb", "login",
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
- NULL,
+ details,
false,
UID_INVALID,
&m->manager->polkit_registry,
else if (!path_is_absolute(dest) || !path_is_normalized(dest))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
+ const char *details[] = {
+ "machine", m->name,
+ "verb", "bind",
+ "src", src,
+ "dest", dest,
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
- NULL,
+ details,
false,
UID_INVALID,
&m->manager->polkit_registry,
else if (!path_is_absolute(dest))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
+ const char *details[] = {
+ "machine", m->name,
+ "verb", "copy",
+ "src", src,
+ "dest", dest,
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
- NULL,
+ details,
false,
UID_INVALID,
&m->manager->polkit_registry,
assert(message);
assert(m);
+ const char *details[] = {
+ "machine", m->name,
+ "verb", "open_root_directory",
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
- NULL,
+ details,
false,
UID_INVALID,
&m->manager->polkit_registry,
else
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm);
+ const char *details[] = {
+ "verb", "clean_pool",
+ "mode", mm,
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
- NULL,
+ details,
false,
UID_INVALID,
&m->polkit_registry,
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ const char *details[] = {
+ "verb", "set_pool_limit",
+ NULL
+ };
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
- NULL,
+ details,
false,
UID_INVALID,
&m->polkit_registry,
if (s->tunnel && s->section)
ordered_hashmap_remove(s->tunnel->sessions_by_section, s->section);
- network_config_section_free(s->section);
+ config_section_free(s->section);
free(s->name);
return mfree(s);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(L2tpSession, l2tp_session_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(L2tpSession, l2tp_session_free);
static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned section_line, L2tpSession **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(l2tp_session_freep) L2tpSession *s = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
.section = TAKE_PTR(n),
};
- r = ordered_hashmap_ensure_put(&t->sessions_by_section, &network_config_hash_ops, s->section, s);
+ r = ordered_hashmap_ensure_put(&t->sessions_by_section, &config_section_hash_ops, s->section, s);
if (r < 0)
return r;
typedef struct L2tpSession {
L2tpTunnel *tunnel;
- NetworkConfigSection *section;
+ ConfigSection *section;
char *name;
if (c->macsec && c->section)
ordered_hashmap_remove(c->macsec->receive_associations_by_section, c->section);
- network_config_section_free(c->section);
+ config_section_free(c->section);
security_association_clear(&c->sa);
return mfree(c);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free);
static int macsec_receive_association_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveAssociation **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(macsec_receive_association_freep) ReceiveAssociation *c = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
security_association_init(&c->sa);
- r = ordered_hashmap_ensure_put(&s->receive_associations_by_section, &network_config_hash_ops, c->section, c);
+ r = ordered_hashmap_ensure_put(&s->receive_associations_by_section, &config_section_hash_ops, c->section, c);
if (r < 0)
return r;
ordered_hashmap_remove(c->macsec->receive_channels_by_section, c->section);
}
- network_config_section_free(c->section);
+ config_section_free(c->section);
return mfree(c);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free);
static int macsec_receive_channel_new(MACsec *s, uint64_t sci, ReceiveChannel **ret) {
ReceiveChannel *c;
}
static int macsec_receive_channel_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveChannel **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(macsec_receive_channel_freep) ReceiveChannel *c = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
c->section = TAKE_PTR(n);
- r = ordered_hashmap_ensure_put(&s->receive_channels_by_section, &network_config_hash_ops, c->section, c);
+ r = ordered_hashmap_ensure_put(&s->receive_channels_by_section, &config_section_hash_ops, c->section, c);
if (r < 0)
return r;
if (a->macsec && a->section)
ordered_hashmap_remove(a->macsec->transmit_associations_by_section, a->section);
- network_config_section_free(a->section);
+ config_section_free(a->section);
security_association_clear(&a->sa);
return mfree(a);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free);
static int macsec_transmit_association_new_static(MACsec *s, const char *filename, unsigned section_line, TransmitAssociation **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(macsec_transmit_association_freep) TransmitAssociation *a = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
security_association_init(&a->sa);
- r = ordered_hashmap_ensure_put(&s->transmit_associations_by_section, &network_config_hash_ops, a->section, a);
+ r = ordered_hashmap_ensure_put(&s->transmit_associations_by_section, &config_section_hash_ops, a->section, a);
if (r < 0)
return r;
typedef struct TransmitAssociation {
MACsec *macsec;
- NetworkConfigSection *section;
+ ConfigSection *section;
SecurityAssociation sa;
} TransmitAssociation;
typedef struct ReceiveAssociation {
MACsec *macsec;
- NetworkConfigSection *section;
+ ConfigSection *section;
MACsecSCI sci;
SecurityAssociation sa;
typedef struct ReceiveChannel {
MACsec *macsec;
- NetworkConfigSection *section;
+ ConfigSection *section;
MACsecSCI sci;
ReceiveAssociation *rxsa[MACSEC_MAX_ASSOCIATION_NUMBER];
hashmap_remove(peer->wireguard->peers_by_section, peer->section);
}
- network_config_section_free(peer->section);
+ config_section_free(peer->section);
while ((mask = peer->ipmasks)) {
LIST_REMOVE(ipmasks, peer->ipmasks, mask);
return mfree(peer);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(WireguardPeer, wireguard_peer_free);
static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
LIST_PREPEND(peers, w->peers, peer);
- r = hashmap_ensure_put(&w->peers_by_section, &network_config_hash_ops, peer->section, peer);
+ r = hashmap_ensure_put(&w->peers_by_section, &config_section_hash_ops, peer->section, peer);
if (r < 0)
return r;
typedef struct WireguardPeer {
Wireguard *wireguard;
- NetworkConfigSection *section;
+ ConfigSection *section;
uint8_t public_key[WG_KEY_LEN];
uint8_t preshared_key[WG_KEY_LEN];
hashmap_remove(label->network->address_labels_by_section, label->section);
}
- network_config_section_free(label->section);
+ config_section_free(label->section);
return mfree(label);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(AddressLabel, address_label_free);
static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(address_label_freep) AddressLabel *label = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
.label = UINT32_MAX,
};
- r = hashmap_ensure_put(&network->address_labels_by_section, &network_config_hash_ops, label->section, label);
+ r = hashmap_ensure_put(&network->address_labels_by_section, &config_section_hash_ops, label->section, label);
if (r < 0)
return r;
typedef struct AddressLabel {
Network *network;
- NetworkConfigSection *section;
+ ConfigSection *section;
uint32_t label;
struct in6_addr prefix;
}
static int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(address_freep) Address *address = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
address->section = TAKE_PTR(n);
address->source = NETWORK_CONFIG_SOURCE_STATIC;
- r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address);
+ r = ordered_hashmap_ensure_put(&network->addresses_by_section, &config_section_hash_ops, address->section, address);
if (r < 0)
return r;
sd_ipv4acd_unref(address->acd);
- network_config_section_free(address->section);
+ config_section_free(address->section);
free(address->label);
return mfree(address);
}
struct Address {
Link *link;
Network *network;
- NetworkConfigSection *section;
+ ConfigSection *section;
NetworkConfigSource source;
NetworkConfigState state;
union in_addr_union provider; /* DHCP server or router address */
bool address_is_ready(const Address *a);
void address_set_broadcast(Address *a);
-DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_free);
int link_drop_addresses(Link *link);
int link_drop_foreign_addresses(Link *link);
hashmap_remove(fdb->network->bridge_fdb_entries_by_section, fdb->section);
}
- network_config_section_free(fdb->section);
+ config_section_free(fdb->section);
free(fdb->outgoing_ifname);
return mfree(fdb);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(BridgeFDB, bridge_fdb_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeFDB, bridge_fdb_free);
/* create a new FDB entry or get an existing one. */
static int bridge_fdb_new_static(
unsigned section_line,
BridgeFDB **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(bridge_fdb_freep) BridgeFDB *fdb = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
.ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
};
- r = hashmap_ensure_put(&network->bridge_fdb_entries_by_section, &network_config_hash_ops, fdb->section, fdb);
+ r = hashmap_ensure_put(&network->bridge_fdb_entries_by_section, &config_section_hash_ops, fdb->section, fdb);
if (r < 0)
return r;
typedef struct BridgeFDB {
Network *network;
- NetworkConfigSection *section;
+ ConfigSection *section;
uint32_t vni;
hashmap_remove(mdb->network->bridge_mdb_entries_by_section, mdb->section);
}
- network_config_section_free(mdb->section);
+ config_section_free(mdb->section);
return mfree(mdb);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(BridgeMDB, bridge_mdb_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeMDB, bridge_mdb_free);
/* create a new MDB entry or get an existing one. */
static int bridge_mdb_new_static(
unsigned section_line,
BridgeMDB **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(bridge_mdb_freep) BridgeMDB *mdb = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
.section = TAKE_PTR(n),
};
- r = hashmap_ensure_put(&network->bridge_mdb_entries_by_section, &network_config_hash_ops, mdb->section, mdb);
+ r = hashmap_ensure_put(&network->bridge_mdb_entries_by_section, &config_section_hash_ops, mdb->section, mdb);
if (r < 0)
return r;
typedef struct BridgeMDB {
Network *network;
- NetworkConfigSection *section;
+ ConfigSection *section;
int family;
union in_addr_union group_addr;
if (link->iftype == ARPHRD_CAN)
return false;
+ if (!IN_SET(link->hw_addr.length, ETH_ALEN, INFINIBAND_ALEN) &&
+ !streq_ptr(link->kind, "wwan"))
+ /* Currently, only interfaces whose MAC address length is ETH_ALEN or INFINIBAND_ALEN
+ * are supported. Note, wwan interfaces may be assigned MAC address slightly later.
+ * Hence, let's wait for a while.*/
+ return false;
+
if (!link->network)
return false;
#include "networkd-network.h"
#include "networkd-util.h"
-DEFINE_NETWORK_SECTION_FUNCTIONS(DHCPStaticLease, dhcp_static_lease_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(DHCPStaticLease, dhcp_static_lease_free);
DHCPStaticLease *dhcp_static_lease_free(DHCPStaticLease *static_lease) {
if (!static_lease)
if (static_lease->network && static_lease->section)
hashmap_remove(static_lease->network->dhcp_static_leases_by_section, static_lease->section);
- network_config_section_free(static_lease->section);
+ config_section_free(static_lease->section);
free(static_lease->client_id);
return mfree(static_lease);
}
}
static int lease_new_static(Network *network, const char *filename, unsigned section_line, DHCPStaticLease **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(dhcp_static_lease_freep) DHCPStaticLease *static_lease = NULL;
int r;
assert(section_line > 0);
assert(ret);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
static_lease->network = network;
static_lease->section = TAKE_PTR(n);
- r = hashmap_ensure_put(&network->dhcp_static_leases_by_section, &network_config_hash_ops, static_lease->section, static_lease);
+ r = hashmap_ensure_put(&network->dhcp_static_leases_by_section, &config_section_hash_ops, static_lease->section, static_lease);
if (r < 0)
return r;
#include "in-addr-util.h"
typedef struct Network Network;
-typedef struct NetworkConfigSection NetworkConfigSection;
+typedef struct ConfigSection ConfigSection;
typedef struct DHCPStaticLease {
Network *network;
- NetworkConfigSection *section;
+ ConfigSection *section;
struct in_addr address;
uint8_t *client_id;
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return 0;
+ if (!IN_SET(link->hw_addr.length, ETH_ALEN, INFINIBAND_ALEN) ||
+ hw_addr_is_null(&link->hw_addr))
+ /* No MAC address is assigned to the hardware, or non-supported MAC address length. */
+ return 0;
+
r = dhcp4_configure_duid(link);
if (r <= 0)
return r;
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return 0;
+ if (!IN_SET(link->hw_addr.length, ETH_ALEN, INFINIBAND_ALEN) ||
+ hw_addr_is_null(&link->hw_addr))
+ /* No MAC address is assigned to the hardware, or non-supported MAC address length. */
+ return 0;
+
r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
if (r <= 0)
return r;
int r;
assert(address);
- assert(address->link);
+
+ link = ASSERT_PTR(address->link);
if (address->family != AF_INET)
return 0;
if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4))
return 0;
+ if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
+ return 0;
+
/* Currently, only static and DHCP4 addresses are supported. */
assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4));
return 0;
}
- link = address->link;
-
log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
hashmap_remove(link->manager->links_by_name, link->ifname);
/* bonding master and its slaves have the same hardware address. */
- if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link)
- hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr);
+ hashmap_remove_value(link->manager->links_by_hw_addr, &link->hw_addr, link);
/* The following must be called at last. */
assert_se(hashmap_remove(link->manager->links_by_index, INT_TO_PTR(link->ifindex)) == link);
if (r < 0)
return r;
- r = ndisc_configure(link);
+ r = link_request_ndisc(link);
if (r < 0)
return r;
return 0;
if (link->activated && link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN) {
- log_link_info(link, "ActivationPolicy is \"always-off\", forcing link down.");
+ log_link_info(link, "Activation policy is \"always-down\", forcing link down.");
return link_request_to_bring_up_or_down(link, /* up = */ false);
}
return 0;
if (link->activated && link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) {
- log_link_info(link, "ActivationPolicy is \"always-on\", forcing link up.");
+ log_link_info(link, "Activation policy is \"always-up\", forcing link up.");
return link_request_to_bring_up_or_down(link, /* up = */ true);
}
return 0;
}
-bool link_has_carrier(Link *link) {
- /* see Documentation/networking/operstates.txt in the kernel sources */
-
- if (link->kernel_operstate == IF_OPER_UP)
- return true;
-
- if (link->kernel_operstate == IF_OPER_UNKNOWN)
- /* operstate may not be implemented, so fall back to flags */
- if (FLAGS_SET(link->flags, IFF_LOWER_UP | IFF_RUNNING) &&
- !FLAGS_SET(link->flags, IFF_DORMANT))
- return true;
-
- return false;
-}
-
static bool link_is_enslaved(Link *link) {
if (link->flags & IFF_SLAVE)
return true;
log_link_debug(link, "Hardware address is changed: %s → %s",
HW_ADDR_TO_STR(&link->hw_addr), HW_ADDR_TO_STR(&addr));
- if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link)
- hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr);
+ hashmap_remove_value(link->manager->links_by_hw_addr, &link->hw_addr, link);
}
link->hw_addr = addr;
r = ethtool_get_driver(&manager->ethtool_fd, link->ifname, &link->driver);
if (r < 0)
log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m");
+ else
+ log_link_debug(link, "Found driver: %s", strna(link->driver));
if (streq_ptr(link->driver, "dsa")) {
uint32_t dsa_master_ifindex;
#include "ether-addr-util.h"
#include "log-link.h"
+#include "netif-util.h"
#include "network-util.h"
#include "networkd-util.h"
#include "ordered-set.h"
void link_update_operstate(Link *link, bool also_update_bond_master);
-bool link_has_carrier(Link *link);
+static inline bool link_has_carrier(Link *link) {
+ assert(link);
+ return netif_has_carrier(link->kernel_operstate, link->flags);
+}
bool link_ipv6_enabled(Link *link);
bool link_ipv6ll_enabled(Link *link);
#include <arpa/inet.h>
#include <netinet/icmp6.h>
#include <linux/if.h>
+#include <linux/if_arp.h>
#include "sd-ndisc.h"
if (link->flags & IFF_LOOPBACK)
return false;
+ if (link->iftype == ARPHRD_CAN)
+ return false;
+
+ if (link->hw_addr.length != ETH_ALEN && !streq_ptr(link->kind, "wwan"))
+ /* Currently, only interfaces whose MAC address length is ETH_ALEN are supported.
+ * Note, wwan interfaces may be assigned MAC address slightly later.
+ * Hence, let's wait for a while.*/
+ return false;
+
if (!link->network)
return false;
}
}
-int ndisc_configure(Link *link) {
+static int ndisc_configure(Link *link) {
int r;
assert(link);
}
int ndisc_start(Link *link) {
+ int r;
+
assert(link);
if (!link->ndisc || !link->dhcp6_client)
log_link_debug(link, "Discovering IPv6 routers");
- return sd_ndisc_start(link->ndisc);
+ r = sd_ndisc_start(link->ndisc);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int request_process_ndisc(Request *req) {
+ Link *link;
+ int r;
+
+ assert(req);
+ assert(req->type == REQUEST_TYPE_NDISC);
+
+ link = ASSERT_PTR(req->link);
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
+ /* No MAC address is assigned to the hardware, or non-supported MAC address length. */
+ return 0;
+
+ r = ndisc_configure(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Discovery: %m");
+
+ r = ndisc_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
+
+ log_link_debug(link, "IPv6 Router Discovery is configured%s.",
+ r > 0 ? " and started" : "");
+
+ return 1;
+}
+
+int link_request_ndisc(Link *link) {
+ int r;
+
+ assert(link);
+
+ if (!link_ipv6_accept_ra_enabled(link))
+ return 0;
+
+ if (link->ndisc)
+ return 0;
+
+ r = link_queue_request(link, REQUEST_TYPE_NDISC, NULL, false, NULL, NULL, NULL);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Discovery: %m");
+
+ log_link_debug(link, "Requested configuring of the IPv6 Router Discovery.");
+ return 0;
}
void ndisc_vacuum(Link *link) {
typedef struct Link Link;
typedef struct Network Network;
+typedef struct Request Request;
typedef enum IPv6AcceptRAStartDHCP6Client {
IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO,
void network_adjust_ipv6_accept_ra(Network *network);
-int ndisc_configure(Link *link);
int ndisc_start(Link *link);
void ndisc_vacuum(Link *link);
void ndisc_flush(Link *link);
+int request_process_ndisc(Request *req);
+int link_request_ndisc(Link *link);
+
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_use_domains);
hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
}
- network_config_section_free(neighbor->section);
+ config_section_free(neighbor->section);
if (neighbor->link)
set_remove(neighbor->link->neighbors, neighbor);
return mfree(neighbor);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Neighbor, neighbor_free);
static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
.source = NETWORK_CONFIG_SOURCE_STATIC,
};
- r = hashmap_ensure_put(&network->neighbors_by_section, &network_config_hash_ops, neighbor->section, neighbor);
+ r = hashmap_ensure_put(&network->neighbors_by_section, &config_section_hash_ops, neighbor->section, neighbor);
if (r < 0)
return r;
typedef struct Neighbor {
Network *network;
Link *link;
- NetworkConfigSection *section;
+ ConfigSection *section;
NetworkConfigSource source;
NetworkConfigState state;
Link.ActivationPolicy, config_parse_activation_policy, 0, offsetof(Network, activation_policy)
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
Link.RequiredFamilyForOnline, config_parse_required_family_for_online, 0, offsetof(Network, required_family_for_online)
-SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, 0
-SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, 0
-SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, 0
-SR-IOV.VLANProtocol, config_parse_sr_iov_vlan_proto, 0, 0
-SR-IOV.MACSpoofCheck, config_parse_sr_iov_boolean, 0, 0
-SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, 0, 0
-SR-IOV.Trust, config_parse_sr_iov_boolean, 0, 0
-SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, 0
-SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, 0
+SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, offsetof(Network, sr_iov_by_section)
+SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, offsetof(Network, sr_iov_by_section)
+SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, offsetof(Network, sr_iov_by_section)
+SR-IOV.VLANProtocol, config_parse_sr_iov_vlan_proto, 0, offsetof(Network, sr_iov_by_section)
+SR-IOV.MACSpoofCheck, config_parse_sr_iov_boolean, 0, offsetof(Network, sr_iov_by_section)
+SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, 0, offsetof(Network, sr_iov_by_section)
+SR-IOV.Trust, config_parse_sr_iov_boolean, 0, offsetof(Network, sr_iov_by_section)
+SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, offsetof(Network, sr_iov_by_section)
+SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, offsetof(Network, sr_iov_by_section)
Network.Description, config_parse_string, 0, offsetof(Network, description)
Network.KeepMaster, config_parse_bool, 0, offsetof(Network, keep_master)
Network.BatmanAdvanced, config_parse_ifname, 0, offsetof(Network, batadv_name)
network_drop_invalid_route_prefixes(network);
network_drop_invalid_routing_policy_rules(network);
network_drop_invalid_traffic_control(network);
- network_drop_invalid_sr_iov(network);
+ r = sr_iov_drop_invalid_sections(UINT32_MAX, network->sr_iov_by_section);
+ if (r < 0)
+ return r;
network_drop_invalid_static_leases(network);
network_adjust_dhcp_server(network);
hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
}
- network_config_section_free(nexthop->section);
+ config_section_free(nexthop->section);
if (nexthop->link) {
set_remove(nexthop->link->nexthops, nexthop);
return mfree(nexthop);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop, nexthop_free);
static int nexthop_new(NextHop **ret) {
_cleanup_(nexthop_freep) NextHop *nexthop = NULL;
}
static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(nexthop_freep) NextHop *nexthop = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
nexthop->section = TAKE_PTR(n);
nexthop->source = NETWORK_CONFIG_SOURCE_STATIC;
- r = hashmap_ensure_put(&network->nexthops_by_section, &network_config_hash_ops, nexthop->section, nexthop);
+ r = hashmap_ensure_put(&network->nexthops_by_section, &config_section_hash_ops, nexthop->section, nexthop);
if (r < 0)
return r;
Network *network;
Manager *manager;
Link *link;
- NetworkConfigSection *section;
+ ConfigSection *section;
NetworkConfigSource source;
NetworkConfigState state;
#include "networkd-dhcp6.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-manager.h"
+#include "networkd-ndisc.h"
#include "networkd-neighbor.h"
#include "networkd-nexthop.h"
#include "networkd-route.h"
case REQUEST_TYPE_IPV6_PROXY_NDP:
free(object);
break;
+ case REQUEST_TYPE_NDISC:
+ break;
case REQUEST_TYPE_NEIGHBOR:
neighbor_free(object);
break;
case REQUEST_TYPE_IPV6_PROXY_NDP:
in6_addr_hash_func(req->ipv6_proxy_ndp, state);
break;
+ case REQUEST_TYPE_NDISC:
+ /* This type does not have an object. */
+ break;
case REQUEST_TYPE_NEIGHBOR:
neighbor_hash_func(req->neighbor, state);
break;
return 0;
case REQUEST_TYPE_IPV6_PROXY_NDP:
return in6_addr_compare_func(a->ipv6_proxy_ndp, b->ipv6_proxy_ndp);
+ case REQUEST_TYPE_NDISC:
+ return 0;
case REQUEST_TYPE_NEIGHBOR:
return neighbor_compare_func(a->neighbor, b->neighbor);
case REQUEST_TYPE_NEXTHOP:
REQUEST_TYPE_DHCP_SERVER,
REQUEST_TYPE_DHCP4_CLIENT,
REQUEST_TYPE_DHCP6_CLIENT,
+ REQUEST_TYPE_NDISC,
REQUEST_TYPE_RADV,
REQUEST_TYPE_SET_LINK,
REQUEST_TYPE_UP_DOWN) ||
REQUEST_TYPE_DHCP_SERVER,
REQUEST_TYPE_DHCP4_CLIENT,
REQUEST_TYPE_DHCP6_CLIENT,
+ REQUEST_TYPE_NDISC,
REQUEST_TYPE_RADV) ||
netlink_handler);
case REQUEST_TYPE_IPV6_PROXY_NDP:
r = request_process_ipv6_proxy_ndp_address(req);
break;
+ case REQUEST_TYPE_NDISC:
+ r = request_process_ndisc(req);
+ break;
case REQUEST_TYPE_NEIGHBOR:
r = request_process_neighbor(req);
break;
REQUEST_TYPE_DHCP4_CLIENT,
REQUEST_TYPE_DHCP6_CLIENT,
REQUEST_TYPE_IPV6_PROXY_NDP,
+ REQUEST_TYPE_NDISC,
REQUEST_TYPE_NEIGHBOR,
REQUEST_TYPE_NEXTHOP,
REQUEST_TYPE_RADV,
if (!link_may_have_ipv6ll(link))
return false;
+ if (link->hw_addr.length != ETH_ALEN)
+ return false;
+
return link->network->router_prefix_delegation;
}
hashmap_remove(prefix->network->prefixes_by_section, prefix->section);
}
- network_config_section_free(prefix->section);
+ config_section_free(prefix->section);
set_free(prefix->tokens);
return mfree(prefix);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Prefix, prefix_free);
static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(prefix_freep) Prefix *prefix = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
.address_auto_configuration = true,
};
- r = hashmap_ensure_put(&network->prefixes_by_section, &network_config_hash_ops, prefix->section, prefix);
+ r = hashmap_ensure_put(&network->prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
if (r < 0)
return r;
hashmap_remove(prefix->network->route_prefixes_by_section, prefix->section);
}
- network_config_section_free(prefix->section);
+ config_section_free(prefix->section);
return mfree(prefix);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutePrefix, route_prefix_free);
static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
.lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC,
};
- r = hashmap_ensure_put(&network->route_prefixes_by_section, &network_config_hash_ops, prefix->section, prefix);
+ r = hashmap_ensure_put(&network->route_prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
if (r < 0)
return r;
if (in6_addr_is_null(&link->ipv6ll_address))
return false;
+ if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
+ return false;
+
if (link->network->router_emit_dns && !link->network->router_dns) {
_cleanup_free_ struct in6_addr *dns = NULL;
size_t n_dns;
typedef struct Prefix {
Network *network;
- NetworkConfigSection *section;
+ ConfigSection *section;
struct in6_addr prefix;
uint8_t prefixlen;
typedef struct RoutePrefix {
Network *network;
- NetworkConfigSection *section;
+ ConfigSection *section;
struct in6_addr prefix;
uint8_t prefixlen;
}
static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(route_freep) Route *route = NULL;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
route->section = TAKE_PTR(n);
route->source = NETWORK_CONFIG_SOURCE_STATIC;
- r = hashmap_ensure_put(&network->routes_by_section, &network_config_hash_ops, route->section, route);
+ r = hashmap_ensure_put(&network->routes_by_section, &config_section_hash_ops, route->section, route);
if (r < 0)
return r;
hashmap_remove(route->network->routes_by_section, route->section);
}
- network_config_section_free(route->section);
+ config_section_free(route->section);
if (route->link)
set_remove(route->link->routes, route);
Link *link;
Manager *manager;
Network *network;
- NetworkConfigSection *section;
+ ConfigSection *section;
NetworkConfigSource source;
NetworkConfigState state;
union in_addr_union provider; /* DHCP server or router address */
int route_new(Route **ret);
Route *route_free(Route *route);
-DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Route, route_free);
int route_dup(const Route *src, Route **ret);
int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
if (rule->manager)
set_remove(rule->manager->rules, rule);
- network_config_section_free(rule->section);
+ config_section_free(rule->section);
free(rule->iif);
free(rule->oif);
return mfree(rule);
}
-DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free);
static int routing_policy_rule_new(RoutingPolicyRule **ret) {
RoutingPolicyRule *rule;
static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
int r;
assert(network);
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
rule->source = NETWORK_CONFIG_SOURCE_STATIC;
rule->protocol = RTPROT_STATIC;
- r = hashmap_ensure_put(&network->rules_by_section, &network_config_hash_ops, rule->section, rule);
+ r = hashmap_ensure_put(&network->rules_by_section, &config_section_hash_ops, rule->section, rule);
if (r < 0)
return r;
typedef struct RoutingPolicyRule {
Manager *manager;
Network *network;
- NetworkConfigSection *section;
+ ConfigSection *section;
NetworkConfigSource source;
NetworkConfigState state;
/* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright © 2020 VMware, Inc. */
-#include "alloc-util.h"
#include "netlink-util.h"
+#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-sriov.h"
-#include "parse-util.h"
-#include "set.h"
-#include "string-util.h"
-
-static int sr_iov_new(SRIOV **ret) {
- SRIOV *sr_iov;
-
- sr_iov = new(SRIOV, 1);
- if (!sr_iov)
- return -ENOMEM;
-
- *sr_iov = (SRIOV) {
- .vf = UINT32_MAX,
- .vlan_proto = ETH_P_8021Q,
- .vf_spoof_check_setting = -1,
- .trust = -1,
- .query_rss = -1,
- .link_state = _SR_IOV_LINK_STATE_INVALID,
- };
-
- *ret = TAKE_PTR(sr_iov);
-
- return 0;
-}
-
-static int sr_iov_new_static(Network *network, const char *filename, unsigned section_line, SRIOV **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
- _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL;
- SRIOV *existing = NULL;
- int r;
-
- assert(network);
- assert(ret);
- assert(filename);
- assert(section_line > 0);
-
- r = network_config_section_new(filename, section_line, &n);
- if (r < 0)
- return r;
-
- existing = ordered_hashmap_get(network->sr_iov_by_section, n);
- if (existing) {
- *ret = existing;
- return 0;
- }
-
- r = sr_iov_new(&sr_iov);
- if (r < 0)
- return r;
-
- sr_iov->network = network;
- sr_iov->section = TAKE_PTR(n);
-
- r = ordered_hashmap_ensure_put(&network->sr_iov_by_section, &network_config_hash_ops, sr_iov->section, sr_iov);
- if (r < 0)
- return r;
-
- *ret = TAKE_PTR(sr_iov);
- return 0;
-}
-
-SRIOV *sr_iov_free(SRIOV *sr_iov) {
- if (!sr_iov)
- return NULL;
-
- if (sr_iov->network && sr_iov->section)
- ordered_hashmap_remove(sr_iov->network->sr_iov_by_section, sr_iov->section);
-
- network_config_section_free(sr_iov->section);
-
- return mfree(sr_iov);
-}
static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
-
- r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not open IFLA_VFINFO_LIST container: %m");
-
- r = sd_netlink_message_open_container(req, IFLA_VF_INFO);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not open IFLA_VF_INFO container: %m");
-
- if (!ether_addr_is_null(&sr_iov->mac)) {
- struct ifla_vf_mac ivm = {
- .vf = sr_iov->vf,
- };
-
- memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN);
- r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac));
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_VF_MAC: %m");
- }
-
- if (sr_iov->vf_spoof_check_setting >= 0) {
- struct ifla_vf_spoofchk ivs = {
- .vf = sr_iov->vf,
- .setting = sr_iov->vf_spoof_check_setting,
- };
-
- r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk));
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_VF_SPOOFCHK: %m");
- }
-
- if (sr_iov->query_rss >= 0) {
- struct ifla_vf_rss_query_en ivs = {
- .vf = sr_iov->vf,
- .setting = sr_iov->query_rss,
- };
-
- r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en));
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_VF_RSS_QUERY_EN: %m");
- }
-
- if (sr_iov->trust >= 0) {
- struct ifla_vf_trust ivt = {
- .vf = sr_iov->vf,
- .setting = sr_iov->trust,
- };
-
- r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust));
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_VF_TRUST: %m");
- }
-
- if (sr_iov->link_state >= 0) {
- struct ifla_vf_link_state ivl = {
- .vf = sr_iov->vf,
- .link_state = sr_iov->link_state,
- };
-
- r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state));
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_VF_LINK_STATE: %m");
- }
-
- if (sr_iov->vlan > 0) {
- /* Because of padding, first the buffer must be initialized with 0. */
- struct ifla_vf_vlan_info ivvi = {};
- ivvi.vf = sr_iov->vf;
- ivvi.vlan = sr_iov->vlan;
- ivvi.qos = sr_iov->qos;
- ivvi.vlan_proto = htobe16(sr_iov->vlan_proto);
-
- r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not open IFLA_VF_VLAN_LIST container: %m");
-
- r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info));
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_VF_VLAN_INFO: %m");
-
- r = sd_netlink_message_close_container(req);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not close IFLA_VF_VLAN_LIST container: %m");
- }
-
- r = sd_netlink_message_close_container(req);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not close IFLA_VF_INFO container: %m");
+ return r;
- r = sd_netlink_message_close_container(req);
+ r = sr_iov_set_netlink_message(sr_iov, req);
if (r < 0)
- return log_link_error_errno(link, r, "Could not close IFLA_VFINFO_LIST container: %m");
+ return r;
r = netlink_call_async(link->manager->rtnl, NULL, req, sr_iov_handler,
link_netlink_destroy_callback, link);
if (r < 0)
- return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+ return r;
link_ref(link);
link->sr_iov_messages++;
ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) {
r = sr_iov_configure(link, sr_iov);
if (r < 0)
- return r;
+ return log_link_warning_errno(link, r,
+ "Failed to configure SR-IOV virtual function %"PRIu32": %m",
+ sr_iov->vf);
}
if (link->sr_iov_messages == 0)
return 0;
}
-
-static int sr_iov_section_verify(SRIOV *sr_iov) {
- assert(sr_iov);
-
- if (section_is_invalid(sr_iov->section))
- return -EINVAL;
-
- if (sr_iov->vf == UINT32_MAX)
- return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: [SRIOV] section without VirtualFunction= field configured. "
- "Ignoring [SRIOV] section from line %u.",
- sr_iov->section->filename, sr_iov->section->line);
-
- return 0;
-}
-
-void network_drop_invalid_sr_iov(Network *network) {
- SRIOV *sr_iov;
-
- assert(network);
-
- ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section)
- if (sr_iov_section_verify(sr_iov) < 0)
- sr_iov_free(sr_iov);
-}
-
-int config_parse_sr_iov_uint32(
- 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
- Network *network = data;
- uint32_t k;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
- if (r < 0)
- return r;
-
- if (isempty(rvalue)) {
- if (streq(lvalue, "VirtualFunction"))
- sr_iov->vf = UINT32_MAX;
- else if (streq(lvalue, "VLANId"))
- sr_iov->vlan = 0;
- else if (streq(lvalue, "QualityOfService"))
- sr_iov->qos = 0;
- else
- assert_not_reached();
-
- TAKE_PTR(sr_iov);
- return 0;
- }
-
- r = safe_atou32(rvalue, &k);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
- return 0;
- }
-
- if (streq(lvalue, "VLANId")) {
- if (k == 0 || k > 4095) {
- log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k);
- return 0;
- }
- sr_iov->vlan = k;
- } else if (streq(lvalue, "VirtualFunction")) {
- if (k >= INT_MAX) {
- log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k);
- return 0;
- }
- sr_iov->vf = k;
- } else if (streq(lvalue, "QualityOfService"))
- sr_iov->qos = k;
- else
- assert_not_reached();
-
- TAKE_PTR(sr_iov);
- return 0;
-}
-
-int config_parse_sr_iov_vlan_proto(
- 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
- Network *network = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
- if (r < 0)
- return r;
-
- if (isempty(rvalue) || streq(rvalue, "802.1Q"))
- sr_iov->vlan_proto = ETH_P_8021Q;
- else if (streq(rvalue, "802.1ad"))
- sr_iov->vlan_proto = ETH_P_8021AD;
- else {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
- return 0;
- }
-
- TAKE_PTR(sr_iov);
- return 0;
-}
-
-int config_parse_sr_iov_link_state(
- 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
- Network *network = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
- if (r < 0)
- return r;
-
- /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
- * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
-
- if (isempty(rvalue)) {
- sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
- TAKE_PTR(sr_iov);
- return 0;
- }
-
- if (streq(rvalue, "auto")) {
- sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
- TAKE_PTR(sr_iov);
- return 0;
- }
-
- r = parse_boolean(rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
- return 0;
- }
-
- sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE;
- TAKE_PTR(sr_iov);
- return 0;
-}
-
-int config_parse_sr_iov_boolean(
- 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
- Network *network = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
- if (r < 0)
- return r;
-
- if (isempty(rvalue)) {
- if (streq(lvalue, "MACSpoofCheck"))
- sr_iov->vf_spoof_check_setting = -1;
- else if (streq(lvalue, "QueryReceiveSideScaling"))
- sr_iov->query_rss = -1;
- else if (streq(lvalue, "Trust"))
- sr_iov->trust = -1;
- else
- assert_not_reached();
-
- TAKE_PTR(sr_iov);
- return 0;
- }
-
- r = parse_boolean(rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
- return 0;
- }
-
- if (streq(lvalue, "MACSpoofCheck"))
- sr_iov->vf_spoof_check_setting = r;
- else if (streq(lvalue, "QueryReceiveSideScaling"))
- sr_iov->query_rss = r;
- else if (streq(lvalue, "Trust"))
- sr_iov->trust = r;
- else
- assert_not_reached();
-
- TAKE_PTR(sr_iov);
- return 0;
-}
-
-int config_parse_sr_iov_mac(
- 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
- Network *network = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = sr_iov_new_static(network, filename, section_line, &sr_iov);
- if (r < 0)
- return r;
-
- if (isempty(rvalue)) {
- sr_iov->mac = ETHER_ADDR_NULL;
- TAKE_PTR(sr_iov);
- return 0;
- }
-
- r = parse_ether_addr(rvalue, &sr_iov->mac);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
- return 0;
- }
-
- TAKE_PTR(sr_iov);
- return 0;
-}
* Copyright © 2020 VMware, Inc. */
#pragma once
-#include <linux/if_link.h>
+#include "netif-sriov.h"
-#include "conf-parser.h"
-#include "ether-addr-util.h"
-#include "networkd-link.h"
-#include "networkd-network.h"
-#include "networkd-util.h"
+typedef struct Link Link;
-typedef enum SRIOVLinkState {
- SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO,
- SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE,
- SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE,
- _SR_IOV_LINK_STATE_MAX,
- _SR_IOV_LINK_STATE_INVALID = -EINVAL,
-} SRIOVLinkState;
-
-typedef struct SRIOV {
- NetworkConfigSection *section;
- Network *network;
-
- uint32_t vf; /* 0 - 2147483646 */
- uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */
- uint32_t qos;
- uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */
- int vf_spoof_check_setting;
- int query_rss;
- int trust;
- SRIOVLinkState link_state;
- struct ether_addr mac;
-} SRIOV;
-
-SRIOV *sr_iov_free(SRIOV *sr_iov);
int link_configure_sr_iov(Link *link);
-void network_drop_invalid_sr_iov(Network *network);
-
-DEFINE_NETWORK_SECTION_FUNCTIONS(SRIOV, sr_iov_free);
-
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32);
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean);
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state);
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto);
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac);
return free_and_replace(*url, unescaped);
}
-static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) {
- siphash24_compress_string(c->filename, state);
- siphash24_compress(&c->line, sizeof(c->line), state);
-}
-
-static int network_config_compare_func(const NetworkConfigSection *x, const NetworkConfigSection *y) {
- int r;
-
- r = strcmp(x->filename, y->filename);
- if (r != 0)
- return r;
-
- return CMP(x->line, y->line);
-}
-
-DEFINE_HASH_OPS(network_config_hash_ops, NetworkConfigSection, network_config_hash_func, network_config_compare_func);
-
-int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
- NetworkConfigSection *cs;
-
- cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1);
- if (!cs)
- return -ENOMEM;
-
- strcpy(cs->filename, filename);
- cs->line = line;
-
- *s = TAKE_PTR(cs);
-
- return 0;
-}
-
-unsigned hashmap_find_free_section_line(Hashmap *hashmap) {
- NetworkConfigSection *cs;
- unsigned n = 0;
- void *entry;
-
- HASHMAP_FOREACH_KEY(entry, cs, hashmap)
- if (n < cs->line)
- n = cs->line;
-
- return n + 1;
-}
-
int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg) {
const char *err_msg = NULL;
typedef struct Link Link;
-typedef struct NetworkConfigSection {
- unsigned line;
- bool invalid;
- char filename[];
-} NetworkConfigSection;
-
typedef enum NetworkConfigSource {
NETWORK_CONFIG_SOURCE_FOREIGN, /* configured by kernel */
NETWORK_CONFIG_SOURCE_STATIC,
const char *dhcp_lease_server_type_to_string(sd_dhcp_lease_server_type_t t) _const_;
sd_dhcp_lease_server_type_t dhcp_lease_server_type_from_string(const char *s) _pure_;
-static inline NetworkConfigSection* network_config_section_free(NetworkConfigSection *cs) {
- return mfree(cs);
-}
-DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free);
-
-int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s);
-extern const struct hash_ops network_config_hash_ops;
-unsigned hashmap_find_free_section_line(Hashmap *hashmap);
-
-static inline bool section_is_invalid(NetworkConfigSection *section) {
- /* If this returns false, then it does _not_ mean the section is valid. */
-
- if (!section)
- return false;
-
- return section->invalid;
-}
-
-#define DEFINE_NETWORK_SECTION_FUNCTIONS(type, free_func) \
- static inline type* free_func##_or_set_invalid(type *p) { \
- assert(p); \
- \
- if (p->section) \
- p->section->invalid = true; \
- else \
- free_func(p); \
- return NULL; \
- } \
- DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \
- DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);
-
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)
}
int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(qdisc_freep) QDisc *qdisc = NULL;
TrafficControl *existing;
QDisc *q = NULL;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
qdisc->network = network;
qdisc->section = TAKE_PTR(n);
- r = ordered_hashmap_ensure_put(&network->tc_by_section, &network_config_hash_ops, qdisc->section, TC(qdisc));
+ r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, qdisc->section, TC(qdisc));
if (r < 0)
return r;
if (qdisc->network && qdisc->section)
ordered_hashmap_remove(qdisc->network->tc_by_section, qdisc->section);
- network_config_section_free(qdisc->section);
+ config_section_free(qdisc->section);
free(qdisc->tca_kind);
return mfree(qdisc);
typedef struct QDisc {
TrafficControl meta;
- NetworkConfigSection *section;
+ ConfigSection *section;
Network *network;
int family;
int qdisc_configure(Link *link, QDisc *qdisc);
int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact);
-DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(QDisc, qdisc_free);
DEFINE_TC_CAST(QDISC, QDisc);
}
int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
- _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
_cleanup_(tclass_freep) TClass *tclass = NULL;
TrafficControl *existing;
int r;
assert(filename);
assert(section_line > 0);
- r = network_config_section_new(filename, section_line, &n);
+ r = config_section_new(filename, section_line, &n);
if (r < 0)
return r;
tclass->network = network;
tclass->section = TAKE_PTR(n);
- r = ordered_hashmap_ensure_put(&network->tc_by_section, &network_config_hash_ops, tclass->section, tclass);
+ r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, tclass->section, tclass);
if (r < 0)
return r;
if (tclass->network && tclass->section)
ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section);
- network_config_section_free(tclass->section);
+ config_section_free(tclass->section);
return mfree(tclass);
}
typedef struct TClass {
TrafficControl meta;
- NetworkConfigSection *section;
+ ConfigSection *section;
Network *network;
uint32_t classid;
int tclass_configure(Link *link, TClass *tclass);
int tclass_section_verify(TClass *tclass);
-DEFINE_NETWORK_SECTION_FUNCTIONS(TClass, tclass_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(TClass, tclass_free);
DEFINE_TC_CAST(TCLASS, TClass);
.required_operstate = LINK_OPERSTATE_RANGE_DEFAULT,
};
- r = hashmap_ensure_put(&m->links, NULL, INT_TO_PTR(ifindex), l);
+ r = hashmap_ensure_put(&m->links_by_index, NULL, INT_TO_PTR(ifindex), l);
if (r < 0)
return r;
return NULL;
if (l->manager) {
- hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
+ hashmap_remove(l->manager->links_by_index, INT_TO_PTR(l->ifindex));
hashmap_remove(l->manager->links_by_name, l->ifname);
}
}
int link_update_monitor(Link *l) {
- _cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *required_family = NULL,
+ _cleanup_free_ char *required_operstate = NULL, *required_family = NULL,
*ipv4_address_state = NULL, *ipv6_address_state = NULL, *state = NULL;
int r, ret = 0;
"Failed to parse required operational state, ignoring: %m");
}
- r = sd_network_link_get_operational_state(l->ifindex, &operstate);
+ r = network_link_get_operational_state(l->ifindex, &l->operational_state);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get operational state, ignoring: %m");
- else {
- LinkOperationalState s;
-
- s = link_operstate_from_string(operstate);
- if (s < 0)
- ret = log_link_debug_errno(l, s, "Failed to parse operational state, ignoring: %m");
- else
- l->operational_state = s;
- }
r = sd_network_link_get_required_family_for_online(l->ifindex, &required_family);
if (r < 0)
return true;
/* if interfaces are given on the command line, ignore all others */
- if (m->interfaces && !hashmap_contains(m->interfaces, link->ifname))
+ if (m->command_line_interfaces_by_name &&
+ !hashmap_contains(m->command_line_interfaces_by_name, link->ifname))
return true;
if (!link->required_for_online)
return true;
/* ignore interfaces we explicitly are asked to ignore */
- return strv_fnmatch(m->ignore, link->ifname);
+ return strv_fnmatch(m->ignored_interfaces, link->ifname);
}
static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
* 0: operstate is not enough
* 1: online */
- if (!l->state)
+ if (!l->state || streq(l->state, "pending"))
+ /* If no state string exists, networkd (and possibly also udevd) has not detected the
+ * interface yet, that mean we cannot determine whether the interface is managed or
+ * not. Hence, return negative value.
+ * If the link is in pending state, then udevd has not processed the link, and networkd
+ * has not tried to find .network file for the link. Hence, return negative value. */
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
- "link has not yet been processed by udev");
+ "link has not yet been processed by udev: setup state is %s.",
+ strna(l->state));
+
+ if (streq(l->state, "unmanaged")) {
+ /* If the link is in unmanaged state, then ignore the interface unless the interface is
+ * specified in '--interface/-i' option. */
+ if (!hashmap_contains(m->command_line_interfaces_by_name, l->ifname)) {
+ log_link_debug(l, "link is not managed by networkd (yet?).");
+ return 0;
+ }
- if (STR_IN_SET(l->state, "configuring", "pending"))
+ } else if (!streq(l->state, "configured"))
+ /* If the link is in non-configured state, return negative value here. */
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
- "link is being processed by networkd");
+ "link is being processed by networkd: setup state is %s.",
+ l->state);
if (s.min < 0)
s.min = m->required_operstate.min >= 0 ? m->required_operstate.min
}
}
+ log_link_debug(l, "link is configured by networkd and online.");
return 1;
}
bool manager_configured(Manager *m) {
bool one_ready = false;
const char *ifname;
- void *p;
Link *l;
int r;
- if (!hashmap_isempty(m->interfaces)) {
+ if (!hashmap_isempty(m->command_line_interfaces_by_name)) {
+ LinkOperationalStateRange *range;
+
/* wait for all the links given on the command line to appear */
- HASHMAP_FOREACH_KEY(p, ifname, m->interfaces) {
- LinkOperationalStateRange *range = p;
+ HASHMAP_FOREACH_KEY(range, ifname, m->command_line_interfaces_by_name) {
l = hashmap_get(m->links_by_name, ifname);
if (!l && range->min == LINK_OPERSTATE_MISSING) {
/* wait for all links networkd manages to be in admin state 'configured'
* and at least one link to gain a carrier */
- HASHMAP_FOREACH(l, m->links) {
+ HASHMAP_FOREACH(l, m->links_by_index) {
if (manager_ignore_link(m, l)) {
log_link_debug(l, "link is ignored");
continue;
return 0;
}
- l = hashmap_get(m->links, INT_TO_PTR(ifindex));
+ l = hashmap_get(m->links_by_index, INT_TO_PTR(ifindex));
switch (type) {
sd_network_monitor_flush(m->network_monitor);
- HASHMAP_FOREACH(l, m->links) {
+ HASHMAP_FOREACH(l, m->links_by_index) {
r = link_update_monitor(l);
if (r < 0 && r != -ENODATA)
log_link_warning_errno(l, r, "Failed to update link state, ignoring: %m");
return 0;
}
-int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
+int manager_new(Manager **ret,
+ Hashmap *command_line_interfaces_by_name,
+ char **ignored_interfaces,
LinkOperationalStateRange required_operstate,
AddressFamily required_family,
- bool any, usec_t timeout) {
+ bool any,
+ usec_t timeout) {
+
_cleanup_(manager_freep) Manager *m = NULL;
int r;
return -ENOMEM;
*m = (Manager) {
- .interfaces = interfaces,
- .ignore = ignore,
+ .command_line_interfaces_by_name = command_line_interfaces_by_name,
+ .ignored_interfaces = ignored_interfaces,
.required_operstate = required_operstate,
.required_family = required_family,
.any = any,
if (!m)
return NULL;
- hashmap_free_with_destructor(m->links, link_free);
+ hashmap_free_with_destructor(m->links_by_index, link_free);
hashmap_free(m->links_by_name);
sd_event_source_unref(m->network_monitor_event_source);
typedef struct Link Link;
struct Manager {
- Hashmap *links;
+ Hashmap *links_by_index;
Hashmap *links_by_name;
/* Do not free the two members below. */
- Hashmap *interfaces;
- char **ignore;
+ Hashmap *command_line_interfaces_by_name;
+ char **ignored_interfaces;
LinkOperationalStateRange required_operstate;
AddressFamily required_family;
};
Manager* manager_free(Manager *m);
-int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
+int manager_new(Manager **ret, Hashmap *command_line_interfaces_by_name, char **ignored_interfaces,
LinkOperationalStateRange required_operstate,
AddressFamily required_family,
bool any, usec_t timeout);
if (r < 0)
log_notice_errno(r, "Failed to kill any cgroup(s) based on swap: %m");
else {
- if (selected)
+ if (selected && r > 0)
log_notice("Killed %s due to memory used (%"PRIu64") / total (%"PRIu64") and "
"swap used (%"PRIu64") / total (%"PRIu64") being more than "
PERMYRIAD_AS_PERCENT_FORMAT_STR,
if (r < 0)
log_notice_errno(r, "Failed to kill any cgroup(s) under %s based on pressure: %m", t->path);
else {
- /* Don't act on all the high pressure cgroups at once; return as soon as we kill one */
+ /* Don't act on all the high pressure cgroups at once; return as soon as we kill one.
+ * If r == 0 then it means there were not eligible candidates, the candidate cgroup
+ * disappeared, or the candidate cgroup has no processes by the time we tried to kill
+ * it. In either case, go through the event loop again and select a new candidate if
+ * pressure is still high. */
m->mem_pressure_post_action_delay_start = usec_now;
- if (selected)
+ if (selected && r > 0)
log_notice("Killed %s due to memory pressure for %s being %lu.%02lu%% > %lu.%02lu%%"
" for > %s with reclaim activity",
selected, t->path,
r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL);
else
r = cg_kill(SYSTEMD_CGROUP_CONTROLLER, path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL);
- if (r < 0)
+
+ /* The cgroup could have been cleaned up after we have sent SIGKILL to all of the processes, but before
+ * we could do one last iteration of cgroup.procs to check. Or the service unit could have exited and
+ * was removed between picking candidates and coming into this function. In either case, let's log
+ * about it let the caller decide what to do once they know how many PIDs were killed. */
+ if (IN_SET(r, -ENOENT, -ENODEV))
+ log_debug_errno(r, "Error when sending SIGKILL to processes in cgroup path %s, ignoring: %m", path);
+ else if (r < 0)
return r;
+ if (set_isempty(pids_killed))
+ log_debug("Nothing killed when attempting to kill %s", path);
+
r = increment_oomd_xattr(path, "user.oomd_kill", set_size(pids_killed));
if (r < 0)
log_debug_errno(r, "Failed to set user.oomd_kill on kill: %m");
continue;
r = oomd_cgroup_kill(sorted[i]->path, true, dry_run);
- if (r == 0)
- continue; /* We didn't find anything to kill */
if (r == -ENOMEM)
return r; /* Treat oom as a hard error */
if (r < 0) {
if (!selected)
return -ENOMEM;
*ret_selected = selected;
- return 1;
+ return r;
}
return ret;
continue;
r = oomd_cgroup_kill(sorted[i]->path, true, dry_run);
- if (r == 0)
- continue; /* We didn't find anything to kill */
if (r == -ENOMEM)
return r; /* Treat oom as a hard error */
if (r < 0) {
if (!selected)
return -ENOMEM;
*ret_selected = selected;
- return 1;
+ return r;
}
return ret;
FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to read directory: %m")) {
_cleanup_(portable_metadata_unrefp) PortableMetadata *m = NULL;
+ _cleanup_(mac_selinux_freep) char *con = NULL;
_cleanup_close_ int fd = -1;
if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
}
- if (socket_fd >= 0) {
- _cleanup_(mac_selinux_freep) char *con = NULL;
#if HAVE_SELINUX
- /* The units will be copied on the host's filesystem, so if they had a SELinux label
- * we have to preserve it. Copy it out so that it can be applied later. */
+ /* The units will be copied on the host's filesystem, so if they had a SELinux label
+ * we have to preserve it. Copy it out so that it can be applied later. */
- r = fgetfilecon_raw(fd, &con);
- if (r < 0 && errno != ENODATA)
- log_debug_errno(errno, "Failed to get SELinux file context from '%s', ignoring: %m", de->d_name);
+ r = fgetfilecon_raw(fd, &con);
+ if (r < 0 && errno != ENODATA)
+ log_debug_errno(errno, "Failed to get SELinux file context from '%s', ignoring: %m", de->d_name);
#endif
+
+ if (socket_fd >= 0) {
struct iovec iov[] = {
IOVEC_MAKE_STRING(de->d_name),
IOVEC_MAKE((char *)"\0", sizeof(char)),
return log_debug_errno(r, "Failed to send unit metadata to parent: %m");
}
- m = portable_metadata_new(de->d_name, NULL, NULL, fd);
+ m = portable_metadata_new(de->d_name, where, con, fd);
if (!m)
return -ENOMEM;
fd = -1;
r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, &d);
if (r == -EISDIR) {
+ _cleanup_free_ char *image_name = NULL;
+
/* We can't turn this into a loop-back block device, and this returns EISDIR? Then this is a directory
* tree and not a raw device. It's easy then. */
- r = extract_now(path, matches, NULL, path_is_extension, -1, &os_release, &unit_files);
+ r = path_extract_filename(path, &image_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract image name from path '%s': %m", path);
+
+ r = extract_now(path, matches, image_name, path_is_extension, -1, &os_release, &unit_files);
if (r < 0)
return r;
bool validate_sysext,
Image **ret_image,
OrderedHashmap **ret_extension_images,
+ OrderedHashmap **ret_extension_releases,
PortableMetadata **ret_os_release,
Hashmap **ret_unit_files,
char ***ret_valid_prefixes,
_cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
- _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
+ _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_strv_free_ char **valid_prefixes = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
if (!extension_images)
return -ENOMEM;
+ if (ret_extension_releases) {
+ extension_releases = ordered_hashmap_new(&portable_metadata_hash_ops);
+ if (!extension_releases)
+ return -ENOMEM;
+ }
+
STRV_FOREACH(p, extension_image_paths) {
_cleanup_(image_unrefp) Image *new = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *extension_release_meta = NULL;
_cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
_cleanup_strv_free_ char **extension_release = NULL;
+ _cleanup_close_ int extension_release_fd = -1;
_cleanup_fclose_ FILE *f = NULL;
const char *e;
if (r < 0)
return r;
- if (!validate_sysext && !ret_valid_prefixes)
+ if (!validate_sysext && !ret_valid_prefixes && !ret_extension_releases)
continue;
- r = take_fdopen_unlocked(&extension_release_meta->fd, "r", &f);
+ /* We need to keep the fd valid, to return the PortableMetadata to the caller. */
+ extension_release_fd = fd_reopen(extension_release_meta->fd, O_CLOEXEC);
+ if (extension_release_fd < 0)
+ return extension_release_fd;
+
+ r = take_fdopen_unlocked(&extension_release_fd, "r", &f);
if (r < 0)
return r;
if (r < 0)
return r;
}
+
+ if (ret_extension_releases) {
+ r = ordered_hashmap_put(extension_releases, ext->name, extension_release_meta);
+ if (r < 0)
+ return r;
+ TAKE_PTR(extension_release_meta);
+ }
}
strv_sort(valid_prefixes);
*ret_image = TAKE_PTR(image);
if (ret_extension_images)
*ret_extension_images = TAKE_PTR(extension_images);
+ if (ret_extension_releases)
+ *ret_extension_releases = TAKE_PTR(extension_releases);
if (ret_os_release)
*ret_os_release = TAKE_PTR(os_release);
if (ret_unit_files)
char **matches,
char **extension_image_paths,
PortableMetadata **ret_os_release,
+ OrderedHashmap **ret_extension_releases,
Hashmap **ret_unit_files,
char ***ret_valid_prefixes,
sd_bus_error *error) {
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
- _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
+ _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_(strv_freep) char **valid_prefixes = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
/* validate_sysext= */ false,
&image,
&extension_images,
+ &extension_releases,
&os_release,
&unit_files,
ret_valid_prefixes ? &valid_prefixes : NULL,
if (ret_os_release)
*ret_os_release = TAKE_PTR(os_release);
+ if (ret_extension_releases)
+ *ret_extension_releases = TAKE_PTR(extension_releases);
if (ret_unit_files)
*ret_unit_files = TAKE_PTR(unit_files);
if (ret_valid_prefixes)
return IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "RootDirectory=" : "RootImage=";
}
+static const char *extension_setting_from_image(ImageType type) {
+ return IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "ExtensionDirectories=" : "ExtensionImages=";
+}
+
static int make_marker_text(const char *image_path, OrderedHashmap *extension_images, char **ret_text) {
_cleanup_free_ char *text = NULL, *escaped_image_path = NULL;
Image *ext;
size_t *n_changes) {
_cleanup_free_ char *text = NULL, *dropin = NULL;
- Image *ext;
int r;
assert(image_path);
if (endswith(m->name, ".service")) {
const char *os_release_source, *root_type;
_cleanup_free_ char *base_name = NULL;
+ Image *ext;
root_type = root_setting_from_image(type);
if (m->image_path && !path_equal(m->image_path, image_path))
ORDERED_HASHMAP_FOREACH(ext, extension_images)
- if (!strextend(&text, "ExtensionImages=", ext->path, "\n"))
+ if (!strextend(&text, extension_setting_from_image(ext->type), ext->path, "\n"))
return -ENOMEM;
}
/* validate_sysext= */ true,
&image,
&extension_images,
+ /* extension_releases= */ NULL,
/* os_release= */ NULL,
&unit_files,
&valid_prefixes,
static int portable_get_state_internal(
sd_bus *bus,
const char *name_or_path,
+ char **extension_image_paths,
PortableFlags flags,
PortableState *ret,
sd_bus_error *error) {
if (!IN_SET(de->d_type, DT_LNK, DT_REG))
continue;
- r = test_chroot_dropin(d, where, de->d_name, name_or_path, NULL, NULL);
+ r = test_chroot_dropin(d, where, de->d_name, name_or_path, extension_image_paths, NULL);
if (r < 0)
return r;
if (r == 0)
int portable_get_state(
sd_bus *bus,
const char *name_or_path,
+ char **extension_image_paths,
PortableFlags flags,
PortableState *ret,
sd_bus_error *error) {
/* We look for matching units twice: once in the regular directories, and once in the runtime directories — but
* the latter only if we didn't find anything in the former. */
- r = portable_get_state_internal(bus, name_or_path, flags & ~PORTABLE_RUNTIME, &state, error);
+ r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags & ~PORTABLE_RUNTIME, &state, error);
if (r < 0)
return r;
if (state == PORTABLE_DETACHED) {
- r = portable_get_state_internal(bus, name_or_path, flags | PORTABLE_RUNTIME, &state, error);
+ r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags | PORTABLE_RUNTIME, &state, error);
if (r < 0)
return r;
}
#define PORTABLE_METADATA_IS_UNIT(m) (!IN_SET((m)->name[0], 0, '/'))
typedef enum PortableFlags {
- PORTABLE_RUNTIME = 1 << 0, /* Public API via DBUS, do not change */
- PORTABLE_PREFER_COPY = 1 << 1,
- PORTABLE_PREFER_SYMLINK = 1 << 2,
- PORTABLE_REATTACH = 1 << 3,
- _PORTABLE_MASK_PUBLIC = PORTABLE_RUNTIME,
+ PORTABLE_RUNTIME = 1 << 0,
+ PORTABLE_INSPECT_EXTENSION_RELEASES = 1 << 1, /* Public API via DBUS, do not change */
+ PORTABLE_PREFER_COPY = 1 << 2,
+ PORTABLE_PREFER_SYMLINK = 1 << 3,
+ PORTABLE_REATTACH = 1 << 4,
+ _PORTABLE_MASK_PUBLIC = PORTABLE_RUNTIME | PORTABLE_INSPECT_EXTENSION_RELEASES,
_PORTABLE_TYPE_MAX,
- _PORTABLE_TYPE_INVALID = -EINVAL,
+ _PORTABLE_TYPE_INVALID = -EINVAL,
} PortableFlags;
/* This enum is anonymous, since we usually store it in an 'int', as we overload it with negative errno
int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
-int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
+int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
-int portable_get_state(sd_bus *bus, const char *name_or_path, PortableFlags flags, PortableState *ret, sd_bus_error *error);
+int portable_get_state(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error);
int portable_get_profiles(char ***ret);
static int get_image_metadata(sd_bus *bus, const char *image, char **matches, sd_bus_message **reply) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ PortableFlags flags = PORTABLE_INSPECT_EXTENSION_RELEASES;
const char *method;
- uint64_t flags = 0;
int r;
assert(bus);
if (r < 0)
return bus_log_parse_error(r);
+ /* If we specified any extensions, we'll first get back exactly the
+ * paths (and extension-release content) for each one of the arguments. */
+ for (size_t i = 0; i < strv_length(arg_extension_images); ++i) {
+ const char *name;
+
+ r = sd_bus_message_enter_container(reply, 'e', "say");
+ if (r < 0)
+ return bus_log_parse_error(r);
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_read(reply, "s", &name);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read_array(reply, 'y', &data, &sz);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (arg_cat) {
+ if (nl)
+ fputc('\n', stdout);
+
+ printf("%s-- Extension Release: %s --%s\n", ansi_highlight(), name, ansi_normal());
+ fwrite(data, sz, 1, stdout);
+ fflush(stdout);
+ nl = true;
+ } else {
+ _cleanup_free_ char *pretty_portable = NULL, *pretty_os = NULL, *sysext_level = NULL,
+ *id = NULL, *version_id = NULL, *sysext_scope = NULL, *portable_prefixes = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+
+ f = fmemopen_unlocked((void*) data, sz, "re");
+ if (!f)
+ return log_error_errno(errno, "Failed to open extension-release buffer: %m");
+
+ r = parse_env_file(f, name,
+ "ID", &id,
+ "VERSION_ID", &version_id,
+ "SYSEXT_SCOPE", &sysext_scope,
+ "SYSEXT_LEVEL", &sysext_level,
+ "PORTABLE_PRETTY_NAME", &pretty_portable,
+ "PORTABLE_PREFIXES", &portable_prefixes,
+ "PRETTY_NAME", &pretty_os);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse extension release from '%s': %m", name);
+
+ printf("Extension:\n\t%s\n"
+ "\tExtension Scope:\n\t\t%s\n"
+ "\tExtension Compatibility Level:\n\t\t%s\n"
+ "\tPortable Service:\n\t\t%s\n"
+ "\tPortable Prefixes:\n\t\t%s\n"
+ "\tOperating System:\n\t\t%s (%s %s)\n",
+ name,
+ strna(sysext_scope),
+ strna(sysext_level),
+ strna(pretty_portable),
+ strna(portable_prefixes),
+ strna(pretty_os),
+ strna(id),
+ strna(version_id));
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ }
+
for (;;) {
const char *name;
if (r < 0)
return bus_log_parse_error(r);
+ /* If we specified any extensions, we'll first get back exactly the
+ * paths (and extension-release content) for each one of the arguments. */
+ for (size_t i = 0; i < strv_length(arg_extension_images); ++i) {
+ r = sd_bus_message_skip(reply, "{say}");
+ if (r < 0)
+ return bus_log_parse_error(r);
+ }
+
for (;;) {
const char *name;
}
static int is_image_attached(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *image = NULL;
- const char *state;
+ const char *state, *method;
int r;
r = determine_image(argv[1], true, &image);
if (r < 0)
return r;
- r = bus_call_method(bus, bus_portable_mgr, "GetImageState", &error, &reply, "s", image);
+ method = strv_isempty(arg_extension_images) ? "GetImageState" : "GetImageStateWithExtensions";
+
+ r = bus_message_new_method_call(bus, &m, bus_portable_mgr, method);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", image);
if (r < 0)
- return log_error_errno(r, "Failed to get image state: %s", bus_error_message(&error, r));
+ return bus_log_create_error(r);
+
+ r = attach_extensions_to_message(m, arg_extension_images);
+ if (r < 0)
+ return r;
+
+ if (!strv_isempty(arg_extension_images)) {
+ r = sd_bus_message_append(m, "t", 0);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "%s failed: %s", method, bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &state);
if (r < 0)
r = portable_get_state(
sd_bus_message_get_bus(message),
image->path,
+ NULL,
0,
&state,
&error_state);
}
static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **extension_images = NULL;
const char *name_or_path;
PortableState state;
int r;
if (r < 0)
return r;
+ if (sd_bus_message_is_method_call(message, NULL, "GetImageStateWithExtensions")) {
+ uint64_t input_flags = 0;
+
+ r = sd_bus_message_read_strv(message, &extension_images);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "t", &input_flags);
+ if (r < 0)
+ return r;
+
+ /* No flags are supported by this method for now. */
+ if (input_flags != 0)
+ return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
+ "Invalid 'flags' parameter '%" PRIu64 "'",
+ input_flags);
+ }
+
r = portable_get_state(
sd_bus_message_get_bus(message),
name_or_path,
+ extension_images,
0,
&state,
error);
SD_BUS_RESULT("s", state),
method_get_image_state,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetImageStateWithExtensions",
+ SD_BUS_ARGS("s", image,
+ "as", extensions,
+ "t", flags),
+ SD_BUS_RESULT("s", state),
+ method_get_image_state,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("AttachImage",
SD_BUS_ARGS("s", image,
"as", matches,
Image *image,
sd_bus_error *error) {
+ _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_releases = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_strv_free_ char **matches = NULL, **extension_images = NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ PortableMetadata **sorted = NULL;
- /* Unused for now, but added to the DBUS methods for future-proofing */
- uint64_t input_flags = 0;
+ PortableFlags flags = 0;
size_t i;
int r;
if (sd_bus_message_is_method_call(message, NULL, "GetImageMetadataWithExtensions") ||
sd_bus_message_is_method_call(message, NULL, "GetMetadataWithExtensions")) {
+ uint64_t input_flags = 0;
+
r = sd_bus_message_read(message, "t", &input_flags);
if (r < 0)
return r;
- /* Let clients know that this version doesn't support any flags */
- if (input_flags != 0)
+
+ if ((input_flags & ~_PORTABLE_MASK_PUBLIC) != 0)
return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
"Invalid 'flags' parameter '%" PRIu64 "'",
input_flags);
+ flags |= input_flags;
}
r = bus_image_acquire(m,
matches,
extension_images,
&os_release,
+ &extension_releases,
&unit_files,
NULL,
error);
if (r < 0)
return r;
+ /* If it was requested, also send back the extension path and the content
+ * of each extension-release file. Behind a flag, as it's an incompatible
+ * change. */
+ if (FLAGS_SET(flags, PORTABLE_INSPECT_EXTENSION_RELEASES)) {
+ PortableMetadata *extension_release;
+
+ ORDERED_HASHMAP_FOREACH(extension_release, extension_releases) {
+
+ r = sd_bus_message_open_container(reply, 'e', "say");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", extension_release->image_path);
+ if (r < 0)
+ return r;
+
+ r = append_fd(reply, extension_release);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+ }
+ }
+
for (i = 0; i < hashmap_size(unit_files); i++) {
r = sd_bus_message_open_container(reply, 'e', "say");
void *userdata,
sd_bus_error *error) {
+ _cleanup_strv_free_ char **extension_images = NULL;
Image *image = userdata;
PortableState state;
int r;
assert(message);
assert(image);
+ if (sd_bus_message_is_method_call(message, NULL, "GetStateWithExtensions")) {
+ uint64_t input_flags = 0;
+
+ r = sd_bus_message_read_strv(message, &extension_images);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "t", &input_flags);
+ if (r < 0)
+ return r;
+
+ /* No flags are supported by this method for now. */
+ if (input_flags != 0)
+ return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
+ "Invalid 'flags' parameter '%" PRIu64 "'",
+ input_flags);
+ }
+
r = portable_get_state(
sd_bus_message_get_bus(message),
image->path,
+ extension_images,
0,
&state,
error);
r = portable_get_state(
sd_bus_message_get_bus(message),
image->path,
+ NULL,
0,
&state,
error);
SD_BUS_RESULT("s", state),
bus_image_method_get_state,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetStateWithExtensions",
+ SD_BUS_ARGS("as", extensions,
+ "t", flags),
+ SD_BUS_RESULT("s", state),
+ bus_image_method_get_state,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("Attach",
SD_BUS_ARGS("as", matches,
"s", profile,
* ourselves the mode and owner should be correct anyway. */
r = fchmod_and_chown(seed_fd, 0600, 0, 0);
if (r < 0)
- return log_error_errno(r, "Failed to adjust seed file ownership and access mode.");
+ return log_error_errno(r, "Failed to adjust seed file ownership and access mode: %m");
/* Let's make this whole job asynchronous, i.e. let's make ourselves a barrier for
* proper initialization of the random pool. */
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "fuzz.h"
+#include "resolved-etc-hosts.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(etc_hosts_free) EtcHosts h = {};
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ f = data_to_file(data, size);
+ assert_se(f);
+
+ (void) etc_hosts_parse(&h, f);
+
+ return 0;
+}
resolved-socket-graveyard.h
resolved-varlink.c
resolved-varlink.h
- resolved.c
'''.split())
resolvectl_sources = files('''
[lib_openssl_or_gcrypt,
libm]],
+ [files('test-resolved-stream.c')
+ + basic_dns_sources + systemd_resolved_sources,
+ [libshared],
+ [lib_openssl_or_gcrypt,
+ libm]
+ + systemd_resolved_dependencies,
+ resolve_includes],
+
[files('test-dnssec.c'),
[libsystemd_resolve_core,
libshared],
libshared],
[lib_openssl_or_gcrypt,
libm]],
+ [files('fuzz-etc-hosts.c',
+ 'resolved-etc-hosts.c',
+ 'resolved-etc-hosts.h'),
+ [libsystemd_resolve_core,
+ libshared],
+ [lib_openssl_or_gcrypt,
+ libm]],
]
+
+systemd_resolved_sources += files('resolved.c')
if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family, false, false) & flags) == 0)
return DNS_SCOPE_NO;
+ /* Never resolve empty name. */
+ if (dns_name_is_empty(domain))
+ return DNS_SCOPE_NO;
+
/* Never resolve any loopback hostname or IP address via DNS, LLMNR or mDNS. Instead, always rely on
* synthesized RRs for these. */
if (is_localhost(domain) ||
#include "alloc-util.h"
#include "fd-util.h"
#include "io-util.h"
+#include "macro.h"
#include "missing_network.h"
#include "resolved-dns-stream.h"
#include "resolved-manager.h"
return dns_stream_complete(s, ETIMEDOUT);
}
-static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
- _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */
+static DnsPacket *dns_stream_take_read_packet(DnsStream *s) {
+ assert(s);
+
+ /* Note, dns_stream_update() should be called after this is called. When this is called, the
+ * stream may be already full and the EPOLLIN flag is dropped from the stream IO event source.
+ * Even this makes a room to read in the stream, this does not call dns_stream_update(), hence
+ * EPOLLIN flag is not set automatically. So, to read further packets from the stream,
+ * dns_stream_update() must be called explicitly. Currently, this is only called from
+ * on_stream_io_impl(), and there dns_stream_update() is called. */
+
+ if (!s->read_packet)
+ return NULL;
+
+ if (s->n_read < sizeof(s->read_size))
+ return NULL;
+
+ if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size))
+ return NULL;
+
+ s->n_read = 0;
+ return TAKE_PTR(s->read_packet);
+}
+
+static int on_stream_io_impl(DnsStream *s, uint32_t revents) {
bool progressed = false;
int r;
assert(s);
+ /* This returns 1 when possible remaining stream exists, 0 on completed
+ stream or recoverable error, and negative errno on failure. */
+
#if ENABLE_DNS_OVER_TLS
if (s->encrypted) {
r = dnstls_stream_on_io(s, revents);
s->n_read += ss;
}
- /* Are we done? If so, disable the event source for EPOLLIN */
- if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) {
- /* If there's a packet handler
- * installed, call that. Note that
- * this is optional... */
- if (s->on_packet) {
- r = s->on_packet(s);
- if (r < 0)
- return r;
- }
+ /* Are we done? If so, call the packet handler and re-enable EPOLLIN for the
+ * event source if necessary. */
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = dns_stream_take_read_packet(s);
+ if (p) {
+ assert(s->on_packet);
+ r = s->on_packet(s, p);
+ if (r < 0)
+ return r;
r = dns_stream_update_io(s);
if (r < 0)
return dns_stream_complete(s, -r);
+
+ s->packet_received = true;
}
}
}
- /* Call "complete" callback if finished reading and writing one packet, and there's nothing else left
- * to write. */
- if (s->type == DNS_STREAM_LLMNR_SEND &&
- (s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) &&
- ordered_set_isempty(s->write_queue) &&
- (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size))
- return dns_stream_complete(s, 0);
+ if (s->type == DNS_STREAM_LLMNR_SEND && s->packet_received) {
+ uint32_t events;
+
+ /* Complete the stream if finished reading and writing one packet, and there's nothing
+ * else left to write. */
+
+ r = sd_event_source_get_io_events(s->io_event_source, &events);
+ if (r < 0)
+ return r;
+
+ if (!FLAGS_SET(events, EPOLLOUT))
+ return dns_stream_complete(s, 0);
+ }
/* If we did something, let's restart the timeout event source */
if (progressed && s->timeout_event_source) {
log_warning_errno(errno, "Couldn't restart TCP connection timeout, ignoring: %m");
}
+ return 1;
+}
+
+static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */
+ int r;
+
+ assert(s);
+
+ r = on_stream_io_impl(s, revents);
+ if (r <= 0)
+ return r;
+
+#if ENABLE_DNS_OVER_TLS
+ if (!s->encrypted)
+ return 0;
+
+ /* When using DNS-over-TLS, the underlying TLS library may read the entire TLS record
+ and buffer it internally. If this happens, we will not receive further EPOLLIN events,
+ and unless there's some unrelated activity on the socket, we will hang until time out.
+ To avoid this, if there's buffered TLS data, generate a "fake" EPOLLIN event.
+ This is hacky, but it makes this case transparent to the rest of the IO code. */
+ while (dnstls_stream_has_buffered_data(s)) {
+ uint32_t events;
+
+ /* Make sure the stream still wants to process more data... */
+ r = sd_event_source_get_io_events(s->io_event_source, &events);
+ if (r < 0)
+ return r;
+ if (!FLAGS_SET(events, EPOLLIN))
+ break;
+
+ r = on_stream_io_impl(s, EPOLLIN);
+ if (r <= 0)
+ return r;
+ }
+#endif
+
return 0;
}
DnsProtocol protocol,
int fd,
const union sockaddr_union *tfo_address,
+ int (on_packet)(DnsStream*, DnsPacket*),
+ int (complete)(DnsStream*, int), /* optional */
usec_t connect_timeout_usec) {
_cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
assert(protocol >= 0);
assert(protocol < _DNS_PROTOCOL_MAX);
assert(fd >= 0);
+ assert(on_packet);
if (m->n_dns_streams[type] > DNS_STREAMS_MAX)
return -EBUSY;
s->manager = m;
s->fd = fd;
+ s->on_packet = on_packet;
+ s->complete = complete;
if (tfo_address) {
s->tfo_address = *tfo_address;
return dns_stream_update_io(s);
}
-DnsPacket *dns_stream_take_read_packet(DnsStream *s) {
- assert(s);
-
- if (!s->read_packet)
- return NULL;
-
- if (s->n_read < sizeof(s->read_size))
- return NULL;
-
- if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size))
- return NULL;
-
- s->n_read = 0;
- return TAKE_PTR(s->read_packet);
-}
-
void dns_stream_detach(DnsStream *s) {
assert(s);
int ifindex;
uint32_t ttl;
bool identified;
+ bool packet_received; /* At least one packet is received. Used by LLMNR. */
/* only when using TCP fast open */
union sockaddr_union tfo_address;
size_t n_written, n_read;
OrderedSet *write_queue;
- int (*on_packet)(DnsStream *s);
+ int (*on_packet)(DnsStream *s, DnsPacket *p);
int (*complete)(DnsStream *s, int error);
LIST_HEAD(DnsTransaction, transactions); /* when used by the transaction logic */
LIST_FIELDS(DnsStream, streams);
};
-int dns_stream_new(Manager *m, DnsStream **s, DnsStreamType type, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address, usec_t timeout);
+int dns_stream_new(
+ Manager *m,
+ DnsStream **ret,
+ DnsStreamType type,
+ DnsProtocol protocol,
+ int fd,
+ const union sockaddr_union *tfo_address,
+ int (on_packet)(DnsStream*, DnsPacket*),
+ int (complete)(DnsStream*, int), /* optional */
+ usec_t connect_timeout_usec);
#if ENABLE_DNS_OVER_TLS
int dns_stream_connect_tls(DnsStream *s, void *tls_session);
#endif
return !!s->write_packet;
}
-DnsPacket *dns_stream_take_read_packet(DnsStream *s);
-
void dns_stream_detach(DnsStream *s);
return on_dns_stub_packet_internal(s, fd, revents, l->manager, l);
}
-static int on_dns_stub_stream_packet(DnsStream *s) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
-
+static int on_dns_stub_stream_packet(DnsStream *s, DnsPacket *p) {
assert(s);
-
- p = dns_stream_take_read_packet(s);
+ assert(s->manager);
assert(p);
if (dns_packet_validate_query(p) > 0) {
return -errno;
}
- r = dns_stream_new(m, &stream, DNS_STREAM_STUB, DNS_PROTOCOL_DNS, cfd, NULL, DNS_STREAM_STUB_TIMEOUT_USEC);
+ r = dns_stream_new(m, &stream, DNS_STREAM_STUB, DNS_PROTOCOL_DNS, cfd, NULL,
+ on_dns_stub_stream_packet, dns_stub_stream_complete, DNS_STREAM_STUB_TIMEOUT_USEC);
if (r < 0) {
safe_close(cfd);
return r;
}
stream->stub_listener_extra = l;
- stream->on_packet = on_dns_stub_stream_packet;
- stream->complete = dns_stub_stream_complete;
/* We let the reference to the stream dangle here, it will be dropped later by the complete callback. */
name = dns_resource_key_name(key);
- if (is_localhost(name)) {
+ if (dns_name_is_empty(name)) {
+ /* Do nothing. */
+
+ } else if (dns_name_endswith(name, "0.in-addr.arpa") > 0 ||
+ dns_name_equal(name, "255.255.255.255.in-addr.arpa") > 0 ||
+ dns_name_equal(name, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0 ||
+ dns_name_endswith(name, "invalid") > 0) {
+
+ nxdomain = true;
+ continue;
+
+ } else if (is_localhost(name)) {
r = synthesize_localhost_rr(m, key, ifindex, &answer);
if (r < 0)
return 0;
}
-static int on_stream_packet(DnsStream *s) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+static int on_stream_packet(DnsStream *s, DnsPacket *p) {
DnsTransaction *t;
assert(s);
-
- /* Take ownership of packet to be able to receive new packets */
- assert_se(p = dns_stream_take_read_packet(s));
+ assert(s->manager);
+ assert(p);
t = hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
if (t && t->stream == s) /* Validate that the stream we got this on actually is the stream the
if (fd < 0)
return fd;
- r = dns_stream_new(t->scope->manager, &s, type, t->scope->protocol, fd, &sa, stream_timeout_usec);
+ r = dns_stream_new(t->scope->manager, &s, type, t->scope->protocol, fd, &sa,
+ on_stream_packet, on_stream_complete, stream_timeout_usec);
if (r < 0)
return r;
t->server->stream = dns_stream_ref(s);
}
- s->complete = on_stream_complete;
- s->on_packet = on_stream_packet;
-
/* The interface index is difficult to determine if we are
* connecting to the local host, hence fill this in right away
* instead of determining it from the socket */
return ss;
}
+bool dnstls_stream_has_buffered_data(DnsStream *stream) {
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.session);
+
+ return gnutls_record_check_pending(stream->dnstls_data.session) > 0;
+}
+
void dnstls_server_free(DnsServer *server) {
assert(server);
return ss;
}
+bool dnstls_stream_has_buffered_data(DnsStream *stream) {
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.ssl);
+
+ return SSL_has_pending(stream->dnstls_data.ssl) > 0;
+}
+
void dnstls_server_free(DnsServer *server) {
assert(server);
#if ENABLE_DNS_OVER_TLS
+#include <stdbool.h>
#include <stdint.h>
typedef struct DnsServer DnsServer;
int dnstls_stream_shutdown(DnsStream *stream, int error);
ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count);
ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count);
+bool dnstls_stream_has_buffered_data(DnsStream *stream);
void dnstls_server_free(DnsServer *server);
r = dns_name_is_valid_ldh(name);
if (r <= 0) {
- log_warning_errno(r, "/etc/hosts:%u: hostname \"%s\" is not valid, ignoring.", nr, name);
+ if (r < 0)
+ log_warning_errno(r, "/etc/hosts:%u: Failed to check the validity of hostname \"%s\", ignoring: %m", nr, name);
+ else
+ log_warning("/etc/hosts:%u: hostname \"%s\" is not valid, ignoring.", nr, name);
continue;
}
#include "fileio.h"
#include "log-link.h"
#include "mkdir.h"
+#include "netif-util.h"
#include "parse-util.h"
#include "resolved-link.h"
#include "resolved-llmnr.h"
r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
- log_warning_errno(r, "Failed to allocate DNS scope: %m");
+ log_link_warning_errno(l, r, "Failed to allocate DNS scope, ignoring: %m");
}
} else
l->unicast_scope = dns_scope_free(l->unicast_scope);
if (!l->llmnr_ipv4_scope) {
r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
if (r < 0)
- log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m");
+ log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv4 scope, ignoring: %m");
}
} else
l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
if (!l->llmnr_ipv6_scope) {
r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
if (r < 0)
- log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m");
+ log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv6 scope, ignoring: %m");
}
} else
l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
if (!l->mdns_ipv4_scope) {
r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
if (r < 0)
- log_warning_errno(r, "Failed to allocate mDNS IPv4 scope: %m");
+ log_link_warning_errno(l, r, "Failed to allocate mDNS IPv4 scope, ignoring: %m");
}
} else
l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
if (!l->mdns_ipv6_scope) {
r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
if (r < 0)
- log_warning_errno(r, "Failed to allocate mDNS IPv6 scope: %m");
+ log_link_warning_errno(l, r, "Failed to allocate mDNS IPv6 scope, ignoring: %m");
}
} else
l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope);
if (l->mdns_ipv4_scope) {
r = dns_scope_add_dnssd_services(l->mdns_ipv4_scope);
if (r < 0)
- log_warning_errno(r, "Failed to add IPv4 DNS-SD services: %m");
+ log_link_warning_errno(l, r, "Failed to add IPv4 DNS-SD services, ignoring: %m");
}
if (l->mdns_ipv6_scope) {
r = dns_scope_add_dnssd_services(l->mdns_ipv6_scope);
if (r < 0)
- log_warning_errno(r, "Failed to add IPv6 DNS-SD services: %m");
+ log_link_warning_errno(l, r, "Failed to add IPv6 DNS-SD services, ignoring: %m");
}
} else {
if (l->mdns_ipv4_scope) {
r = dns_scope_remove_dnssd_services(l->mdns_ipv4_scope);
if (r < 0)
- log_warning_errno(r, "Failed to remove IPv4 DNS-SD services: %m");
+ log_link_warning_errno(l, r, "Failed to remove IPv4 DNS-SD services, ignoring: %m");
}
if (l->mdns_ipv6_scope) {
r = dns_scope_remove_dnssd_services(l->mdns_ipv6_scope);
if (r < 0)
- log_warning_errno(r, "Failed to remove IPv6 DNS-SD services: %m");
+ log_link_warning_errno(l, r, "Failed to remove IPv6 DNS-SD services, ignoring: %m");
}
}
}
(void) sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
(void) sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &l->operstate);
- if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
+ if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0 &&
+ !streq_ptr(l->ifname, n)) {
+ if (l->ifname)
+ log_link_debug(l, "Interface name change detected: %s -> %s", l->ifname, n);
+
r = free_and_strdup(&l->ifname, n);
if (r < 0)
return r;
}
- link_allocate_scopes(l);
- link_add_rrs(l, false);
-
return 0;
}
#if ! ENABLE_DNS_OVER_TLS
if (mode != DNS_OVER_TLS_NO)
- log_warning("DNS-over-TLS option for the link cannot be enabled or set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support.");
+ log_link_warning(l,
+ "DNS-over-TLS option for the link cannot be enabled or set to opportunistic "
+ "when systemd-resolved is built without DNS-over-TLS support. "
+ "Turning off DNS-over-TLS support.");
return;
#endif
#if !HAVE_OPENSSL_OR_GCRYPT
if (IN_SET(mode, DNSSEC_YES, DNSSEC_ALLOW_DOWNGRADE))
- log_warning("DNSSEC option for the link cannot be enabled or set to allow-downgrade when systemd-resolved is built without a cryptographic library. Turning off DNSSEC support.");
+ log_link_warning(l,
+ "DNSSEC option for the link cannot be enabled or set to allow-downgrade "
+ "when systemd-resolved is built without a cryptographic library. "
+ "Turning off DNSSEC support.");
return;
#endif
l->is_managed = true;
+ r = network_link_get_operational_state(l->ifindex, &l->networkd_operstate);
+ if (r < 0)
+ log_link_warning_errno(l, r, "Failed to read networkd's link operational state, ignoring: %m");
+
r = link_update_dns_servers(l);
if (r < 0)
log_link_warning_errno(l, r, "Failed to read DNS servers for the interface, ignoring: %m");
}
bool link_relevant(Link *l, int family, bool local_multicast) {
- _cleanup_free_ char *state = NULL;
LinkAddress *a;
assert(l);
* A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at
* least one routable address. */
- if (l->flags & (IFF_LOOPBACK|IFF_DORMANT))
+ if ((l->flags & (IFF_LOOPBACK | IFF_DORMANT)) != 0)
return false;
- if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP))
+ if (!FLAGS_SET(l->flags, IFF_UP | IFF_LOWER_UP))
return false;
- if (local_multicast) {
- if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST)
- return false;
- }
+ if (local_multicast &&
+ !FLAGS_SET(l->flags, IFF_MULTICAST))
+ return false;
- /* Check kernel operstate
- * https://www.kernel.org/doc/Documentation/networking/operstates.txt */
- if (!IN_SET(l->operstate, IF_OPER_UNKNOWN, IF_OPER_UP))
+ if (!netif_has_carrier(l->operstate, l->flags))
return false;
- (void) sd_network_link_get_operational_state(l->ifindex, &state);
- if (state && !STR_IN_SET(state, "unknown", "degraded", "degraded-carrier", "routable"))
+ if (l->is_managed &&
+ !IN_SET(l->networkd_operstate, LINK_OPERSTATE_DEGRADED_CARRIER, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_ROUTABLE))
return false;
LIST_FOREACH(addresses, a, l->addresses)
return s;
if (s)
- log_debug("Switching to DNS server %s for interface %s.", strna(dns_server_string_full(s)), l->ifname);
+ log_link_debug(l, "Switching to DNS server %s.", strna(dns_server_string_full(s)));
dns_server_unref(l->current_dns_server);
l->current_dns_server = dns_server_ref(s);
r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
if (r < 0)
- log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
+ log_link_warning_errno(a->link, r, "Failed to add A record to LLMNR zone, ignoring: %m");
r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
if (r < 0)
- log_warning_errno(r, "Failed to add IPv4 PTR record to LLMNR zone: %m");
+ log_link_warning_errno(a->link, r, "Failed to add IPv4 PTR record to LLMNR zone, ignoring: %m");
} else {
if (a->llmnr_address_rr) {
if (a->link->llmnr_ipv4_scope)
r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_address_rr, true);
if (r < 0)
- log_warning_errno(r, "Failed to add A record to MDNS zone: %m");
+ log_link_warning_errno(a->link, r, "Failed to add A record to MDNS zone, ignoring: %m");
r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_ptr_rr, false);
if (r < 0)
- log_warning_errno(r, "Failed to add IPv4 PTR record to MDNS zone: %m");
+ log_link_warning_errno(a->link, r, "Failed to add IPv4 PTR record to MDNS zone, ignoring: %m");
} else {
if (a->mdns_address_rr) {
if (a->link->mdns_ipv4_scope)
r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
if (r < 0)
- log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
+ log_link_warning_errno(a->link, r, "Failed to add AAAA record to LLMNR zone, ignoring: %m");
r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
if (r < 0)
- log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
+ log_link_warning_errno(a->link, r, "Failed to add IPv6 PTR record to LLMNR zone, ignoring: %m");
} else {
if (a->llmnr_address_rr) {
if (a->link->llmnr_ipv6_scope)
r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_address_rr, true);
if (r < 0)
- log_warning_errno(r, "Failed to add AAAA record to MDNS zone: %m");
+ log_link_warning_errno(a->link, r, "Failed to add AAAA record to MDNS zone, ignoring: %m");
r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_ptr_rr, false);
if (r < 0)
- log_warning_errno(r, "Failed to add IPv6 PTR record to MDNS zone: %m");
+ log_link_warning_errno(a->link, r, "Failed to add IPv6 PTR record to MDNS zone, ignoring: %m");
} else {
if (a->mdns_address_rr) {
if (a->link->mdns_ipv6_scope)
return;
fail:
- log_debug_errno(r, "Failed to update address RRs: %m");
+ log_link_debug_errno(a->link, r, "Failed to update address RRs, ignoring: %m");
}
int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
if (temp_path)
(void) unlink(temp_path);
- return log_error_errno(r, "Failed to save link data %s: %m", l->state_file);
+ return log_link_error_errno(l, r, "Failed to save link data %s: %m", l->state_file);
}
int link_load_user(Link *l) {
r = link_update_dns_server_one(l, word);
if (r < 0) {
- log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word);
+ log_link_debug_errno(l, r, "Failed to load DNS server '%s', ignoring: %m", word);
continue;
}
}
r = link_update_search_domain_one(l, n, is_route);
if (r < 0) {
- log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word);
+ log_link_debug_errno(l, r, "Failed to load search domain '%s', ignoring: %m", word);
continue;
}
}
return 0;
fail:
- return log_error_errno(r, "Failed to load link data %s: %m", l->state_file);
+ return log_link_error_errno(l, r, "Failed to load link data %s: %m", l->state_file);
}
void link_remove_user(Link *l) {
#include "sd-netlink.h"
#include "in-addr-util.h"
+#include "network-util.h"
#include "ratelimit.h"
#include "resolve-util.h"
DnsScope *mdns_ipv6_scope;
struct stat networkd_state_file_stat;
+ LinkOperationalState networkd_operstate;
bool is_managed;
char *ifname;
return m->llmnr_ipv6_udp_fd = TAKE_FD(s);
}
-static int on_llmnr_stream_packet(DnsStream *s) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+static int on_llmnr_stream_packet(DnsStream *s, DnsPacket *p) {
DnsScope *scope;
assert(s);
-
- p = dns_stream_take_read_packet(s);
+ assert(s->manager);
assert(p);
scope = manager_find_scope(s->manager, p);
} else
log_debug("Invalid LLMNR TCP packet, ignoring.");
- dns_stream_unref(s);
return 0;
}
return -errno;
}
- r = dns_stream_new(m, &stream, DNS_STREAM_LLMNR_RECV, DNS_PROTOCOL_LLMNR, cfd, NULL, DNS_STREAM_DEFAULT_TIMEOUT_USEC);
+ /* We don't configure a "complete" handler here, we rely on the default handler, thus freeing it */
+ r = dns_stream_new(m, &stream, DNS_STREAM_LLMNR_RECV, DNS_PROTOCOL_LLMNR, cfd, NULL,
+ on_llmnr_stream_packet, NULL, DNS_STREAM_DEFAULT_TIMEOUT_USEC);
if (r < 0) {
safe_close(cfd);
return r;
}
- stream->on_packet = on_llmnr_stream_packet;
- /* We don't configure a "complete" handler here, we rely on the default handler than simply drops the
- * reference to the stream, thus freeing it */
return 0;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "log.h"
+#include "process-util.h"
+#include "resolved-dns-packet.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-rr.h"
+#if ENABLE_DNS_OVER_TLS
+#include "resolved-dnstls.h"
+#endif
+#include "resolved-dns-server.h"
+#include "resolved-dns-stream.h"
+#include "resolved-manager.h"
+#include "sd-event.h"
+#include "sparse-endian.h"
+#include "tests.h"
+
+static struct sockaddr_in SERVER_ADDRESS;
+
+/* Bytes of the questions & answers used in the test, including TCP DNS 2-byte length prefix */
+static const uint8_t QUESTION_A[] = {
+ 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
+ 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01
+};
+static const uint8_t QUESTION_AAAA[] = {
+ 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
+ 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01
+};
+static const uint8_t ANSWER_A[] = {
+ 0x00, 0x2D, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
+ 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0,
+ 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x52, 0x8D, 0x00, 0x04, 0x5D, 0xB8, 0xD8, 0x22,
+};
+static const uint8_t ANSWER_AAAA[] = {
+ 0x00, 0x39, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
+ 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01, 0xC0,
+ 0x0C, 0x00, 0x1C, 0x00, 0x01, 0x00, 0x00, 0x54, 0x4B, 0x00, 0x10, 0x26, 0x06, 0x28, 0x00, 0x02,
+ 0x20, 0x00, 0x01, 0x02, 0x48, 0x18, 0x93, 0x25, 0xC8, 0x19, 0x46,
+};
+
+/**
+ * A mock TCP DNS server that asserts certain questions are received
+ * and replies with the same answer every time.
+ */
+static void receive_and_check_question(int fd, const uint8_t *expected_question,
+ size_t question_size) {
+ uint8_t *actual_question;
+ size_t n_read = 0;
+
+ actual_question = newa(uint8_t, question_size);
+ while (n_read < question_size) {
+ ssize_t r = read(fd, actual_question + n_read, question_size - n_read);
+ assert_se(r >= 0);
+ n_read += (size_t)r;
+ }
+ assert_se(n_read == question_size);
+
+ assert_se(memcmp(expected_question, actual_question, question_size) == 0);
+}
+
+static void send_answer(int fd, const uint8_t *answer, size_t answer_size) {
+ assert_se(write(fd, answer, answer_size) == (ssize_t)answer_size);
+}
+
+/* Sends two answers together in a single write operation,
+ * so they hopefully end up in a single TCP packet / TLS record */
+static void send_answers_together(int fd,
+ const uint8_t *answer1, size_t answer1_size,
+ const uint8_t *answer2, size_t answer2_size) {
+ uint8_t *answer;
+ size_t answer_size = answer1_size + answer2_size;
+
+ answer = newa(uint8_t, answer_size);
+ memcpy(answer, answer1, answer1_size);
+ memcpy(answer + answer1_size, answer2, answer2_size);
+ assert_se(write(fd, answer, answer_size) == (ssize_t)answer_size);
+}
+
+static void server_handle(int fd) {
+ receive_and_check_question(fd, QUESTION_A, sizeof(QUESTION_A));
+ send_answer(fd, ANSWER_A, sizeof(ANSWER_A));
+
+ receive_and_check_question(fd, QUESTION_AAAA, sizeof(QUESTION_AAAA));
+ send_answer(fd, ANSWER_AAAA, sizeof(ANSWER_AAAA));
+
+ receive_and_check_question(fd, QUESTION_A, sizeof(QUESTION_A));
+ receive_and_check_question(fd, QUESTION_AAAA, sizeof(QUESTION_AAAA));
+ send_answers_together(fd, ANSWER_A, sizeof(ANSWER_A),
+ ANSWER_AAAA, sizeof(ANSWER_AAAA));
+}
+
+static void *tcp_dns_server(void *p) {
+ _cleanup_close_ int bindfd = -1, acceptfd = -1;
+
+ assert_se((bindfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0);
+ assert_se(setsockopt(bindfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) >= 0);
+ assert_se(bind(bindfd, &SERVER_ADDRESS, sizeof(SERVER_ADDRESS)) >= 0);
+ assert_se(listen(bindfd, 1) >= 0);
+ assert_se((acceptfd = accept(bindfd, NULL, NULL)) >= 0);
+ server_handle(acceptfd);
+ return NULL;
+}
+
+#if ENABLE_DNS_OVER_TLS
+/*
+ * Spawns a DNS TLS server using the command line "openssl s_server" tool.
+ */
+static void *tls_dns_server(void *p) {
+ pid_t openssl_pid;
+ int r;
+ _cleanup_close_ int fd_server = -1, fd_tls = -1;
+ _cleanup_free_ char *cert_path = NULL, *key_path = NULL;
+ _cleanup_free_ char *ip_str = NULL, *bind_str = NULL;
+
+ assert_se(get_testdata_dir("test-resolve/selfsigned.cert", &cert_path) >= 0);
+ assert_se(get_testdata_dir("test-resolve/selfsigned.key", &key_path) >= 0);
+
+ assert_se(in_addr_to_string(SERVER_ADDRESS.sin_family,
+ &(union in_addr_union){.in = SERVER_ADDRESS.sin_addr},
+ &ip_str) >= 0);
+ assert_se(asprintf(&bind_str, "%s:%d", ip_str, be16toh(SERVER_ADDRESS.sin_port)) >= 0);
+
+ /* We will hook one of the socketpair ends to OpenSSL's TLS server
+ * stdin/stdout, so we will be able to read and write plaintext
+ * from the other end's file descriptor like an usual TCP server */
+ {
+ int fd[2];
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) >= 0);
+ fd_server = fd[0];
+ fd_tls = fd[1];
+ }
+
+ r = safe_fork_full("(test-resolved-stream-tls-openssl)", (int[]) { fd_server, fd_tls }, 2,
+ FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_REOPEN_LOG, &openssl_pid);
+ assert(r >= 0);
+ if (r == 0) {
+ /* Child */
+ assert_se(dup2(fd_tls, STDIN_FILENO) >= 0);
+ assert_se(dup2(fd_tls, STDOUT_FILENO) >= 0);
+ close(TAKE_FD(fd_server));
+ close(TAKE_FD(fd_tls));
+
+ execlp("openssl", "openssl", "s_server", "-accept", bind_str,
+ "-key", key_path, "-cert", cert_path,
+ "-quiet", "-naccept", "1", NULL);
+ log_error("exec failed, is something wrong with the 'openssl' command?");
+ _exit(EXIT_FAILURE);
+ } else {
+ pthread_mutex_t *server_lock = (pthread_mutex_t *)p;
+
+ server_handle(fd_server);
+
+ /* Once the test is done kill the TLS server to release the port */
+ assert_se(pthread_mutex_lock(server_lock) == 0);
+ assert_se(kill(openssl_pid, SIGTERM) >= 0);
+ assert_se(waitpid(openssl_pid, NULL, 0) >= 0);
+ assert_se(pthread_mutex_unlock(server_lock) == 0);
+ }
+
+ return NULL;
+}
+#endif
+
+static const char *TEST_DOMAIN = "example.com";
+static const uint64_t EVENT_TIMEOUT_USEC = 5 * 1000 * 1000;
+
+static void send_simple_question(DnsStream *stream, uint16_t type) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+
+ assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 0);
+ assert_se(question = dns_question_new(1));
+ assert_se(key = dns_resource_key_new(DNS_CLASS_IN, type, TEST_DOMAIN));
+ assert_se(dns_question_add(question, key, 0) >= 0);
+ assert_se(dns_packet_append_question(p, question) >= 0);
+ DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(question));
+ assert_se(dns_stream_write_packet(stream, p) >= 0);
+}
+
+static const size_t MAX_RECEIVED_PACKETS = 2;
+static DnsPacket *received_packets[2] = {};
+static size_t n_received_packets = 0;
+
+static int on_stream_packet(DnsStream *stream, DnsPacket *p) {
+ assert_se(n_received_packets < MAX_RECEIVED_PACKETS);
+ assert_se(received_packets[n_received_packets++] = dns_packet_ref(p));
+ return 0;
+}
+
+static void test_dns_stream(bool tls) {
+ Manager manager = {};
+ _cleanup_(dns_stream_unrefp) DnsStream *stream = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_close_ int clientfd = -1;
+ int r;
+
+ void *(*server_entrypoint)(void *);
+ pthread_t server_thread;
+ pthread_mutex_t server_lock;
+
+ log_info("test-resolved-stream: Started %s test", tls ? "TLS" : "TCP");
+
+#if ENABLE_DNS_OVER_TLS
+ if (tls) {
+ /* For TLS mode, use DNS_OVER_TLS_OPPORTUNISTIC instead of
+ * DNS_OVER_TLS_YES, just to make certificate validation more
+ * lenient, allowing us to use self-signed certificates.
+ * We never downgrade, everything we test always goes over TLS */
+ manager.dns_over_tls_mode = DNS_OVER_TLS_OPPORTUNISTIC;
+ }
+#endif
+
+ assert_se(sd_event_new(&event) >= 0);
+ manager.event = event;
+
+ /* Set up a mock DNS (over TCP or TLS) server */
+ server_entrypoint = tcp_dns_server;
+#if ENABLE_DNS_OVER_TLS
+ if (tls)
+ server_entrypoint = tls_dns_server;
+#endif
+ assert_se(pthread_mutex_init(&server_lock, NULL) == 0);
+ assert_se(pthread_mutex_lock(&server_lock) == 0);
+ assert_se(pthread_create(&server_thread, NULL, server_entrypoint, &server_lock) == 0);
+
+ /* Create a socket client and connect to the TCP or TLS server
+ * The server may not be up immediately, so try to connect a few times before failing */
+ assert_se((clientfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0);
+
+ for (int i = 0; i < 100; i++) {
+ r = connect(clientfd, &SERVER_ADDRESS, sizeof(SERVER_ADDRESS));
+ if (r >= 0)
+ break;
+ usleep(EVENT_TIMEOUT_USEC / 100);
+ }
+ assert_se(r >= 0);
+
+ /* systemd-resolved uses (and requires) the socket to be in nonblocking mode */
+ assert_se(fcntl(clientfd, F_SETFL, O_NONBLOCK) >= 0);
+
+ /* Initialize DNS stream */
+ assert_se(dns_stream_new(&manager, &stream, DNS_STREAM_LOOKUP, DNS_PROTOCOL_DNS,
+ TAKE_FD(clientfd), NULL, on_stream_packet, NULL,
+ DNS_STREAM_DEFAULT_TIMEOUT_USEC) >= 0);
+#if ENABLE_DNS_OVER_TLS
+ if (tls) {
+ DnsServer server = {
+ .manager = &manager,
+ .family = SERVER_ADDRESS.sin_family,
+ .address.in = SERVER_ADDRESS.sin_addr
+ };
+
+ assert_se(dnstls_manager_init(&manager) >= 0);
+ assert_se(dnstls_stream_connect_tls(stream, &server) >= 0);
+ }
+#endif
+
+ /* Test: Question of type A and associated answer */
+ log_info("test-resolved-stream: A record");
+ send_simple_question(stream, DNS_TYPE_A);
+ while (n_received_packets != 1)
+ assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1);
+ assert_se(DNS_PACKET_DATA(received_packets[0]));
+ assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]),
+ ANSWER_A + 2, sizeof(ANSWER_A) - 2) == 0);
+ dns_packet_unref(TAKE_PTR(received_packets[0]));
+ n_received_packets = 0;
+
+ /* Test: Question of type AAAA and associated answer */
+ log_info("test-resolved-stream: AAAA record");
+ send_simple_question(stream, DNS_TYPE_AAAA);
+ while (n_received_packets != 1)
+ assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1);
+ assert_se(DNS_PACKET_DATA(received_packets[0]));
+ assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]),
+ ANSWER_AAAA + 2, sizeof(ANSWER_AAAA) - 2) == 0);
+ dns_packet_unref(TAKE_PTR(received_packets[0]));
+ n_received_packets = 0;
+
+ /* Test: Question of type A and AAAA and associated answers
+ * Both answers are sent back in a single packet or TLS record
+ * (tests the fix of PR #22132: "Fix DoT timeout on multiple answer records") */
+ log_info("test-resolved-stream: A + AAAA record");
+ send_simple_question(stream, DNS_TYPE_A);
+ send_simple_question(stream, DNS_TYPE_AAAA);
+
+ while (n_received_packets != 2)
+ assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1);
+ assert_se(DNS_PACKET_DATA(received_packets[0]));
+ assert_se(DNS_PACKET_DATA(received_packets[1]));
+ assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]),
+ ANSWER_A + 2, sizeof(ANSWER_A) - 2) == 0);
+ assert_se(memcmp(DNS_PACKET_DATA(received_packets[1]),
+ ANSWER_AAAA + 2, sizeof(ANSWER_AAAA) - 2) == 0);
+ dns_packet_unref(TAKE_PTR(received_packets[0]));
+ dns_packet_unref(TAKE_PTR(received_packets[1]));
+ n_received_packets = 0;
+
+#if ENABLE_DNS_OVER_TLS
+ if (tls)
+ dnstls_manager_free(&manager);
+#endif
+
+ /* Stop the DNS server */
+ assert_se(pthread_mutex_unlock(&server_lock) == 0);
+ assert_se(pthread_join(server_thread, NULL) == 0);
+ assert_se(pthread_mutex_destroy(&server_lock) == 0);
+
+ log_info("test-resolved-stream: Finished %s test", tls ? "TLS" : "TCP");
+}
+
+int main(int argc, char **argv) {
+ SERVER_ADDRESS = (struct sockaddr_in) {
+ .sin_family = AF_INET,
+ .sin_port = htobe16(12345),
+ .sin_addr.s_addr = htobe32(INADDR_LOOPBACK)
+ };
+
+ test_setup_logging(LOG_DEBUG);
+
+ test_dns_stream(false);
+#if ENABLE_DNS_OVER_TLS
+ if (system("openssl version >/dev/null 2>&1") != 0)
+ return log_tests_skipped("Skipping TLS test since the 'openssl' command does not seem to be available");
+ test_dns_stream(true);
+#endif
+
+ return 0;
+}
e = getenv("TERM");
if (e) {
- char *n;
+ _cleanup_free_ char *n = NULL;
+
+ n = strjoin("TERM=", e);
+ if (!n)
+ return log_oom();
- n = strjoina("TERM=", e);
r = sd_bus_message_append(m,
"(sv)",
"Environment", "as", 1, n);
int boot_entries_augment_from_loader(
BootConfig *config,
- char **found_by_loader,
- bool only_auto) {
+ char **found_by_loader) {
static const char *const title_table[] = {
/* Pretty names for a few well-known automatically discovered entries. */
if (boot_config_has_entry(config, *i))
continue;
- if (only_auto && !startswith(*i, "auto-"))
+ /*
+ * consider the 'auto-' entries only, because the others
+ * ones are detected scanning the 'esp' and 'xbootldr'
+ * directories by boot_entries_load_config()
+ */
+ if (!startswith(*i, "auto-"))
continue;
c = strdup(*i);
void boot_config_free(BootConfig *config);
int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config);
int boot_entries_load_config_auto(const char *override_esp_path, const char *override_xbootldr_path, BootConfig *config);
-int boot_entries_augment_from_loader(BootConfig *config, char **list, bool only_auto);
+int boot_entries_augment_from_loader(BootConfig *config, char **list);
static inline const char* boot_entry_title(const BootEntry *entry) {
return entry->show_title ?: entry->title ?: entry->id;
(void) bpf_program_cgroup_detach(p);
safe_close(p->kernel_fd);
+ free(p->prog_name);
free(p->instructions);
free(p->attached_path);
return RET_NERRNO(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)));
}
-int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
+int bpf_program_new(uint32_t prog_type, const char *prog_name, BPFProgram **ret) {
_cleanup_(bpf_program_freep) BPFProgram *p = NULL;
+ _cleanup_free_ char *name = NULL;
+
+ if (prog_name) {
+ if (strlen(prog_name) >= BPF_OBJ_NAME_LEN)
+ return -ENAMETOOLONG;
+
+ name = strdup(prog_name);
+ if (!name)
+ return -ENOMEM;
+ }
p = new(BPFProgram, 1);
if (!p)
*p = (BPFProgram) {
.prog_type = prog_type,
.kernel_fd = -1,
+ .prog_name = TAKE_PTR(name),
};
*ret = TAKE_PTR(p);
attr.log_buf = PTR_TO_UINT64(log_buf);
attr.log_level = !!log_buf;
attr.log_size = log_size;
+ if (p->prog_name)
+ strncpy(attr.prog_name, p->prog_name, BPF_OBJ_NAME_LEN - 1);
p->kernel_fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
if (p->kernel_fd < 0)
/* The loaded BPF program, if loaded */
int kernel_fd;
uint32_t prog_type;
+ char *prog_name;
/* The code of it BPF program, if known */
size_t n_instructions;
uint32_t attached_flags;
};
-int bpf_program_new(uint32_t prog_type, BPFProgram **ret);
+int bpf_program_new(uint32_t prog_type, const char *prog_name, BPFProgram **ret);
int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret);
BPFProgram *bpf_program_free(BPFProgram *p);
if (impl != main_impl)
bus_introspect_implementation(&intro, impl);
- _cleanup_set_free_ Set *nodes = NULL;
+ _cleanup_ordered_set_free_ OrderedSet *nodes = NULL;
for (size_t i = 0; impl->children && impl->children[i]; i++) {
- r = set_put_strdup(&nodes, impl->children[i]->path);
+ r = ordered_set_put_strdup(&nodes, impl->children[i]->path);
if (r < 0)
return log_oom();
}
"ExecPaths",
"NoExecPaths",
"ExecSearchPath",
+ "ExtensionDirectories",
"ConfigurationDirectory",
"SupplementaryGroups",
"SystemCallArchitectures"))
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "bus-error.h"
#include "bus-map-properties.h"
#include "bus-wait-for-units.h"
#include "hashmap.h"
return 0;
}
-static int on_get_all_properties(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int on_get_all_properties(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
WaitForItem *item = userdata;
+ const sd_bus_error *e;
int r;
assert(item);
- if (sd_bus_error_is_set(error)) {
+ e = sd_bus_message_get_error(m);
+ if (e) {
BusWaitForUnits *d = item->parent;
d->has_failed = true;
- log_debug_errno(sd_bus_error_get_errno(error), "GetAll() failed for %s: %s",
- item->bus_path, error->message);
+ r = sd_bus_error_get_errno(e);
+ log_debug_errno(r, "GetAll() failed for %s: %s",
+ item->bus_path, bus_error_message(e, r));
call_unit_callback_and_wait(d, item, false);
bus_wait_for_units_check_ready(d);
return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
}
+static void config_section_hash_func(const ConfigSection *c, struct siphash *state) {
+ siphash24_compress_string(c->filename, state);
+ siphash24_compress(&c->line, sizeof(c->line), state);
+}
+
+static int config_section_compare_func(const ConfigSection *x, const ConfigSection *y) {
+ int r;
+
+ r = strcmp(x->filename, y->filename);
+ if (r != 0)
+ return r;
+
+ return CMP(x->line, y->line);
+}
+
+DEFINE_HASH_OPS(config_section_hash_ops, ConfigSection, config_section_hash_func, config_section_compare_func);
+
+int config_section_new(const char *filename, unsigned line, ConfigSection **s) {
+ ConfigSection *cs;
+
+ cs = malloc0(offsetof(ConfigSection, filename) + strlen(filename) + 1);
+ if (!cs)
+ return -ENOMEM;
+
+ strcpy(cs->filename, filename);
+ cs->line = line;
+
+ *s = TAKE_PTR(cs);
+
+ return 0;
+}
+
+unsigned hashmap_find_free_section_line(Hashmap *hashmap) {
+ ConfigSection *cs;
+ unsigned n = 0;
+ void *entry;
+
+ HASHMAP_FOREACH_KEY(entry, cs, hashmap)
+ if (n < cs->line)
+ n = cs->line;
+
+ return n + 1;
+}
+
#define DEFINE_PARSER(type, vartype, conv_func) \
DEFINE_CONFIG_PARSE_PTR(config_parse_##type, conv_func, vartype, "Failed to parse " #type " value")
void *userdata,
Hashmap **ret_stats_by_path); /* possibly NULL */
+typedef struct ConfigSection {
+ unsigned line;
+ bool invalid;
+ char filename[];
+} ConfigSection;
+
+static inline ConfigSection* config_section_free(ConfigSection *cs) {
+ return mfree(cs);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free);
+
+int config_section_new(const char *filename, unsigned line, ConfigSection **s);
+extern const struct hash_ops config_section_hash_ops;
+unsigned hashmap_find_free_section_line(Hashmap *hashmap);
+
+static inline bool section_is_invalid(ConfigSection *section) {
+ /* If this returns false, then it does _not_ mean the section is valid. */
+
+ if (!section)
+ return false;
+
+ return section->invalid;
+}
+
+#define DEFINE_SECTION_CLEANUP_FUNCTIONS(type, free_func) \
+ static inline type* free_func##_or_set_invalid(type *p) { \
+ assert(p); \
+ \
+ if (p->section) \
+ p->section->invalid = true; \
+ else \
+ free_func(p); \
+ return NULL; \
+ } \
+ DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \
+ DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);
+
CONFIG_PARSER_PROTOTYPE(config_parse_int);
CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
CONFIG_PARSER_PROTOTYPE(config_parse_long);
return 0;
}
+static int create_hole(int fd, off_t size) {
+ off_t offset;
+ off_t end;
+
+ offset = lseek(fd, 0, SEEK_CUR);
+ if (offset < 0)
+ return -errno;
+
+ end = lseek(fd, 0, SEEK_END);
+ if (end < 0)
+ return -errno;
+
+ /* If we're not at the end of the target file, try to punch a hole in the existing space using fallocate(). */
+
+ if (offset < end &&
+ fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, MIN(size, end - offset)) < 0 &&
+ !ERRNO_IS_NOT_SUPPORTED(errno))
+ return -errno;
+
+ if (end - offset >= size) {
+ /* If we've created the full hole, set the file pointer to the end of the hole we created and exit. */
+ if (lseek(fd, offset + size, SEEK_SET) < 0)
+ return -errno;
+
+ return 0;
+ }
+
+ /* If we haven't created the full hole, use ftruncate() to grow the file (and the hole) to the
+ * required size and move the file pointer to the end of the file. */
+
+ size -= end - offset;
+
+ if (ftruncate(fd, end + size) < 0)
+ return -errno;
+
+ if (lseek(fd, 0, SEEK_END) < 0)
+ return -errno;
+
+ return 0;
+}
+
int copy_bytes_full(
int fdf, int fdt,
uint64_t max_bytes,
/* If we're in a hole (current offset is not a data offset), create a hole of the
* same size in the target file. */
- if (e > c && lseek(fdt, e - c, SEEK_CUR) < 0)
- return -errno;
+ if (e > c) {
+ r = create_hole(fdt, e - c);
+ if (r < 0)
+ return r;
+ }
c = e; /* Set c to the start of the data segment. */
/* If we got os-release values from the caller, then we need to match them with the image's
* extension-release.d/ content. Return -EINVAL if there's any mismatch.
* First, check the distro ID. If that matches, then check the new SYSEXT_LEVEL value if
- * available, or else fallback to VERSION_ID. */
- if (required_host_os_release_id &&
- (required_host_os_release_version_id || required_host_os_release_sysext_level)) {
+ * available, or else fallback to VERSION_ID. If neither is present (eg: rolling release),
+ * then a simple match on the ID will be performed. */
+ if (required_host_os_release_id) {
_cleanup_strv_free_ char **extension_release = NULL;
r = load_extension_release_pairs(dest, dissected_image->image_name, &extension_release);
return 1;
}
+static inline bool dns_name_is_empty(const char *s) {
+ return isempty(s) || streq(s, ".");
+}
+
void dns_name_hash_func(const char *s, struct siphash *state);
int dns_name_compare_func(const char *a, const char *b);
extern const struct hash_ops dns_name_hash_ops;
}
int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]) {
+ /* Refuse invalid fds, regardless if fexecve() use is enabled or not */
+ if (executable_fd < 0)
+ return -EBADF;
+
+ /* Block any attempts on exploiting Linux' liberal argv[] handling, i.e. CVE-2021-4034 and suchlike */
+ if (isempty(executable) || strv_isempty(argv))
+ return -EINVAL;
+
#if ENABLE_FEXECVE
+
execveat(executable_fd, "", argv, envp, AT_EMPTY_PATH);
if (IN_SET(errno, ENOSYS, ENOENT) || ERRNO_IS_PRIVILEGE(errno))
#include "user-util.h"
static int specifier_prefix_and_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const UnitFileInstallInfo *i = userdata;
+ const UnitFileInstallInfo *i = ASSERT_PTR(userdata);
_cleanup_free_ char *prefix = NULL;
int r;
- assert(i);
-
r = unit_name_to_prefix_and_instance(i->name, &prefix);
if (r < 0)
return r;
}
static int specifier_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const UnitFileInstallInfo *i = userdata;
+ const UnitFileInstallInfo *i = ASSERT_PTR(userdata);
char *ans;
- assert(i);
-
if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE) && i->default_instance)
return unit_name_replace_instance(i->name, i->default_instance, ret);
}
static int specifier_prefix(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const UnitFileInstallInfo *i = userdata;
-
- assert(i);
+ const UnitFileInstallInfo *i = ASSERT_PTR(userdata);
return unit_name_to_prefix(i->name, ret);
}
static int specifier_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- const UnitFileInstallInfo *i = userdata;
+ const UnitFileInstallInfo *i = ASSERT_PTR(userdata);
char *instance;
int r;
- assert(i);
-
r = unit_name_to_instance(i->name, &instance);
if (r < 0)
return r;
char *dash;
int r;
+ assert(ret);
+
r = specifier_prefix(specifier, data, root, userdata, &prefix);
if (r < 0)
return r;
#define _DEFINE_MAIN_FUNCTION(intro, impl, ret) \
int main(int argc, char *argv[]) { \
int r; \
+ assert_se(argc > 0 && !isempty(argv[0])); \
save_argc_argv(argc, argv); \
intro; \
r = impl; \
net-condition.h
netif-naming-scheme.c
netif-naming-scheme.h
+ netif-sriov.c
+ netif-sriov.h
netif-util.c
netif-util.h
nscd-flush.h
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "device-util.h"
+#include "netlink-util.h"
+#include "netif-sriov.h"
+#include "parse-util.h"
+#include "set.h"
+#include "stdio-util.h"
+#include "string-util.h"
+
+static int sr_iov_new(SRIOV **ret) {
+ SRIOV *sr_iov;
+
+ assert(ret);
+
+ sr_iov = new(SRIOV, 1);
+ if (!sr_iov)
+ return -ENOMEM;
+
+ *sr_iov = (SRIOV) {
+ .vf = UINT32_MAX,
+ .vlan_proto = ETH_P_8021Q,
+ .vf_spoof_check_setting = -1,
+ .trust = -1,
+ .query_rss = -1,
+ .link_state = _SR_IOV_LINK_STATE_INVALID,
+ };
+
+ *ret = TAKE_PTR(sr_iov);
+
+ return 0;
+}
+
+static int sr_iov_new_static(OrderedHashmap **sr_iov_by_section, const char *filename, unsigned section_line, SRIOV **ret) {
+ _cleanup_(config_section_freep) ConfigSection *n = NULL;
+ _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL;
+ SRIOV *existing = NULL;
+ int r;
+
+ assert(sr_iov_by_section);
+ assert(filename);
+ assert(section_line > 0);
+ assert(ret);
+
+ r = config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ existing = ordered_hashmap_get(*sr_iov_by_section, n);
+ if (existing) {
+ *ret = existing;
+ return 0;
+ }
+
+ r = sr_iov_new(&sr_iov);
+ if (r < 0)
+ return r;
+
+ r = ordered_hashmap_ensure_put(sr_iov_by_section, &config_section_hash_ops, n, sr_iov);
+ if (r < 0)
+ return r;
+
+ sr_iov->section = TAKE_PTR(n);
+ sr_iov->sr_iov_by_section = *sr_iov_by_section;
+
+ *ret = TAKE_PTR(sr_iov);
+ return 0;
+}
+
+SRIOV *sr_iov_free(SRIOV *sr_iov) {
+ if (!sr_iov)
+ return NULL;
+
+ if (sr_iov->sr_iov_by_section && sr_iov->section)
+ ordered_hashmap_remove(sr_iov->sr_iov_by_section, sr_iov->section);
+
+ config_section_free(sr_iov->section);
+
+ return mfree(sr_iov);
+}
+
+int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req) {
+ int r;
+
+ assert(sr_iov);
+ assert(req);
+
+ r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_open_container(req, IFLA_VF_INFO);
+ if (r < 0)
+ return r;
+
+ if (!ether_addr_is_null(&sr_iov->mac)) {
+ struct ifla_vf_mac ivm = {
+ .vf = sr_iov->vf,
+ };
+
+ memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN);
+ r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac));
+ if (r < 0)
+ return r;
+ }
+
+ if (sr_iov->vf_spoof_check_setting >= 0) {
+ struct ifla_vf_spoofchk ivs = {
+ .vf = sr_iov->vf,
+ .setting = sr_iov->vf_spoof_check_setting,
+ };
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk));
+ if (r < 0)
+ return r;
+ }
+
+ if (sr_iov->query_rss >= 0) {
+ struct ifla_vf_rss_query_en ivs = {
+ .vf = sr_iov->vf,
+ .setting = sr_iov->query_rss,
+ };
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en));
+ if (r < 0)
+ return r;
+ }
+
+ if (sr_iov->trust >= 0) {
+ struct ifla_vf_trust ivt = {
+ .vf = sr_iov->vf,
+ .setting = sr_iov->trust,
+ };
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust));
+ if (r < 0)
+ return r;
+ }
+
+ if (sr_iov->link_state >= 0) {
+ struct ifla_vf_link_state ivl = {
+ .vf = sr_iov->vf,
+ .link_state = sr_iov->link_state,
+ };
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state));
+ if (r < 0)
+ return r;
+ }
+
+ if (sr_iov->vlan > 0) {
+ /* Because of padding, first the buffer must be initialized with 0. */
+ struct ifla_vf_vlan_info ivvi = {};
+ ivvi.vf = sr_iov->vf;
+ ivvi.vlan = sr_iov->vlan;
+ ivvi.qos = sr_iov->qos;
+ ivvi.vlan_proto = htobe16(sr_iov->vlan_proto);
+
+ r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info));
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sr_iov_get_num_vfs(sd_device *device, uint32_t *ret) {
+ const char *str;
+ uint32_t n;
+ int r;
+
+ assert(device);
+ assert(ret);
+
+ r = sd_device_get_sysattr_value(device, "device/sriov_numvfs", &str);
+ if (r < 0)
+ return r;
+
+ r = safe_atou32(str, &n);
+ if (r < 0)
+ return r;
+
+ *ret = n;
+ return 0;
+}
+
+int sr_iov_set_num_vfs(sd_device *device, uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) {
+ char val[DECIMAL_STR_MAX(uint32_t)];
+ const char *str;
+ int r;
+
+ assert(device);
+
+ if (num_vfs == UINT32_MAX) {
+ uint32_t current_num_vfs;
+ SRIOV *sr_iov;
+
+ /* If the number of virtual functions is not specified, then use the maximum number of VF + 1. */
+
+ num_vfs = 0;
+ ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section)
+ num_vfs = MAX(num_vfs, sr_iov->vf + 1);
+
+ if (num_vfs == 0) /* No VF is configured. */
+ return 0;
+
+ r = sr_iov_get_num_vfs(device, ¤t_num_vfs);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to get the current number of SR-IOV virtual functions: %m");
+
+ /* Enough VFs already exist. */
+ if (num_vfs <= current_num_vfs)
+ return 0;
+
+ } else if (num_vfs == 0) {
+ r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0");
+ if (r < 0)
+ log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute, ignoring: %m");
+
+ /* Gracefully handle the error in disabling VFs when the interface does not support SR-IOV. */
+ return r == -ENOENT ? 0 : r;
+ }
+
+ /* So, the interface does not have enough VFs. Before increasing the number of VFs, check the
+ * maximum allowed number of VFs from the sriov_totalvfs sysattr. Note that the sysattr
+ * currently exists only for PCI drivers. Hence, ignore -ENOENT.
+ * TODO: netdevsim provides the information in debugfs. */
+ r = sd_device_get_sysattr_value(device, "device/sriov_totalvfs", &str);
+ if (r >= 0) {
+ uint32_t max_num_vfs;
+
+ r = safe_atou32(str, &max_num_vfs);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to parse device/sriov_totalvfs sysfs attribute '%s': %m", str);
+
+ if (num_vfs > max_num_vfs)
+ return log_device_debug_errno(device, SYNTHETIC_ERRNO(ERANGE),
+ "Specified number of virtual functions is out of range. "
+ "The maximum allowed value is %"PRIu32".",
+ max_num_vfs);
+
+ } else if (r != -ENOENT) /* Currently, only PCI driver has the attribute. */
+ return log_device_debug_errno(device, r, "Failed to read device/sriov_totalvfs sysfs attribute: %m");
+
+ xsprintf(val, "%"PRIu32, num_vfs);
+ r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val);
+ if (r == -EBUSY) {
+ /* Some devices e.g. netdevsim refuse to set sriov_numvfs if it has non-zero value. */
+ r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0");
+ if (r >= 0)
+ r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val);
+ }
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute: %m");
+
+ log_device_debug(device, "device/sriov_numvfs sysfs attribute set to '%s'.", val);
+ return 0;
+}
+
+static int sr_iov_section_verify(uint32_t num_vfs, SRIOV *sr_iov) {
+ assert(sr_iov);
+
+ if (section_is_invalid(sr_iov->section))
+ return -EINVAL;
+
+ if (sr_iov->vf == UINT32_MAX)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: [SR-IOV] section without VirtualFunction= field configured. "
+ "Ignoring [SR-IOV] section from line %u.",
+ sr_iov->section->filename, sr_iov->section->line);
+
+ if (sr_iov->vf >= num_vfs)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: VirtualFunction= must be smaller than the value specified in SR-IOVVirtualFunctions=. "
+ "Ignoring [SR-IOV] section from line %u.",
+ sr_iov->section->filename, sr_iov->section->line);
+
+ return 0;
+}
+
+int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) {
+ _cleanup_hashmap_free_ Hashmap *hashmap = NULL;
+ SRIOV *sr_iov;
+ int r;
+
+ ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section) {
+ SRIOV *dup;
+
+ if (sr_iov_section_verify(num_vfs, sr_iov) < 0) {
+ sr_iov_free(sr_iov);
+ continue;
+ }
+
+ assert(sr_iov->vf < INT_MAX);
+
+ dup = hashmap_remove(hashmap, UINT32_TO_PTR(sr_iov->vf + 1));
+ if (dup) {
+ log_warning("%s: Conflicting [SR-IOV] section is specified at line %u and %u, "
+ "dropping the [SR-IOV] section specified at line %u.",
+ dup->section->filename, sr_iov->section->line,
+ dup->section->line, dup->section->line);
+ sr_iov_free(dup);
+ }
+
+ r = hashmap_ensure_put(&hashmap, NULL, UINT32_TO_PTR(sr_iov->vf + 1), sr_iov);
+ if (r < 0)
+ return log_oom();
+ assert(r > 0);
+ }
+
+ return 0;
+}
+
+int config_parse_sr_iov_uint32(
+ 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ OrderedHashmap **sr_iov_by_section = data;
+ uint32_t k;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue)) {
+ if (streq(lvalue, "VirtualFunction"))
+ sr_iov->vf = UINT32_MAX;
+ else if (streq(lvalue, "VLANId"))
+ sr_iov->vlan = 0;
+ else if (streq(lvalue, "QualityOfService"))
+ sr_iov->qos = 0;
+ else
+ assert_not_reached();
+
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (streq(lvalue, "VLANId")) {
+ if (k == 0 || k > 4095) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k);
+ return 0;
+ }
+ sr_iov->vlan = k;
+ } else if (streq(lvalue, "VirtualFunction")) {
+ if (k >= INT_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k);
+ return 0;
+ }
+ sr_iov->vf = k;
+ } else if (streq(lvalue, "QualityOfService"))
+ sr_iov->qos = k;
+ else
+ assert_not_reached();
+
+ TAKE_PTR(sr_iov);
+ return 0;
+}
+
+int config_parse_sr_iov_vlan_proto(
+ 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ OrderedHashmap **sr_iov_by_section = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue) || streq(rvalue, "802.1Q"))
+ sr_iov->vlan_proto = ETH_P_8021Q;
+ else if (streq(rvalue, "802.1ad"))
+ sr_iov->vlan_proto = ETH_P_8021AD;
+ else {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(sr_iov);
+ return 0;
+}
+
+int config_parse_sr_iov_link_state(
+ 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ OrderedHashmap **sr_iov_by_section = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
+ * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
+
+ if (isempty(rvalue)) {
+ sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ if (streq(rvalue, "auto")) {
+ sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE;
+ TAKE_PTR(sr_iov);
+ return 0;
+}
+
+int config_parse_sr_iov_boolean(
+ 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ OrderedHashmap **sr_iov_by_section = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue)) {
+ if (streq(lvalue, "MACSpoofCheck"))
+ sr_iov->vf_spoof_check_setting = -1;
+ else if (streq(lvalue, "QueryReceiveSideScaling"))
+ sr_iov->query_rss = -1;
+ else if (streq(lvalue, "Trust"))
+ sr_iov->trust = -1;
+ else
+ assert_not_reached();
+
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (streq(lvalue, "MACSpoofCheck"))
+ sr_iov->vf_spoof_check_setting = r;
+ else if (streq(lvalue, "QueryReceiveSideScaling"))
+ sr_iov->query_rss = r;
+ else if (streq(lvalue, "Trust"))
+ sr_iov->trust = r;
+ else
+ assert_not_reached();
+
+ TAKE_PTR(sr_iov);
+ return 0;
+}
+
+int config_parse_sr_iov_mac(
+ 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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ OrderedHashmap **sr_iov_by_section = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue)) {
+ sr_iov->mac = ETHER_ADDR_NULL;
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ r = parse_ether_addr(rvalue, &sr_iov->mac);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(sr_iov);
+ return 0;
+}
+
+int config_parse_sr_iov_num_vfs(
+ 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) {
+
+ uint32_t n, *num_vfs = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ *num_vfs = UINT32_MAX;
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &n);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (n > INT_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "The number of SR-IOV virtual functions is too large. It must be equal to "
+ "or smaller than 2147483647. Ignoring assignment: %"PRIu32, n);
+ return 0;
+ }
+
+ *num_vfs = n;
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <linux/if_link.h>
+
+#include "sd-device.h"
+
+#include "conf-parser.h"
+#include "ether-addr-util.h"
+#include "hashmap.h"
+
+typedef enum SRIOVLinkState {
+ SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO,
+ SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE,
+ SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE,
+ _SR_IOV_LINK_STATE_MAX,
+ _SR_IOV_LINK_STATE_INVALID = -EINVAL,
+} SRIOVLinkState;
+
+typedef struct SRIOV {
+ ConfigSection *section;
+ OrderedHashmap *sr_iov_by_section;
+
+ uint32_t vf; /* 0 - 2147483646 */
+ uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */
+ uint32_t qos;
+ uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */
+ int vf_spoof_check_setting;
+ int query_rss;
+ int trust;
+ SRIOVLinkState link_state;
+ struct ether_addr mac;
+} SRIOV;
+
+SRIOV *sr_iov_free(SRIOV *sr_iov);
+int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req);
+int sr_iov_get_num_vfs(sd_device *device, uint32_t *ret);
+int sr_iov_set_num_vfs(sd_device *device, uint32_t num_vfs, OrderedHashmap *sr_iov_by_section);
+int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_section);
+
+DEFINE_SECTION_CLEANUP_FUNCTIONS(SRIOV, sr_iov_free);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_num_vfs);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <linux/if.h>
#include <linux/if_arp.h>
#include "arphrd-util.h"
#include "sparse-endian.h"
#include "strv.h"
+bool netif_has_carrier(uint8_t operstate, unsigned flags) {
+ /* see Documentation/networking/operstates.txt in the kernel sources */
+
+ if (operstate == IF_OPER_UP)
+ return true;
+
+ if (operstate != IF_OPER_UNKNOWN)
+ return false;
+
+ /* operstate may not be implemented, so fall back to flags */
+ return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) &&
+ !FLAGS_SET(flags, IFF_DORMANT);
+}
+
int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) {
const char *t;
char *p;
#include "ether-addr-util.h"
+bool netif_has_carrier(uint8_t operstate, unsigned flags);
int net_get_type_string(sd_device *device, uint16_t iftype, char **ret);
const char *net_get_persistent_name(sd_device *device);
int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret);
void pager_open(PagerFlags flags) {
_cleanup_close_pair_ int fd[2] = { -1, -1 }, exe_name_pipe[2] = { -1, -1 };
_cleanup_strv_free_ char **pager_args = NULL;
+ _cleanup_free_ char *l = NULL;
const char *pager, *less_opts;
int r;
less_opts = getenv("SYSTEMD_LESS");
if (!less_opts)
less_opts = "FRSXMK";
- if (flags & PAGER_JUMP_TO_END)
- less_opts = strjoina(less_opts, " +G");
+ if (flags & PAGER_JUMP_TO_END) {
+ l = strjoin(less_opts, " +G");
+ if (!l)
+ return (void) log_oom();
+ less_opts = l;
+ }
/* We set SIGINT as PR_DEATHSIG signal here, to match the "K" parameter we set in $LESS, which enables SIGINT behaviour. */
r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGINT|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pager_pid);
assert(label);
- if (fsetfilecon(fd, label) < 0)
+ if (setfilecon(FORMAT_PROC_FD_PATH(fd), label) < 0)
return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, strna(path));
#endif
return 0;
return 0;
if (label)
- r = fsetxattr(fd, smack_attr_to_string(attr), label, strlen(label), 0);
+ r = setxattr(FORMAT_PROC_FD_PATH(fd), smack_attr_to_string(attr), label, strlen(label), 0);
else
- r = fremovexattr(fd, smack_attr_to_string(attr));
+ r = removexattr(FORMAT_PROC_FD_PATH(fd), smack_attr_to_string(attr));
if (r < 0)
return -errno;
#include "id128-util.h"
#include "macro.h"
#include "os-util.h"
+#include "path-util.h"
#include "specifier.h"
#include "string-util.h"
#include "strv.h"
int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret) {
_cleanup_free_ char *result = NULL;
bool percent = false;
- const char *f;
size_t l;
char *t;
int r;
return -ENOMEM;
t = result;
- for (f = text; *f != '\0'; f++, l--) {
+ for (const char *f = text; *f != '\0'; f++, l--) {
if (percent) {
+ percent = false;
+
if (*f == '%')
*(t++) = '%';
else {
r = i->lookup(i->specifier, i->data, root, userdata, &w);
if (r < 0)
return r;
+ if (isempty(w))
+ continue;
j = t - result;
k = strlen(w);
*(t++) = *f;
}
}
-
- percent = false;
} else if (*f == '%')
percent = true;
else
/* Generic handler for simple string replacements */
int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- char *n;
+ char *n = NULL;
- n = strdup(strempty(data));
- if (!n)
- return -ENOMEM;
+ if (!isempty(data)) {
+ n = strdup(data);
+ if (!n)
+ return -ENOMEM;
+ }
*ret = n;
return 0;
}
+int specifier_real_path(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+ const char *path = data;
+
+ if (!path)
+ return -ENOENT;
+
+ return chase_symlinks(path, root, 0, ret, NULL);
+}
+
+int specifier_real_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ r = specifier_real_path(specifier, data, root, userdata, &path);
+ if (r < 0)
+ return r;
+
+ assert(path);
+ return path_extract_directory(path, ret);
+}
+
int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
sd_id128_t id;
char *n;
int specifier_kernel_release(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
struct utsname uts;
char *n;
- int r;
- r = uname(&uts);
- if (r < 0)
+ if (uname(&uts) < 0)
return -errno;
n = strdup(uts.release);
return 0;
}
-static int specifier_os_release_common(const char *field, const char *root, char **ret) {
- char *t = NULL;
- int r;
-
- r = parse_os_release(root, field, &t);
- if (r < 0)
- return r;
- if (!t) {
- /* fields in /etc/os-release might quite possibly be missing, even if everything is entirely
- * valid otherwise. Let's hence return "" in that case. */
- t = strdup("");
- if (!t)
- return -ENOMEM;
- }
-
- *ret = t;
- return 0;
-}
+/* Note: fields in /etc/os-release might quite possibly be missing, even if everything is entirely valid
+ * otherwise. We'll return an empty value or NULL in that case from the functions below. */
int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- return specifier_os_release_common("ID", root, ret);
+ return parse_os_release(root, "ID", ret);
}
int specifier_os_version_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- return specifier_os_release_common("VERSION_ID", root, ret);
+ return parse_os_release(root, "VERSION_ID", ret);
}
int specifier_os_build_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- return specifier_os_release_common("BUILD_ID", root, ret);
+ return parse_os_release(root, "BUILD_ID", ret);
}
int specifier_os_variant_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- return specifier_os_release_common("VARIANT_ID", root, ret);
+ return parse_os_release(root, "VARIANT_ID", ret);
}
int specifier_os_image_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- return specifier_os_release_common("IMAGE_ID", root, ret);
+ return parse_os_release(root, "IMAGE_ID", ret);
}
int specifier_os_image_version(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
- return specifier_os_release_common("IMAGE_VERSION", root, ret);
+ return parse_os_release(root, "IMAGE_VERSION", ret);
}
int specifier_group_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
}
int specifier_user_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-
if (asprintf(ret, UID_FMT, getuid()) < 0)
return -ENOMEM;
int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret);
int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_real_path(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_real_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret);
int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
int specifier_boot_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
+#include "id128-util.h"
#include "log.h"
#include "macro.h"
#include "parse-util.h"
void log_device_uevent(sd_device *device, const char *str) {
sd_device_action_t action = _SD_DEVICE_ACTION_INVALID;
+ sd_id128_t event_id = SD_ID128_NULL;
uint64_t seqnum = 0;
if (!DEBUG_LOGGING)
(void) sd_device_get_seqnum(device, &seqnum);
(void) sd_device_get_action(device, &action);
- log_device_debug(device, "%s%s(SEQNUM=%"PRIu64", ACTION=%s)",
+ (void) sd_device_get_trigger_uuid(device, &event_id);
+ log_device_debug(device, "%s%s(SEQNUM=%"PRIu64", ACTION=%s%s%s)",
strempty(str), isempty(str) ? "" : " ",
- seqnum, strna(device_action_to_string(action)));
+ seqnum, strna(device_action_to_string(action)),
+ sd_id128_is_null(event_id) ? "" : ", UUID=",
+ sd_id128_is_null(event_id) ? "" : id128_to_uuid_string(event_id, (char[ID128_UUID_STRING_MAX]){}));
}
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
#include <fcntl.h>
#include <getopt.h>
+#include <linux/loop.h>
#include <sys/mount.h>
#include <unistd.h>
if (verity_settings.data_path)
flags |= DISSECT_IMAGE_NO_PARTITION_TABLE;
- r = loop_device_make_by_path(img->path, O_RDONLY, 0, &d);
+ r = loop_device_make_by_path(
+ img->path,
+ O_RDONLY,
+ FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+ &d);
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device for %s: %m", img->path);
int halt_main(void) {
int r;
- r = logind_check_inhibitors(arg_action);
- if (r < 0)
- return r;
-
- /* Delayed shutdown requested, and was successful */
- if (arg_when > 0 && logind_schedule_shutdown() == 0)
- return 0;
-
- /* No delay, or logind failed or is not at all available */
- if (geteuid() != 0) {
- if (arg_dry_run || arg_force > 0) {
- (void) must_be_root();
- return -EPERM;
- }
+ /* always try logind first */
+ if (arg_when > 0)
+ r = logind_schedule_shutdown();
+ else {
+ r = logind_check_inhibitors(arg_action);
+ if (r < 0)
+ return r;
- /* Try logind if we are a normal user and no special mode applies. Maybe polkit allows us to
- * shutdown the machine. */
- if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT, ACTION_KEXEC, ACTION_HALT)) {
- r = logind_reboot(arg_action);
- if (r >= 0)
- return r;
- if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
- /* Requested operation is not supported on the local system or already in
- * progress */
- return r;
-
- /* on all other errors, try low-level operation */
- }
+ r = logind_reboot(arg_action);
}
+ if (r >= 0)
+ return r;
+ if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
+ /* Requested operation requires auth, is not supported on the local system or already in
+ * progress */
+ return r;
+ /* on all other errors, try low-level operation */
/* In order to minimize the difference between operation with and without logind, we explicitly
* enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */
if (!arg_dry_run && !arg_force)
return start_with_fallback();
- assert(geteuid() == 0);
+ if (geteuid() != 0) {
+ (void) must_be_root();
+ return -EPERM;
+ }
if (!arg_no_wtmp) {
if (sd_booted() > 0)
r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when);
if (r < 0)
- return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r));
+ return log_warning_errno(r, "Failed to schedule shutdown: %s", bus_error_message(&error, r));
if (!arg_quiet)
logind_show_shutdown();
r = logind_reboot(a);
if (r >= 0)
return r;
- if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
- /* Requested operation is not supported or already in progress */
+ if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
+ /* Requested operation requires auth, is not supported or already in progress */
return r;
/* On all other errors, try low-level operation. In order to minimize the difference
assert_se(set_unit_path(unit_dir) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
- r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p);
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, "sd_trivial", &p);
assert_se(r == 0);
r = bpf_program_add_instructions(p, exit_insn, ELEMENTSOF(exit_insn));
if (r < 0)
return log_error_errno(r, "Failed to convert program to string");
- r = bpf_program_new(test_suite[i].prog_type, &prog);
+ r = bpf_program_new(test_suite[i].prog_type, "sd_trivial", &prog);
if (r < 0)
return log_error_errno(r, "Failed to create program '%s'", str);
char fn[] = "/var/tmp/test-copy-hole-fd-XXXXXX";
char fn_copy[] = "/var/tmp/test-copy-hole-fd-XXXXXX";
struct stat stat;
+ off_t blksz;
int r, fd, fd_copy;
+ char *buf;
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
return log_tests_skipped("Filesystem doesn't support hole punching");
assert_se(r >= 0);
- /* We need to make sure to create a large enough hole and to write some data after it, otherwise
- * filesystems (btrfs) might silently discard it. */
+ assert_se(fstat(fd, &stat) >= 0);
+ blksz = stat.st_blksize;
+ buf = alloca0(blksz);
- assert_se(lseek(fd, 1024 * 1024, SEEK_CUR) >= 0);
- assert_se(write(fd, "abc", strlen("abc")) >= 0);
+ /* We need to make sure to create hole in multiples of the block size, otherwise filesystems (btrfs)
+ * might silently truncate/extend the holes. */
+
+ assert_se(lseek(fd, blksz, SEEK_CUR) >= 0);
+ assert_se(write(fd, buf, blksz) >= 0);
+ assert_se(lseek(fd, 0, SEEK_END) == 2 * blksz);
+ /* Only ftruncate() can create holes at the end of a file. */
+ assert_se(ftruncate(fd, 3 * blksz) >= 0);
assert_se(lseek(fd, 0, SEEK_SET) >= 0);
assert_se(copy_bytes(fd, fd_copy, UINT64_MAX, COPY_HOLES) >= 0);
/* Test that the hole starts at the beginning of the file. */
assert_se(lseek(fd_copy, 0, SEEK_HOLE) == 0);
/* Test that the hole has the expected size. */
- assert_se(lseek(fd_copy, 0, SEEK_DATA) == 1024 * 1024);
+ assert_se(lseek(fd_copy, 0, SEEK_DATA) == blksz);
+ assert_se(lseek(fd_copy, blksz, SEEK_HOLE) == 2 * blksz);
+ assert_se(lseek(fd_copy, 2 * blksz, SEEK_DATA) < 0 && errno == ENXIO);
/* Test that the copied file has the correct size. */
assert_se(fstat(fd_copy, &stat) >= 0);
- assert_se(stat.st_size == 1024 * 1024 + strlen("abc"));
+ assert_se(stat.st_size == 3 * blksz);
close(fd);
close(fd_copy);
"a=a\n" \
"b=b\\\n" \
"c\n" \
- "d=d\\\n" \
- "e\\\n" \
- "f\n" \
+ "d= d\\\n" \
+ "e \\\n" \
+ "f \n" \
"g=g\\ \n" \
- "h=h\n" \
+ "h= ąęół\\ śćńźżµ \n" \
"i=i\\"
#define env_file_2 \
#define env_file_3 \
"#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
"#--nouser-config \\\n" \
- "normal=line"
-
-#define env_file_4 \
- "# Generated\n" \
- "\n" \
- "HWMON_MODULES=\"coretemp f71882fg\"\n" \
- "\n" \
- "# For compatibility reasons\n" \
- "\n" \
- "MODULE_0=coretemp\n" \
- "MODULE_1=f71882fg"
+ "normal=line \\\n" \
+ ";normal=ignored \\\n" \
+ "normal_ignored \\\n" \
+ "normal ignored \\\n"
+
+#define env_file_4 \
+ "# Generated\n" \
+ "\n" \
+ "HWMON_MODULES=\"coretemp f71882fg\"\n" \
+ "\n" \
+ "# For compatibility reasons\n" \
+ "\n" \
+ "MODULE_0=coretemp\n" \
+ "MODULE_1=f71882fg"
#define env_file_5 \
- "a=\n" \
+ "a=\n" \
"b="
+#define env_file_6 \
+ "a=\\ \\n \\t \\x \\y \\' \n" \
+ "b= \\$' \n" \
+ "c= ' \\n\\t\\$\\`\\\\\n" \
+ "' \n" \
+ "d= \" \\n\\t\\$\\`\\\\\n" \
+ "\" \n"
+
+
TEST(load_env_file_1) {
_cleanup_strv_free_ char **data = NULL;
int r;
assert_se(r == 0);
assert_se(streq(data[0], "a=a"));
assert_se(streq(data[1], "b=bc"));
- assert_se(streq(data[2], "d=def"));
+ assert_se(streq(data[2], "d=de f"));
assert_se(streq(data[3], "g=g "));
- assert_se(streq(data[4], "h=h"));
+ assert_se(streq(data[4], "h=ąęół śćńźżµ"));
assert_se(streq(data[5], "i=i"));
assert_se(data[6] == NULL);
}
assert_se(data[2] == NULL);
}
+TEST(load_env_file_6) {
+ _cleanup_strv_free_ char **data = NULL;
+ int r;
+
+ _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX";
+ _cleanup_close_ int fd;
+
+ fd = mkostemp_safe(name);
+ assert_se(fd >= 0);
+ assert_se(write(fd, env_file_6, strlen(env_file_6)) == strlen(env_file_6));
+
+ r = load_env_file(NULL, name, &data);
+ assert_se(r == 0);
+ assert_se(streq(data[0], "a= n t x y '"));
+ assert_se(streq(data[1], "b=$'"));
+ assert_se(streq(data[2], "c= \\n\\t\\$\\`\\\\\n"));
+ assert_se(streq(data[3], "d= \\n\\t$`\\\n"));
+ assert_se(data[4] == NULL);
+}
+
TEST(write_and_load_env_file) {
const char *v;
assert_se(streq(passenv[0], "normal_name"));
}
+TEST(config_parse_unit_env_file) {
+ /* int config_parse_unit_env_file(
+ 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_(manager_freep) Manager *m = NULL;
+ Unit *u;
+ _cleanup_strv_free_ char **files = NULL;
+ int r;
+
+ r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
+ if (manager_errno_skip_test(r)) {
+ log_notice_errno(r, "Skipping test: manager_new: %m");
+ return;
+ }
+
+ assert_se(r >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+
+ assert_se(u = unit_new(m, sizeof(Service)));
+ assert_se(unit_add_name(u, "foobar.service") == 0);
+
+ r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+ "EnvironmentFile", 0, "not-absolute",
+ &files, u);
+ assert_se(r == 0);
+ assert_se(strv_isempty(files));
+
+ r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+ "EnvironmentFile", 0, "/absolute1",
+ &files, u);
+ assert_se(r == 0);
+ assert_se(strv_length(files) == 1);
+
+ r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+ "EnvironmentFile", 0, "/absolute2",
+ &files, u);
+ assert_se(r == 0);
+ assert_se(strv_length(files) == 2);
+ assert_se(streq(files[0], "/absolute1"));
+ assert_se(streq(files[1], "/absolute2"));
+
+ r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+ "EnvironmentFile", 0, "",
+ &files, u);
+ assert_se(r == 0);
+ assert_se(strv_isempty(files));
+
+ r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+ "EnvironmentFile", 0, "/path/%n.conf",
+ &files, u);
+ assert_se(r == 0);
+ assert_se(strv_length(files) == 1);
+ assert_se(streq(files[0], "/path/foobar.service.conf"));
+}
+
TEST(unit_dump_config_items) {
unit_dump_config_items(stdout);
}
NULL,
NULL,
NULL,
+ NULL,
NULL);
assert_se(r == 0);
NULL,
NULL,
NULL,
+ NULL,
NULL);
if (r < 0) {
log_error_errno(r, "Failed to set up namespace: %m");
#include "alloc-util.h"
#include "log.h"
#include "specifier.h"
+#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
static const Specifier table[] = {
{ 'X', specifier_string, (char*) "AAAA" },
{ 'Y', specifier_string, (char*) "BBBB" },
+ { 'e', specifier_string, NULL },
COMMON_SYSTEM_SPECIFIERS,
{}
};
_cleanup_free_ char *w = NULL;
int r;
- r = specifier_printf("xxx a=%X b=%Y yyy", SIZE_MAX, table, NULL, NULL, &w);
+ r = specifier_printf("xxx a=%X b=%Y e=%e yyy", SIZE_MAX, table, NULL, NULL, &w);
assert_se(r >= 0);
assert_se(w);
puts(w);
- assert_se(streq(w, "xxx a=AAAA b=BBBB yyy"));
+ assert_se(streq(w, "xxx a=AAAA b=BBBB e= yyy"));
free(w);
- r = specifier_printf("machine=%m, boot=%b, host=%H, version=%v, arch=%a", SIZE_MAX, table, NULL, NULL, &w);
+ r = specifier_printf("machine=%m, boot=%b, host=%H, version=%v, arch=%a, empty=%e", SIZE_MAX, table, NULL, NULL, &w);
assert_se(r >= 0);
assert_se(w);
puts(w);
w = mfree(w);
- specifier_printf("os=%o, os-version=%w, build=%B, variant=%W", SIZE_MAX, table, NULL, NULL, &w);
+ specifier_printf("os=%o, os-version=%w, build=%B, variant=%W, empty=%e%e%e", SIZE_MAX, table, NULL, NULL, &w);
if (w)
puts(w);
}
+TEST(specifier_real_path) {
+ static const Specifier table[] = {
+ { 'p', specifier_string, "/dev/initctl" },
+ { 'y', specifier_real_path, "/dev/initctl" },
+ { 'Y', specifier_real_directory, "/dev/initctl" },
+ { 'w', specifier_real_path, "/dev/tty" },
+ { 'W', specifier_real_directory, "/dev/tty" },
+ {}
+ };
+
+ _cleanup_free_ char *w = NULL;
+ int r;
+
+ r = specifier_printf("p=%p y=%y Y=%Y w=%w W=%W", SIZE_MAX, table, NULL, NULL, &w);
+ assert_se(r >= 0 || r == -ENOENT);
+ assert_se(w || r == -ENOENT);
+ puts(strnull(w));
+
+ /* /dev/initctl should normally be a symlink to /run/initctl */
+ if (files_same("/dev/initctl", "/run/initctl", 0) > 0)
+ assert_se(streq(w, "p=/dev/initctl y=/run/initctl Y=/run w=/dev/tty W=/dev"));
+}
+
+TEST(specifier_real_path_missing_file) {
+ static const Specifier table[] = {
+ { 'p', specifier_string, "/dev/-no-such-file--" },
+ { 'y', specifier_real_path, "/dev/-no-such-file--" },
+ { 'Y', specifier_real_directory, "/dev/-no-such-file--" },
+ {}
+ };
+
+ _cleanup_free_ char *w = NULL;
+ int r;
+
+ r = specifier_printf("p=%p y=%y", SIZE_MAX, table, NULL, NULL, &w);
+ assert_se(r == -ENOENT);
+
+ r = specifier_printf("p=%p Y=%Y", SIZE_MAX, table, NULL, NULL, &w);
+ assert_se(r == -ENOENT);
+}
+
TEST(specifiers) {
for (const Specifier *s = specifier_table; s->specifier; s++) {
char spec[3];
install : true,
install_dir : udevlibexecdir)
- udev_prog_paths += {name : exe.full_path()}
+ udev_prog_paths += {name : exe}
endforeach
if install_sysconfdir_samples
#include "ethtool-util.h"
#include "link-config.h"
#include "net-condition.h"
+#include "netif-sriov.h"
#include "socket-util.h"
%}
struct ConfigPerfItem;
Link.TxCoalesceHighSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_high)
Link.TxMaxCoalescedHighFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_high)
Link.CoalescePacketRateSampleIntervalSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rate_sample_interval)
+Link.SR-IOVVirtualFunctions, config_parse_sr_iov_num_vfs, 0, offsetof(LinkConfig, sr_iov_num_vfs)
+SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.VLANProtocol, config_parse_sr_iov_vlan_proto, 0, offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.MACSpoofCheck, config_parse_sr_iov_boolean, 0, offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, 0, offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.Trust, config_parse_sr_iov_boolean, 0, offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, offsetof(LinkConfig, sr_iov_by_section)
#include "log-link.h"
#include "memory-util.h"
#include "net-condition.h"
+#include "netif-sriov.h"
#include "netif-util.h"
#include "netlink-util.h"
#include "parse-util.h"
free(config->wol_password_file);
erase_and_free(config->wol_password);
+ ordered_hashmap_free_with_destructor(config->sr_iov_by_section, sr_iov_free);
+
return mfree(config);
}
.txqueuelen = UINT32_MAX,
.coalesce.use_adaptive_rx_coalesce = -1,
.coalesce.use_adaptive_tx_coalesce = -1,
+ .sr_iov_num_vfs = UINT32_MAX,
};
for (i = 0; i < ELEMENTSOF(config->features); i++)
STRV_MAKE_CONST(filename),
(const char* const*) CONF_PATHS_STRV("systemd/network"),
dropin_dirname,
- "Match\0Link\0",
+ "Match\0"
+ "Link\0"
+ "SR-IOV\0",
config_item_perf_lookup, link_config_gperf_lookup,
CONFIG_PARSE_WARN, config, NULL);
if (r < 0)
if (r < 0)
return r;
+ r = sr_iov_drop_invalid_sections(config->sr_iov_num_vfs, config->sr_iov_by_section);
+ if (r < 0)
+ return r;
+
log_debug("Parsed configuration file %s", filename);
LIST_PREPEND(configs, ctx->configs, TAKE_PTR(config));
return 0;
}
+static int sr_iov_configure(Link *link, sd_netlink **rtnl, SRIOV *sr_iov) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ int r;
+
+ assert(link);
+ assert(rtnl);
+ assert(link->ifindex > 0);
+
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_rtnl_message_new_link(*rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return r;
+
+ r = sr_iov_set_netlink_message(sr_iov, req);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(*rtnl, req, 0, NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) {
+ SRIOV *sr_iov;
+ uint32_t n;
+ int r;
+
+ assert(link);
+ assert(link->config);
+ assert(link->device);
+
+ r = sr_iov_set_num_vfs(link->device, link->config->sr_iov_num_vfs, link->config->sr_iov_by_section);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to set the number of SR-IOV virtual functions, ignoring: %m");
+
+ if (ordered_hashmap_isempty(link->config->sr_iov_by_section))
+ return 0;
+
+ r = sr_iov_get_num_vfs(link->device, &n);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to get the number of SR-IOV virtual functions, ignoring [SR-IOV] sections: %m");
+ return 0;
+ }
+ if (n == 0) {
+ log_link_warning(link, "No SR-IOV virtual function exists, ignoring [SR-IOV] sections: %m");
+ return 0;
+ }
+
+ ORDERED_HASHMAP_FOREACH(sr_iov, link->config->sr_iov_by_section) {
+ if (sr_iov->vf >= n) {
+ log_link_warning(link, "SR-IOV virtual function %"PRIu32" does not exist, ignoring.", sr_iov->vf);
+ continue;
+ }
+
+ r = sr_iov_configure(link, rtnl, sr_iov);
+ if (r < 0)
+ log_link_warning_errno(link, r,
+ "Failed to configure SR-IOV virtual function %"PRIu32", ignoring: %m",
+ sr_iov->vf);
+ }
+
+ return 0;
+}
+
int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) {
int r;
if (r < 0)
return r;
+ r = link_apply_sr_iov_config(link, rtnl);
+ if (r < 0)
+ return r;
+
return 0;
}
#include "condition.h"
#include "conf-parser.h"
#include "ethtool-util.h"
+#include "hashmap.h"
#include "list.h"
#include "net-condition.h"
#include "netif-naming-scheme.h"
int autoneg_flow_control;
netdev_coalesce_param coalesce;
+ uint32_t sr_iov_num_vfs;
+ OrderedHashmap *sr_iov_by_section;
+
LIST_FIELDS(LinkConfig, configs);
};
static usec_t arg_wait_for_initialization_timeout = 0;
static bool skip_attribute(const char *name) {
+ assert(name);
+
/* Those are either displayed separately or should not be shown at all. */
return STR_IN_SET(name,
"uevent",
STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
+ assert(a);
+ assert(b);
+
return strcmp(a->name, b->name);
}
size_t n_items = 0;
int r;
+ assert(device);
+
value = NULL;
(void) sd_device_get_devpath(device, &value);
printf(" looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
sd_device *child, *parent;
int r;
+ assert(device);
+
printf("\n"
"Udevadm info starts with the device specified by the devpath and then\n"
"walks up the chain of parent devices. It prints for every device\n"
const char *str, *val;
int i;
+ assert(device);
+
(void) sd_device_get_devpath(device, &str);
printf("P: %s\n", str);
static int stat_device(const char *name, bool export, const char *prefix) {
struct stat statbuf;
+ assert(name);
+
if (stat(name, &statbuf) != 0)
return -errno;
}
static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
+ assert(dir);
+
if (depth <= 0)
return;
FOREACH_DIRENT_ALL(dent, dir, break) {
struct stat stats;
- if (dent->d_name[0] == '.')
+ if (dot_or_dot_dot(dent->d_name))
continue;
- if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
+ if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
continue;
if ((stats.st_mode & mask) != 0)
continue;
}
}
+/*
+ * Assume that dir is a directory with file names matching udev data base
+ * entries for devices in /run/udev/data (such as "b8:16"), and removes
+ * all files except those that haven't been deleted in /run/udev/data
+ * (i.e. they were skipped during db cleanup because of the db_persist flag).
+ */
+static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) {
+ assert(dir);
+ assert(datadir);
+
+ FOREACH_DIRENT_ALL(dent, dir, break) {
+ if (dot_or_dot_dot(dent->d_name))
+ continue;
+
+ if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
+ /* The corresponding udev database file still exists.
+ * Assuming the parsistent flag is set for the database. */
+ continue;
+
+ (void) unlinkat(dirfd(dir), dent->d_name, 0);
+ }
+}
+
+static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
+ assert(dir);
+ assert(datadir);
+
+ FOREACH_DIRENT_ALL(dent, dir, break) {
+ struct stat stats;
+
+ if (dot_or_dot_dot(dent->d_name))
+ continue;
+ if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
+ continue;
+ if (S_ISDIR(stats.st_mode)) {
+ _cleanup_closedir_ DIR *dir2 = NULL;
+
+ dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+ if (dir2)
+ cleanup_dir_after_db_cleanup(dir2, datadir);
+
+ (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
+ } else
+ (void) unlinkat(dirfd(dir), dent->d_name, 0);
+ }
+}
+
static void cleanup_db(void) {
- _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
+ _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL;
dir1 = opendir("/run/udev/data");
if (dir1)
dir2 = opendir("/run/udev/links");
if (dir2)
- cleanup_dir(dir2, 0, 2);
+ cleanup_dirs_after_db_cleanup(dir2, dir1);
dir3 = opendir("/run/udev/tags");
if (dir3)
- cleanup_dir(dir3, 0, 2);
+ cleanup_dirs_after_db_cleanup(dir3, dir1);
dir4 = opendir("/run/udev/static_node-tags");
if (dir4)
cleanup_dir(dir4, 0, 2);
- dir5 = opendir("/run/udev/watch");
- if (dir5)
- cleanup_dir(dir5, 0, 1);
+ /* Do not remove /run/udev/watch. It will be handled by udevd well on restart.
+ * And should not be removed by external program when udevd is running. */
}
static int query_device(QueryType query, sd_device* device) {
case QUERY_ALL:
return print_record(device);
- }
- assert_not_reached();
- return 0;
+ default:
+ assert_not_reached();
+ }
}
static int help(void) {
# Execute each currently defined function starting with "testcase_"
for testcase in "${TESTCASES[@]}"; do
- _image_cleanup
echo "------ $testcase: BEGIN ------"
# Note for my future frustrated self: `fun && xxx` (as well as ||, if, while,
# until, etc.) _DISABLES_ the `set -e` behavior in _ALL_ nested function
# So, be careful when adding clean up snippets in the testcase_*() functions -
# if the `test_run_one()` function isn't the last command, you have propagate
# the exit code correctly (e.g. `test_run_one() || return $?`, see below).
- ec=0
- "$testcase" "$test_id" || ec=$?
+
+ # FIXME: temporary workaround for intermittent fails in certain tests
+ # See: https://github.com/systemd/systemd/issues/21819
+ for ((_i = 0; _i < 3; _i++)); do
+ _image_cleanup
+ ec=0
+ "$testcase" "$test_id" && break || ec=$?
+ done
case $ec in
0)
passed+=("$testcase")
return 77
fi
+ local i
local qemu_opts=(
"-device megasas-gen2,id=scsi0"
"-device megasas-gen2,id=scsi1"
return 77
fi
+ local i
+ local qemu_opts=()
+
for i in {0..27}; do
qemu_opts+=(
"-device nvme,drive=nvme$i,serial=deadbeef$i,num_queues=8"
# and attach them to a virtio-scsi controller
local qemu_opts=("-device virtio-scsi-pci,id=scsi0,num_queues=4")
local diskpath="${TESTDIR:?}/namedpart0.img"
- local lodev qemu_timeout
+ local i lodev qemu_timeout
dd if=/dev/zero of="$diskpath" bs=1M count=18
lodev="$(losetup --show -f -P "$diskpath")"
fi
local qemu_opts=("-device ahci,id=ahci0")
- local diskpath
+ local diskpath i
# Attach 4 SATA disks to the VM (and set their model and serial fields
# to something predictable, so we can refer to them later)
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+TEST_DESCRIPTION="shutdown testing"
+IMAGE_NAME="shutdown"
+TEST_NO_QEMU=1
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+_ORIG_NSPAWN="${SYSTEMD_NSPAWN:?}"
+SYSTEMD_NSPAWN="${STATEDIR:?}/run-nspawn"
+
+setup_nspawn_root_hook() {
+ cat > "${STATEDIR:?}"/run-nspawn <<-EOF
+ #!/bin/bash
+ exec "${TEST_BASE_DIR:?}"/test-shutdown.py -- "$_ORIG_NSPAWN" "\$@"
+ exit 1
+ EOF
+ chmod 755 "${STATEDIR:?}"/run-nspawn
+}
+
+test_append_files() {
+ local workspace="${1:?}"
+ # prevent shutdown in test suite, the expect script does that manually.
+ rm "${workspace:?}/usr/lib/systemd/tests/testdata/units/end.service"
+ inst /usr/bin/screen
+ echo "PS1='screen\$WINDOW # '" >>"$workspace/root/.bashrc"
+ echo 'startup_message off' >"$workspace/etc/screenrc"
+ echo 'bell_msg ""' >>"$workspace/etc/screenrc"
+}
+
+do_test "$@"
TxCoalesceHighSec=
TxMaxCoalescedHighFrames=
CoalescePacketRateSampleIntervalSec=
+SR-IOVVirtualFunctions=
+[SR-IOV]
+VirtualFunction=
+MACSpoofCheck=
+VLANId=
+VLANProtocol=
+QualityOfService=
+QueryReceiveSideScaling=
+Trust=
+LinkState=
+MACAddress=
RootHash=
RootHashSignature=
RootVerity=
+ExtensionDirectories=
ExtensionImages=
RuntimeMaxSec=
SELinuxContextFromNet=
Environment=
EnvironmentFile=
ExecPaths=
+ExtensionDirectories=
ExtensionImages=
FinalKillSignal=
ForceUnmount=
ExecStop=
ExecStopPost=
ExitType=
+ExtensionDirectories=
ExtensionImages=
FailureAction=
FileDescriptorStoreMax=
ExecStartPre=
ExecStopPost=
ExecStopPre=
+ExtensionDirectories=
ExtensionImages=
FileDescriptorName=
FinalKillSignal=
Environment=
EnvironmentFile=
ExecPaths=
+ExtensionDirectories=
ExtensionImages=
FinalKillSignal=
Group=
############################################################
-if conf.get('HAVE_SYSV_COMPAT') == 1
- sysv_generator_test_py = find_program('sysv-generator-test.py')
- if want_tests != 'false'
- test('sysv-generator-test',
- sysv_generator_test_py)
- endif
-endif
-
-############################################################
-
if install_tests
install_data('run-unit-tests.py',
install_mode : 'rwxr-xr-x',
test(name,
udev_dmi_memory_id_test,
- args : [udev_prog_paths['dmi_memory_id'],
+ args : [udev_prog_paths['dmi_memory_id'].full_path(),
source,
- source + '.txt'])
+ source + '.txt'],
+ depends : udev_prog_paths['dmi_memory_id'])
endforeach
endif
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eo pipefail
-
-out="${1:?}"
-systemd_efi="${2:?}"
-boot_stub="${3:?}"
-splash_bmp="${4:?}"
-
-efi_dir="$(bootctl -p)"
-entry_dir="${efi_dir}/$(cat /etc/machine-id)/$(uname -r)"
-
-# create GPT table with EFI System Partition
-rm -f "$out"
-dd if=/dev/null of="$out" bs=1M seek=512 count=1 status=none
-parted --script "$out" "mklabel gpt" "mkpart ESP fat32 1MiB 511MiB" "set 1 boot on"
-
-# create FAT32 file system
-LOOP="$(losetup --show -f -P "$out")"
-mkfs.vfat -F32 "${LOOP}p1"
-mkdir -p mnt
-mount "${LOOP}p1" mnt
-
-mkdir -p mnt/EFI/{BOOT,systemd}
-cp "$systemd_efi" mnt/EFI/BOOT/BOOTX64.efi
-
-if [ -e /boot/shellx64.efi ]; then
- cp /boot/shellx64.efi mnt/
-fi
-
-mkdir mnt/EFI/Linux
-echo -n "foo=yes bar=no root=/dev/fakeroot debug rd.break=initqueue" >mnt/cmdline.txt
-objcopy \
- --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
- --add-section .cmdline=mnt/cmdline.txt --change-section-vma .cmdline=0x30000 \
- --add-section .splash="$splash_bmp" --change-section-vma .splash=0x40000 \
- --add-section .linux="${entry_dir}/linux" --change-section-vma .linux=0x2000000 \
- --add-section .initrd="${entry_dir}/initrd" --change-section-vma .initrd=0x3000000 \
- "$boot_stub" mnt/EFI/Linux/linux-test.efi
-
-# install entries
-mkdir -p mnt/loader/entries
-echo -e "timeout 3\n" > mnt/loader/loader.conf
-echo -e "title Test\nefi /test\n" > mnt/loader/entries/test.conf
-echo -e "title Test2\nlinux /test2\noptions option=yes word number=1000 more\n" > mnt/loader/entries/test2.conf
-echo -e "title Test3\nlinux /test3\n" > mnt/loader/entries/test3.conf
-echo -e "title Test4\nlinux /test4\n" > mnt/loader/entries/test4.conf
-echo -e "title Test5\nefi /test5\n" > mnt/loader/entries/test5.conf
-echo -e "title Test6\nlinux /test6\n" > mnt/loader/entries/test6.conf
-
-sync
-umount mnt
-rmdir mnt
-losetup -d "$LOOP"
oldinitdir="$initdir"
rm -rfv "$TESTDIR/minimal"
export initdir="$TESTDIR/minimal"
- mkdir -p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt"
+ # app0 will use TemporaryFileSystem=/var/lib, app1 will need the mount point in the base image
+ mkdir -p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt" "$initdir/var/lib/app1"
setup_basic_dirs
install_basic_tools
# Shellcheck treats [[ -v VAR ]] as an assignment to avoid a different
touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf"
touch "$initdir/opt/some_file"
echo MARKER=1 >>"$initdir/usr/lib/os-release"
- echo "PORTABLE_PREFIXES=app0 minimal" >>"$initdir/usr/lib/os-release"
- echo -e "[Service]\nExecStartPre=cat /usr/lib/os-release\nExecStart=sleep 120" >"$initdir/usr/lib/systemd/system/app0.service"
- cp "$initdir/usr/lib/systemd/system/app0.service" "$initdir/usr/lib/systemd/system/app0-foo.service"
+ echo "PORTABLE_PREFIXES=app0 minimal minimal-app0" >>"$initdir/usr/lib/os-release"
+ cat >"$initdir/usr/lib/systemd/system/minimal-app0.service" <<EOF
+[Service]
+ExecStartPre=cat /usr/lib/os-release
+ExecStart=sleep 120
+EOF
+ cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
- mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_0.raw"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_0.raw" -noappend
veritysetup format "$oldinitdir/usr/share/minimal_0.raw" "$oldinitdir/usr/share/minimal_0.verity" | \
grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_0.roothash"
sed -i "s/MARKER=1/MARKER=2/g" "$initdir/usr/lib/os-release"
- rm "$initdir/usr/lib/systemd/system/app0-foo.service"
- cp "$initdir/usr/lib/systemd/system/app0.service" "$initdir/usr/lib/systemd/system/app0-bar.service"
+ rm "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
+ cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-bar.service"
- mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend
veritysetup format "$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \
grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash"
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/script0.sh
+TemporaryFileSystem=/var/lib
+StateDirectory=app0
+RuntimeDirectory=app0
EOF
cat >"$initdir/opt/script0.sh" <<EOF
#!/bin/bash
set -e
test -e /usr/lib/os-release
+echo bar > \${STATE_DIRECTORY}/foo
cat /usr/lib/extension-release.d/extension-release.app0
EOF
chmod +x "$initdir/opt/script0.sh"
echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
- mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw" -noappend
export initdir="$TESTDIR/app1"
mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/script1.sh
+StateDirectory=app1
+RuntimeDirectory=app1
EOF
cat >"$initdir/opt/script1.sh" <<EOF
#!/bin/bash
set -e
test -e /usr/lib/os-release
+echo baz > \${STATE_DIRECTORY}/foo
cat /usr/lib/extension-release.d/extension-release.app2
EOF
chmod +x "$initdir/opt/script1.sh"
echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file"
- mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw" -noappend
)
}
mkdir -p "$initdir/etc/systemd/system/service.d/"
echo -e "[Service]\nProtectSystem=no\nProtectHome=no\n" >"$initdir/etc/systemd/system/service.d/gcov-override.conf"
fi
+
+ # If we're built with -Dportabled=false, tests with systemd-analyze
+ # --profile will fail. Since we need just the profile (text) files, let's
+ # copy them into the image if they don't exist there.
+ local portable_dir="${initdir:?}${ROOTLIBDIR:?}/portable"
+ if [[ ! -d "$portable_dir/profile/strict" ]]; then
+ dinfo "Couldn't find portable profiles in the test image"
+ dinfo "Copying them directly from the source tree"
+ mkdir -p "$portable_dir"
+ cp -frv "${SOURCE_DIR:?}/src/portable/profile" "$portable_dir"
+ fi
}
get_ldpath() {
fi
}
+setup_nspawn_root_hook() { :;}
+
setup_nspawn_root() {
if [ -z "${initdir}" ]; then
dfatal "\$initdir not defined"
ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root"
fi
+
+ setup_nspawn_root_hook
}
setup_basic_dirs() {
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=eni99np1
+
+[Network]
+Address=192.168.100.100/24
+IPv6AcceptRA=no
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Driver=netdevsim
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+AlternativeNamesPolicy=database onboard slot path mac
+MACAddressPolicy=persistent
+
+[SR-IOV]
+VirtualFunction=0
+VLANId=5
+VLANProtocol=802.1ad
+QualityOfService=1
+MACSpoofCheck=yes
+QueryReceiveSideScaling=yes
+Trust=yes
+LinkState=yes
+MACAddress=00:11:22:33:44:55
+
+[SR-IOV]
+VirtualFunction=1
+VLANId=6
+VLANProtocol=802.1Q
+QualityOfService=2
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=no
+MACAddress=00:11:22:33:44:56
+
+[SR-IOV]
+VirtualFunction=2
+VLANId=7
+QualityOfService=3
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=auto
+MACAddress=00:11:22:33:44:57
call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
rc = call('ip link prop add dev dummy98 altname hogehogehogehogehoge', stderr=subprocess.DEVNULL)
if rc == 0:
- rc = call('ip link show dev hogehogehogehogehoge', stderr=subprocess.DEVNULL)
+ rc = call('ip link show dev hogehogehogehogehoge', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if rc == 0:
supported = True
def check_link_attr(self, *args):
self.assertEqual(read_link_attr(*args[:-1]), args[-1]);
+ def wait_activated(self, link, state='down', timeout=20, fail_assert=True):
+ # wait for the interface is activated.
+ invocation_id = check_output('systemctl show systemd-networkd -p InvocationID --value')
+ needle = f'{link}: Bringing link {state}'
+ flag = state.upper()
+ for iteration in range(timeout+1):
+ output = check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id)
+ if needle in output and flag in check_output(f'ip link show {link}'):
+ return True
+ if iteration < timeout:
+ time.sleep(1)
+ if fail_assert:
+ self.fail(f'Timed out waiting for {link} activated.')
+ return False
+
def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
"""Wait for the link to reach the specified operstate and/or setup state.
'25-route-vrf.network',
'25-gateway-static.network',
'25-gateway-next-static.network',
- '25-sriov.network',
'25-sysctl-disable-ipv6.network',
'25-sysctl.network',
'25-test1.network',
self.wait_operstate('test1', 'routable')
def _test_activation_policy(self, test):
- self.setUp()
conffile = '25-activation-policy.network'
if test:
conffile = f'{conffile}.d/{test}.conf'
start_networkd()
always = test.startswith('always')
- if test == 'manual':
- initial_up = 'UP' in check_output('ip link show test1')
- else:
- initial_up = not test.endswith('down') # note: default is up
+ initial_up = test != 'manual' and not test.endswith('down') # note: default is up
expect_up = initial_up
next_up = not expect_up
+ if test.endswith('down'):
+ self.wait_activated('test1')
+
for iteration in range(4):
with self.subTest(iteration=iteration, expect_up=expect_up):
operstate = 'routable' if expect_up else 'off'
check_output('ip link set dev test1 down')
expect_up = initial_up if always else next_up
next_up = not next_up
-
- self.tearDown()
+ if always:
+ time.sleep(1)
def test_activation_policy(self):
for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
with self.subTest(test=test):
+ self.setUp()
self._test_activation_policy(test)
+ self.tearDown()
def _test_activation_policy_required_for_online(self, policy, required):
- self.setUp()
conffile = '25-activation-policy.network'
units = ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile]
if policy:
copy_unit_to_networkd_unit_path(*units, dropins=False)
start_networkd()
+ if policy.endswith('down'):
+ self.wait_activated('test1')
+
if policy.endswith('down') or policy == 'manual':
self.wait_operstate('test1', 'off', setup_state='configuring')
else:
yesno = 'yes' if expected else 'no'
self.assertRegex(output, f'Required For Online: {yesno}')
- self.tearDown()
-
def test_activation_policy_required_for_online(self):
for policy in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
for required in ['yes', 'no', '']:
with self.subTest(policy=policy, required=required):
+ self.setUp()
self._test_activation_policy_required_for_online(policy, required)
+ self.tearDown()
def test_domain(self):
copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network')
self.assertRegex(output, 'qdisc fq_pie 3a: root')
self.assertRegex(output, 'limit 200000p')
- @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
- def test_sriov(self):
- call('rmmod netdevsim', stderr=subprocess.DEVNULL)
- call('modprobe netdevsim', stderr=subprocess.DEVNULL)
- with open('/sys/bus/netdevsim/new_device', mode='w') as f:
- f.write('99 1')
-
- call('udevadm settle')
- call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
- with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
- f.write('3')
-
- copy_unit_to_networkd_unit_path('25-sriov.network')
- start_networkd()
- self.wait_online(['eni99np1:routable'])
-
- output = check_output('ip link show dev eni99np1')
- print(output)
- self.assertRegex(output,
- 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
- 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
- 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
- )
-
- call('rmmod netdevsim', stderr=subprocess.DEVNULL)
-
def test_wait_online_ipv4(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-with-ipv6-prefix.network', 'dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network')
start_networkd()
print(output)
self.assertIn('from all to 8.8.8.8 lookup 100', output)
+class NetworkdSRIOVTests(unittest.TestCase, Utilities):
+ units = [
+ '25-sriov-udev.network',
+ '25-sriov.link',
+ '25-sriov.network',
+ ]
+
+ def setUp(self):
+ stop_networkd(show_logs=False)
+ call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+
+ def tearDown(self):
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
+ call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+
+ @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
+ def test_sriov(self):
+ call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+
+ with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+ f.write('99 1')
+
+ call('udevadm settle')
+ call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
+ with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
+ f.write('3')
+
+ copy_unit_to_networkd_unit_path('25-sriov.network')
+ start_networkd()
+ self.wait_online(['eni99np1:routable'])
+
+ output = check_output('ip link show dev eni99np1')
+ print(output)
+ self.assertRegex(output,
+ 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+ 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+ 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
+ )
+
+ @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
+ def test_sriov_udev(self):
+ call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+
+ copy_unit_to_networkd_unit_path('25-sriov.link', '25-sriov-udev.network')
+ call('udevadm control --reload')
+
+ with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+ f.write('99 1')
+
+ start_networkd()
+ self.wait_online(['eni99np1:routable'])
+
+ output = check_output('ip link show dev eni99np1')
+ print(output)
+ self.assertRegex(output,
+ 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+ 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+ 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
+ )
+ self.assertNotIn('vf 3', output)
+ self.assertNotIn('vf 4', output)
+
+ with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
+ f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
+
+ call('udevadm control --reload')
+ call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+
+ output = check_output('ip link show dev eni99np1')
+ print(output)
+ self.assertRegex(output,
+ 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+ 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+ 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
+ 'vf 3'
+ )
+ self.assertNotIn('vf 4', output)
+
+ with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
+ f.write('[Link]\nSR-IOVVirtualFunctions=\n')
+
+ call('udevadm control --reload')
+ call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+
+ output = check_output('ip link show dev eni99np1')
+ print(output)
+ self.assertRegex(output,
+ 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+ 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+ 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
+ 'vf 3'
+ )
+ self.assertNotIn('vf 4', output)
+
+ with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
+ f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
+
+ call('udevadm control --reload')
+ call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+
+ output = check_output('ip link show dev eni99np1')
+ print(output)
+ self.assertRegex(output,
+ 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+ 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
+ )
+ self.assertNotIn('vf 2', output)
+ self.assertNotIn('vf 3', output)
+ self.assertNotIn('vf 4', output)
+
+ with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
+ f.write('[Link]\nSR-IOVVirtualFunctions=\n')
+
+ call('udevadm control --reload')
+ call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+
+ output = check_output('ip link show dev eni99np1')
+ print(output)
+ self.assertRegex(output,
+ 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+ 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+ 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
+ )
+ self.assertNotIn('vf 3', output)
+ self.assertNotIn('vf 4', output)
+
class NetworkdLLDPTests(unittest.TestCase, Utilities):
links = ['veth99']
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIFmzCCA4OgAwIBAgIUZlvbV3+2YGjHJDTW+u0XL/ypvsowDQYJKoZIhvcNAQEL
+BQAwXDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkRlbmlhbDEUMBIGA1UEBwwLU3By
+aW5nZmllbGQxDDAKBgNVBAoMA0RpczEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29t
+MCAXDTIyMDEyMzE0MjYyMloYDzMwMjEwNTI2MTQyNjIyWjBcMQswCQYDVQQGEwJV
+UzEPMA0GA1UECAwGRGVuaWFsMRQwEgYDVQQHDAtTcHJpbmdmaWVsZDEMMAoGA1UE
+CgwDRGlzMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQC14826tEEHKICM/AKOKsyBUDaa6Z6KqS927ifb43LJ
+fxtg8vW+vX9OGtje2qVAoI1UMSu4yttItSd9cjrnAFLPFvQGC8dFhn436ehLWiKP
+AP5KvhIQ3equ5fTicn+Hxdm7C3Um2SEEE347I/vArfzuNE7PIJ57sh7KeBHGCrU3
+6iPl1DkkUilbqJAcgFoepozx1SbPq4h8LsdqJDKg+XUtvtuUS850D5Hb5ErTc8NL
+VrA+urSIr+yIX4jAeLXbktNLGuAc3+cJTjwuWdDvP51yS0qpj459dFaRWQE9gu0b
+DkRwYLF7Mpel66B8TBkHAWBhSs+oLNnv//Zv945wbUkTK29N2VtSEI4pd/47nTX9
+MwGn4q/ZAjhI7JUN3LcRDsrLdVAUabbK/U+xkL/lOlRRBK/1iuLELkaJlMUiuqZh
+q3DvNjqeT5yY8GTU5iXoBcvY0lac3+zYaemTgD5cZfF4gpTflGfc5Gf+he6U3Dol
+TT+4JfMrw0YdbqsH4oyEtmLBfMvvp+PQysiOELSFbSAphZOOcy8QSzoRrniNynPd
+kM7kIM+0t2XUaz0lKtNuZSo9DnhTMvTLPnnbk5aJt5nPxPprcdqhcJhrHw7gVhBo
+EceYJmXGiJJMLYuBNymZ4u7YrBg0e0qO+Fi9a4Kfh/QNMq/6VrpWvycb9LtCLhU+
+qQIDAQABo1MwUTAdBgNVHQ4EFgQU3ugK1HtfPaq90JC5Qf5ekrn4uUcwHwYDVR0j
+BBgwFoAU3ugK1HtfPaq90JC5Qf5ekrn4uUcwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEATzNvQP+VNLY0qK5b/8j8C5PHB0WKsTHSVNOhITr0Bq67
+AeOq5Mh2qZvJd/QAPTb/Cg60802RLQxp6uhCcfMdxTXkz1mxq6dKEeDAu/zAZzXk
+WSpJl/VORnibjvXf2OS6ucb4KPOxfkYiD328CdSYBJapmQbnUmwZph2SO0bpY7K3
+EbTY9fIyabGMrjbXL5EGRvqA0NSnJHVUYx1h3b32PYKHrQKu6syCE4OrMY0yjdLH
+1WnHeC3iB02AFy7TTfmeUiMTxaiPXAPjBDDIQtv1GHt8GR7WHSD3seIhAu6Lzbyr
+0zrxk52C9v0YP1lgOwnvmQfbUSpWc29yhrXFkqkZToqbmYjNO55gPN8JA/2GrWan
+s8gQwQ8z+yWAqNJQA5S+9+hNlBlcq659gCjIxoyCmkol4EepwR1WWdZjs2I00FHk
+mQL1ig6H81rg/Bh2SraKR1tGdmjCNFi4RfWHsxCBcd1cGFeUIN+ygNmjXmzXJDFP
+5vUXL9J5iu+WD1rnwB2gPRSvZUrmKUZnOGk0/kt1RpgbcFdOza+6vZmB51fXZYpD
+YyvXHTbuHVOyXA160/Fmg6BNy5BfrTuXaZ3YVeZmvDf+ywVl2BFDQZDoLLQMIHzl
+L2DdMuhVmgITqx8ZtrSxqBxW0DQXFZiMT+sv81+o2SO5nDzSYjoXfQv/Xkgpx44=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN PRIVATE KEY-----
+MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC14826tEEHKICM
+/AKOKsyBUDaa6Z6KqS927ifb43LJfxtg8vW+vX9OGtje2qVAoI1UMSu4yttItSd9
+cjrnAFLPFvQGC8dFhn436ehLWiKPAP5KvhIQ3equ5fTicn+Hxdm7C3Um2SEEE347
+I/vArfzuNE7PIJ57sh7KeBHGCrU36iPl1DkkUilbqJAcgFoepozx1SbPq4h8Lsdq
+JDKg+XUtvtuUS850D5Hb5ErTc8NLVrA+urSIr+yIX4jAeLXbktNLGuAc3+cJTjwu
+WdDvP51yS0qpj459dFaRWQE9gu0bDkRwYLF7Mpel66B8TBkHAWBhSs+oLNnv//Zv
+945wbUkTK29N2VtSEI4pd/47nTX9MwGn4q/ZAjhI7JUN3LcRDsrLdVAUabbK/U+x
+kL/lOlRRBK/1iuLELkaJlMUiuqZhq3DvNjqeT5yY8GTU5iXoBcvY0lac3+zYaemT
+gD5cZfF4gpTflGfc5Gf+he6U3DolTT+4JfMrw0YdbqsH4oyEtmLBfMvvp+PQysiO
+ELSFbSAphZOOcy8QSzoRrniNynPdkM7kIM+0t2XUaz0lKtNuZSo9DnhTMvTLPnnb
+k5aJt5nPxPprcdqhcJhrHw7gVhBoEceYJmXGiJJMLYuBNymZ4u7YrBg0e0qO+Fi9
+a4Kfh/QNMq/6VrpWvycb9LtCLhU+qQIDAQABAoICAEbiyfm6aCFnCnpneIN5cIvw
+++bxpyT4/JOICyaqBME8dSoaZeV5KpUA54Yqhf6i05F9PEHfZQh3+TTtgMEoIh2t
+H1r/2iBhYu1djndXYGKFC5WLb7T9F4oj+oUKBGOgmtNHiteiBTj2c9qOkn2sEQew
+gQo99yXT7CYSFzMsVyW8bVMTm1VpY87h6ZACAZ0yYXmaDW8ftahX/sWB5+1Oavly
+CVdJF+OpcbnVxceUtQa2eSdpUhR3I2KegMgqAw3YsdnyVmdKZ1r8D34s6L1k+HJj
+n2xnkyuXXGl224HidY92xvtY47JUrD8wjjIC4joVsj8YjcdH+4OKKLvIKc3s+W4C
++fF6Uhxe7V0kg6bw+UE5eGC0CYeQww4Ruwd7Y8MHpF+6QAK2QsVWGCrhvCgXf9ix
+s4iJBjaGG3fEMVyTrMeaJ/IhI4x6awjN8f/viqXfpwU5a06/JpILghgNTigHTCi2
++Bpv4pXNmdn5AdLbGU+H+XVannY1obM2TV3XK5xMDpEblHG/Y2uNuOGa1YvSUlnl
+AkYCV9pk39Hpnp0TWZ0MQmV4gG0VqyS7t6rh+3xehK+dxD5O9T+BSeB2H5hRrN5V
+qImdsRvJMjj1WMQcREfC+9DTY/YOBlMdymtm7cix0JhnlK4UlvInQYvrnPasYvUA
+wUVINpxGc87HxFEWeqSBAoIBAQDv8f5vS2w1yZNnz1cQwCPvUmLEevruCeO0IR71
+zNGRncoseMr8la4QOzAFCdosAqrfMlIuHK6Y1LVGgyEK4RYsU3zpOWHMzBalhChJ
+dXeZoP/5a7p/Hxrh1YoZCCenTQne/KHIzhyOUzkYn2YltibkOR+sVUeYx8ZgDXxV
+WwJmeMRV9Y19sE3fGYEQAo4gd1/8kObWlT49VwqDuUAjTtwwFQKs2b6UF77cAOgt
+U1rclYg9LyB+liGof1TMA1S0z63keOYBX2S3154rB/91vXJdWPx0q1kCjyFfBlAp
+ckxa7pygcWluGlUUqQlKglEEzQrA32OXRBa1loy2bzeU+px5AoIBAQDCD2HfzCzE
+gl91ZgPnmTK7vDz3Dc+7nGP9ZVFBJory5zC/aCasaQ2FndpSQiC1kzVKdrW1h+U/
+yY2Bg8KrxyV6xP/yR2l0dM4tdjhZRJtCHIua2/K9w8aC713ak1ZbjVQtQPdX0tNy
+zQADb/WaI3cLoXBul9vtEqdILqsMe3RsaTsAtswheCuT8n4ySbgdepavNOufTvh8
+TJVZq4+V0Vas+jYt4+yODHqfswGG1Ud2kZ4rucv8XsIOkA24eDsAgzBQormbeSXJ
+KS37LRT3Swf+jA7WajfwrKV6cO7Y9mwOPMWZzFz3ES/qFYbK9s1KftkcudiYRc25
+KJZslS5xVcexAoIBAHjAKtAtf65t2/2xDVrDpxHoPwYr8Z3bYjkjNeZzBcAnTTgm
+LdkBJpDKiHbwp1fgm8cpFsxX6NHGsddjZDyKW9NAzKq+Euayim8PXArjz6WDrW4C
+9d7Fc4zVHuNMBFCgZ2hNcMmSWDKT1Tb7+LbfvSC7UqIyZI6Rctah0sFNxJ53Bi9Q
+HL10/StaNWYuMwJJsQd0kIbKooDSDduOXaWnKQ4VdLwx9EOo04b5+d3dheteYSqR
+TeQGf7fBJJZq0rUPkq5Y3T8xl4khPFrhcoD5LtWlU58PIAM2ro+YqLzC5YQZcr8X
+c/xRyiFUk/VoMYed/Fxlz0Ovo1INCpFA1RLnL9kCggEAX/0923Zh+n3GfAqPCeME
+bkpJGacSRumvp+qSy5gmCMqEmVkKMCPylVIkaKXfChGbvY6EiRuEMQ4gWZz0EQX7
+qwOA2rWqGvmf9mrQqo8+APCfuWTsaCNLsP53vSM+ByEcLxpAfoeBIfr287xQjwLV
+4sHjHEEvfs/IQPMclpsGVo2iqtLAnBmV7KN4+qTuVl6J5HZXykBEty8mfOlYp7GZ
+nwxQ+lgQbZ8MlKv1qF0c8TBMPbK0jMvOT2e/8aw++xzpLCmhh57gKuWcoe6FvWC2
+vplGyZZWv0yWub7c1iLmBhDXaSDmJyuwOKiXORPlLeEawZPH6GI2xUynQ2RzSYo1
+sQKCAQAbVhs1HcP5PAOTF5jAUdMbx+LeLUgKjO3Nx+YUeQCKVOgypd8w7N9k7WPi
+BvTu7nkMtiK5UCix+UGthUFYyMClD2wnQ1h6nhVVz/D98cukksr1awNu6ms9M2ol
+u6U7tfViEJhPxL+1pdAnFmqAoQx8fGpiyZQbb9DAcVBrIqQEjCRr4yZ8XaHOcTL0
+OeQO6ZCgxYOO5ac4snc1PDnRrlLs++b6tyaunLsFRSBkuzkMugXgUc+y3xgzBUQf
+LOb/QIZvtqyF6s/YJZtLjLC8vdoe0ZqINh5Dq1xoGvtI1/QMgWraom999w9liFWs
+VULYeUwXocBKk6rBSgDlsFF5LW22
+-----END PRIVATE KEY-----
--- /dev/null
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+
+import argparse
+import logging
+import pexpect
+import sys
+
+
+def run(args):
+
+ ret = 1
+ logger = logging.getLogger("test-shutdown")
+
+ logger.info("spawning test")
+ console = pexpect.spawn(args.command, args.arg, env={
+ "TERM": "linux",
+ }, encoding='utf-8', timeout=30)
+
+ if args.verbose:
+ console.logfile = sys.stdout
+
+ logger.debug("child pid %d" % console.pid)
+
+ try:
+ logger.info("waiting for login prompt")
+ console.expect('H login: ', 10)
+
+ logger.info("log in and start screen")
+ console.sendline('root')
+ console.expect('bash.*# ', 10)
+ console.sendline('screen')
+ console.expect('screen0 ', 10)
+ console.sendcontrol('a')
+ console.send('c')
+ console.expect('screen1 ', 10)
+
+# console.interact()
+
+ console.sendline('tty')
+ console.expect(r'/dev/(pts/\d+)')
+ pty = console.match.group(1)
+ logger.info("window 1 at line %s", pty)
+
+ logger.info("schedule reboot")
+ console.sendline('shutdown -r')
+ console.expect("Reboot scheduled for (?P<date>.*), use 'shutdown -c' to cancel", 2)
+ date = console.match.group('date')
+ logger.info("reboot scheduled for %s", date)
+
+ console.sendcontrol('a')
+ console.send('0')
+ logger.info("verify broadcast message")
+ console.expect('Broadcast message from root@H on %s' % pty, 2)
+ console.expect('The system is going down for reboot at %s' % date, 2)
+
+ logger.info("check show output")
+ console.sendline('shutdown --show')
+ console.expect("Reboot scheduled for %s, use 'shutdown -c' to cancel" % date, 2)
+
+ logger.info("cancel shutdown")
+ console.sendline('shutdown -c')
+ console.sendcontrol('a')
+ console.send('1')
+ console.expect('The system shutdown has been cancelled', 2)
+
+ logger.info("call for reboot")
+ console.sendline('sleep 10; shutdown -r now')
+ console.sendcontrol('a')
+ console.send('0')
+ console.expect("The system is going down for reboot NOW!", 12)
+
+ logger.info("waiting for reboot")
+
+ console.expect('H login: ', 10)
+ console.sendline('root')
+ console.expect('bash.*# ', 10)
+
+ console.sendline('> /testok')
+
+ logger.info("power off")
+ console.sendline('poweroff')
+
+ logger.info("expect termination now")
+ console.expect(pexpect.EOF)
+
+ ret = 0
+ except Exception as e:
+ logger.error(e)
+ logger.info("killing child pid %d" % console.pid)
+ console.terminate()
+
+ return ret
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='test logind shutdown feature')
+ parser.add_argument("-v", "--verbose", action="store_true", help="verbose")
+ parser.add_argument("command", help="command to run")
+ parser.add_argument("arg", nargs='*', help="args for command")
+
+ args = parser.parse_args()
+
+ if args.verbose:
+ level = logging.DEBUG
+ else:
+ level = logging.INFO
+
+ logging.basicConfig(level=level)
+
+ sys.exit(run(args))
+
+# vim: sw=4 et
ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", PROGRAM=="/bin/sleep 60"
EOF
- echo "event_timeout=10" >>/etc/udev/udev.conf
- echo "timeout_signal=SIGABRT" >>/etc/udev/udev.conf
+ cat >>/etc/udev/udev.conf <<EOF
+event_timeout=10
+timeout_signal=SIGABRT
+EOF
systemctl restart systemd-udevd.service
}
set -o pipefail
ARGS=()
+state_directory=/var/lib/private/
if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
# If we're running under sanitizers, we need to use a less restrictive
# profile, otherwise LSan syscall would get blocked by seccomp
ARGS+=(--profile=trusted)
+ # With the trusted profile DynamicUser is disabled, so the storage is not in private/
+ state_directory=/var/lib/
fi
systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service'
Environment=SYSTEMD_LOG_LEVEL=debug
EOF
-portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw app0
+portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0
-systemctl is-active app0.service
-systemctl is-active app0-foo.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
set +o pipefail
set +e
-systemctl is-active app0-bar.service && exit 1
+systemctl is-active minimal-app0-bar.service && exit 1
set -e
set -o pipefail
-portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw app0
+portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
-systemctl is-active app0.service
-systemctl is-active app0-bar.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
set +o pipefail
set +e
-systemctl is-active app0-foo.service && exit 1
+systemctl is-active minimal-app0-foo.service && exit 1
set -e
set -o pipefail
portablectl list | grep -q -F "minimal_1"
-portablectl detach --now --runtime /usr/share/minimal_1.raw app0
+portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl list | grep -q -F "No images."
unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw
unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 app0
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
-systemctl is-active app0.service
-systemctl is-active app0-foo.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
set +o pipefail
set +e
-systemctl is-active app0-bar.service && exit 1
+systemctl is-active minimal-app0-bar.service && exit 1
set -e
set -o pipefail
-portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 app0
+portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
-systemctl is-active app0.service
-systemctl is-active app0-bar.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
set +o pipefail
set +e
-systemctl is-active app0-foo.service && exit 1
+systemctl is-active minimal-app0-foo.service && exit 1
set -e
set -o pipefail
portablectl list | grep -q -F "minimal_1"
-portablectl detach --now --enable --runtime /tmp/minimal_1 app0
+portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
portablectl list | grep -q -F "No images."
portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
portablectl detach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
+# Ensure that the combination of read-only images, state directory and dynamic user works, and that
+# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
+# after the service is attached before the file appears.
+grep -q -F bar "${state_directory}/app0/foo"
+grep -q -F baz "${state_directory}/app1/foo"
+
# portablectl also works with directory paths rather than images
-mkdir /tmp/rootdir /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
+mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
+mount /usr/share/app0.raw /tmp/app0
mount /usr/share/app1.raw /tmp/app1
mount /usr/share/minimal_0.raw /tmp/rootdir
portablectl detach --now --runtime overlay app1
umount /tmp/overlay
+
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
+systemctl is-active app0.service
+systemctl is-active app1.service
+
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service
+
+portablectl detach --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
umount /tmp/rootdir
+umount /tmp/app0
umount /tmp/app1
echo OK >/testok
inspect test-user2
fi
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \
&& { echo 'unexpected success'; exit 1; }
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \
&& { echo 'unexpected success'; exit 1; }
cat >/run/systemd/system/testservice-50e.service <<EOF
[Service]
MountAPIVFS=yes
-TemporaryFileSystem=/run
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
RootImage=${image}.raw
ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
# Relevant only for sanitizer runs
systemctl start testservice-50e.service
systemctl is-active testservice-50e.service
+# ExtensionDirectories will set up an overlay
+mkdir -p "${image_dir}/app0" "${image_dir}/app1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; }
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; }
+systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0"
+systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+cat >/run/systemd/system/testservice-50f.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=${image}.raw
+ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=/bin/bash -c '/opt/script0.sh | grep ID'
+ExecStart=/bin/bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50f.service
+systemctl is-active testservice-50f.service
+umount "${image_dir}/app0"
+umount "${image_dir}/app1"
+
echo OK >/testok
exit 0
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 22257 Repro with Restart=always
+
+[Service]
+Type=simple
+Restart=always
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
systemctl start testsuite-51-repro-1
systemctl start testsuite-51-repro-2
+systemctl start testsuite-51-repro-3
sleep 5 # wait a bit in case there are restarts so we can count them below
[[ "$(systemctl show testsuite-51-repro-1 -P NRestarts)" == "0" ]]
[[ "$(systemctl show testsuite-51-repro-2 -P NRestarts)" == "0" ]]
+[[ "$(systemctl show testsuite-51-repro-3 -P NRestarts)" == "0" ]]
touch /testok
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-69-SHUTDOWN
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
binary=$(realpath "${1}")
if [[ "${1}" =~ systemd-boot([[:alnum:]]+).efi ]]; then
target="systemd-boot"
- symbols=$(realpath "$(dirname "${1}")/systemd_boot.so")
+ symbols=$(realpath "${1%efi}elf")
elif [[ "${1}" =~ linux([[:alnum:]]+).efi.stub ]]; then
target="systemd-stub"
- symbols=$(realpath "$(dirname "${1}")/linux${BASH_REMATCH[1]}.elf.stub")
+ symbols=$(realpath "${1%efi.stub}elf.stub")
else
echo "Cannot detect EFI binary '${1}'."
exit 1
apt-get update
apt-get install -y gperf m4 gettext python3-pip \
libcap-dev libmount-dev libkmod-dev \
- pkg-config wget python3-jinja2
+ pkg-config wget python3-jinja2 zipmerge
# gnu-efi is installed here to enable -Dgnu-efi behind which fuzz-bcd
# is hidden. It isn't linked against efi. It doesn't
zip -jqr "$OUT/fuzz-bcd_seed_corpus.zip" "$bcd"
rm -rf "$bcd"
+hosts=$(mktemp)
+wget -O "$hosts" https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
+zip -jq "$OUT/fuzz-etc-hosts_seed_corpus.zip" "$hosts"
+rm -rf "$hosts"
+
# The seed corpus is a separate flat archive for each fuzzer,
# with a fixed name ${fuzzer}_seed_corpus.zip.
-for d in "$(dirname "$0")/../test/fuzz/fuzz-"*; do
+for d in test/fuzz/fuzz-*; do
zip -jqr "$OUT/$(basename "$d")_seed_corpus.zip" "$d"
done
find "$build" -maxdepth 1 -type f -executable -name "fuzz-*" -exec mv {} "$OUT" \;
find src -type f -name "fuzz-*.dict" -exec cp {} "$OUT" \;
cp src/fuzz/*.options "$OUT"
+
+if [[ "$MERGE_WITH_OSS_FUZZ_CORPORA" == "yes" ]]; then
+ for f in "$OUT/"fuzz-*; do
+ [[ -x "$f" ]] || continue
+ fuzzer=$(basename "$f")
+ t=$(mktemp)
+ if wget -O "$t" "https://storage.googleapis.com/systemd-backup.clusterfuzz-external.appspot.com/corpus/libFuzzer/systemd_${fuzzer}/public.zip"; then
+ zipmerge "$OUT/${fuzzer}_seed_corpus.zip" "$t"
+ fi
+ rm -rf "$t"
+ done
+fi
['systemd-network-generator.service', ''],
['systemd-networkd.service', 'ENABLE_NETWORKD'],
['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD'],
+ ['systemd-networkd-wait-online@.service','ENABLE_NETWORKD'],
['systemd-nspawn@.service', ''],
['systemd-oomd.service', 'ENABLE_OOMD'],
['systemd-portabled.service', 'ENABLE_PORTABLED',
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Wait for Network Interface %i to be Configured
+Documentation=man:systemd-networkd-wait-online.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Requires=systemd-networkd.service
+After=systemd-networkd.service
+Before=network-online.target shutdown.target
+
+[Service]
+Type=oneshot
+ExecStart={{ROOTLIBEXECDIR}}/systemd-networkd-wait-online -i %i
+RemainAfterExit=yes
+
+[Install]
+WantedBy=network-online.target