- debian
- ubuntu
- fedora
+ - opensuse
steps:
- uses: actions/checkout@v2
- - uses: systemd/mkosi@v9
+ - uses: systemd/mkosi@v10
- name: Install
run: sudo apt-get update && sudo apt-get install --no-install-recommends python3-pexpect python3-jinja2
systemd-nspawn --version
- name: Build ${{ matrix.distro }}
- run: sudo python3 -m mkosi --password= --qemu-headless build
+ run: sudo python3 -m mkosi --password= --network-veth=no --qemu-headless build
- name: Show ${{ matrix.distro }} image summary
run: sudo python3 -m mkosi --password= --qemu-headless summary
- name: Boot ${{ matrix.distro }} systemd-nspawn
- run: sudo ./.github/workflows/test_mkosi_boot.py python3 -m mkosi --password= --qemu-headless boot
+ run: sudo ./.github/workflows/test_mkosi_boot.py python3 -m mkosi --password= --network-veth=no --qemu-headless boot
- name: Boot ${{ matrix.distro }} QEMU
- run: sudo ./.github/workflows/test_mkosi_boot.py python3 -m mkosi --password= --qemu-headless qemu
+ run: sudo ./.github/workflows/test_mkosi_boot.py python3 -m mkosi --password= --network-veth=no --qemu-headless qemu
# SPDX-License-Identifier: LGPL-2.1-or-later
import pexpect
+import re
import sys
def run() -> None:
p = pexpect.spawnu(" ".join(sys.argv[1:]), logfile=sys.stdout, timeout=300)
- p.expect("#")
+ # distro-independent root prompt
+ p.expect(re.compile("~[^#]{0,3}#"))
p.sendline("systemctl poweroff")
p.expect(pexpect.EOF)
- libp11-kit-dev
- libssl-dev
- python3-jinja2
+ after_prepare:
+ - pip3 install meson==0.53.2
+ - export PATH="/opt/work/.local/bin:$PATH"
python:
python_setup:
version: 3
pcre-devel
python3
python3-lxml
- python3-jinja2
+ python3-Jinja2
qrencode-devel
system-user-nobody
systemd-sysvinit
libapparmor1
libcrypt1
libcryptsetup12
+ libgcrypt20
libkmod2
liblz4-1
libmount1
Linux kernel >= 4.17 for cgroup-bpf socket address hooks
Linux kernel >= 5.3 for bounded-loops in BPF program
Linux kernel >= 5.4 for signed Verity images support
+ Linux kernel >= 5.7 for BPF links
Kernel Config Options:
CONFIG_DEVTMPFS
CONFIG_HAVE_EBPF_JIT
CONFIG_CGROUP_BPF
- Required for SocketBind{Allow|Deny}= in resource control unit settings
+ Required for SocketBind{Allow|Deny}=, RestrictNetworkInterfaces= in
+ resource control unit settings
CONFIG_BPF
CONFIG_BPF_SYSCALL
CONFIG_BPF_JIT
Features:
+* tpm2: figure out if we need to do anything for TPM2 parameter encryption? And
+ if so, what precisely?
+
+* insert pkcs7 signature for verity gpt
+
+* when mounting disk images: if IMAGE_ID/IMAGE_VERSION is set in os-release
+ data in the image, make sure the image filename actually matches this, so
+ that images cannot be misused.
+
+* use credentials logic/TPM2 logic to store homed signing key
+
* New udev block device symlink names:
/dev/disk/by-parttypelabel/<pttype>/<ptlabel>. Use case: if pt label is used
as partition image version string, this is a safe way to reference a specific
* bootctl,sd-boot: actually honour the "architecture" key
-* sd-boot: add service that automatically runs "bootctl update" on every boot,
- in a graceful way, so that updated /usr trees automatically propagate into
- updated boot loaders on reboot.
-
* bootctl:
- teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation
- teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host
System shutdown has been initiated. The shutdown has now begun and
all system services are terminated and all file systems unmounted.
+-- c14aaf76ec284a5fa1f105f88dfb061c
+Subject: System factory reset initiated
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+System factory reset has been initiated. The precise operation this
+executes is implementation-defined, but typically has the effect of
+reverting the system's state and configuration to vendor defaults.
+
-- 7d4958e842da4a758f6c1cdc7b36dcc5
Subject: A start job for unit @UNIT@ has begun execution
Defined-By: systemd
Note that is may be also useful to set `$SYSTEMD_LOG_LEVEL`, since all logging
is suppressed by default.
+
+systemd-importd:
+
+* `SYSTEMD_IMPORT_BTRFS_SUBVOL` – takes a boolean, which controls whether to
+ prefer creating btrfs subvolumes over plain directories for machine
+ images. Has no effect on non-btrfs file systems where subvolumes are not
+ available anyway. If not set, defaults to true.
+
+* `SYSTEMD_IMPORT_BTRFS_QUOTA` – takes a boolean, which controls whether to set
+ up quota automatically for created btrfs subvolumes for machine images. If
+ not set, defaults to true. Has no effect if machines are placed in regular
+ directories, because btrfs subvolumes are not supported or disabled. If
+ enabled, the quota group of the subvolume is automatically added to a
+ combined quota group for all such machine subvolumes.
+
+* `SYSTEMD_IMPORT_SYNC` – takes a boolean, which controls whether to
+ synchronize images to disk after installing them, before completing the
+ operation. If not set, defaults to true. If disabled installation of images
+ will be quicker, but not as safe.
Now, your editor will start clangd in the mkosi build image and all of clangd's features will work as
expected.
+
+## Debugging systemd with mkosi + vscode
+
+To simplify debugging systemd when testing changes using mkosi, we're going to show how to attach
+[VSCode](https://code.visualstudio.com/)'s debugger to an instance of systemd running in a mkosi image
+(either using QEMU or systemd-nspawn).
+
+To allow VSCode's debugger to attach to systemd running in a mkosi image, we have to make sure it can access
+the container/virtual machine spawned by mkosi where systemd is running. mkosi makes this possible via a
+handy SSH option that makes the generated image accessible via SSH when booted. The easiest way to set the
+option is to create a file 20-local.conf in mkosi.default.d/ and add the following contents:
+
+```
+[Host]
+Ssh=yes
+```
+
+Next, make sure systemd-networkd is running on the host system so that it can configure the network interface
+connecting the host system to the container/VM spawned by mkosi. Once systemd-networkd is running, you should
+be able to connect to a running mkosi image by executing `mkosi ssh` in the systemd repo directory.
+
+Now we need to configure VSCode. First, make sure the C/C++ extension is installed. If you're already using
+a different extension for code completion and other IDE features for C in VSCode, make sure to disable the
+corresponding parts of the C/C++ extension in your VSCode user settings by adding the following entries:
+
+```json
+"C_Cpp.formatting": "Disabled",
+"C_Cpp.intelliSenseEngine": "Disabled",
+"C_Cpp.enhancedColorization": "Disabled",
+"C_Cpp.suggestSnippets": false,
+```
+
+With the extension set up, we can create the launch.json file in the .vscode/ directory to tell the VSCode
+debugger how to attach to the systemd instance running in our mkosi container/VM. Create the file and add the
+following contents:
+
+```json
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "cppdbg",
+ "program": "/usr/lib/systemd/systemd",
+ "processId": "${command:pickProcess}",
+ "request": "attach",
+ "name": "systemd",
+ "pipeTransport": {
+ "pipeProgram": "mkosi",
+ "pipeArgs": [
+ "-C",
+ "/path/to/systemd/repo/directory/on/host/system/",
+ "ssh"
+ ],
+ "debuggerPath": "/usr/bin/gdb"
+ },
+ "MIMode": "gdb",
+ "sourceFileMap": {
+ "/root/build/../src": {
+ "editorPath": "${workspaceFolder}",
+ "useForBreakpoints": false
+ },
+ "/root/build/*": {
+ "editorPath": "${workspaceFolder}/mkosi.builddir",
+ "useForBreakpoints": false
+ }
+ }
+ }
+ ]
+}
+```
+
+Now that the debugger knows how to connect to our process in the container/VM and we've set up the necessary
+source mappings, go to the "Run and Debug" window and run the "systemd" debug configuration. If everything
+goes well, the debugger should now be attached to the systemd instance running in the container/VM. You can
+attach breakpoints from the editor and enjoy all the other features of VSCode's debugger.
+
+To debug systemd components other than PID 1, set "program" to the full path of the component you want to
+debug and set "processId" to "${command:pickProcess}". Now, when starting the debugger, VSCode will ask you
+the PID of the process you want to debug. Run `systemctl show --property MainPID --value <component>` in the
+container to figure out the PID and enter it when asked and VSCode will attach to that process instead.
# Portable Services Introduction
-This systemd version includes a preview of the "portable service"
-concept. "Portable Services" are supposed to be an incremental improvement over
-traditional system services, making two specific facets of container management
-available to system services more readily. Specifically:
+systemd (since version 239) supports a concept of "Portable Services".
+"Portable Services" are a delivery method for system services that uses
+two specific features of container management:
-1. The bundling of applications, i.e. packing up multiple services, their
- binaries and all their dependencies in a single image, and running them
- directly from it.
+1. Applications are bundled. I.e. multiple services, their binaries and all
+ their dependencies are packaged in an image, and are run directly from it.
2. Stricter default security policies, i.e. sand-boxing of applications.
-The primary tool for interfacing with "portable services" is the new
-"portablectl" program. It's currently shipped in /usr/lib/systemd/portablectl
-(i.e. not in the `$PATH`), since it's not yet considered part of the officially
-supported systemd interfaces — it's a preview still after all.
+The primary tool for interacting with Portable Services is `portablectl`,
+and they are managed by the `systemd-portabled` service.
Portable services don't bring anything inherently new to the table. All they do
is put together known concepts in a slightly nicer way to cover a specific set
## So, what *is* a "Portable Service"?
A portable service is ultimately just an OS tree, either inside of a directory
-tree, or inside a raw disk image containing a Linux file system. This tree is
-called the "image". It can be "attached" or "detached" from the system. When
-"attached" specific systemd units from the image are made available on the host
-system, then behaving pretty much exactly like locally installed system
-services. When "detached" these units are removed again from the host, leaving
-no artifacts around (except maybe messages they might have logged).
+tree, or inside a raw disk image (or a set of images that get layered, see
+[Layered Images](#layered-images)) containing a Linux file system. This tree is called the
+"image". It can be "attached" or "detached" from the system. When "attached"
+specific systemd units from the image are made available on the host system,
+then behaving pretty much exactly like locally installed system services. When
+"detached" these units are removed again from the host, leaving no artifacts
+around (except maybe messages they might have logged).
The OS tree/image can be created with any tool of your choice. For example, you
can use `dnf --installroot=` if you like, or `debootstrap`, the image format is
again.
Note that `portablectl attach` won't enable or start any of the units it copies
-out. This still has to take place in a second, separate step. (That said We
-might add options to do this automatically later on.).
+out by default, but `--enable` and `--now` parameter are available as shortcuts.
+The same is true for the opposite `detach` operation.
+
+A `portablectl reattach` command is made available to combine a `detach` with an
+`attach`, and it is useful in case an image gets upgraded, as it allows a to
+perform a `restart` operation on the unit(s) instead of `stop` plus `start`,
+thus providing lower downtime and avoiding losing runtime state associated with
+the unit such as the file descriptor store.
## Requirements on Images
image. As mentioned `mkosi -b` takes care of all of that for you, but any other
image generator should work too.
+## Extension Images
+
+Portable services can be delivered as one or multiple images that extend the base
+image, and are combined with OverlayFS at runtime, when they are attached. This
+enables a workflow that splits the base 'runtime' from the daemon, so that multiple
+portable services can share the same 'runtime' image (libraries, tools) without
+having to include everything each time, with the layering happening only at runtime.
+The `--extension` parameter of `portablectl` can be used to specify as many upper
+layers as desired. On top of the requirements listed in the previous section, the
+following must be also be observed.
+
+1. The base/OS image must contain an os-release file, either in `/etc/os-release` or
+ `/usr/lib/os-release`. The file should follow the standard format.
+
+2. The upper extension(s) image(s) must contain an extension-release file in
+ `/usr/lib/extension-release.d/`, with an `ID=` and `SYSEXT_LEVEL=`/`VERSION_ID=`
+ matching the base image.
+
+3. The base/OS image does not need to have any unit files.
+
+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).
+
+```
+# /usr/lib/systemd/portablectl attach --extension foobar_0.7.23.raw debian-runtime_11.1.raw foobar
+# /usr/lib/systemd/portablectl attach --extension barbaz_7.0.23.raw debian-runtime_11.1.raw barbaz
+```
+
## Execution Environment
Note that the code in portable service images is run exactly like regular
usb:v5501p08FF*
ID_AUTOSUSPEND=1
+# Supported by libfprint driver egis0570
+usb:v1C7Ap0570*
+usb:v1C7Ap0571*
+ ID_AUTOSUSPEND=1
+
# Supported by libfprint driver elan
usb:v04F3p0903*
usb:v04F3p0907*
usb:v04F3p0C4D*
usb:v04F3p0C4F*
usb:v04F3p0C63*
+usb:v04F3p0C6E*
+ ID_AUTOSUSPEND=1
+
+# Supported by libfprint driver elanmoc
+usb:v04F3p0C7E*
ID_AUTOSUSPEND=1
# Supported by libfprint driver etes603
usb:v1491p0088*
usb:v16D1p1027*
usb:v1C7Ap0300*
-usb:v1C7Ap0570*
usb:v1C7Ap0575*
usb:v27C6p5042*
usb:v27C6p5110*
EVDEV_ABS_35=117:3958:36
EVDEV_ABS_36=104:1960:26
-# Lenovo Thinkpad T490 and T14 Gen1
+# Lenovo Thinkpad T490 and T14/P14s Gen1/2
evdev:name:SynPS/2 Synaptics TouchPad:dmi:*:svnLENOVO:*pvrThinkPadT490:*
evdev:name:SynPS/2 Synaptics TouchPad:dmi:*:svnLENOVO:*pvrThinkPadT14Gen1:*
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:*:svnLENOVO:*pvrThinkPadP14sGen1:*
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:*:svnLENOVO:*pvrThinkPadP14sGen2a:*
EVDEV_ABS_00=::44
EVDEV_ABS_01=::52
EVDEV_ABS_35=::44
sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:pnVostro5581:*
ACCEL_LOCATION=base
-sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:pnLatitude9520:*:ct10:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0A3E:*
ACCEL_LOCATION=base
-sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:pnLatitude7420:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0B0B:*
ACCEL_LOCATION=base
# Dell Venue 8 Pro 3845
# BridgeCo. Enhancement BreakOut Box (BeBoB) for DM1000, DM1100, and DM1500 ASICs.
#
-# Match to eAR Master One, Eroica, Figaro, and Ciaccona.
-ieee1394:node:ven0x000aacmo0x000002units0x00a02d:0x010001
-ieee1394:ven00000AACmo00000002sp0000A02Dver00010001
- ID_VENDOR_FROM_DATABASE=Acoustic Reality
- ID_MODEL_FROM_DATABASE=eAR FireWire Audio
- IEEE1394_UNIT_FUNCTION_AUDIO=1
- IEEE1394_UNIT_FUNCTION_VIDEO=0
-
ieee1394:node:ven0x0003dbmo0x01eeeeunits0x00a02d:0x010001
ieee1394:ven000003DBmo0001EEEEsp0000A02Dver00010001
ID_VENDOR_FROM_DATABASE=Apogee Electronics
IEEE1394_UNIT_FUNCTION_AUDIO=1
IEEE1394_UNIT_FUNCTION_VIDEO=0
-# An extension card for Mackie d.2.
+# An extension card for Mackie d.2. Mackie d.2 Pro is preinstalled model.
ieee1394:node:ven0x000ff2mo0x010067units0x00a02d:0x010001
ieee1394:ven00000FF2mo00010067sp0000A02Dver00010001
ID_VENDOR_FROM_DATABASE=Loud Technologies
IEEE1394_UNIT_FUNCTION_AUDIO=1
IEEE1394_UNIT_FUNCTION_VIDEO=0
-# Match to FireFly 202, 302, 808, and 808 Universal.
-# Match to HelixBoard 12 mk II, 18 mk II, 24 mk II, 12 Universal, 18 Universal, and 24 Universal.
+ieee1394:node:ven0x001496mo0x050000units0x00a02d:0x010001
+ieee1394:ven00001496mo00050000sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=Phonic
+ ID_MODEL_FROM_DATABASE=Helixboard 12 FireWire MkII
+ IEEE1394_UNIT_FUNCTION_MIDI=1
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
+ieee1394:node:ven0x001496mo0x060000units0x00a02d:0x010001
+ieee1394:ven00001496mo00060000sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=Phonic
+ ID_MODEL_FROM_DATABASE=Helixboard 18 FireWire MkII
+ IEEE1394_UNIT_FUNCTION_MIDI=1
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
+ieee1394:node:ven0x001496mo0x070000units0x00a02d:0x010001
+ieee1394:ven00001496mo00070000sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=Phonic
+ ID_MODEL_FROM_DATABASE=Helixboard 24 FireWire MkII
+ IEEE1394_UNIT_FUNCTION_MIDI=1
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
+ieee1394:node:ven0x001496mo0x080000units0x00a02d:0x010001
+ieee1394:ven00001496mo00080000sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=Phonic
+ ID_MODEL_FROM_DATABASE=Firefly 808 FireWire
+ IEEE1394_UNIT_FUNCTION_MIDI=1
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
+# Match to FireFly 202, 302, 808 Universal.
+# Match to HelixBoard 12 FireWire, 18 FireWire, 24 FireWire.
+# Match to HelixBoard 12 Universal, 18 Universal, and 24 Universal.
ieee1394:node:ven0x001496mo0x000000units0x00a02d:0x010001
ieee1394:ven00001496mo00000000sp0000A02Dver00010001
ID_VENDOR_FROM_DATABASE=Phonic
IEEE1394_UNIT_FUNCTION_AUDIO=1
IEEE1394_UNIT_FUNCTION_VIDEO=0
+# Match to TerraTec Aureon 7.1 FireWire.
+# Match to eAR Master One, Eroica, Figaro, and Ciaccona. OEM by TerraTec perhaps.
+ieee1394:node:ven0x000aacmo0x000002units0x00a02d:0x010001
+ieee1394:ven00000AACmo00000002sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=TerraTec
+ ID_MODEL_FROM_DATABASE=Aureon 7.1 FireWire
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
ieee1394:node:ven0x000aacmo0x000003units0x00a02d:0x010001
ieee1394:ven00000AACmo00000003sp0000A02Dver00010001
ID_VENDOR_FROM_DATABASE=TerraTec Electronic
IEEE1394_UNIT_FUNCTION_AUDIO=1
IEEE1394_UNIT_FUNCTION_VIDEO=0
+# Match to former model of Onyx 1640i.
ieee1394:node:ven0x000ff2mo0x001640units0x00a02d:0x010001
ieee1394:ven00000FF2mo00001640sp0000A02Dver00010001
ID_VENDOR_FROM_DATABASE=Loud Technologies
ID_MODEL_FROM_DATABASE=Mackie Onyx Blackbird
IEEE1394_UNIT_FUNCTION_AUDIO=1
-# Match to Onyx 1640i, and latter models of Onyx 820i, 1220i, and 1620i.
+# Match to latter models of Onyx 820i, 1220i, 1620i, and 1640i.
ieee1394:node:ven0x000ff2mo0x000006units0x000ff2:0x000001
ieee1394:ven00000FF2mo00000006sp00000FF2ver00000001
ID_VENDOR_FROM_DATABASE=Loud Technologies
if install_sysconfdir
meson.add_install_script('sh', '-c',
- mkdir_p.format(join_paths(sysconfdir, 'udev/hwdb.d')))
+ mkdir_p.format(sysconfdir / 'udev/hwdb.d'))
meson.add_install_script('sh', '-c',
'test -n "$DESTDIR" || @0@/systemd-hwdb update'.format(rootbindir))
<varlistentry>
<term><option>--graceful</option></term>
- <listitem><para>Ignore failure when the EFI System Partition cannot be found, or when EFI variables
- cannot be written. Currently only applies to random seed operations.</para></listitem>
+ <listitem><para>Ignore failure when the EFI System Partition cannot be found, when EFI variables
+ cannot be written, or a different or newer boot loader is already installed. Currently only applies
+ to random seed and update operations.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--make-machine-id-directory=yes|no|auto</option></term>
<listitem><para>Control creation and deletion of the top-level machine ID directory on the file
- system containing boot loader entries (i.e. beneath the file system returned by
- <option>--print-boot-path</option> above) during <option>install</option> and
+ system containing boot loader entries (i.e. beneath the file system returned by the
+ <option>--print-boot-path</option> option, see above) during <option>install</option> and
<option>remove</option>, respectively. <literal>auto</literal> is equivalent to
<literal>yes</literal> if <filename>/etc/machine-id</filename> resides on a filesystem other than
tmpfs and <literal>no</literal> otherwise (in the latter case the machine ID is likely transient and
<varlistentry>
<term><varname>ProcessSizeMax=</varname></term>
- <listitem><para>The maximum size in bytes of a core
- which will be processed. Core dumps exceeding this size
- may be stored, but the backtrace will not be generated.
- Like other sizes in this same config file, the usual
- suffixes to the base of 1024 are allowed (B, K, M,
- G, T, P, and E.)</para>
+ <listitem><para>The maximum size in bytes of a core which will be processed. Core dumps exceeding
+ this size may be stored, but the backtrace will not be generated. Like other sizes in this same
+ config file, the usual suffixes to the base of 1024 are allowed (B, K, M, G, T, P, and E).</para>
<para>Setting <varname>Storage=none</varname> and <varname>ProcessSizeMax=0</varname>
disables all coredump handling except for a log entry.</para>
<term><varname>ExternalSizeMax=</varname></term>
<term><varname>JournalSizeMax=</varname></term>
- <listitem><para>The maximum (compressed or uncompressed) size in bytes of a
- core to be saved. Unit suffixes are allowed just as in
- <option>ProcessSizeMax=</option></para></listitem>.
+ <listitem><para>The maximum (compressed or uncompressed) size in bytes of a core to be saved. Unit
+ suffixes are allowed just as in <option>ProcessSizeMax=</option>.</para></listitem>
</varlistentry>
<varlistentry>
by core dumps might temporarily exceed these limits while
core dumps are processed. Note that old core dumps are also
removed based on time via
- <citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>8</manvolnum></citerefentry>. Set
- either value to 0 to turn off size-based
- clean-up.</para></listitem>
+ <citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ Set either value to 0 to turn off size-based cleanup.</para></listitem>
</varlistentry>
</variablelist>
<term><option>-1</option></term>
<listitem><para>Show information of the most recent core dump only, instead of listing all known core
- dumps. (Equivalent to <option>--reverse -n 1</option></para></listitem>
+ dumps. Equivalent to <option>--reverse -n 1</option>.</para></listitem>
</varlistentry>
<varlistentry>
</varlistentry>
<varlistentry>
- <term><option>--setenv=</option><replaceable>VARIABLE</replaceable>=<replaceable>VALUE</replaceable></term>
+ <term><option>--setenv=</option><replaceable>VARIABLE</replaceable>[=<replaceable>VALUE</replaceable>]</term>
- <listitem><para>Takes an environment variable assignment to set for all user processes. Note that a
- number of other settings also result in environment variables to be set for the user, including
- <option>--email=</option>, <option>--timezone=</option> and <option>--language=</option>. May be used
- multiple times to set multiple environment variables.</para></listitem>
+ <listitem><para>Takes an environment variable assignment to set for all user processes. May be used
+ multiple times to set multiple environment variables. When <literal>=</literal> and
+ <replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
+ program environment will be used.</para>
+
+ <para>Note that a number of other settings also result in environment variables to be set for the
+ user, including <option>--email=</option>, <option>--timezone=</option> and
+ <option>--language=</option>.</para></listitem>
</varlistentry>
<varlistentry>
#include <sd-hwdb.h>
int print_usb_properties(uint16_t vid, uint16_t pid) {
- char match[15];
+ char match[STRLEN("usb:vp") + DECIMAL_STR_MAX(uint16_t) * 2];
sd_hwdb *hwdb;
const char *key, *value;
int r;
/* Match this USB vendor and product ID combination */
- snprintf(match, sizeof match, "usb:v%04Xp%04X", vid, pid);
+ xsprintf(match, "usb:v%04Xp%04X", vid, pid);
r = sd_hwdb_new(&hwdb);
if (r < 0)
a comment line. Empty and comment lines are ignored.</para>
<para>Boolean arguments may be written as
- <literal>yes</literal>/<literal>y</literal>/<literal>true</literal>/<literal>1</literal> or
- <literal>no</literal>/<literal>n</literal>/<literal>false</literal>/<literal>0</literal>.
+ <literal>yes</literal>/<literal>y</literal>/<literal>true</literal>/<literal>t</literal>/<literal>on</literal>/<literal>1</literal> or
+ <literal>no</literal>/<literal>n</literal>/<literal>false</literal>/<literal>f</literal>/<literal>off</literal>/<literal>0</literal>.
</para>
</refsect1>
<listitem><para>A glob pattern to select the default entry. The default entry
may be changed in the boot menu itself, in which case the name of the
selected entry will be stored as an EFI variable, overriding this option.
- </para></listitem>
+ </para>
+
+ <table>
+ <title>Automatically detected entries will use the following names:</title>
+
+ <tgroup cols='2'>
+ <colspec colname='name' />
+ <colspec colname='expl' />
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>auto-efi-default</entry>
+ <entry>EFI Default Loader</entry>
+ </row>
+ <row>
+ <entry>auto-efi-shell</entry>
+ <entry>EFI Shell</entry>
+ </row>
+ <row>
+ <entry>auto-osx</entry>
+ <entry>macOS</entry>
+ </row>
+ <row>
+ <entry>auto-reboot-to-firmware-setup</entry>
+ <entry>Reboot Into Firmware Interface</entry>
+ </row>
+ <row>
+ <entry>auto-windows</entry>
+ <entry>Windows Boot Manager</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table></listitem>
</varlistentry>
<varlistentry>
<literal>suspend</literal>,
<literal>hibernate</literal>,
<literal>hybrid-sleep</literal>,
- <literal>suspend-then-hibernate</literal>, and
- <literal>lock</literal>.
+ <literal>suspend-then-hibernate</literal>,
+ <literal>lock</literal>, and
+ <literal>factory-reset</literal>.
If <literal>ignore</literal>, logind will never handle these
keys. If <literal>lock</literal>, all running sessions will be
screen-locked; otherwise, the specified action will be taken
</varlistentry>
<varlistentry>
- <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
- <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
-
- <listitem><para>When used with the <command>shell</command> command, sets an environment
- variable to pass to the executed shell. Takes an environment variable name and value,
- separated by <literal>=</literal>. This switch may be used multiple times to set multiple
- environment variables. Note that this switch is not supported for the
- <command>login</command> command (see below).</para></listitem>
+ <term><option>-E <replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+ <term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+
+ <listitem><para>When used with the <command>shell</command> command, sets an environment variable for
+ the executed shell. This option may be used more than once to set multiple variables. When
+ <literal>=</literal> and <replaceable>VALUE</replaceable> are omitted, the value of the variable with
+ the same name in the program environment will be used.</para>
+
+ <para>Note that this option is not supported for the <command>login</command> command.
+ </para></listitem>
</varlistentry>
<varlistentry>
html_pages = []
source_xml_files = []
dbus_docs = []
-foreach tuple : xsltproc.found() ? manpages : []
+foreach tuple : manpages
stem = tuple[0]
section = tuple[1]
aliases = tuple[2]
htmlaliases += alias + '.html'
endforeach
- mandirn = join_paths(get_option('mandir'), 'man' + section)
+ mandirn = get_option('mandir') / ('man' + section)
if condition == '' or conf.get(condition) == 1
- p1 = custom_target(
- man,
- input : xml,
- output : [man] + manaliases,
- command : xslt_cmd + [custom_man_xsl, '@INPUT@'],
- depends : custom_entities_ent,
- install : want_man,
- install_dir : mandirn)
- man_pages += p1
-
- p2 = []
- foreach htmlalias : htmlaliases
- link = custom_target(
- htmlalias,
- output : htmlalias,
- command : [ln, '-fs', html, '@OUTPUT@'])
- if want_html
- dst = join_paths(docdir, 'html', htmlalias)
- cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
- meson.add_install_script('sh', '-c', cmd)
- p2 += link
- endif
- html_pages += link
- endforeach
-
- p3 = custom_target(
- html,
- input : xml,
- output : html,
- command : xslt_cmd + [custom_html_xsl, '@INPUT@'],
- depends : [custom_entities_ent, p2],
- install : want_html,
- install_dir : join_paths(docdir, 'html'))
- html_pages += p3
-
file = files(tuple[0] + '.xml')
- source_xml_files += file
if tuple[0].startswith('org.freedesktop.')
dbus_docs += file
endif
+
+ if xsltproc.found()
+ p1 = custom_target(
+ man,
+ input : xml,
+ output : [man] + manaliases,
+ command : xslt_cmd + [custom_man_xsl, '@INPUT@'],
+ depends : custom_entities_ent,
+ install : want_man,
+ install_dir : mandirn)
+ man_pages += p1
+
+ p2 = []
+ foreach htmlalias : htmlaliases
+ link = custom_target(
+ htmlalias,
+ output : htmlalias,
+ command : [ln, '-fs', html, '@OUTPUT@'])
+ if want_html
+ dst = docdir / 'html' / htmlalias
+ cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
+ meson.add_install_script('sh', '-c', cmd)
+ p2 += link
+ endif
+ html_pages += link
+ endforeach
+
+ p3 = custom_target(
+ html,
+ input : xml,
+ output : html,
+ command : xslt_cmd + [custom_html_xsl, '@INPUT@'],
+ depends : [custom_entities_ent, p2],
+ install : want_html,
+ install_dir : docdir / 'html')
+ html_pages += p3
+ endif
else
message('Skipping @0@.@1@ because @2@ is false'.format(stem, section, condition))
endif
html = stem + '.html'
man = stem + '.' + section
- mandirn = join_paths(get_option('mandir'), 'man' + section)
+ mandirn = get_option('mandir') / ('man' + section)
p1 = custom_target(
man,
output : htmlalias,
command : [ln, '-fs', html, '@OUTPUT@'])
if want_html
- dst = join_paths(docdir, 'html', htmlalias)
+ dst = docdir / 'html' / htmlalias
cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
meson.add_install_script('sh', '-c', cmd)
p2 += link
command : xslt_cmd + [custom_html_xsl, '@INPUT@'],
depends : [custom_entities_ent, p2],
install : want_html and have_lxml,
- install_dir : join_paths(docdir, 'html'))
+ install_dir : docdir / 'html')
html_pages += p3
endforeach
input : 'html.in',
output : 'html',
configuration : buildroot_substs)
+
+############################################################
+
+update_dbus_docs = custom_target(
+ 'update-dbus-docs',
+ output : 'update-dbus-docs',
+ command : [update_dbus_docs_py, '--build-dir', project_build_root, '@INPUT@'],
+ input : dbus_docs)
+
+if conf.get('BUILD_MODE_DEVELOPER') == 1
+ test('dbus-docs-fresh',
+ update_dbus_docs_py,
+ args : ['--build-dir', project_build_root, '--test', dbus_docs])
+endif
+
+update_man_rules = custom_target(
+ 'update-man-rules',
+ output : 'update-man-rules',
+ command : [sh, '-c',
+ 'cd @0@ && '.format(project_build_root) +
+ 'python3 @0@/tools/update-man-rules.py $(find @0@ -wholename "*/man/*.xml") >t && '.format(project_source_root) +
+ 'mv t @0@/rules/meson.build'.format(meson.current_source_dir())],
+ depends : custom_entities_ent)
<filename>/etc/hosts</filename>.</para>
<para>Please keep in mind that <command>nss-myhostname</command> (and <command>nss-resolve</command>) also resolve
- in the other direction — from locally attached IP adresses to
+ in the other direction — from locally attached IP addresses to
hostnames. If you rely on that lookup being provided by DNS, you might
want to order things differently.
</para>
<command>systemd-resolved</command> is not running.</para>
<para>Please keep in mind that <command>nss-myhostname</command> (and <command>nss-resolve</command>) also resolve
- in the other direction — from locally attached IP adresses to
+ in the other direction — from locally attached IP addresses to
hostnames. If you rely on that lookup being provided by DNS, you might
want to order things differently.
</para>
<para>This is a simple mechanism to provide static user and group records via JSON drop-in files. Such
user records should be defined in the format described by the <ulink
- url="https://systemd.io/USER_RECORD">JSON User Record</ulink> specification and be placed in one of the
+ url="https://systemd.io/USER_RECORD">JSON User Records</ulink> specification and be placed in one of the
aforementioned directories under a file name composed of the user name suffixed with
<filename>.user</filename>, with a world-readable access mode. A symlink named after the user record's
UID formatted in decimal and suffixed with <filename>.user</filename> pointing to the primary record file
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b DefaultDependencies = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
- readonly s OnSuccesJobMode = '...';
+ readonly s OnSuccessJobMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s OnFailureJobMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property CanFreeze is not documented!-->
- <!--property OnSuccesJobMode is not documented!-->
+ <!--property OnSuccessJobMode is not documented!-->
<!--property OnFailureJobMode is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="DefaultDependencies"/>
- <variablelist class="dbus-property" generated="True" extra-ref="OnSuccesJobMode"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="OnSuccessJobMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="OnFailureJobMode"/>
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property SocketBindDeny is not documented!-->
+ <!--property RestrictNetworkInterfaces is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property SocketBindDeny is not documented!-->
+ <!--property RestrictNetworkInterfaces is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property SocketBindDeny is not documented!-->
+ <!--property RestrictNetworkInterfaces is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property SocketBindDeny is not documented!-->
+ <!--property RestrictNetworkInterfaces is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly (bas) RestrictNetworkInterfaces = ...;
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
<!--property SocketBindDeny is not documented!-->
+ <!--property RestrictNetworkInterfaces is not documented!-->
+
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
<!--End of Autogenerated section-->
<refsect2>
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property SocketBindDeny is not documented!-->
+ <!--property RestrictNetworkInterfaces is not documented!-->
+
<!--property KillMode is not documented!-->
<!--property KillSignal is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
<refnamediv>
<refname>os-release</refname>
<refname>initrd-release</refname>
+ <refname>extension-release</refname>
<refpurpose>Operating system identification</refpurpose>
</refnamediv>
<para><filename>/etc/os-release</filename></para>
<para><filename>/usr/lib/os-release</filename></para>
<para><filename>/etc/initrd-release</filename></para>
+ <para><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename></para>
</refsynopsisdiv>
<refsect1>
above) work correctly. The rest of this document that talks about <filename>os-release</filename>
should be understood to apply to <filename>initrd-release</filename> too.</para>
</refsect2>
+
+ <refsect2>
+ <title><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename></title>
+
+ <para><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename>
+ for extension images plays the same role as <filename>os-release</filename> in the main system, and follows the
+ same syntax and rules as described in the <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services Documentation</ulink>.
+ The purpose of this file is to allow the operating system to correctly match an extension image
+ to a base OS image, This is typically implemented by first checking that the <varname>ID=</varname>
+ options match, and if they do either <varname>SYSEXT_LEVEL=</varname> has to match too (preferred), or
+ as a fallback if that is not present <varname>VERSION_ID=</varname> is checked. This ensures that ABI/API
+ between the layers matches and no incompatible images are merged in an overlay.
+ It is preferred that the <filename>extension-release.<replaceable>IMAGE</replaceable></filename> filename is suffixed
+ with the exact file name of the image that contains it, so that all such files in every layer of an overlay are visible.
+ But for the purpose of parsing metadata, in case it is not possible to guarantee that an image file name is stable
+ and doesn't change between the build and the deployment phases, the first and only file which name starts with
+ <filename>extension-release.</filename>, is located in the same directory and is tagged with a
+ <varname>user.extension-release.strict</varname> <citerefentry><refentrytitle>xattr</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ set to the string <literal>0</literal>, will be parsed instead, if the one with the expected name cannot be found.
+ The rest of this document that talks about <filename>os-release</filename> should be understood to apply to
+ <filename>extension-release</filename> too.</para>
+ </refsect2>
</refsect1>
<refsect1>
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 0–9,
a–z, ".", "_" and "-") identifying the operating system extensions support level, to indicate which
- extension images are supported. See:
+ extension images are supported. See <filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename>,
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/initrd.html">initrd</ulink> and
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
for more information.</para>
VARIANT_ID=workstation</programlisting>
</example>
+ <example>
+ <title><filename>extension-release</filename> file for an extension for Fedora Workstation 32</title>
+
+ <programlisting>ID=fedora
+VERSION_ID=32</programlisting>
+ </example>
+
<example>
<title>Reading <filename>os-release</filename> in
<citerefentry><refentrytitle>sh</refentrytitle><manvolnum>1</manvolnum></citerefentry></title>
and hence the systemd control group hierarchy.</para>
<para>The module also applies various resource management and runtime parameters to the new session, as
- configured in the <ulink url="https://systemd.io/USER_RECORD">JSON User Record</ulink> of the user, when
+ configured in the <ulink url="https://systemd.io/USER_RECORD">JSON User Records</ulink> of the user, when
one is defined.</para>
<para>On login, this module — in conjunction with <filename>systemd-logind.service</filename> — ensures the
top of <replaceable>IMAGE</replaceable> when attaching/detaching. This argument can be specified
multiple times, in which case the order in which images are laid down follows the rules specified in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for the <varname>ExtensionImages=</varname> directive.</para>
+ for the <varname>ExtensionImages=</varname> directive. 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>.
+ </para>
<para>Note that the same extensions have to be specified, in the same order, when attaching
and detaching.</para></listitem>
<para>If the special value <literal>auto</literal> is specified, the source to copy from is
automatically picked up from the running system (or the image specified with
<option>--image=</option> — if used). A partition that matches both the configured partition type (as
- declared with <varname>Type=</varname> above), and the currently mounted directory appropriate for
- that partition type is determined. For example, if the partition type is set to
+ declared with <varname>Type=</varname> described above), and the currently mounted directory
+ appropriate for that partition type is determined. For example, if the partition type is set to
<literal>root</literal> the partition backing the root directory (<filename>/</filename>) is used as
source to copy from — if its partition type is set to <literal>root</literal> as well. If the
declared type is <literal>usr</literal> the partition backing <filename>/usr/</filename> is used as
<varlistentry>
<term><varname>MakeDirectories=</varname></term>
- <listitem><para>akes one or more absolute paths, separated by whitespace, each declaring a directory
+ <listitem><para>Takes one or more absolute paths, separated by whitespace, each declaring a directory
to create within the new file system. Behaviour is similar to <varname>CopyFiles=</varname>, but
instead of copying in a set of files this just creates the specified directories with the default
mode of 0755 owned by the root user and group, plus all their parent directories (with the same
are copied in or the file system configured with <varname>Format=</varname> is created.</para>
<para>The LUKS2 UUID is automatically derived from the partition UUID in a stable fashion. If
- <literal>key-file</literal> or <literal>key-file+tpm2</literal> is used a key is added to the LUKS2
- superblock, configurable with the <option>--key-file=</option> switch to
+ <literal>key-file</literal> or <literal>key-file+tpm2</literal> is used, a key is added to the LUKS2
+ superblock, configurable with the <option>--key-file=</option> option to
<command>systemd-repart</command>. If <literal>tpm2</literal> or <literal>key-file+tpm2</literal> is
- used a key is added to the LUKS2 superblock that is enrolled to the local TPM2 chip, as configured
+ used, a key is added to the LUKS2 superblock that is enrolled to the local TPM2 chip, as configured
with the <option>--tpm2-device=</option> and <option>--tpm2-pcrs=</option> options to
<command>systemd-repart</command>.</para>
has no effect on explicit mounts, such as those done via <citerefentry
project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
<citerefentry
- project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry></para>
+ project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<para>If both bit 50 and 59 are set for a partition (i.e. the partition is marked both read-only and
marked for file system growing) the latter is typically without effect: the read-only flag takes
['org.freedesktop.resolve1', '5', [], 'ENABLE_RESOLVE'],
['org.freedesktop.systemd1', '5', [], ''],
['org.freedesktop.timedate1', '5', [], 'ENABLE_TIMEDATED'],
- ['os-release', '5', ['initrd-release'], ''],
+ ['os-release', '5', ['extension-release', 'initrd-release'], ''],
['pam_systemd', '8', [], 'HAVE_PAM'],
['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'],
['portablectl', '1', [], 'ENABLE_PORTABLED'],
'sd_id128_get_machine_app_specific'],
''],
['sd_id128_randomize', '3', [], ''],
- ['sd_id128_to_string', '3', ['sd_id128_from_string'], ''],
+ ['sd_id128_to_string',
+ '3',
+ ['SD_ID128_STRING_MAX', 'SD_ID128_TO_STRING', 'sd_id128_from_string'],
+ ''],
['sd_is_fifo',
'3',
['sd_is_mq',
<programlisting>#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)</programlisting>
<para><constant>SD_ID128_NULL</constant> may be used to refer to the 128-bit ID consisting of only
- <constant>NUL</constant> bytes.</para>
+ <constant>NUL</constant> bytes (i.e. all bits off).</para>
+
+ <para><constant>SD_ID128_ALLF</constant> may be used to refer to the 128-bit ID consisting of only
+ <constant>0xFF</constant> bytes (i.e. all bits on).</para>
<para><function>SD_ID128_MAKE_STR()</function> is similar to <function>SD_ID128_MAKE()</function>, but creates a
<type>const char*</type> expression that can be conveniently used in message formats and such:</para>
<refsect1>
<title>Description</title>
- <para><function>sd_id128_randomize()</function> generates a new
- randomized 128-bit ID and returns it in
- <parameter>ret</parameter>. Every invocation returns a new
- randomly generated ID. This uses the
- <filename>/dev/urandom</filename> kernel random number
- generator.</para>
+ <para><function>sd_id128_randomize()</function> generates a new randomized 128-bit ID and returns it in
+ <parameter>ret</parameter>. Every invocation returns a new randomly generated ID. This uses the
+ <citerefentry
+ project='man-pages'><refentrytitle>getrandom</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+ kernel random number generator.</para>
<para>Note that <function>sd_id128_randomize()</function> always returns a UUID Variant 1 Version 4
compatible ID. It is hence guaranteed that this function will never return the ID consisting of all zero
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>getrandom</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_id128_get_machine</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
<refnamediv>
<refname>sd_id128_to_string</refname>
+ <refname>SD_ID128_TO_STRING</refname>
<refname>sd_id128_from_string</refname>
+ <refname>SD_ID128_STRING_MAX</refname>
<refpurpose>Format or parse 128-bit IDs as strings</refpurpose>
</refnamediv>
<funcsynopsis>
<funcsynopsisinfo>#include <systemd/sd-id128.h></funcsynopsisinfo>
+ <funcsynopsisinfo>#define SD_ID128_STRING_MAX 33U</funcsynopsisinfo>
+
+ <funcsynopsisinfo>#define SD_ID128_TO_STRING(id) …</funcsynopsisinfo>
+
<funcprototype>
<funcdef>char *<function>sd_id128_to_string</function></funcdef>
- <paramdef>sd_id128_t <parameter>id</parameter>, char <parameter>s</parameter>[33]</paramdef>
+ <paramdef>sd_id128_t <parameter>id</parameter>, char <parameter>s</parameter>[static SD_ID128_STRING_MAX]</paramdef>
</funcprototype>
<funcprototype>
<refsect1>
<title>Description</title>
- <para><function>sd_id128_to_string()</function> formats a 128-bit
- ID as a character string. It expects the ID and a string array
- capable of storing 33 characters. The ID will be formatted as 32
- lowercase hexadecimal digits and be terminated by a
- <constant>NUL</constant> byte.</para>
+ <para><function>sd_id128_to_string()</function> formats a 128-bit ID as a character string. It expects
+ the ID and a string array capable of storing 33 characters
+ (<constant>SD_ID128_STRING_MAX</constant>). The ID will be formatted as 32 lowercase hexadecimal digits
+ and be terminated by a <constant>NUL</constant> byte.</para>
+
+ <para><function>SD_ID128_TO_STRING()</function> is a macro that wraps
+ <function>sd_id128_to_string()</function> and passes an appropriately sized buffer as second argument,
+ allocated as C99 compound literal. Each use will thus implicitly acquire a suitable buffer on the stack
+ which remains valid until the end of the current code block. This is usually the simplest way to acquire
+ a string representation of a 128-bit ID in a buffer that is valid in the current code block.</para>
- <para><function>sd_id128_from_string()</function> implements the reverse operation: it takes a 33 character string
- with 32 hexadecimal digits (either lowercase or uppercase, terminated by <constant>NUL</constant>) and parses them
- back into a 128-bit ID returned in <parameter>ret</parameter>. Alternatively, this call can also parse a
- 37-character string with a 128-bit ID formatted as RFC UUID. If <parameter>ret</parameter> is passed as
- <constant>NULL</constant> the function will validate the passed ID string, but not actually return it in parsed
- form.</para>
+ <para><function>sd_id128_from_string()</function> implements the reverse operation: it takes a 33
+ character string with 32 hexadecimal digits (either lowercase or uppercase, terminated by
+ <constant>NUL</constant>) and parses them back into a 128-bit ID returned in
+ <parameter>ret</parameter>. Alternatively, this call can also parse a 37-character string with a 128-bit
+ ID formatted as RFC UUID. If <parameter>ret</parameter> is passed as <constant>NULL</constant> the
+ function will validate the passed ID string, but not actually return it in parsed form.</para>
<para>Note that when parsing 37 character UUIDs this is done strictly in Big Endian byte order,
- i.e. according to <ulink url="https://tools.ietf.org/html/rfc4122">RFC4122</ulink> Variant 1
- rules, even if the UUID encodes a different variant. This matches behaviour in various other Linux
- userspace tools. It's probably wise to avoid UUIDs of other variant types.</para>
-
- <para>For more information about the <literal>sd_id128_t</literal>
- type see
- <citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
- Note that these calls operate the same way on all architectures,
- i.e. the results do not depend on endianness.</para>
-
- <para>When formatting a 128-bit ID into a string, it is often
- easier to use a format string for
- <citerefentry project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
- This is easily done using the
- <constant>SD_ID128_FORMAT_STR</constant> and <function>SD_ID128_FORMAT_VAL()</function> macros. For
- more information see
+ i.e. according to <ulink url="https://tools.ietf.org/html/rfc4122">RFC4122</ulink> Variant 1 rules, even
+ if the UUID encodes a different variant. This matches behaviour in various other Linux userspace
+ tools. It's probably wise to avoid UUIDs of other variant types.</para>
+
+ <para>For more information about the <literal>sd_id128_t</literal> type see
+ <citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>. Note that
+ these calls operate the same way on all architectures, i.e. the results do not depend on
+ endianness.</para>
+
+ <para>When formatting a 128-bit ID into a string, it is often easier to use a format string for
+ <citerefentry
+ project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>. This
+ is easily done using the <constant>SD_ID128_FORMAT_STR</constant> and
+ <function>SD_ID128_FORMAT_VAL()</function> macros. For more information see
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
- <para><function>sd_id128_to_string()</function> always succeeds
- and returns a pointer to the string array passed in.
- <function>sd_id128_from_string()</function> returns 0 on success, in
- which case <parameter>ret</parameter> is filled in, or a negative
- errno-style error code.</para>
+ <para><function>sd_id128_to_string()</function> always succeeds and returns a pointer to the string array
+ passed in. <function>sd_id128_from_string()</function> returns 0 on success, in which case
+ <parameter>ret</parameter> is filled in, or a negative errno-style error code.</para>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
</varlistentry>
<varlistentry>
- <term><command>bind</command> <replaceable>UNIT</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
-
- <listitem><para>Bind mounts a file or directory from the host into the specified unit's view. The first path
- argument is the source file or directory on the host, the second path argument is the destination file or
- directory in the unit's view. When the latter is omitted, the destination path in the unit's view is the same as
- the source path on the host. When combined with the <option>--read-only</option> switch, a ready-only bind
- mount is created. When combined with the <option>--mkdir</option> switch, the destination path is first created
- before the mount is applied. Note that this option is currently only supported for units that run within a mount
- namespace (e.g.: with <option>RootImage=</option>, <option>PrivateMounts=</option>, etc.). This command supports bind
- mounting directories, regular files, device nodes, <constant>AF_UNIX</constant> socket nodes, as well as FIFOs.
- The bind mount is ephemeral, and it is undone as soon as the current unit process exists.
- Note that the namespace mentioned here, where the bind mount will be added to, is the one where the main service
- process runs, as other processes run in distinct namespaces (e.g.: <option>ExecReload=</option>,
- <option>ExecStartPre=</option>, etc.) </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><command>mount-image</command> <replaceable>UNIT</replaceable> <replaceable>IMAGE</replaceable> [<replaceable>PATH</replaceable> [<replaceable>PARTITION_NAME</replaceable>:<replaceable>MOUNT_OPTIONS</replaceable>]]</term>
-
- <listitem><para>Mounts an image from the host into the specified unit's view. The first path argument is the source
- image on the host, the second path argument is the destination directory in the unit's view (ie: inside
- <option>RootImage=</option>/<option>RootDirectory=</option>). Any following argument is interpreted as a
- colon-separated tuple of partition name and comma-separated list of mount options for that partition. The format is the
- same as the service <option>MountImages=</option> setting. When combined with the <option>--read-only</option> switch, a
- ready-only mount is created. When combined with the <option>--mkdir</option> switch, the destination path is first
- created before the mount is applied. Note that this option is currently only supported for units that run within a mount
- namespace (e.g.: with <option>RootImage=</option>, <option>PrivateMounts=</option>, etc.).
- Note that the namespace mentioned here, where the image mount will be added to, is the one where the main service
- process runs, as other processes run in distinct namespaces (e.g.: <option>ExecReload=</option>,
- <option>ExecStartPre=</option>, etc.). Example:
+ <term>
+ <command>bind</command>
+ <replaceable>UNIT</replaceable>
+ <replaceable>PATH</replaceable>
+ [<replaceable>PATH</replaceable>]
+ </term>
+
+ <listitem><para>Bind-mounts a file or directory from the host into the specified unit's mount
+ namespace. The first path argument is the source file or directory on the host, the second path
+ argument is the destination file or directory in the unit's mount namespace. When the latter is
+ omitted, the destination path in the unit's mount namespace is the same as the source path on the
+ host. When combined with the <option>--read-only</option> switch, a ready-only bind mount is
+ created. When combined with the <option>--mkdir</option> switch, the destination path is first
+ created before the mount is applied.</para>
+
+ <para>Note that this option is currently only supported for units that run within a mount namespace
+ (e.g.: with <option>RootImage=</option>, <option>PrivateMounts=</option>, etc.). This command
+ supports bind-mounting directories, regular files, device nodes, <constant>AF_UNIX</constant>
+ socket nodes, as well as FIFOs. The bind mount is ephemeral, and it is undone as soon as the
+ current unit process exists. Note that the namespace mentioned here, where the bind mount will be
+ added to, is the one where the main service process runs. Other processes (those exececuted by
+ <option>ExecReload=</option>, <option>ExecStartPre=</option>, etc.) run in distinct namespaces.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <command>mount-image</command>
+ <replaceable>UNIT</replaceable>
+ <replaceable>IMAGE</replaceable>
+ [<replaceable>PATH</replaceable>
+ [<replaceable>PARTITION_NAME</replaceable>:<replaceable>MOUNT_OPTIONS</replaceable>]]
+ </term>
+
+ <listitem><para>Mounts an image from the host into the specified unit's mount namespace. The first
+ path argument is the source image on the host, the second path argument is the destination
+ directory in the unit's mount namespace (i.e. inside
+ <option>RootImage=</option>/<option>RootDirectory=</option>). The following argument, if any, is
+ interpreted as a colon-separated tuple of partition name and comma-separated list of mount options
+ for that partition. The format is the same as the service <option>MountImages=</option>
+ setting. When combined with the <option>--read-only</option> switch, a ready-only mount is
+ created. When combined with the <option>--mkdir</option> switch, the destination path is first
+ created before the mount is applied.</para>
+
+ <para>Note that this option is currently only supported for units that run within a mount namespace
+ (i.e. with <option>RootImage=</option>, <option>PrivateMounts=</option>, etc.). Note that the
+ namespace mentioned here where the image mount will be added to, is the one where the main service
+ process runs. Note that the namespace mentioned here, where the bind mount will be
+ added to, is the one where the main service process runs. Other processes (those exececuted by
+ <option>ExecReload=</option>, <option>ExecStartPre=</option>, etc.) run in distinct namespaces.
+ </para>
+
+ <para>Example:
<programlisting>systemctl mount-image foo.service /tmp/img.raw /var/lib/image root:ro,nosuid</programlisting>
- <programlisting>systemctl mount-image --mkdir bar.service /tmp/img.raw /var/lib/baz/img</programlisting></para></listitem>
+ <programlisting>systemctl mount-image --mkdir bar.service /tmp/img.raw /var/lib/baz/img</programlisting>
+ </para></listitem>
</varlistentry>
<varlistentry>
<term><option>--lines=</option></term>
<listitem>
- <para>When used with <command>status</command>, controls the number of journal lines to show, counting from
- the most recent ones. Takes a positive integer argument, or 0 to disable journal output. Defaults to
- 10.</para>
+ <para>When used with <command>status</command>, controls the number of journal lines to show,
+ counting from the most recent ones. Takes a positive integer argument, or 0 to disable journal
+ output. Defaults to 10.</para>
</listitem>
</varlistentry>
<term><option>--firmware-setup</option></term>
<listitem>
- <para>When used with the <command>reboot</command> command, indicate to the system's firmware to reboot into
- the firmware setup interface. Note that this functionality is not available on all systems.</para>
+ <para>When used with the <command>reboot</command> command, indicate to the system's firmware to
+ reboot into the firmware setup interface. Note that this functionality is not available on all
+ systems.</para>
</listitem>
</varlistentry>
<term><option>--boot-loader-menu=</option></term>
<listitem>
- <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to show the
- boot loader menu on the following boot. Takes a time value as parameter — indicating the menu timeout. Pass
- zero in order to disable the menu timeout. Note that not all boot loaders support this
- functionality.</para>
+ <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to
+ show the boot loader menu on the following boot. Takes a time value as parameter — indicating the
+ menu timeout. Pass zero in order to disable the menu timeout. Note that not all boot loaders
+ support this functionality.</para>
</listitem>
</varlistentry>
<term><option>--boot-loader-entry=</option></term>
<listitem>
- <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to boot into
- a specific boot loader entry on the following boot. Takes a boot loader entry identifier as argument, or
- <literal>help</literal> in order to list available entries. Note that not all boot loaders support this
- functionality.</para>
+ <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to
+ boot into a specific boot loader entry on the following boot. Takes a boot loader entry identifier
+ as argument, or <literal>help</literal> in order to list available entries. Note that not all boot
+ loaders support this functionality.</para>
</listitem>
</varlistentry>
generators enabled will generally result in some warnings.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--recursive-errors=<replaceable>MODE</replaceable></option></term>
+
+ <listitem><para>Control verification of units and their dependencies and whether
+ <command>systemd-analyze verify</command> exits with a non-zero process exit status or not. With
+ <command>yes</command>, return a non-zero process exit status when warnings arise during verification
+ of either the specified unit or any of its associated dependencies. This is the default. With
+ <command>no</command>, return a non-zero process exit status when warnings arise during verification
+ of only the specified unit. With <command>one</command>, return a non-zero process exit status when
+ warnings arise during verification of either the specified unit or its immediate dependencies. </para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--root=<replaceable>PATH</replaceable></option></term>
- <listitem><para>With <command>cat-files</command>, show config files underneath
- the specified root path <replaceable>PATH</replaceable>.</para></listitem>
+ <listitem><para>With <command>cat-files</command> and <command>verify</command>,
+ operate on files underneath the specified root path <replaceable>PATH</replaceable>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--image=<replaceable>PATH</replaceable></option></term>
+
+ <listitem><para>With <command>cat-files</command> and <command>verify</command>,
+ operate on files inside the specified image path <replaceable>PATH</replaceable>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--offline=<replaceable>BOOL</replaceable></option></term>
+
+ <listitem><para>With <command>security</command>, perform an offline security review
+ of the specified unit file(s), i.e. does not have to rely on PID 1 to acquire security
+ information for the files like the <command>security</command> verb when used by itself does.
+ This means that <option>--offline=</option> can be used with <option>--root=</option> and
+ <option>--image=</option> as well. If a unit's overall exposure level is above that set by
+ <option>--threshold=</option> (default value is 100), <option>--offline=</option> will return
+ an error.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--threshold=<replaceable>NUMBER</replaceable></option></term>
+
+ <listitem><para>With <command>security</command>, allow the user to set a custom value
+ to compare the overall exposure level with, for the specified unit file(s). If a unit's
+ overall exposure level, is greater than that set by the user, <command>security</command>
+ will return an error. <option>--threshold=</option> can be used with <option>--offline=</option>
+ as well and its default value is 100.</para></listitem>
</varlistentry>
<varlistentry>
<title>Key bindings</title>
<para>The following keys may be used in the boot menu:</para>
+ <!-- Developer commands Q/v/Ctrl+l deliberately not advertised. -->
+
<variablelist>
<varlistentry>
<term><keycap>↑</keycap> (Up)</term>
</varlistentry>
<varlistentry>
- <term><keycap>v</keycap></term>
- <listitem><para>Show systemd-boot, UEFI, and firmware versions</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><keycap>P</keycap></term>
+ <term><keycap>p</keycap></term>
<listitem><para>Print status</para></listitem>
</varlistentry>
- <varlistentry>
- <term><keycap>Q</keycap></term>
- <listitem><para>Quit</para></listitem>
- </varlistentry>
-
<varlistentry>
<term><keycap>h</keycap></term>
<term><keycap>?</keycap></term>
<term><keycap>F1</keycap></term>
<listitem><para>Show a help screen</para></listitem>
</varlistentry>
-
- <varlistentry>
- <term><keycombo><keycap>Ctrl</keycap><keycap>l</keycap></keycombo></term>
- <listitem><para>Reprint the screen</para></listitem>
- </varlistentry>
</variablelist>
<para>The following keys may be pressed during bootup or in the boot menu to directly boot a specific
<term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
<listitem><para>Enroll a PKCS#11 security token or smartcard (e.g. a YubiKey). Expects a PKCS#11
- smart card URI referring to the token. Alternatively the special value <literal>auto</literal> may
+ smartcard URI referring to the token. Alternatively the special value <literal>auto</literal> may
be specified, in order to automatically determine the URI of a currently plugged in security token
(of which there must be exactly one). The special value <literal>list</literal> may be used to
enumerate all suitable PKCS#11 tokens currently plugged in. The security token must contain an RSA
<varlistentry>
<term><literal>passwd.shell.root</literal></term>
- <listitem><para>Specifies the shell binary to use for the specified account when creating it.
+ <listitem><para>Specifies the shell binary to use for the specified account.
Equivalent to the credential of the same name defined for the
<citerefentry><refentrytitle>systemd-sysusers.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
service.</para></listitem>
<varlistentry>
<term><option>--image=<replaceable>path</replaceable></option></term>
- <listitem><para>Takes a path to a device node or refular file as argument. This is similar to
+ <listitem><para>Takes a path to a device node or regular file as argument. This is similar to
<option>--root=</option> as described above, but operates on a disk image instead of a directory
tree.</para></listitem>
</varlistentry>
</varlistentry>
<varlistentry>
- <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
- <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
-
- <listitem><para>Specifies an environment variable assignment
- to pass to the init process in the container, in the format
- <literal>NAME=VALUE</literal>. This may be used to override
- the default variables or to set additional variables. This
- parameter may be used more than once.</para></listitem>
+ <term><option>-E <replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+ <term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+
+ <listitem><para>Specifies an environment variable to pass to the init process in the container. This
+ may be used to override the default variables or to set additional variables. It may be used more
+ than once to set multiple variables. When <literal>=</literal> and <replaceable>VALUE</replaceable>
+ are omitted, the value of the variable with the same name in the program environment will be used.
+ </para></listitem>
</varlistentry>
<varlistentry>
</orderedlist>
<para>The combination of the three operations above ensures that it is possible to log into the
- host's user account inside the container as if it was local to the container. The user is only mapped
- transiently, while the container is running and the mapping itself does not result in persistent
- changes to the container (except maybe for generated log messages at login time, and similar). Note
- that in particular the UID/GID assignment in the container is not made persistently. If the user is
- mapped transiently, it is best to not allow the user to make persistent changes to the container. If
- the user leaves files or directories owned by the user, and those UIDs/GIDs are recycled during later
+ container using the same account information as on the host. The user is only mapped transiently,
+ while the container is running, and the mapping itself does not result in persistent changes to the
+ container (except maybe for log messages generated at login time, and similar). Note that in
+ particular the UID/GID assignment in the container is not made persistently. If the user is mapped
+ transiently, it is best to not allow the user to make persistent changes to the container. If the
+ user leaves files or directories owned by the user, and those UIDs/GIDs are reused during later
container invocations (possibly with a different <option>--bind-user=</option> mapping), those files
and directories will be accessible to the "new" user.</para>
-b</programlisting>
<para>The above command line will invoke the specified image file <filename>image.raw</filename> in
- volatile mode, i.e with an empty <filename>/etc/</filename> and <filename>/var/</filename>, so that
- the container's payload recognizes this as first boot condition, and will invoke
- <filename>systemd-firstboot.service</filename>, which then read the two passed credentials to
+ volatile mode, i.e. with empty <filename>/etc/</filename> and <filename>/var/</filename>. The
+ container payload will recognize this as a first boot, and will invoke
+ <filename>systemd-firstboot.service</filename>, which then reads the two passed credentials to
configure the system's initial locale and root password.</para>
</listitem>
</varlistentry>
<para>Most of <command>systemd-portabled</command>'s functionality is accessible through the
<citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> command.</para>
- <para>See the <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable
- Services Documentation</ulink> for details about the concepts this service implements.</para>
+ <para>See <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> for details about
+ the concepts this service implements.</para>
</refsect1>
<refsect1>
fragility in both directions: a valid global name could be obscured by a local name, and resolution of
a relative local name could suddenly break when a new top-level domain is created, or when a new
subdomain of a top-level domain in registered. Resolving any given name as either relative or absolute
- avoids this ambiguity.)</para></footnote></para></listitem>
+ avoids this ambiguity.</para></footnote></para></listitem>
<listitem><para>This resolver has a notion of the special <literal>.local</literal> domain used for
MulticastDNS, and will not route queries with that suffix to unicast DNS servers unless explicitly
</varlistentry>
<varlistentry>
- <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
- <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
+ <term><option>-E <replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+ <term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
- <listitem><para>Runs the service process with the specified environment variable set.
- Also see <varname>Environment=</varname> in
+ <listitem><para>Runs the service process with the specified environment variable set. This parameter
+ may be used more than once to set multiple variables. When <literal>=</literal> and
+ <replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
+ program environment will be used.</para>
+
+ <para>Also see <varname>Environment=</varname> in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
operating system tree. When one or more system extension images are activated, their
<filename>/usr/</filename> and <filename>/opt/</filename> hierarchies are combined via
<literal>overlayfs</literal> with the same hierarchies of the host OS, and the host
- <filename>/usr/</filename> and <filename>/opt</filename> overmounted with it ("merging"). When they are
+ <filename>/usr/</filename> and <filename>/opt/</filename> overmounted with it ("merging"). When they are
deactivated, the mount point is disassembled — again revealing the unmodified original host version of
the hierarchy ("unmerging"). Merging thus makes the extension's resources suddenly appear below the
<filename>/usr/</filename> and <filename>/opt/</filename> hierarchies as if they were included in the
<orderedlist>
<listitem><para>Plain directories or btrfs subvolumes containing the OS tree</para></listitem>
<listitem><para>Disk images with a GPT disk label, following the <ulink
- url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partition Specification</ulink></para></listitem>
+ url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink></para></listitem>
<listitem><para>Disk images lacking a partition table, with a naked Linux file system (e.g. squashfs or ext4)</para></listitem>
</orderedlist>
<title>Uses</title>
<para>The primary use case for system images are immutable environments where debugging and development
- tools shall optionally be made available, but not included in the immutable base OS image itself
- (e.g. <filename>strace</filename> and <filename>gdb</filename> shall be an optionally installable
- addition in order to make debugging/development easier). System extension images should not be
- misunderstood as a generic software packaging framework, as no dependency scheme is available: system
- extensions should carry all files they need themselves, except for those already shipped in the
- underlying host system image. Typically, system extension images are built at the same time as the base
- OS image — within the same build system.</para>
+ tools shall optionally be made available, but not included in the immutable base OS image itself (e.g.
+ <citerefentry project='man-pages'><refentrytitle>strace</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ and
+ <citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ shall be an optionally installable addition in order to make debugging/development easier). System
+ extension images should not be misunderstood as a generic software packaging framework, as no dependency
+ scheme is available: system extensions should carry all files they need themselves, except for those
+ already shipped in the underlying host system image. Typically, system extension images are built at the
+ same time as the base OS image — within the same build system.</para>
<para>Another use case for the system extension concept is temporarily overriding OS supplied resources
with newer ones, for example to install a locally compiled development version of some low-level
names in status messages (e.g. <literal>systemd-journald.service</literal>), instead of the longer
and more informative descriptions set with <varname>Description=</varname> (e.g. <literal>Journal
Logging Service</literal>). If <option>combined</option>, the system manager will use both unit names
- and descriptions in status messages (e.g. <literal>systemdmd-jouranld.service - Journal Logging
+ and descriptions in status messages (e.g. <literal>systemd-journald.service - Journal Logging
Service</literal>).</para>
<para>See
for the first time it is possible to configure the root user's password to be <literal>systemd</literal>
like this:</para>
- <para><programlisting># systemd-nspawn --image=… --set-credential=password.hashed-password.root:'$y$j9T$yAuRJu1o5HioZAGDYPU5d.$F64ni6J2y2nNQve90M/p0ZP0ECP/qqzipNyaY9fjGpC' …</programlisting></para>
+ <para><programlisting># systemd-nspawn --image=… --set-credential=passwd.hashed-password.root:'$y$j9T$yAuRJu1o5HioZAGDYPU5d.$F64ni6J2y2nNQve90M/p0ZP0ECP/qqzipNyaY9fjGpC' …</programlisting></para>
<para>Note again that the data specified in these credentials is consulted only when creating an account
for the first time, it may not be used for changing the password or shell of an account that already
<term><filename>/var/lib/systemd/timesync/clock</filename></term>
<listitem>
- <para>The modification time ("mtime") of this file indicates the timestamp of the last successful
- synchronization (or at least the systemd build date, in case synchronization was not possible). It
- is used to ensure that the system clock remains roughly monotonic across reboots, in case no local
- RTC is available.</para>
+ <para>The modification time ("mtime") of this file is updated on each successful NTP synchronization
+ or after each <varname>SaveIntervalSec=</varname> time interval, as specified in
+ <citerefentry><refentrytitle>timesyncd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ At the minimum, it will be set to the systemd build date. It is used to ensure that the system clock
+ remains roughly monotonic across reboots, in case no local RTC is available.</para>
</listitem>
</varlistentry>
JSON user/group records from classic UNIX/glibc NSS user/group records in order to provide full backwards
compatibility. It may also pick up statically defined JSON user/group records from drop-in files in
<filename>/etc/userdb/</filename>, <filename>/run/userdb/</filename>,
- <filename>/run/host/userdb/</filename> and <filename>/use/lib/userdb/</filename>.</para>
+ <filename>/run/host/userdb/</filename> and <filename>/usr/lib/userdb/</filename>.</para>
<para>Most of <command>systemd-userdbd</command>'s functionality is accessible through the
<citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
command.</para>
<para>The user and group records this service provides access to follow the <ulink
- url="https://systemd.io/USER_RECORD">JSON User Record</ulink> and <ulink
+ url="https://systemd.io/USER_RECORD">JSON User Records</ulink> and <ulink
url="https://systemd.io/GROUP_RECORD">JSON Group Record</ulink> definitions. This service implements the
<ulink url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>, and
multiplexes access other services implementing this API, too. It is thus both server and client of this
<para>At early boot and when the system manager configuration is reloaded kernel command line configuration for
integrity protected block devices is translated into <filename>systemd-veritysetup@.service</filename> units by
<citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+ <para><filename>systemd-veritysetup@.service</filename> calls <command>systemd-veritysetup</command>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Commands</title>
+
+ <para>The following commands are understood by <command>systemd-veritysetup</command>:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>attach</option>
+ <replaceable>volume</replaceable>
+ <replaceable>datadevice</replaceable>
+ <replaceable>hashdevice</replaceable>
+ <replaceable>roothash</replaceable>
+ [<replaceable>option</replaceable>...]
+ </term>
+
+ <listitem><para>Create a block device <replaceable>volume</replaceable> using
+ <replaceable>datadevice</replaceable> and <replaceable>hashdevice</replaceable> as the backing
+ devices. <replaceable>roothash</replaceable> forms the root of the tree of hashes stored on
+ <replaceable>hashdevice</replaceable>. See
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/verity.html">
+ Kernel dm-verity</ulink> documentation for details.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>detach</option>
+ <replaceable>volume</replaceable>
+ </term>
+
+ <listitem><para>Detach (destroy) the block device
+ <replaceable>volume</replaceable>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>help</option>
+ </term>
+
+ <listitem><para>Print short information about command syntax.</para></listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
<refsect1>
<para>This option is supported only for disk images that contain a single file system, without an
enveloping partition table. Images that contain a GPT partition table should instead include both
root file system and matching Verity data in the same image, implementing the <ulink
- url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partition Specification</ulink>.</para>
+ url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>.</para>
<xi:include href="system-only.xml" xpointer="singular"/></listitem>
</varlistentry>
<term><varname>ExtensionImages=</varname></term>
<listitem><para>This setting is similar to <varname>MountImages=</varname> in that it mounts a file
- system hierarchy from a block device node or loopback file, but instead of providing a destination path,
- an overlay will be set up. This option expects a whitespace separated list of mount definitions. Each
- definition consists of a source path, optionally followed by a colon and a list of mount options.</para>
+ system hierarchy from a block device node or loopback file, but instead of providing a destination
+ path, an overlay will be set up. This option expects a whitespace separated list of mount
+ definitions. Each definition consists of a source path, optionally followed by a colon and a list of
+ mount options.</para>
<para>A read-only OverlayFS will be set up on top of <filename>/usr/</filename> and
- <filename>/opt/</filename> hierarchies from the root. The order in which the images are listed
- will determine the order in which the overlay is laid down: images specified first to last will result
- in overlayfs layers bottom to top.</para>
+ <filename>/opt/</filename> hierarchies. The order in which the images are listed will determine the
+ order in which the overlay is laid down: images specified first to last will result in overlayfs
+ layers bottom to top.</para>
<para>Mount options may be defined as a single comma-separated list of options, in which case they
will be implicitly applied to the root partition on the image, or a series of colon-separated tuples
paths. If the empty string is assigned, the entire list of mount paths defined prior to this is
reset.</para>
+ <para>Each image must carry 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>
+
<para>When <varname>DevicePolicy=</varname> is set to <literal>closed</literal> or
<literal>strict</literal>, or set to <literal>auto</literal> and <varname>DeviceAllow=</varname> is
set, then this setting adds <filename>/dev/loop-control</filename> with <constant>rw</constant> mode,
<listitem><para>Sets environment variables for executed processes. Each line is unquoted using the
rules described in "Quoting" section in
- <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
and becomes a list of variable assignments. If you need to assign a value containing spaces or the
equals sign to a variable, put quotes around the whole assignment. Variable expansion is not
performed inside the strings and the <literal>$</literal> character has no special meaning. Specifier
<term><varname>MTUBytes=</varname></term>
<listitem>
<para>The maximum transmission unit in bytes to set for the
- device. The usual suffixes K, M, G, are supported and are
+ device. The usual suffixes K, M, G are supported and are
understood to the base of 1024.</para>
</listitem>
</varlistentry>
<term><varname>BitsPerSecond=</varname></term>
<listitem>
<para>The speed to set for the device, the value is rounded
- down to the nearest Mbps. The usual suffixes K, M, G, are
+ down to the nearest Mbps. The usual suffixes K, M, G are
supported and are understood to the base of 1000.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ReceiveChecksumOffload=</varname></term>
<listitem>
- <para>Takes a boolean. If set to true, the hardware offload for checksumming of ingress
+ <para>Takes a boolean. If set to true, hardware offload for checksumming of ingress
network packets is enabled. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>TransmitChecksumOffload=</varname></term>
<listitem>
- <para>Takes a boolean. If set to true, the hardware offload for checksumming of egress
+ <para>Takes a boolean. If set to true, hardware offload for checksumming of egress
network packets is enabled. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>TCPSegmentationOffload=</varname></term>
<listitem>
- <para>Takes a boolean. If set to true, the TCP Segmentation Offload (TSO) is enabled.
+ <para>Takes a boolean. If set to true, TCP Segmentation Offload (TSO) is enabled.
When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>TCP6SegmentationOffload=</varname></term>
<listitem>
- <para>Takes a boolean. If set to true, the TCP6 Segmentation Offload (tx-tcp6-segmentation) is enabled.
+ <para>Takes a boolean. If set to true, TCP6 Segmentation Offload (tx-tcp6-segmentation) is enabled.
When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>GenericSegmentationOffload=</varname></term>
<listitem>
- <para>Takes a boolean. If set to true, the Generic Segmentation Offload (GSO) is enabled.
+ <para>Takes a boolean. If set to true, Generic Segmentation Offload (GSO) is enabled.
When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>GenericReceiveOffload=</varname></term>
<listitem>
- <para>Takes a boolean. If set to true, the Generic Receive Offload (GRO) is enabled.
+ <para>Takes a boolean. If set to true, Generic Receive Offload (GRO) is enabled.
When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>GenericReceiveOffloadHardware=</varname></term>
+ <listitem>
+ <para>Takes a boolean. If set to true, hardware accelerated Generic Receive Offload (GRO) is
+ enabled. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>LargeReceiveOffload=</varname></term>
<listitem>
- <para>Takes a boolean. If set to true, the Large Receive Offload (LRO) is enabled.
+ <para>Takes a boolean. If set to true, Large Receive Offload (LRO) is enabled.
When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RxChannels=</varname></term>
+ <term><varname>TxChannels=</varname></term>
+ <term><varname>OtherChannels=</varname></term>
+ <term><varname>CombinedChannels=</varname></term>
<listitem>
- <para>Sets the number of receive channels (a number between 1 and 4294967295) .</para>
+ <para>Specifies the number of receive, transmit, other, or combined channels, respectively.
+ Takes an unsigned integer in the range 1…4294967295 or <literal>max</literal>. If set to
+ <literal>max</literal>, the advertised maximum value of the hardware will be used. When
+ unset, the number will not be changed. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>TxChannels=</varname></term>
+ <term><varname>RxBufferSize=</varname></term>
+ <term><varname>RxMiniBufferSize=</varname></term>
+ <term><varname>RxJumboBufferSize=</varname></term>
+ <term><varname>TxBufferSize=</varname></term>
<listitem>
- <para>Sets the number of transmit channels (a number between 1 and 4294967295).</para>
+ <para>Specifies the maximum number of pending packets in the NIC receive buffer, mini receive
+ buffer, jumbo receive buffer, or transmit buffer, respectively. Takes an unsigned integer in
+ the range 1…4294967295 or <literal>max</literal>. If set to <literal>max</literal>, the
+ advertised maximum value of the hardware will be used. When unset, the number will not be
+ changed. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>OtherChannels=</varname></term>
+ <term><varname>RxFlowControl=</varname></term>
<listitem>
- <para>Sets the number of other channels (a number between 1 and 4294967295).</para>
+ <para>Takes a boolean. When set, enables receive flow control, also known as the ethernet
+ receive PAUSE message (generate and send ethernet PAUSE frames). When unset, the kernel's
+ default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>CombinedChannels=</varname></term>
+ <term><varname>TxFlowControl=</varname></term>
<listitem>
- <para>Sets the number of combined set channels (a number between 1 and 4294967295).</para>
+ <para>Takes a boolean. When set, enables transmit flow control, also known as the ethernet
+ transmit PAUSE message (respond to received ethernet PAUSE frames). When unset, the kernel's
+ default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>RxBufferSize=</varname></term>
+ <term><varname>AutoNegotiationFlowControl=</varname></term>
<listitem>
- <para>Takes an integer. Specifies the maximum number of pending packets in the NIC receive buffer.
- When unset, the kernel's default will be used.</para>
+ <para>Takes a boolean. When set, auto negotiation enables the interface to exchange state
+ advertisements with the connected peer so that the two devices can agree on the ethernet
+ PAUSE configuration. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>RxMiniBufferSize=</varname></term>
+ <term><varname>GenericSegmentOffloadMaxBytes=</varname></term>
<listitem>
- <para>Takes an integer. Specifies the maximum number of pending packets in the NIC mini receive buffer.
- When unset, the kernel's default will be used.</para>
+ <para>Specifies the maximum size of a Generic Segment Offload (GSO) packet the
+ device should accept. The usual suffixes K, M, G are supported and are
+ understood to the base of 1024. An unsigned integer in the range 1…65536.
+ Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>RxJumboBufferSize=</varname></term>
+ <term><varname>GenericSegmentOffloadMaxSegments=</varname></term>
<listitem>
- <para>Takes an integer. Specifies the maximum number of pending packets in the NIC jumbo receive buffer.
- When unset, the kernel's default will be used.</para>
+ <para>Specifies the maximum number of Generic Segment Offload (GSO) segments the device should
+ accept. An unsigned integer in the range 1…65535. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>TxBufferSize=</varname></term>
+ <term><varname>UseAdaptiveRxCoalesce=</varname></term>
+ <term><varname>UseAdaptiveTxCoalesce=</varname></term>
<listitem>
- <para>Takes an integer. Specifies the maximum number of pending packets in the NIC transmit buffer.
- When unset, the kernel's default will be used.</para>
+ <para>Boolean properties that, when set, enable/disable adaptive Rx/Tx coalescing if the hardware
+ supports it. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>RxFlowControl=</varname></term>
+ <term><varname>RxCoalesceSec=</varname></term>
+ <term><varname>RxCoalesceIrqSec=</varname></term>
+ <term><varname>RxCoalesceLowSec=</varname></term>
+ <term><varname>RxCoalesceHighSec=</varname></term>
+ <term><varname>TxCoalesceSec=</varname></term>
+ <term><varname>TxCoalesceIrqSec=</varname></term>
+ <term><varname>TxCoalesceLowSec=</varname></term>
+ <term><varname>TxCoalesceHighSec=</varname></term>
<listitem>
- <para>Takes a boolean. When set, enables the receive flow control, also known as the ethernet
- receive PAUSE message (generate and send ethernet PAUSE frames). When unset, the kernel's
- default will be used.</para>
+ <para>These properties configure the delay before Rx/Tx interrupts are generated after a packet is
+ sent/received. The <literal>Irq</literal> properties come into effect when the host is servicing an
+ IRQ. The <literal>Low</literal> and <literal>High</literal> properties come into effect when the
+ packet rate drops below the low packet rate threshold or exceeds the high packet rate threshold
+ respectively if adaptive Rx/Tx coalescing is enabled. When unset, the kernel's defaults will be
+ used.</para>
</listitem>
</varlistentry>
- <varlistentry>
- <term><varname>TxFlowControl=</varname></term>
+ <varlistentry>
+ <term><varname>RxMaxCoalescedFrames=</varname></term>
+ <term><varname>RxMaxCoalescedIrqFrames=</varname></term>
+ <term><varname>RxMaxCoalescedLowFrames=</varname></term>
+ <term><varname>RxMaxCoalescedHighFrames=</varname></term>
+ <term><varname>TxMaxCoalescedFrames=</varname></term>
+ <term><varname>TxMaxCoalescedIrqFrames=</varname></term>
+ <term><varname>TxMaxCoalescedLowFrames=</varname></term>
+ <term><varname>TxMaxCoalescedHighFrames=</varname></term>
<listitem>
- <para>Takes a boolean. When set, enables the transmit flow control, also known as the ethernet
- transmit PAUSE message (respond to received ethernet PAUSE frames). When unset, the kernel's
- default will be used.</para>
+ <para>These properties configure the maximum number of frames that are sent/received before a Rx/Tx
+ interrupt is generated. The <literal>Irq</literal> properties come into effect when the host is
+ servicing an IRQ. The <literal>Low</literal> and <literal>High</literal> properties come into
+ effect when the packet rate drops below the low packet rate threshold or exceeds the high packet
+ rate threshold respectively if adaptive Rx/Tx coalescing is enabled. When unset, the kernel's
+ defaults will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>AutoNegotiationFlowControl=</varname></term>
+ <term><varname>CoalescePacketRateLow=</varname></term>
+ <term><varname>CoalescePacketRateHigh=</varname></term>
<listitem>
- <para>Takes a boolean. When set, the auto negotiation enables the interface to exchange state
- advertisements with the connected peer so that the two devices can agree on the ethernet
- PAUSE configuration. When unset, the kernel's default will be used.</para>
+ <para>These properties configure the low and high packet rate (expressed in packets per second)
+ threshold respectively and are used to determine when the corresponding coalescing settings for low
+ and high packet rates come into effect if adaptive Rx/Tx coalescing is enabled. If unset, the
+ kernel's defaults will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>GenericSegmentOffloadMaxBytes=</varname></term>
+ <term><varname>CoalescePacketRateSampleIntervalSec=</varname></term>
<listitem>
- <para>Specifies the maximum size of a Generic Segment Offload (GSO) packet the
- device should accept. The usual suffixes K, M, G, are supported and are
- understood to the base of 1024. An unsigned integer in the range 1…65536.
- Defaults to unset.</para>
+ <para>Configures how often to sample the packet rate used for adaptive Rx/Tx coalescing. This
+ property cannot be zero. This lowest time granularity supported by this property is seconds.
+ Partial seconds will be rounded up before being passed to the kernel. If unset, the kernel's
+ default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>GenericSegmentOffloadMaxSegments=</varname></term>
+ <term><varname>StatisticsBlockCoalesceSec=</varname></term>
<listitem>
- <para>Specifies the maximum number of a Generic Segment Offload (GSO) segments the device should
- accept. An unsigned integer in the range 1…65535. Defaults to unset.</para>
+ <para>How long to delay driver in-memory statistics block updates. If the driver does not have an
+ in-memory statistic block, this property is ignored. This property cannot be zero. If unset, the
+ kernel's default will be used.</para>
</listitem>
</varlistentry>
<term><varname>DefaultPVID=</varname></term>
<listitem>
<para>This specifies the default port VLAN ID of a newly attached bridge port.
- Set this to an integer in the range 1â\80\934094 or <literal>none</literal> to disable the PVID.</para>
+ Set this to an integer in the range 1â\80¦4094 or <literal>none</literal> to disable the PVID.</para>
</listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>Id=</varname></term>
<listitem>
- <para>The VLAN ID to use. An integer in the range 0â\80\934094.
+ <para>The VLAN ID to use. An integer in the range 0â\80¦4094.
This setting is compulsory.</para>
</listitem>
</varlistentry>
<term><varname>TTL=</varname></term>
<listitem>
<para>A fixed Time To Live N on Virtual eXtensible Local Area Network packets.
- Takes <literal>inherit</literal> or a number in the range 0â\80\93255. 0 is a special
+ Takes <literal>inherit</literal> or a number in the range 0â\80¦255. 0 is a special
value meaning inherit the inner protocol's TTL value. <literal>inherit</literal>
means that it will inherit the outer protocol's TTL value.</para>
</listitem>
<varlistentry>
<term><varname>TunnelId=</varname></term>
<listitem>
- <para>Specifies the tunnel identifier. Takes an number in the range 1â\80\934294967295. The value used
+ <para>Specifies the tunnel identifier. Takes an number in the range 1â\80¦4294967295. The value used
must match the <literal>PeerTunnelId=</literal> value being used at the peer. This setting is
compulsory.</para>
</listitem>
<varlistentry>
<term><varname>SessionId=</varname></term>
<listitem>
- <para>Specifies the session identifier. Takes an number in the range 1â\80\934294967295. The value used
+ <para>Specifies the session identifier. Takes an number in the range 1â\80¦4294967295. The value used
must match the <literal>SessionId=</literal> value being used at the peer. This setting is
compulsory.</para>
</listitem>
<varlistentry>
<term><varname>PeerSessionId=</varname></term>
<listitem>
- <para>Specifies the peer session identifier. Takes an number in the range 1â\80\934294967295.
+ <para>Specifies the peer session identifier. Takes an number in the range 1â\80¦4294967295.
The value used must match the <literal>PeerSessionId=</literal> value being used at the peer.
This setting is compulsory.</para>
</listitem>
<term><varname>TTL=</varname></term>
<listitem>
<para>A fixed Time To Live N on tunneled packets. N is a
- number in the range 1â\80\93255. 0 is a special value meaning that
+ number in the range 1â\80¦255. 0 is a special value meaning that
packets inherit the TTL value. The default value for IPv4
tunnels is 0 (inherit). The default value for IPv6 tunnels is
64.</para>
It is only used for IPv6 tunnels.
A flow label of zero is used to indicate packets that have
not been labeled.
- It can be configured to a value in the range 0â\80\930xFFFFF, or be
+ It can be configured to a value in the range 0â\80¦0xFFFFF, or be
set to <literal>inherit</literal>, in which case the original flowlabel is used.</para>
</listitem>
</varlistentry>
<para>Sets a comma-separated list of IP (v4 or v6) addresses with CIDR masks
from which this peer is allowed to send incoming traffic and to
which outgoing traffic for this peer is directed.</para>
+
<para>The catch-all 0.0.0.0/0 may be specified for matching all IPv4 addresses,
and ::/0 may be specified for matching all IPv6 addresses.</para>
- <para>Note that this only affects "routing inside the network interface itself",
- as in, which wireguard peer packets with a specific destination address are sent to,
- and what source addresses are accepted from which peer.</para>
- <para>To cause packets to be sent via wireguard in first place, a route needs
- to be added, as well - either in the <literal>[Routes]</literal> section on the
- <literal>.network</literal> matching the wireguard interface, or outside of networkd.
- </para>
+
+ <para>Note that this only affects <emphasis>routing inside the network interface itself</emphasis>,
+ i.e. the packets that pass through the tunnel itself. To cause packets to be sent via the tunnel in
+ the first place, an appropriate route needs to be added as well — either in the
+ <literal>[Routes]</literal> section on the <literal>.network</literal> matching the wireguard
+ interface, or externally to <filename>systemd-networkd</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>AdUserPortKey=</varname></term>
<listitem>
<para>Specifies the 802.3ad user defined portion of the port key. Takes a number in the range
- 0â\80\931023.</para>
+ 0â\80¦1023.</para>
</listitem>
</varlistentry>
<refsect1>
<title>[BatmanAdvanced] Section Options</title>
- <para>The [BatmanAdvanced] section only applies for
- netdevs of kind <literal>batadv</literal> and accepts the
- following keys:</para>
+
+ <para>The [BatmanAdvanced] section only applies for netdevs of kind <literal>batadv</literal> and accepts
+ the following keys:</para>
<variablelist class='network-directives'>
<varlistentry>
<listitem>
<para>Takes a boolean. If set to true, promiscuous mode of the interface is enabled.
Defaults to unset.</para>
+ <para>If this is set to false for the underlying link of a <literal>passthru</literal> mode MACVLAN/MACVTAP,
+ the virtual interface will be created with the <literal>nopromisc</literal> flag set.</para>
</listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>Label=</varname></term>
<listitem>
- <para>An address label.</para>
+ <para>Specifies the label for the IPv4 address. The label must be a 7-bit ASCII string with
+ a length of 1…15 characters. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Priority=</varname></term>
<listitem>
<para>Specifies the priority of this rule. <varname>Priority=</varname> is an unsigned
- integer. Higher number means lower priority, and rules get processed in order of increasing number.</para>
+ integer in the range 0…4294967295. Higher number means lower priority, and rules get
+ processed in order of increasing number. Defaults to unset, and the kernel will pick
+ a value dynamically.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Metric=</varname></term>
<listitem>
<para>The metric of the route. Takes an unsigned integer in the range 0…4294967295.
- Defaluts to unset, and the kernel's default will be used.</para>
+ Defaults to unset, and the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>SendHostname=</varname></term>
<listitem>
<para>When true (the default), the machine's hostname (or the value specified with
- <varname>Hostname=</varname> below) will be sent to the DHCP server. Note that the hostname must
- consist only of 7-bit ASCII lower-case characters and no spaces or dots, and be formatted as a
- valid DNS domain name. Otherwise, the hostname is not sent even if this option is true.</para>
+ <varname>Hostname=</varname>, described below) will be sent to the DHCP server. Note that the
+ hostname must consist only of 7-bit ASCII lower-case characters and no spaces or dots, and be
+ formatted as a valid DNS domain name. Otherwise, the hostname is not sent even if this option is
+ true.</para>
</listitem>
</varlistentry>
<!-- How to use the DHCP lease -->
+ <varlistentry>
+ <term><varname>Label=</varname></term>
+ <listitem>
+ <para>Specifies the label for the IPv4 address received from the DHCP server.
+ The label must be a 7-bit ASCII string with a length of 1…15 characters.
+ Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>UseDNS=</varname></term>
<listitem>
<varlistentry>
<term><varname>RouteMetric=</varname></term>
<listitem>
- <para>Set the routing metric for routes specified by the DHCP server. Takes an unsigned
- integer in the range 0…4294967295. Defaults to 1024.</para>
+ <para>Set the routing metric for routes specified by the DHCP server (including the prefix
+ route added for the specified prefix). Takes an unsigned integer in the range 0…4294967295.
+ Defaults to 1024.</para>
</listitem>
</varlistentry>
<term><varname>FallbackLeaseLifetimeSec=</varname></term>
<listitem>
<para>Allows to set DHCPv4 lease lifetime when DHCPv4 server does not send the lease lifetime.
- Takes one of <literal>forever</literal> or <literal>infinity</literal> means that the address
- never expires. Defaults to unset.</para>
+ Takes one of <literal>forever</literal> or <literal>infinity</literal>. The latter means that the
+ address never expires. Defaults to unset.</para>
</listitem>
</varlistentry>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>UseMTU=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When true, the MTU received in the Router Advertisement will be
+ used. Defaults to true.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>UseAutonomousPrefix=</varname></term>
<listitem>
<varlistentry>
<term><varname>ServerAddress=</varname></term>
<listitem><para>Specifies server address for the DHCP server. Takes an IPv4 address with prefix
- length, e.g., <literal>192.168.0.1/24</literal>. This setting may be useful when the link which
- DHCP server running on has multiple static addresses. When unset, one of static addresses in
- the link will be automatically selected. Defaults to unset.</para></listitem>
+ length, for example <literal>192.168.0.1/24</literal>. This setting may be useful when the link on
+ which the DHCP server is running has multiple static addresses. When unset, one of static addresses
+ in the link will be automatically selected. Defaults to unset.</para></listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>UplinkInterface=</varname></term>
- <listitem><para>Specifies name or index of uplink interface, or one of the special values
- <literal>:none</literal> and <literal>:auto</literal>. When emitting DNS, NTP, or SIP servers
- are enabled but no servers are specified, the servers configured in the uplink interface will
- be emitted. When <literal>:auto</literal>, the link which has default gateway with higher
- priority will be automatically selected. When <literal>:none</literal>, no uplink interface
- will be selected. Defaults to <literal>:auto</literal>.</para></listitem>
+ <listitem><para>Specifies the name or the index of the uplink interface, or one of the special
+ values <literal>:none</literal> and <literal>:auto</literal>. When emitting DNS, NTP, or SIP
+ servers is enabled but no servers are specified, the servers configured in the uplink interface
+ will be emitted. When <literal>:auto</literal>, the link which has a default gateway with the
+ highest priority will be automatically selected. When <literal>:none</literal>, no uplink
+ interface will be selected. Defaults to <literal>:auto</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<refsect1>
<title>[DHCPServerStaticLease] Section Options</title>
- <para>The <literal>[DHCPServerStaticLease]</literal> section configures a static DHCP lease to
- assign a pre-set IPv4 address to a specific device based on its MAC address. This section can be
- specified multiple times.</para>
+ <para>The <literal>[DHCPServerStaticLease]</literal> section configures a static DHCP lease to assign a
+ fixed IPv4 address to a specific device based on its MAC address. This section can be specified multiple
+ times.</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>MACAddress=</varname></term>
- <listitem><para>The hardware address of a device which should be assigned IPv4 address
- specified in <varname>Address=</varname>. This key is mandatory.</para></listitem>
+ <listitem><para>The hardware address of a device to match. This key is mandatory.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Address=</varname></term>
- <listitem><para>IPv4 address that should be assigned to a device with a hardware address
- specified in <varname>MACAddress=</varname>. This key is mandatory.</para></listitem>
+ <listitem><para>The IPv4 address that should be assigned to the device that was matched with
+ <varname>MACAddress=</varname>. This key is mandatory.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
for details. Defaults to <literal>medium</literal>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>UplinkInterface=</varname></term>
+ <listitem><para>Specifies the name or the index of the uplink interface, or one of the special
+ values <literal>:none</literal> and <literal>:auto</literal>. When emitting DNS servers or
+ search domains is enabled but no servers are specified, the servers configured in the uplink
+ interface will be emitted. When <literal>:auto</literal>, the link which has a default gateway
+ with the highest priority will be automatically selected. When <literal>:none</literal>, no
+ uplink interface will be selected. Defaults to <literal>:auto</literal>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>EmitDNS=</varname></term>
<term><varname>DNS=</varname></term>
- <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses that
- are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is
- true. <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that case
- the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS servers are read
- from the [Network] section. If the [Network] section does not contain any DNS servers either, DNS
- servers from the uplink with the highest priority default route are used. When
- <varname>EmitDNS=</varname> is false, no DNS server information is sent in Router Advertisement
- messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem>
+ <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses
+ that are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is true.
+ <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that case
+ the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS servers are
+ read from the [Network] section. If the [Network] section does not contain any DNS servers
+ either, DNS servers from the uplink interface specified in <varname>UplinkInterface=</varname>
+ will be used. When <varname>EmitDNS=</varname> is false, no DNS server information is sent in
+ Router Advertisement messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Domains=</varname></term>
<listitem><para>A list of DNS search domains distributed via Router Advertisement messages when
- <varname>EmitDomains=</varname> is true. If <varname>Domains=</varname> is empty, DNS search domains
- are read from the [Network] section. If the [Network] section does not contain any DNS search domains
- either, DNS search domains from the uplink with the highest priority default route are used. When
- <varname>EmitDomains=</varname> is false, no DNS search domain information is sent in Router
- Advertisement messages. <varname>EmitDomains=</varname> defaults to true.</para></listitem>
+ <varname>EmitDomains=</varname> is true. If <varname>Domains=</varname> is empty, DNS search
+ domains are read from the [Network] section. If the [Network] section does not contain any DNS
+ search domains either, DNS search domains from the uplink interface specified in
+ <varname>UplinkInterface=</varname> will be used. When <varname>EmitDomains=</varname> is false,
+ no DNS search domain information is sent in Router Advertisement messages.
+ <varname>EmitDomains=</varname> defaults to true.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>SamplePoint=</varname></term>
<listitem>
<para>Optional sample point in percent with one decimal (e.g. <literal>75%</literal>,
- <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>).</para>
+ <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>). This will be ignored when
+ <varname>BitRate=</varname> is unspecified.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>TimeQuantaNSec=</varname></term>
+ <term><varname>PropagationSegment=</varname></term>
+ <term><varname>PhaseBufferSegment1=</varname></term>
+ <term><varname>PhaseBufferSegment2=</varname></term>
+ <term><varname>SyncJumpWidth=</varname></term>
+ <listitem>
+ <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
+ synchronization jump width, which allow to define the CAN bit-timing in a hardware
+ independent format as proposed by the Bosch CAN 2.0 Specification.
+ <varname>TimeQuantaNSec=</varname> takes a timespan in nanoseconds.
+ <varname>PropagationSegment=</varname>, <varname>PhaseBufferSegment1=</varname>,
+ <varname>PhaseBufferSegment2=</varname>, and <varname>SyncJumpWidth=</varname> take number
+ of time quantum specified in <varname>TimeQuantaNSec=</varname> and must be an unsigned
+ integer in the range 0…4294967295. These settings except for
+ <varname>SyncJumpWidth=</varname> will be ignored when <varname>BitRate=</varname> is
+ specified.</para>
</listitem>
</varlistentry>
<varlistentry>
analogous to the <varname>BitRate=</varname> and <varname>SamplePoint=</varname> keys.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>DataTimeQuantaNSec=</varname></term>
+ <term><varname>DataPropagationSegment=</varname></term>
+ <term><varname>DataPhaseBufferSegment1=</varname></term>
+ <term><varname>DataPhaseBufferSegment2=</varname></term>
+ <term><varname>DataSyncJumpWidth=</varname></term>
+ <listitem>
+ <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
+ synchronization jump width for the data phase, if CAN-FD is used. These settings are
+ analogous to the <varname>TimeQuantaNSec=</varname> or related settings.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>FDMode=</varname></term>
<listitem>
<para>Takes a boolean. When <literal>yes</literal>, CAN-FD mode is enabled for the interface.
Note, that a bitrate and optional sample point should also be set for the CAN-FD data phase using
- the <varname>DataBitRate=</varname> and <varname>DataSamplePoint=</varname> keys.</para>
+ the <varname>DataBitRate=</varname> and <varname>DataSamplePoint=</varname> keys, or
+ <varname>DataTimeQuanta=</varname> and related settings.</para>
</listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>Termination=</varname></term>
<listitem>
- <para>Takes a boolean. When <literal>yes</literal>, the termination resistor will be selected for
- the bias network. When unset, the kernel's default will be used.</para>
+ <para>Takes a boolean or a termination resistor value in ohm in the range 0–65535. When
+ <literal>yes</literal>, the termination resistor is set to 120 ohm. When
+ <literal>no</literal> or <literal>0</literal> is set, the termination resistor is disabled.
+ When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Loopback=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, loopback mode is enabled. When the
+ loopback mode is enabled, the interface treats messages transmitted by itself as received
+ messages. The loopback mode is important to debug CAN networks. When unset, the kernel's
+ default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>OneShot=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, one-shot mode is enabled. When unset,
+ the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>PresumeAck=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, the interface will ignore missing CAN
+ ACKs. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ClassicDataLengthCode=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, the interface will handle the 4bit data
+ length code (DLC). When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
capabilities (see
<citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for details). The <varname>AmbientCapability=</varname> setting
- specifies capability which will be passed to to started program
+ specifies capability which will be passed to the started program
in the inheritable and ambient capability sets. This will grant
these capabilities to this process. This setting correspond to
the <option>--ambient-capability=</option> command line switch.
<citerefentry><refentrytitle>systemd-delta</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
- <para><citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ <para><citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>
has a discussion of packaging scriptlets.</para>
<para>Fedora page introducing the use of presets:
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>RestrictNetworkInterfaces=</varname></term>
+
+ <listitem>
+ <para>Takes a list of space-separated network interface names. This option restricts the network
+ interfaces that processes of this unit can use. By default processes can only use the network interfaces
+ listed (allow-list). If the first character of the rule is <literal>~</literal>, the effect is inverted:
+ the processes can only use network interfaces not listed (deny-list).
+ </para>
+
+ <para>This option can appear multiple times, in which case the network interface names are merged. If the
+ empty string is assigned the set is reset, all prior assigments will have not effect.
+ </para>
+
+ <para>If you specify both types of this option (i.e. allow-listing and deny-listing), the first encountered
+ will take precedence and will dictate the default action (allow vs deny). Then the next occurrences of this
+ option will add or delete the listed network interface names from the set, depending of its type and the
+ default action.
+ </para>
+
+ <para>The loopback interface ("lo") is not treated in any special way, you have to configure it explicitly
+ in the unit file.
+ </para>
+ <para>Example 1: allow-list
+ <programlisting>
+RestrictNetworkInterfaces=eth1
+RestrictNetworkInterfaces=eth2</programlisting>
+ Programs in the unit will be only able to use the eth1 and eth2 network
+ interfaces.
+ </para>
+
+ <para>Example 2: deny-list
+ <programlisting>
+RestrictNetworkInterfaces=~eth1 eth2</programlisting>
+ Programs in the unit will be able to use any network interface but eth1 and eth2.
+ </para>
+
+ <para>Example 3: mixed
+ <programlisting>
+RestrictNetworkInterfaces=eth1 eth2
+RestrictNetworkInterfaces=~eth1</programlisting>
+ Programs in the unit will be only able to use the eth2 network interface.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>DeviceAllow=</varname></term>
<literal>\;</literal>.</para>
<para>Each command line is unquoted using the rules described in "Quoting" section in
- <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>. The
+ <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The
first item becomes the command to execute, and the subsequent items the arguments.</para>
<para>This syntax is inspired by shell syntax, but only the meta-characters and expansions
<filename>default.target</filename>,
<filename>emergency.target</filename>,
<filename>exit.target</filename>,
+ <filename>factory-reset.target</filename>,
<filename>final.target</filename>,
<filename>first-boot-complete.target</filename>,
<filename>getty.target</filename>,
shutdown when the service manager starts to exit.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><filename>factory-reset.target</filename></term>
+ <listitem>
+ <para>A special target to trigger a factory reset.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><filename>final.target</filename></term>
<listitem>
<varlistentry>
<term><varname>ConditionControlGroupController=</varname></term>
- <listitem><para>Check whether given cgroup controllers (eg. <literal>cpu</literal>) are available
+ <listitem><para>Check whether given cgroup controllers (e.g. <literal>cpu</literal>) are available
for use on the system or whether the legacy v1 cgroup or the modern v2 cgroup hierarchy is used.
</para>
for --user instances).</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><constant>SIGRTMIN+25</constant></term>
+
+ <listitem><para>Upon receiving this signal the systemd manager will reexecute itself. This
+ is mostly equivalent to <command>systemctl daemon-reexec</command> except that it will be
+ done asynchronously.</para>
+
+ <para>The systemd system manager treats this signal the same way as
+ <constant>SIGTERM</constant>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><constant>SIGRTMIN+26</constant></term>
Defaults to 30 seconds and must not be smaller than 1 second.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>SaveIntervalSec=</varname></term>
+ <listitem><para>The interval at which the current time is periodically saved to disk, in the absence
+ of any recent synchronisation from an NTP server. This is especially useful for offline systems
+ with no local RTC, as it will guarantee that the system clock remains roughly monotonic across
+ reboots.</para>
+
+ <para>Takes a time interval value. The default unit is seconds, but other units may be specified, see
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Defaults to 60 seconds.</para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
<refsect2>
<title>Age</title>
+
<para>The date field, when set, is used to decide what files to
delete when cleaning. If a file or directory is older than the
current time minus the age field, it is deleted. The field
and <varname>X</varname>. If omitted or set to
<literal>-</literal>, no automatic clean-up is done.</para>
- <para>If the age field starts with a tilde character
- <literal>~</literal>, the clean-up is only applied to files and
- directories one level inside the directory specified, but not
- the files and directories immediately inside it.</para>
+ <para>If the age field starts with a tilde character <literal>~</literal>, clean-up is only applied to
+ files and directories one level inside the directory specified, but not the files and directories
+ immediately inside it.</para>
<para>The age of a file system entry is determined from its last
modification timestamp (mtime), its last access timestamp (atime),
the age field. To restrict the deletion based on particular type
of file timestamps, the age-by argument can be used.</para>
- <para>The age-by argument, when (optionally) specified along
- with age will check if the file system entry has aged by the
- type of file timestamp(s) provided. It can be specified by
- prefixing the age argument with a set of file timestamp types
- followed by a colon character <literal>:</literal>, i.e.,
- <literal><replaceable>age-by</replaceable>:<replaceable>cleanup-age</replaceable></literal>.
- The argument can be a set of:
- <constant>a</constant> (<constant>A</constant> for directories),
- <constant>b</constant> (<constant>B</constant> for directories),
- <constant>c</constant> (<constant>C</constant> for directories; ignored by default), or
- <constant>m</constant> (<constant>M</constant> for directories),
- indicating access, creation, last status change, and last
- modification times of a file system entry respectively. See
- <citerefentry project='man-pages'><refentrytitle>statx</refentrytitle><manvolnum>2</manvolnum></citerefentry>
- file timestamp fields for more details.</para>
-
- <para>If unspecified, the age-by field defaults to
- <constant>abcmABM</constant>,
- i.e., by default all file timestamps are taken into consideration,
- with the exception of the last status change timestamp (ctime) for
- directories. This is because the aging logic itself will alter the
- ctime whenever it deletes a file inside it. To ensure that running
- the aging logic does not feed back into the next iteration of it,
- ctime for directories is ignored by default.</para>
+ <para>The age-by argument overrides the timestamp types to be used for the age check. It can be
+ specified by prefixing the age argument with a sequence of characters to specify the timestamp types
+ and a colon (<literal>:</literal>):
+ <literal><replaceable>age-by</replaceable>...:<replaceable>cleanup-age</replaceable></literal>. The
+ argument can consist of <constant>a</constant> (<constant>A</constant> for directories),
+ <constant>b</constant> (<constant>B</constant> for directories), <constant>c</constant>
+ (<constant>C</constant> for directories), or <constant>m</constant> (<constant>M</constant> for
+ directories). Those respectively indicate access, creation, last status change, and last modification
+ time of a file system entry. The lower-case letter signifies that the given timestamp type should be
+ considered for files, while the upper-case letter signifies that the given timestamp type should be
+ considered for directories. See <citerefentry
+ project='man-pages'><refentrytitle>statx</refentrytitle><manvolnum>2</manvolnum></citerefentry> file
+ timestamp fields for more details about timestamp types.</para>
+
+ <para>If not specified, the age-by field defaults to <constant>abcmABM</constant>, i.e. by default all
+ file timestamps are taken into consideration, with the exception of the last status change timestamp
+ (ctime) for directories. This is because the aging logic itself will alter the ctime whenever it
+ deletes a file inside it. To ensure that running the aging logic does not feed back into the next
+ iteration of itself, ctime for directories is ignored by default.</para>
<para>For example:<programlisting>
# Files created and modified, and directories accessed more than
for device <replaceable>DEVPATH</replaceable>, and print debug
output.</para>
<variablelist>
+ <varlistentry>
+ <term><option>-a</option></term>
+ <term><option>--action=<replaceable>ACTION</replaceable></option></term>
+ <listitem>
+ <para>Type of event to be simulated. Possible actions are <literal>add</literal>,
+ <literal>remove</literal>, <literal>change</literal>, <literal>move</literal>,
+ <literal>online</literal>, <literal>offline</literal>, <literal>bind</literal>,
+ and <literal>unbind</literal>. Also, the special value <literal>help</literal> can be used
+ to list the possible actions. The default value is <literal>add</literal>.</para>
+ </listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect2>
url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>, and may also
pick up drop-in JSON user and group records from <filename>/etc/userdb/</filename>,
<filename>/run/userdb/</filename>, <filename>/run/host/userdb/</filename>,
- <filename>/use/lib/userdb/</filename>.</para>
+ <filename>/usr/lib/userdb/</filename>.</para>
</refsect1>
<refsect1>
<listitem><para>Controls whether to include user/group lookups in the output that are defined using
drop-in files in <filename>/etc/userdb/</filename>, <filename>/run/userdb/</filename>,
- <filename>/run/host/userdb/</filename>, <filename>/use/lib/userdb/</filename>. If
+ <filename>/run/host/userdb/</filename>, <filename>/usr/lib/userdb/</filename>. If
<option>--with-dropin=no</option> is used these records are suppressed. If
<option>--with-dropin=yes</option> is specified such users/groups are included in the output (which
is the default).</para></listitem>
<citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
and picks up JSON user/group records from <filename>/etc/userdb/</filename>,
<filename>/run/userdb/</filename>, <filename>/run/host/userdb/</filename>,
- <filename>/use/lib/userdb/</filename>.</para></listitem>
+ <filename>/usr/lib/userdb/</filename>.</para></listitem>
</varlistentry>
</variablelist>
'localstatedir=/var',
'warning_level=2',
],
- meson_version : '>= 0.47',
+ meson_version : '>= 0.53.2',
)
libsystemd_version = '0.32.0'
conf.set10('BUMP_PROC_SYS_FS_NR_OPEN', get_option('bump-proc-sys-fs-nr-open'))
conf.set('HIGH_RLIMIT_NOFILE', 512*1024)
-# join_paths ignores the preceding arguments if an absolute component is
-# encountered, so this should canonicalize various paths when they are
-# absolute or relative.
+# Meson ignores the preceding arguments when joining paths if an absolute
+# component is encountered, so this should canonicalize various paths when they
+# are absolute or relative.
prefixdir = get_option('prefix')
if not prefixdir.startswith('/')
error('Prefix is not absolute: "@0@"'.format(prefixdir))
rootprefixdir, prefixdir))
endif
-bindir = join_paths(prefixdir, get_option('bindir'))
-libdir = join_paths(prefixdir, get_option('libdir'))
-sysconfdir = join_paths(prefixdir, get_option('sysconfdir'))
-includedir = join_paths(prefixdir, get_option('includedir'))
-datadir = join_paths(prefixdir, get_option('datadir'))
-localstatedir = join_paths('/', get_option('localstatedir'))
+bindir = prefixdir / get_option('bindir')
+libdir = prefixdir / get_option('libdir')
+sysconfdir = prefixdir / get_option('sysconfdir')
+includedir = prefixdir / get_option('includedir')
+datadir = prefixdir / get_option('datadir')
+localstatedir = '/' / get_option('localstatedir')
-rootbindir = join_paths(rootprefixdir, 'bin')
-rootsbindir = join_paths(rootprefixdir, split_bin ? 'sbin' : 'bin')
-rootlibexecdir = join_paths(rootprefixdir, 'lib/systemd')
+rootbindir = rootprefixdir / 'bin'
+rootsbindir = rootprefixdir / (split_bin ? 'sbin' : 'bin')
+rootlibexecdir = rootprefixdir / 'lib/systemd'
rootlibdir = get_option('rootlibdir')
if rootlibdir == ''
- rootlibdir = join_paths(rootprefixdir, libdir.split('/')[-1])
+ rootlibdir = rootprefixdir / libdir.split('/')[-1]
endif
install_sysconfdir = get_option('install-sysconfdir') != 'false'
install_sysconfdir_samples = get_option('install-sysconfdir') == 'true'
# Dirs of external packages
-pkgconfigdatadir = get_option('pkgconfigdatadir') == '' ? join_paths(datadir, 'pkgconfig') : get_option('pkgconfigdatadir')
-pkgconfiglibdir = get_option('pkgconfiglibdir') == '' ? join_paths(libdir, 'pkgconfig') : get_option('pkgconfiglibdir')
-polkitpolicydir = join_paths(datadir, 'polkit-1/actions')
-polkitrulesdir = join_paths(datadir, 'polkit-1/rules.d')
-polkitpkladir = join_paths(localstatedir, 'lib/polkit-1/localauthority/10-vendor.d')
-xinitrcdir = get_option('xinitrcdir') == '' ? join_paths(sysconfdir, 'X11/xinit/xinitrc.d') : get_option('xinitrcdir')
+pkgconfigdatadir = get_option('pkgconfigdatadir') != '' ? get_option('pkgconfigdatadir') : datadir / 'pkgconfig'
+pkgconfiglibdir = get_option('pkgconfiglibdir') != '' ? get_option('pkgconfiglibdir') : libdir / 'pkgconfig'
+polkitpolicydir = datadir / 'polkit-1/actions'
+polkitrulesdir = datadir / 'polkit-1/rules.d'
+polkitpkladir = localstatedir / 'lib/polkit-1/localauthority/10-vendor.d'
+xinitrcdir = get_option('xinitrcdir') != '' ? get_option('xinitrcdir') : sysconfdir / 'X11/xinit/xinitrc.d'
rpmmacrosdir = get_option('rpmmacrosdir')
if rpmmacrosdir != 'no'
- rpmmacrosdir = join_paths(prefixdir, rpmmacrosdir)
+ rpmmacrosdir = prefixdir / rpmmacrosdir
endif
-modprobedir = join_paths(rootprefixdir, 'lib/modprobe.d')
+modprobedir = rootprefixdir / 'lib/modprobe.d'
# Our own paths
-pkgdatadir = join_paths(datadir, 'systemd')
-environmentdir = join_paths(prefixdir, 'lib/environment.d')
-pkgsysconfdir = join_paths(sysconfdir, 'systemd')
-userunitdir = join_paths(prefixdir, 'lib/systemd/user')
-userpresetdir = join_paths(prefixdir, 'lib/systemd/user-preset')
-tmpfilesdir = join_paths(prefixdir, 'lib/tmpfiles.d')
-sysusersdir = join_paths(prefixdir, 'lib/sysusers.d')
-sysctldir = join_paths(prefixdir, 'lib/sysctl.d')
-binfmtdir = join_paths(prefixdir, 'lib/binfmt.d')
-modulesloaddir = join_paths(prefixdir, 'lib/modules-load.d')
-networkdir = join_paths(rootprefixdir, 'lib/systemd/network')
-pkgincludedir = join_paths(includedir, 'systemd')
-systemgeneratordir = join_paths(rootlibexecdir, 'system-generators')
-usergeneratordir = join_paths(prefixdir, 'lib/systemd/user-generators')
-systemenvgeneratordir = join_paths(prefixdir, 'lib/systemd/system-environment-generators')
-userenvgeneratordir = join_paths(prefixdir, 'lib/systemd/user-environment-generators')
-systemshutdowndir = join_paths(rootlibexecdir, 'system-shutdown')
-systemsleepdir = join_paths(rootlibexecdir, 'system-sleep')
-systemunitdir = join_paths(rootprefixdir, 'lib/systemd/system')
-systempresetdir = join_paths(rootprefixdir, 'lib/systemd/system-preset')
-udevlibexecdir = join_paths(rootprefixdir, 'lib/udev')
-udevrulesdir = join_paths(udevlibexecdir, 'rules.d')
-udevhwdbdir = join_paths(udevlibexecdir, 'hwdb.d')
-catalogdir = join_paths(prefixdir, 'lib/systemd/catalog')
-kernelinstalldir = join_paths(prefixdir, 'lib/kernel/install.d')
-factorydir = join_paths(datadir, 'factory')
-bootlibdir = join_paths(prefixdir, 'lib/systemd/boot/efi')
-testsdir = join_paths(prefixdir, 'lib/systemd/tests')
-systemdstatedir = join_paths(localstatedir, 'lib/systemd')
-catalogstatedir = join_paths(systemdstatedir, 'catalog')
-randomseeddir = join_paths(localstatedir, 'lib/systemd')
-profiledir = join_paths(rootlibexecdir, 'portable', 'profile')
-ntpservicelistdir = join_paths(rootprefixdir, 'lib/systemd/ntp-units.d')
+pkgdatadir = datadir / 'systemd'
+environmentdir = prefixdir / 'lib/environment.d'
+pkgsysconfdir = sysconfdir / 'systemd'
+userunitdir = prefixdir / 'lib/systemd/user'
+userpresetdir = prefixdir / 'lib/systemd/user-preset'
+tmpfilesdir = prefixdir / 'lib/tmpfiles.d'
+sysusersdir = prefixdir / 'lib/sysusers.d'
+sysctldir = prefixdir / 'lib/sysctl.d'
+binfmtdir = prefixdir / 'lib/binfmt.d'
+modulesloaddir = prefixdir / 'lib/modules-load.d'
+networkdir = rootprefixdir / 'lib/systemd/network'
+pkgincludedir = includedir / 'systemd'
+systemgeneratordir = rootlibexecdir / 'system-generators'
+usergeneratordir = prefixdir / 'lib/systemd/user-generators'
+systemenvgeneratordir = prefixdir / 'lib/systemd/system-environment-generators'
+userenvgeneratordir = prefixdir / 'lib/systemd/user-environment-generators'
+systemshutdowndir = rootlibexecdir / 'system-shutdown'
+systemsleepdir = rootlibexecdir / 'system-sleep'
+systemunitdir = rootprefixdir / 'lib/systemd/system'
+systempresetdir = rootprefixdir / 'lib/systemd/system-preset'
+udevlibexecdir = rootprefixdir / 'lib/udev'
+udevrulesdir = udevlibexecdir / 'rules.d'
+udevhwdbdir = udevlibexecdir / 'hwdb.d'
+catalogdir = prefixdir / 'lib/systemd/catalog'
+kernelinstalldir = prefixdir / 'lib/kernel/install.d'
+factorydir = datadir / 'factory'
+bootlibdir = prefixdir / 'lib/systemd/boot/efi'
+testsdir = prefixdir / 'lib/systemd/tests'
+systemdstatedir = localstatedir / 'lib/systemd'
+catalogstatedir = systemdstatedir / 'catalog'
+randomseeddir = localstatedir / 'lib/systemd'
+profiledir = rootlibexecdir / 'portable' / 'profile'
+ntpservicelistdir = rootprefixdir / 'lib/systemd/ntp-units.d'
docdir = get_option('docdir')
if docdir == ''
- docdir = join_paths(datadir, 'doc/systemd')
+ docdir = datadir / 'doc/systemd'
endif
dbuspolicydir = get_option('dbuspolicydir')
if dbuspolicydir == ''
- dbuspolicydir = join_paths(datadir, 'dbus-1/system.d')
+ dbuspolicydir = datadir / 'dbus-1/system.d'
endif
dbussessionservicedir = get_option('dbussessionservicedir')
if dbussessionservicedir == ''
- dbussessionservicedir = join_paths(datadir, 'dbus-1/services')
+ dbussessionservicedir = datadir / 'dbus-1/services'
endif
dbussystemservicedir = get_option('dbussystemservicedir')
if dbussystemservicedir == ''
- dbussystemservicedir = join_paths(datadir, 'dbus-1/system-services')
+ dbussystemservicedir = datadir / 'dbus-1/system-services'
endif
pamlibdir = get_option('pamlibdir')
if pamlibdir == ''
- pamlibdir = join_paths(rootlibdir, 'security')
+ pamlibdir = rootlibdir / 'security'
endif
pamconfdir = get_option('pamconfdir')
if pamconfdir == ''
- pamconfdir = join_paths(prefixdir, 'lib/pam.d')
+ pamconfdir = prefixdir / 'lib/pam.d'
endif
libcryptsetup_plugins_dir = get_option('libcryptsetup-plugins-dir')
if libcryptsetup_plugins_dir == ''
- libcryptsetup_plugins_dir = join_paths(rootlibdir, 'cryptsetup')
+ libcryptsetup_plugins_dir = rootlibdir / 'cryptsetup'
endif
memory_accounting_default = get_option('memory-accounting-default')
conf.set_quoted('BINFMT_DIR', binfmtdir)
conf.set_quoted('BOOTLIBDIR', bootlibdir)
-conf.set_quoted('CATALOG_DATABASE', join_paths(catalogstatedir, 'database'))
+conf.set_quoted('CATALOG_DATABASE', catalogstatedir / 'database')
conf.set_quoted('CERTIFICATE_ROOT', get_option('certificate-root'))
conf.set_quoted('DOC_DIR', docdir)
-conf.set_quoted('DOCUMENT_ROOT', join_paths(pkgdatadir, 'gatewayd'))
+conf.set_quoted('DOCUMENT_ROOT', pkgdatadir / 'gatewayd')
conf.set_quoted('ENVIRONMENT_DIR', environmentdir)
conf.set_quoted('INCLUDE_DIR', includedir)
conf.set_quoted('LIBDIR', libdir)
conf.set_quoted('MODPROBE_DIR', modprobedir)
conf.set_quoted('MODULESLOAD_DIR', modulesloaddir)
conf.set_quoted('PKGSYSCONFDIR', pkgsysconfdir)
-conf.set_quoted('POLKIT_AGENT_BINARY_PATH', join_paths(bindir, 'pkttyagent'))
+conf.set_quoted('POLKIT_AGENT_BINARY_PATH', bindir / 'pkttyagent')
conf.set_quoted('PREFIX', prefixdir)
-conf.set_quoted('RANDOM_SEED', join_paths(randomseeddir, 'random-seed'))
+conf.set_quoted('RANDOM_SEED', randomseeddir / 'random-seed')
conf.set_quoted('RANDOM_SEED_DIR', randomseeddir)
conf.set_quoted('RC_LOCAL_PATH', get_option('rc-local'))
conf.set_quoted('ROOTBINDIR', rootbindir)
conf.set_quoted('ROOTPREFIX_NOSLASH', rootprefixdir_noslash)
conf.set_quoted('SYSCONF_DIR', sysconfdir)
conf.set_quoted('SYSCTL_DIR', sysctldir)
-conf.set_quoted('SYSTEMCTL_BINARY_PATH', join_paths(rootbindir, 'systemctl'))
-conf.set_quoted('SYSTEMD_BINARY_PATH', join_paths(rootlibexecdir, 'systemd'))
+conf.set_quoted('SYSTEMCTL_BINARY_PATH', rootbindir / 'systemctl')
+conf.set_quoted('SYSTEMD_BINARY_PATH', rootlibexecdir / 'systemd')
conf.set_quoted('SYSTEMD_CATALOG_DIR', catalogdir)
-conf.set_quoted('SYSTEMD_CGROUPS_AGENT_PATH', join_paths(rootlibexecdir, 'systemd-cgroups-agent'))
-conf.set_quoted('SYSTEMD_CRYPTSETUP_PATH', join_paths(rootlibexecdir, 'systemd-cryptsetup'))
-conf.set_quoted('SYSTEMD_EXPORT_PATH', join_paths(rootlibexecdir, 'systemd-export'))
-conf.set_quoted('SYSTEMD_FSCK_PATH', join_paths(rootlibexecdir, 'systemd-fsck'))
-conf.set_quoted('SYSTEMD_GROWFS_PATH', join_paths(rootlibexecdir, 'systemd-growfs'))
-conf.set_quoted('SYSTEMD_HOMEWORK_PATH', join_paths(rootlibexecdir, 'systemd-homework'))
-conf.set_quoted('SYSTEMD_IMPORT_FS_PATH', join_paths(rootlibexecdir, 'systemd-import-fs'))
-conf.set_quoted('SYSTEMD_IMPORT_PATH', join_paths(rootlibexecdir, 'systemd-import'))
-conf.set_quoted('SYSTEMD_KBD_MODEL_MAP', join_paths(pkgdatadir, 'kbd-model-map'))
-conf.set_quoted('SYSTEMD_LANGUAGE_FALLBACK_MAP', join_paths(pkgdatadir, 'language-fallback-map'))
-conf.set_quoted('SYSTEMD_MAKEFS_PATH', join_paths(rootlibexecdir, 'systemd-makefs'))
-conf.set_quoted('SYSTEMD_PULL_PATH', join_paths(rootlibexecdir, 'systemd-pull'))
-conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-shutdown'))
-conf.set_quoted('SYSTEMD_STDIO_BRIDGE_BINARY_PATH', join_paths(bindir, 'systemd-stdio-bridge'))
-conf.set_quoted('SYSTEMD_TEST_DATA', join_paths(testsdir, 'testdata'))
-conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', join_paths(rootbindir, 'systemd-tty-ask-password-agent'))
-conf.set_quoted('SYSTEMD_UPDATE_HELPER_PATH', join_paths(rootlibexecdir, 'systemd-update-helper'))
-conf.set_quoted('SYSTEMD_USERWORK_PATH', join_paths(rootlibexecdir, 'systemd-userwork'))
-conf.set_quoted('SYSTEMD_VERITYSETUP_PATH', join_paths(rootlibexecdir, 'systemd-veritysetup'))
-conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR', join_paths(pkgsysconfdir, 'system'))
+conf.set_quoted('SYSTEMD_CGROUPS_AGENT_PATH', rootlibexecdir / 'systemd-cgroups-agent')
+conf.set_quoted('SYSTEMD_CRYPTSETUP_PATH', rootlibexecdir / 'systemd-cryptsetup')
+conf.set_quoted('SYSTEMD_EXPORT_PATH', rootlibexecdir / 'systemd-export')
+conf.set_quoted('SYSTEMD_FSCK_PATH', rootlibexecdir / 'systemd-fsck')
+conf.set_quoted('SYSTEMD_GROWFS_PATH', rootlibexecdir / 'systemd-growfs')
+conf.set_quoted('SYSTEMD_HOMEWORK_PATH', rootlibexecdir / 'systemd-homework')
+conf.set_quoted('SYSTEMD_IMPORT_FS_PATH', rootlibexecdir / 'systemd-import-fs')
+conf.set_quoted('SYSTEMD_IMPORT_PATH', rootlibexecdir / 'systemd-import')
+conf.set_quoted('SYSTEMD_KBD_MODEL_MAP', pkgdatadir / 'kbd-model-map')
+conf.set_quoted('SYSTEMD_LANGUAGE_FALLBACK_MAP', pkgdatadir / 'language-fallback-map')
+conf.set_quoted('SYSTEMD_MAKEFS_PATH', rootlibexecdir / 'systemd-makefs')
+conf.set_quoted('SYSTEMD_PULL_PATH', rootlibexecdir / 'systemd-pull')
+conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH', rootlibexecdir / 'systemd-shutdown')
+conf.set_quoted('SYSTEMD_STDIO_BRIDGE_BINARY_PATH', bindir / 'systemd-stdio-bridge')
+conf.set_quoted('SYSTEMD_TEST_DATA', testsdir / 'testdata')
+conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', rootbindir / 'systemd-tty-ask-password-agent')
+conf.set_quoted('SYSTEMD_UPDATE_HELPER_PATH', rootlibexecdir / 'systemd-update-helper')
+conf.set_quoted('SYSTEMD_USERWORK_PATH', rootlibexecdir / 'systemd-userwork')
+conf.set_quoted('SYSTEMD_VERITYSETUP_PATH', rootlibexecdir / 'systemd-veritysetup')
+conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR', pkgsysconfdir / 'system')
conf.set_quoted('SYSTEM_DATA_UNIT_DIR', systemunitdir)
conf.set_quoted('SYSTEM_ENV_GENERATOR_DIR', systemenvgeneratordir)
conf.set_quoted('SYSTEM_GENERATOR_DIR', systemgeneratordir)
conf.set_quoted('UDEV_HWDB_DIR', udevhwdbdir)
conf.set_quoted('UDEV_RULES_DIR', udevrulesdir)
conf.set_quoted('UPDATE_HELPER_USER_TIMEOUT', get_option('update-helper-user-timeout'))
-conf.set_quoted('USER_CONFIG_UNIT_DIR', join_paths(pkgsysconfdir, 'user'))
+conf.set_quoted('USER_CONFIG_UNIT_DIR', pkgsysconfdir / 'user')
conf.set_quoted('USER_DATA_UNIT_DIR', userunitdir)
conf.set_quoted('USER_ENV_GENERATOR_DIR', userenvgeneratordir)
conf.set_quoted('USER_GENERATOR_DIR', usergeneratordir)
-conf.set_quoted('USER_KEYRING_PATH', join_paths(pkgsysconfdir, 'import-pubring.gpg'))
+conf.set_quoted('USER_KEYRING_PATH', pkgsysconfdir / 'import-pubring.gpg')
conf.set_quoted('USER_PRESET_DIR', userpresetdir)
-conf.set_quoted('VENDOR_KEYRING_PATH', join_paths(rootlibexecdir, 'import-pubring.gpg'))
+conf.set_quoted('VENDOR_KEYRING_PATH', rootlibexecdir / 'import-pubring.gpg')
conf.set('ANSI_OK_COLOR', 'ANSI_' + get_option('ok-color').underscorify().to_upper())
conf.set10('ENABLE_URLIFY', get_option('urlify'))
'-Werror=shift-count-overflow',
'-Werror=shift-overflow=2',
'-Werror=undef',
+ '-Werror=unused-function',
'-Wfloat-equal',
'-Wimplicit-fallthrough=5',
'-Winit-self',
endif
# --as-needed and --no-undefined are provided by meson by default,
-# run mesonconf to see what is enabled
+# run 'meson configure' to see what is enabled
possible_link_flags = [
'-Wl,-z,relro',
'-Wl,-z,now',
mkdir_p = 'mkdir -p $DESTDIR/@0@'
splash_bmp = files('test/splash.bmp')
-# if -Dxxx-path option is found, use that. Otherwise, check in $PATH,
+# If -Dxxx-path option is found, use that. Otherwise, check in $PATH,
# /usr/sbin, /sbin, and fall back to the default from middle column.
progs = [['quotaon', '/usr/sbin/quotaon' ],
['quotacheck', '/usr/sbin/quotacheck' ],
endif
conf.set('TIME_EPOCH', time_epoch)
+conf.set('CLOCK_VALID_RANGE_USEC_MAX', get_option('clock-valid-range-usec-max'))
+
foreach tuple : [['system-alloc-uid-min', 'SYS_UID_MIN', 1], # Also see login.defs(5).
['system-uid-max', 'SYS_UID_MAX', 999],
['system-alloc-gid-min', 'SYS_GID_MIN', 1],
default_user_path = get_option('user-path')
if default_user_path != ''
conf.set_quoted('DEFAULT_USER_PATH', default_user_path)
- default_user_path_display = default_user_path
-else
- # meson 0.49 fails when ?: is used in .format()
- default_user_path_display = '(same as system services)'
endif
else
clang = find_program('clang', required : bpf_framework_required)
llvm_strip = find_program('llvm-strip', required : bpf_framework_required)
- # Debian installs this in /usr/sbin/ which is not in $PATH
- # FIXME: use the 'dirs' parameter once we bump Meson version to >= 0.53
+
+ # Debian installs this in /usr/sbin/ which is not in $PATH.
+ # We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
bpftool = find_program('bpftool', '/usr/sbin/bpftool', required : bpf_framework_required)
+
bpf_arches = ['x86_64']
deps_found = libbpf.found() and clang.found() and llvm_strip.found() and bpftool.found()
# Can build BPF program from source code in restricted C
'cryptsetup-token-systemd-tpm2',
link_args : ['-shared',
'-Wl,--version-script=' + cryptsetup_token_sym_path],
- dependencies : libshared_deps + [libcryptsetup],
+ dependencies : libshared_deps + [libcryptsetup, versiondep],
link_with : [libshared],
link_whole : [cryptsetup_token_systemd_tpm2_static],
link_depends : cryptsetup_token_sym,
install : true,
install_dir : libcryptsetup_plugins_dir)
endif
+
+ if conf.get('HAVE_LIBFIDO2') == 1
+ cryptsetup_token_systemd_fido2 = shared_library(
+ 'cryptsetup-token-systemd-fido2',
+ link_args : ['-shared',
+ '-Wl,--version-script=' + cryptsetup_token_sym_path],
+ dependencies : libshared_deps + [libcryptsetup, versiondep],
+ link_with : [libshared],
+ link_whole : [cryptsetup_token_systemd_fido2_static],
+ link_depends : cryptsetup_token_sym,
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : libcryptsetup_plugins_dir)
+ endif
+
+ if conf.get('HAVE_P11KIT') == 1
+ cryptsetup_token_systemd_pkcs11 = shared_library(
+ 'cryptsetup-token-systemd-pkcs11',
+ link_args : ['-shared',
+ '-Wl,--version-script=' + cryptsetup_token_sym_path],
+ dependencies : libshared_deps + [libcryptsetup, versiondep],
+ link_with : [libshared],
+ link_whole : [cryptsetup_token_systemd_pkcs11_static],
+ link_depends : cryptsetup_token_sym,
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : libcryptsetup_plugins_dir)
+ endif
endif
############################################################
module = tuple[0]
sym = 'src/nss-@0@/nss-@0@.sym'.format(module)
- version_script_arg = join_paths(project_source_root, sym)
+ version_script_arg = project_source_root / sym
sources = ['src/nss-@0@/nss-@0@.c'.format(module)]
if tuple.length() > 2
install_dir : rootlibexecdir)
meson.add_install_script(meson_make_symlink,
- join_paths(rootlibexecdir, 'systemd'),
- join_paths(rootsbindir, 'init'))
+ rootlibexecdir / 'systemd',
+ rootsbindir / 'init')
public_programs += executable(
'systemd-analyze',
install_dir : userenvgeneratordir)
meson.add_install_script(meson_make_symlink,
- join_paths(sysconfdir, 'environment'),
- join_paths(environmentdir, '99-environment.conf'))
+ sysconfdir / 'environment',
+ environmentdir / '99-environment.conf')
endif
if conf.get('ENABLE_HIBERNATE') == 1
install : true)
meson.add_install_script(meson_make_symlink,
- join_paths(bindir, 'resolvectl'),
- join_paths(rootsbindir, 'resolvconf'))
+ bindir / 'resolvectl',
+ rootsbindir / 'resolvconf')
meson.add_install_script(meson_make_symlink,
- join_paths(bindir, 'resolvectl'),
- join_paths(bindir, 'systemd-resolve'))
+ bindir / 'resolvectl',
+ bindir / 'systemd-resolve')
endif
if conf.get('ENABLE_LOGIND') == 1
install_dir : rootbindir)
if conf.get('HAVE_PAM') == 1
- version_script_arg = join_paths(project_source_root, pam_systemd_sym)
+ version_script_arg = project_source_root / pam_systemd_sym
pam_systemd = shared_library(
'pam_systemd',
pam_systemd_c,
install_dir : rootbindir)
if conf.get('HAVE_PAM') == 1
- version_script_arg = join_paths(project_source_root, pam_systemd_home_sym)
+ version_script_arg = project_source_root / pam_systemd_home_sym
pam_systemd = shared_library(
'pam_systemd_home',
pam_systemd_home_c,
foreach alias : (['halt', 'poweroff', 'reboot', 'shutdown'] +
(conf.get('HAVE_SYSV_COMPAT') == 1 ? ['runlevel', 'telinit'] : []))
meson.add_install_script(meson_make_symlink,
- join_paths(rootbindir, 'systemctl'),
- join_paths(rootsbindir, alias))
+ rootbindir / 'systemctl',
+ rootsbindir / alias)
endforeach
meson.add_install_script(meson_make_symlink,
- join_paths(rootbindir, 'udevadm'),
- join_paths(rootlibexecdir, 'systemd-udevd'))
+ rootbindir / 'udevadm',
+ rootlibexecdir / 'systemd-udevd')
if conf.get('ENABLE_BACKLIGHT') == 1
executable(
mkdir_p.format(binfmtdir))
if install_sysconfdir
meson.add_install_script('sh', '-c',
- mkdir_p.format(join_paths(sysconfdir, 'binfmt.d')))
+ mkdir_p.format(sysconfdir / 'binfmt.d'))
endif
endif
install : true)
meson.add_install_script(meson_make_symlink,
- 'systemd-mount', join_paths(bindir, 'systemd-umount'))
+ 'systemd-mount', bindir / 'systemd-umount')
public_programs += executable(
'systemd-run',
mkdir_p.format(modulesloaddir))
if install_sysconfdir
meson.add_install_script('sh', '-c',
- mkdir_p.format(join_paths(sysconfdir, 'modules-load.d')))
+ mkdir_p.format(sysconfdir / 'modules-load.d'))
endif
endif
output : 'systemd-runtest.env',
command : [sh, '-c',
'{ echo SYSTEMD_TEST_DATA=@0@; echo SYSTEMD_CATALOG_DIR=@1@; } >@OUTPUT@'.format(
- join_paths(project_source_root, 'test'),
- join_paths(project_build_root, 'catalog'))],
+ project_source_root / 'test',
+ project_build_root / 'catalog')],
build_by_default : true)
test_cflags = ['-DTEST_CODE=1']
build_by_default : want_tests != 'false',
install_rpath : rootlibexecdir,
install : install_tests,
- install_dir : join_paths(testsdir, type))
+ install_dir : testsdir / type)
if type == 'manual'
message('@0@ is a manual test'.format(name))
if b == name
test('@0@_@1@'.format(b, c),
exe,
- args : [join_paths(project_source_root, p)])
+ args : [project_source_root / p])
endif
endforeach
endif
endforeach
-run_target(
- 'fuzzers',
- depends : fuzzer_exes,
- command : ['true'])
+alias_target('fuzzers', fuzzer_exes)
############################################################
test('github-pages',
jekyll,
args : ['build',
- '--source', join_paths(project_source_root, 'docs'),
- '--destination', join_paths(project_build_root, '_site')])
+ '--source', project_source_root / 'docs',
+ '--destination', project_build_root / '_site'])
endif
############################################################
output : name,
depends : build,
command : [ln, '-fs',
- join_paths(build.full_path(), b),
+ build.full_path() / b,
'@OUTPUT@'],
build_by_default : true)
else
env : ['UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1'],
timeout : 60,
args : [exe.full_path(),
- join_paths(project_source_root, p)])
+ project_source_root / p])
endif
endforeach
endif
depends : [man, libsystemd, libudev],
command : [check_api_docs_sh, libsystemd.full_path(), libudev.full_path()])
-############################################################
-
-if dbus_docs.length() > 0
- custom_target(
- 'update-dbus-docs',
- output : 'update-dbus-docs',
- command : [update_dbus_docs_py,
- '--build-dir=@0@'.format(project_build_root),
- '@INPUT@'],
- input : dbus_docs)
-
- if conf.get('BUILD_MODE_DEVELOPER') == 1
- test('dbus-docs-fresh',
- update_dbus_docs_py,
- args : ['--build-dir=@0@'.format(project_build_root),
- '--test'] + dbus_docs)
- endif
-endif
-
-custom_target(
- 'update-man-rules',
- output : 'update-man-rules',
- command : [sh, '-c',
- 'cd @0@ && '.format(meson.build_root()) +
- 'python3 @0@/tools/update-man-rules.py $(find @0@ -wholename "*/man/*.xml") >t && '.format(project_source_root) +
- 'mv t @0@/man/rules/meson.build'.format(meson.current_source_dir())],
- depends : custom_entities_ent)
+alias_target('update-dbus-docs', update_dbus_docs)
+alias_target('update-man-rules', update_man_rules)
############################################################
-watchdog_opt = service_watchdog == '' ? 'disabled' : service_watchdog
-
-status = [
- '@0@ @1@'.format(meson.project_name(), meson.project_version()),
-
- 'build mode: @0@'.format(get_option('mode')),
- 'split /usr: @0@'.format(split_usr),
- 'split bin-sbin: @0@'.format(split_bin),
- 'prefix directory: @0@'.format(prefixdir),
- 'rootprefix directory: @0@'.format(rootprefixdir),
- 'sysconf directory: @0@'.format(sysconfdir),
- 'include directory: @0@'.format(includedir),
- 'lib directory: @0@'.format(libdir),
- 'rootlib directory: @0@'.format(rootlibdir),
- 'SysV init scripts: @0@'.format(sysvinit_path),
- 'SysV rc?.d directories: @0@'.format(sysvrcnd_path),
- 'PAM modules directory: @0@'.format(pamlibdir),
- 'PAM configuration directory: @0@'.format(pamconfdir),
- 'libcryptsetup plugins directory: @0@'.format(libcryptsetup_plugins_dir),
- 'RPM macros directory: @0@'.format(rpmmacrosdir),
- 'modprobe.d directory: @0@'.format(modprobedir),
- 'D-Bus policy directory: @0@'.format(dbuspolicydir),
- 'D-Bus session directory: @0@'.format(dbussessionservicedir),
- 'D-Bus system directory: @0@'.format(dbussystemservicedir),
- 'bash completions directory: @0@'.format(bashcompletiondir),
- 'zsh completions directory: @0@'.format(zshcompletiondir),
- 'extra start script: @0@'.format(get_option('rc-local')),
- 'debug shell: @0@ @ @1@'.format(get_option('debug-shell'),
- get_option('debug-tty')),
- 'system UIDs: <=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_UID_MAX'),
- conf.get('SYSTEM_ALLOC_UID_MIN')),
- 'system GIDs: <=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_GID_MAX'),
- conf.get('SYSTEM_ALLOC_GID_MIN')),
- 'dynamic UIDs: @0@…@1@'.format(dynamic_uid_min, dynamic_uid_max),
- 'container UID bases: @0@…@1@'.format(container_uid_base_min, container_uid_base_max),
- 'static UID/GID allocations: @0@'.format(' '.join(static_ugids)),
- '/dev/kvm access mode: @0@'.format(get_option('dev-kvm-mode')),
- 'render group access mode: @0@'.format(get_option('group-render-mode')),
- 'certificate root directory: @0@'.format(get_option('certificate-root')),
- 'support URL: @0@'.format(support_url),
- 'nobody user name: @0@'.format(nobody_user),
- 'nobody group name: @0@'.format(nobody_group),
- 'fallback hostname: @0@'.format(get_option('fallback-hostname')),
-
- 'default DNSSEC mode: @0@'.format(default_dnssec),
- 'default DNS-over-TLS mode: @0@'.format(default_dns_over_tls),
- 'default mDNS mode: @0@'.format(default_mdns),
- 'default LLMNR mode: @0@'.format(default_llmnr),
- 'default cgroup hierarchy: @0@'.format(default_hierarchy),
- 'default net.naming-scheme setting: @0@'.format(default_net_naming_scheme),
- 'default KillUserProcesses setting: @0@'.format(kill_user_processes),
- 'default locale: @0@'.format(default_locale),
- 'default user $PATH: @0@'.format(default_user_path_display),
- 'systemd service watchdog: @0@'.format(watchdog_opt)]
-
-alt_dns_servers = '\n '.join(dns_servers.split(' '))
-alt_ntp_servers = '\n '.join(ntp_servers.split(' '))
-status += [
- 'default DNS servers: @0@'.format(alt_dns_servers),
- 'default NTP servers: @0@'.format(alt_ntp_servers)]
alt_time_epoch = run_command('date', '-Is', '-u', '-d',
'@@0@'.format(time_epoch)).stdout().strip()
-status += [
- 'time epoch: @0@ (@1@)'.format(time_epoch, alt_time_epoch)]
+
+summary({
+ 'build mode' : get_option('mode'),
+ 'split /usr' : split_usr,
+ 'split bin-sbin' : split_bin,
+ 'prefix directory' : prefixdir,
+ 'rootprefix directory' : rootprefixdir,
+ 'sysconf directory' : sysconfdir,
+ 'include directory' : includedir,
+ 'lib directory' : libdir,
+ 'rootlib directory' : rootlibdir,
+ 'SysV init scripts' : sysvinit_path,
+ 'SysV rc?.d directories' : sysvrcnd_path,
+ 'PAM modules directory' : pamlibdir,
+ 'PAM configuration directory' : pamconfdir,
+ 'libcryptsetup plugins directory' : libcryptsetup_plugins_dir,
+ 'RPM macros directory' : rpmmacrosdir,
+ 'modprobe.d directory' : modprobedir,
+ 'D-Bus policy directory' : dbuspolicydir,
+ 'D-Bus session directory' : dbussessionservicedir,
+ 'D-Bus system directory' : dbussystemservicedir,
+ 'bash completions directory' : bashcompletiondir,
+ 'zsh completions directory' : zshcompletiondir,
+ 'extra start script' : get_option('rc-local'),
+ 'debug shell' : '@0@ @ @1@'.format(get_option('debug-shell'),
+ get_option('debug-tty')),
+ 'system UIDs' : '<=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_UID_MAX'),
+ conf.get('SYSTEM_ALLOC_UID_MIN')),
+ 'system GIDs' : '<=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_GID_MAX'),
+ conf.get('SYSTEM_ALLOC_GID_MIN')),
+ 'dynamic UIDs' : '@0@…@1@'.format(dynamic_uid_min, dynamic_uid_max),
+ 'container UID bases' : '@0@…@1@'.format(container_uid_base_min, container_uid_base_max),
+ 'static UID/GID allocations' : ' '.join(static_ugids),
+ '/dev/kvm access mode' : get_option('dev-kvm-mode'),
+ 'render group access mode' : get_option('group-render-mode'),
+ 'certificate root directory' : get_option('certificate-root'),
+ 'support URL' : support_url,
+ 'nobody user name' : nobody_user,
+ 'nobody group name' : nobody_group,
+ 'fallback hostname' : get_option('fallback-hostname'),
+ 'default DNSSEC mode' : default_dnssec,
+ 'default DNS-over-TLS mode' : default_dns_over_tls,
+ 'default mDNS mode' : default_mdns,
+ 'default LLMNR mode' : default_llmnr,
+ 'default DNS servers' : dns_servers.split(' '),
+ 'default NTP servers' : ntp_servers.split(' '),
+ 'default cgroup hierarchy' : default_hierarchy,
+ 'default net.naming-scheme value' : default_net_naming_scheme,
+ 'default KillUserProcesses value' : kill_user_processes,
+ 'default locale' : default_locale,
+ 'default user $PATH' :
+ default_user_path != '' ? default_user_path : '(same as system services)',
+ 'systemd service watchdog' : service_watchdog == '' ? 'disabled' : service_watchdog,
+ 'time epoch' : '@0@ (@1@)'.format(time_epoch, alt_time_epoch)})
# TODO:
# CFLAGS: ${OUR_CFLAGS} ${CFLAGS}
# LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS}
if conf.get('ENABLE_EFI') == 1
- status += 'efi arch: @0@'.format(efi_arch)
+ summary({'efi arch' : efi_arch},
+ section : 'Extensible Firmware Interface')
if have_gnu_efi
- status += [
- 'EFI machine type: @0@'.format(EFI_MACHINE_TYPE_NAME),
- 'EFI CC @0@'.format(' '.join(efi_cc)),
- 'EFI lds: @0@'.format(efi_lds),
- 'EFI crt0: @0@'.format(efi_crt0),
- 'EFI include directory: @0@'.format(efi_incdir)]
+ summary({
+ 'EFI machine type' : EFI_MACHINE_TYPE_NAME,
+ 'EFI CC' : '@0@'.format(' '.join(efi_cc)),
+ 'EFI lds' : efi_lds,
+ 'EFI crt0' : efi_crt0,
+ 'EFI include directory' : efi_incdir},
+ section : 'Extensible Firmware Interface')
endif
endif
missing += 'DNS-over-TLS'
endif
-status += [
- '',
- 'enabled features: @0@'.format(', '.join(found)),
- '',
- 'disabled features: @0@'.format(', '.join(missing)),
- '']
-message('\n '.join(status))
+summary({
+ 'enabled' : ', '.join(found),
+ 'disabled' : ', '.join(missing)},
+ section : 'Features')
if rootprefixdir != rootprefix_default
warning('\n' +
description : 'use unit name or description in messages by default')
option('time-epoch', type : 'integer', value : '-1',
description : 'time epoch for time clients')
+option('clock-valid-range-usec-max', type : 'integer', value : '473364000000000', # 15 years
+ description : 'maximum value in microseconds for the difference between RTC and epoch, exceeding which is considered an RTC error')
option('system-alloc-uid-min', type : 'integer', value : '-1',
description : 'minimum system UID used when allocating')
description : 'SBAT distribution package version, e.g. 248-7.fc34')
option('sbat-distro-url', type : 'string',
description : 'SBAT distribution URL, e.g. https://src.fedoraproject.org/rpms/systemd')
+option('efi-color-normal', type : 'string', value : 'lightgray,black',
+ description : 'general boot loader color in "foreground,background" form, see constants from eficon.h')
+option('efi-color-entry', type : 'string', value : 'lightgray,black',
+ description : 'boot loader color for entries')
+option('efi-color-highlight', type : 'string', value : 'black,lightgray',
+ description : 'boot loader color for selected entries')
+option('efi-color-edit', type : 'string', value : 'black,lightgray',
+ description : 'boot loader color for option line edit')
option('bashcompletiondir', type : 'string',
description : 'directory for bash completion scripts ["no" disables]')
if install_sysconfdir
meson.add_install_script('sh', '-c',
- mkdir_p.format(join_paths(sysconfdir, 'systemd/network')))
+ mkdir_p.format(sysconfdir / 'systemd/network'))
endif
endif
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2021-02-25 18:41+0000\n"
+"PO-Revision-Date: 2021-08-17 07:04+0000\n"
"Last-Translator: Gustavo Costa <xfgusta@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://translate.fedoraproject.org/"
"projects/systemd/master/pt_BR/>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.4.2\n"
+"X-Generator: Weblate 4.7.2\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/login/org.freedesktop.login1.policy:117
msgid "Allow applications to inhibit system handling of the reboot key"
msgstr ""
-"Permitir que aplicativos inibam o sistema de gerenciar o botão de "
+"Permitir que os aplicativos inibam o gerenciamento do sistema da tecla de "
"reinicialização"
#: src/login/org.freedesktop.login1.policy:118
"Authentication is required for an application to inhibit system handling of "
"the reboot key."
msgstr ""
-"É necessária autenticação para que um aplicativo iniba o sistema de "
-"gerenciar o botão de reinicialização."
+"A autenticação é necessária para um aplicativo para inibir o gerenciamento "
+"do sistema da tecla de reinicialização."
#: src/login/org.freedesktop.login1.policy:128
msgid "Allow non-logged-in user to run programs"
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
+"PO-Revision-Date: 2021-08-19 07:04+0000\n"
+"Last-Translator: Hela Basa <r45xveza@pm.me>\n"
+"Language-Team: Sinhala <https://translate.fedoraproject.org/projects/systemd/"
+"master/si/>\n"
"Language: si\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n > 1;\n"
+"X-Generator: Weblate 4.7.2\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
-msgstr ""
+msgstr "පද්ධතියේ වේලාව සකසන්න"
#: src/timedate/org.freedesktop.timedate1.policy:23
msgid "Authentication is required to set the system time."
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2019-10-23 19:19+0800\n"
+"PO-Revision-Date: 2021-08-10 11:36+0800\n"
"Last-Translator: pan93412 <pan93412@gmail.com>\n"
"Language-Team: Chinese <chinese-l10n@googlegroups.com>\n"
"Language: zh_TW\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Lokalize 19.08.2\n"
+"X-Generator: Poedit 2.2.1\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/home/org.freedesktop.home1.policy:13
msgid "Create a home area"
-msgstr ""
+msgstr "創建一個家區域"
#: src/home/org.freedesktop.home1.policy:14
-#, fuzzy
-#| msgid "Authentication is required to set NTP servers."
msgid "Authentication is required to create a user's home area."
-msgstr "設定 NTP 伺服器需要身份驗證。"
+msgstr "創建用戶家區域需要身份驗證。"
#: src/home/org.freedesktop.home1.policy:23
msgid "Remove a home area"
-msgstr ""
+msgstr "移除一個家區域"
#: src/home/org.freedesktop.home1.policy:24
-#, fuzzy
-#| msgid "Authentication is required to set NTP servers."
msgid "Authentication is required to remove a user's home area."
-msgstr "設定 NTP 伺服器需要身份驗證。"
+msgstr "移除用戶家區域需要身份驗證。"
#: src/home/org.freedesktop.home1.policy:33
msgid "Check credentials of a home area"
-msgstr ""
+msgstr "檢查家區域憑證"
#: src/home/org.freedesktop.home1.policy:34
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to attach or detach a portable service image."
msgid ""
"Authentication is required to check credentials against a user's home area."
-msgstr "連結或取消連結可攜式服務映像需要身份驗證。"
+msgstr "根據用戶家區域檢查憑證需要認證。"
#: src/home/org.freedesktop.home1.policy:43
msgid "Update a home area"
-msgstr ""
+msgstr "更新一個家區域"
#: src/home/org.freedesktop.home1.policy:44
-#, fuzzy
-#| msgid "Authentication is required to attach a device to a seat."
msgid "Authentication is required to update a user's home area."
-msgstr "將設備連接到座位需要驗證。"
+msgstr "更新用戶家區域需要認證。"
#: src/home/org.freedesktop.home1.policy:53
msgid "Resize a home area"
-msgstr ""
+msgstr "調整家區域大小"
#: src/home/org.freedesktop.home1.policy:54
-#, fuzzy
-#| msgid "Authentication is required to set NTP servers."
msgid "Authentication is required to resize a user's home area."
-msgstr "è¨å®\9a NTP 伺æ\9c\8då\99¨é\9c\80è¦\81身份é©\97證。"
+msgstr "調æ\95´å®¶å\8d\80å\9f\9f大å°\8fé\9c\80è¦\81èª\8d證。"
#: src/home/org.freedesktop.home1.policy:63
msgid "Change password of a home area"
-msgstr ""
+msgstr "更改家區域的密碼"
#: src/home/org.freedesktop.home1.policy:64
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to manage active sessions, users and seats."
msgid ""
"Authentication is required to change the password of a user's home area."
-msgstr "管理活躍的工作階段、使用者與座位需要驗證。"
+msgstr "更改家區域密碼需要認證。"
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set hostname"
msgstr "要讓應用程式阻止系統處理上蓋開關需要驗證。"
#: src/login/org.freedesktop.login1.policy:117
-#, fuzzy
-#| msgid "Allow applications to inhibit system handling of the power key"
msgid "Allow applications to inhibit system handling of the reboot key"
-msgstr "å\85\81許æ\87\89ç\94¨ç¨\8bå¼\8fé\98»æ¢ç³»çµ±è\99\95ç\90\86é\9b»æº\90鍵"
+msgstr "å\85\81許æ\87\89ç\94¨ç¨\8bå¼\8fé\98»æ¢ç³»çµ±è\99\95ç\90\86é\87\8då\95\93鍵"
#: src/login/org.freedesktop.login1.policy:118
-#, fuzzy
-#| msgid ""
-#| "Authentication is required for an application to inhibit system handling "
-#| "of the power key."
msgid ""
"Authentication is required for an application to inhibit system handling of "
"the reboot key."
-msgstr "è¦\81è®\93æ\87\89ç\94¨ç¨\8bå¼\8fé\98»æ¢ç³»çµ±è\99\95ç\90\86é\9b»æº\90鍵需要驗證。"
+msgstr "è¦\81è®\93æ\87\89ç\94¨ç¨\8bå¼\8fé\98»æ¢ç³»çµ±è\99\95ç\90\86é\87\8då\95\93鍵需要驗證。"
#: src/login/org.freedesktop.login1.policy:128
msgid "Allow non-logged-in user to run programs"
msgstr "在應用程式阻止時停止系統"
#: src/login/org.freedesktop.login1.policy:258
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to hibernate the system while an application "
-#| "is inhibiting this."
msgid ""
"Authentication is required to halt the system while an application is "
"inhibiting this."
-msgstr "當應用程式阻止系統冬眠時將系統冬眠需要驗證。"
+msgstr "當應用程式阻止停止系統時需要驗證。"
#: src/login/org.freedesktop.login1.policy:268
msgid "Suspend the system"
#: src/login/org.freedesktop.login1.policy:406
msgid "Change Session"
-msgstr ""
+msgstr "更改會話"
#: src/login/org.freedesktop.login1.policy:407
-#, fuzzy
-#| msgid "Authentication is required to halt the system."
msgid "Authentication is required to change the virtual terminal."
-msgstr "停止系統需要身份驗證。"
+msgstr "更改虛擬終端需要身份驗證。"
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
#: src/network/org.freedesktop.network1.policy:143
msgid "DHCP server sends force renew message"
-msgstr ""
+msgstr "DHCP 服務器發送強制更新訊息"
#: src/network/org.freedesktop.network1.policy:144
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to send force renew message."
-msgstr "設定 wall 訊息需要身份驗證"
+msgstr "發送強制更新訊息需要身份驗證。"
#: src/network/org.freedesktop.network1.policy:154
msgid "Renew dynamic addresses"
msgstr "重新產生動態位址需要身份驗證。"
#: src/network/org.freedesktop.network1.policy:165
-#, fuzzy
-#| msgid "Revert NTP settings"
msgid "Reload network settings"
-msgstr "é\82\84å\8e\9f NTP 設定"
+msgstr "é\87\8dæ\96°å\8a è¼\89網絡設定"
#: src/network/org.freedesktop.network1.policy:166
-#, fuzzy
-#| msgid "Authentication is required to reset NTP settings."
msgid "Authentication is required to reload network settings."
-msgstr "重設 NTP 設定需要身份驗證。"
+msgstr "重新加載網絡設定需要身份驗證。"
#: src/network/org.freedesktop.network1.policy:176
msgid "Reconfigure network interface"
-msgstr ""
+msgstr "重新配置網絡接口"
#: src/network/org.freedesktop.network1.policy:177
-#, fuzzy
-#| msgid "Authentication is required to reboot the system."
msgid "Authentication is required to reconfigure network interface."
-msgstr "重新啟動系統需要驗證。"
+msgstr "重新配置網絡接口需要驗證。"
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
msgstr "刪除與 '$(unit)' 相關的檔案及目錄需要身份驗證。"
#: src/core/dbus-unit.c:757
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to send a UNIX signal to the processes of "
-#| "'$(unit)'."
msgid ""
"Authentication is required to freeze or thaw the processes of '$(unit)' unit."
-msgstr "å\82³é\80\81 UNIX ä¿¡è\99\9fè\87³「$(unit)」的程序需要身份驗證。"
+msgstr "å\87\8dçµ\90æ\88\96解å\87\8d「$(unit)」的程序需要身份驗證。"
#~ msgid ""
#~ "Authentication is required to halt the system while an application asked "
enable systemd-homed.service
enable systemd-userdbd.socket
enable systemd-pstore.service
+enable systemd-boot-update.service
disable console-getty.service
disable debug-shell.service
if bash_completion.found()
bashcompletiondir = bash_completion.get_pkgconfig_variable('completionsdir')
else
- bashcompletiondir = join_paths(datadir, 'bash-completion/completions')
+ bashcompletiondir = datadir / 'bash-completion/completions'
endif
endif
elif __contains_word "$verb" ${VERBS[VERIFY]}; then
if [[ $cur = -* ]]; then
- comps='--help --version --system --user --global --man=no --generators=yes'
+ comps='--help --version --system --user --global --man=no --generators=yes --root --image --recursive-errors=no --recursive-errors=yes --recursive-errors=one'
else
comps=$( compgen -A file -- "$cur" )
compopt -o filenames
elif __contains_word "$verb" ${VERBS[SECURITY]}; then
if [[ $cur = -* ]]; then
- comps='--help --version --no-pager --system --user -H --host -M --machine'
+ comps='--help --version --no-pager --system --user -H --host -M --machine --offline --threshold'
else
if __contains_word "--user" ${COMP_WORDS[*]}; then
mode=--user
[MONITOR_STANDALONE]='-k --kernel -u --udev -p --property'
[MONITOR_ARG]='-s --subsystem-match -t --tag-match'
[TEST]='-a --action -N --resolve-names'
+ [TEST_BUILTIN]='-a --action'
)
local verbs=(info trigger settle control monitor test-builtin test)
;;
'test-builtin')
+ if __contains_word "$prev" ${OPTS[TEST_BUILTIN]}; then
+ case $prev in
+ -a|--action)
+ comps=$( udevadm test-builtin --action help )
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
for ((i=0; i < COMP_CWORD; i++)); do
if __contains_word "${COMP_WORDS[i]}" "${builtins[@]}"; then
builtin=${COMP_WORDS[i]}
if [[ -z $builtin ]]; then
comps="${builtins[@]}"
elif [[ $cur = -* ]]; then
- comps="${OPTS[COMMON]}"
+ comps="${OPTS[COMMON]} ${OPTS[TEST_BUILTIN]}"
else
comps=$( __get_all_sysdevs )
local IFS=$'\n'
'--system[Operate on system systemd instance]' \
'--user[Operate on user systemd instance]' \
'--global[Show global user instance config]' \
+ '--root=[Add support for root argument]:PATH' \
+ '--image=[Add support for discrete images]:PATH' \
+ '--recursive-errors=[When verifying a unit, control dependency verification]:MODE' \
+ '--offline=[Perform a security review of the specified unit file(s)]:BOOL' \
+ '--threshold=[Set a value to compare the overall security exposure level with]: NUMBER' \
'--no-pager[Do not pipe output into a pager]' \
'--man=[Do (not) check for existence of man pages]:boolean:(1 0)' \
'--order[When generating graph for dot, show only order]' \
'--dry-run[Do not actually trigger the event.]' \
'--quiet[Suppress error logging in triggering events.]' \
'--type=[Trigger a specific type of devices.]:types:(devices subsystems failed)' \
- '--action=[Type of event to be triggered.]:actions:(add change remove)' \
+ '--action=[Type of event to be triggered.]:actions:(add change remove move online offline bind unbind)' \
'--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]' \
'--subsystem-nomatch=[Do not trigger events for devices which belong to a matching subsystem.]' \
'--attr-match=attribute=[Trigger events for devices with a matching sysfs attribute.]' \
(( $+functions[_udevadm_test] )) ||
_udevadm_test(){
_arguments \
- '--action=[The action string.]:actions:(add change remove)' \
+ '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
'--subsystem=[The subsystem string.]' \
'--help[Print help text.]' \
'*::devpath:_files -P /sys/ -W /sys'
_udevadm_test-builtin(){
if (( CURRENT == 2 )); then
_arguments \
+ '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
'--help[Print help text]' \
'*::builtins:(blkid btrfs hwdb input_id net_id net_setup_link kmod path_id usb_id uaccess)'
elif (( CURRENT == 3 )); then
_arguments \
+ '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
'--help[Print help text]' \
'*::syspath:_files -P /sys -W /sys'
else
_arguments \
+ '--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
'--help[Print help text]'
fi
}
zshcompletiondir = get_option('zshcompletiondir')
if zshcompletiondir == ''
- zshcompletiondir = join_paths(datadir, 'zsh/site-functions')
+ zshcompletiondir = datadir / 'zsh/site-functions'
endif
custom_target(
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind < argc)
#include "sd-daemon.h"
#include "alloc-util.h"
+#include "env-util.h"
#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
return count;
}
-static int exec_process(const char *name, char **argv, char **env, int start_fd, size_t n_fds) {
+static int exec_process(const char *name, char **argv, int start_fd, size_t n_fds) {
_cleanup_strv_free_ char **envp = NULL;
- _cleanup_free_ char *joined = NULL;
- size_t n_env = 0, length;
- const char *tocopy;
+ const char *var;
char **s;
int r;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--inetd only supported for single file descriptors.");
- length = strv_length(arg_setenv);
-
- /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
- envp = new0(char *, length + 8);
- if (!envp)
- return log_oom();
-
- STRV_FOREACH(s, arg_setenv) {
-
- if (strchr(*s, '=')) {
- char *k;
-
- k = strdup(*s);
- if (!k)
- return log_oom();
-
- envp[n_env++] = k;
- } else {
- _cleanup_free_ char *p = NULL;
- const char *n;
-
- p = strjoin(*s, "=");
- if (!p)
- return log_oom();
-
- n = strv_find_prefix(env, p);
- if (!n)
- continue;
-
- envp[n_env] = strdup(n);
- if (!envp[n_env])
- return log_oom();
-
- n_env++;
- }
- }
-
- FOREACH_STRING(tocopy, "TERM=", "PATH=", "USER=", "HOME=") {
+ FOREACH_STRING(var, "TERM", "PATH", "USER", "HOME") {
const char *n;
- n = strv_find_prefix(env, tocopy);
+ n = strv_find_prefix(environ, var);
if (!n)
continue;
- envp[n_env] = strdup(n);
- if (!envp[n_env])
- return log_oom();
-
- n_env++;
+ r = strv_extend(&envp, n);
+ if (r < 0)
+ return r;
}
if (arg_inetd) {
safe_close(start_fd);
}
- if (asprintf((char **) (envp + n_env++), "LISTEN_FDS=%zu", n_fds) < 0)
- return log_oom();
+ r = strv_extendf(&envp, "LISTEN_FDS=%zu", n_fds);
+ if (r < 0)
+ return r;
- if (asprintf((char **) (envp + n_env++), "LISTEN_PID=" PID_FMT, getpid_cached()) < 0)
- return log_oom();
+ r = strv_extendf(&envp, "LISTEN_PID=" PID_FMT, getpid_cached());
+ if (r < 0)
+ return r;
if (arg_fdnames) {
_cleanup_free_ char *names = NULL;
size_t len;
- char *e;
len = strv_length(arg_fdnames);
if (len == 1)
if (!names)
return log_oom();
- e = strjoin("LISTEN_FDNAMES=", names);
- if (!e)
+ char *t = strjoin("LISTEN_FDNAMES=", names);
+ if (!t)
return log_oom();
- envp[n_env++] = e;
+ r = strv_consume(&envp, t);
+ if (r < 0)
+ return r;
}
}
- joined = strv_join(argv, " ");
+ STRV_FOREACH(s, arg_setenv) {
+ r = strv_env_replace_strdup(&envp, *s);
+ if (r < 0)
+ return r;
+ }
+
+ _cleanup_free_ char *joined = strv_join(argv, " ");
if (!joined)
return log_oom();
return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
}
-static int fork_and_exec_process(const char *child, char **argv, char **env, int fd) {
+static int fork_and_exec_process(const char *child, char **argv, int fd) {
_cleanup_free_ char *joined = NULL;
pid_t child_pid;
int r;
return r;
if (r == 0) {
/* In the child */
- exec_process(child, argv, env, fd, 1);
+ exec_process(child, argv, fd, 1);
_exit(EXIT_FAILURE);
}
return 0;
}
-static int do_accept(const char *name, char **argv, char **envp, int fd) {
+static int do_accept(const char *name, char **argv, int fd) {
_cleanup_free_ char *local = NULL, *peer = NULL;
_cleanup_close_ int fd_accepted = -1;
(void) getpeername_pretty(fd_accepted, true, &peer);
log_info("Connection from %s to %s", strna(peer), strna(local));
- return fork_and_exec_process(name, argv, envp, fd_accepted);
+ return fork_and_exec_process(name, argv, fd_accepted);
}
/* SIGCHLD handler. */
break;
case 'E':
- r = strv_extend(&arg_setenv, optarg);
+ r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
if (r < 0)
- return log_oom();
-
+ return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
break;
case ARG_FDNAME: {
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind == argc)
return 1 /* work to do */;
}
-int main(int argc, char **argv, char **envp) {
+int main(int argc, char **argv) {
int r, n;
int epoll_fd = -1;
log_info("Communication attempt on fd %i.", event.data.fd);
if (arg_accept) {
- r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
+ r = do_accept(argv[optind], argv + optind, event.data.fd);
if (r < 0)
return EXIT_FAILURE;
} else
break;
}
- exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, (size_t) n);
+ exec_process(argv[optind], argv + optind, SD_LISTEN_FDS_START, (size_t) n);
return EXIT_SUCCESS;
}
return log_error_errno(r, "Failed to initialize manager: %m");
log_debug("Starting manager...");
- r = manager_startup(m, NULL, NULL);
+ r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, /* root= */ NULL);
if (r < 0)
return r;
#include <sys/utsname.h>
+#include "af-list.h"
#include "analyze-security.h"
+#include "analyze-verify.h"
#include "bus-error.h"
#include "bus-map-properties.h"
#include "bus-unit-util.h"
#include "in-addr-util.h"
#include "locale-util.h"
#include "macro.h"
+#include "manager.h"
#include "missing_capability.h"
#include "missing_sched.h"
#include "nulstr-util.h"
#if HAVE_SECCOMP
# include "seccomp-util.h"
#endif
+#include "service.h"
#include "set.h"
#include "stdio-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "unit-def.h"
#include "unit-name.h"
+#include "unit-serialize.h"
-struct security_info {
+typedef struct SecurityInfo {
char *id;
char *type;
char *load_state;
bool restrict_address_family_packet;
bool restrict_address_family_other;
- uint64_t restrict_namespaces;
+ unsigned long long restrict_namespaces;
bool restrict_realtime;
bool restrict_suid_sgid;
char *device_policy;
bool device_allow_non_empty;
- char **system_call_architectures;
+ Set *system_call_architectures;
bool system_call_filter_allow_list;
- Set *system_call_filter;
+ Hashmap *system_call_filter;
- uint32_t _umask;
-};
+ mode_t _umask;
+} SecurityInfo;
struct security_assessor {
const char *id;
uint64_t range;
int (*assess)(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description);
bool default_dependencies_only;
};
-static void security_info_free(struct security_info *i) {
+static SecurityInfo *security_info_new(void) {
+ SecurityInfo *info = new(SecurityInfo, 1);
+ if (!info)
+ return NULL;
+
+ *info = (SecurityInfo) {
+ .default_dependencies = true,
+ .capability_bounding_set = UINT64_MAX,
+ .restrict_namespaces = UINT64_MAX,
+ ._umask = 0002,
+ };
+
+ return info;
+}
+
+static SecurityInfo *security_info_free(SecurityInfo *i) {
if (!i)
- return;
+ return NULL;
free(i->id);
free(i->type);
free(i->device_policy);
strv_free(i->supplementary_groups);
- strv_free(i->system_call_architectures);
+ set_free(i->system_call_architectures);
- set_free(i->system_call_filter);
+ hashmap_free(i->system_call_filter);
+
+ return mfree(i);
}
-static bool security_info_runs_privileged(const struct security_info *i) {
+DEFINE_TRIVIAL_CLEANUP_FUNC(SecurityInfo*, security_info_free);
+
+static bool security_info_runs_privileged(const SecurityInfo *i) {
assert(i);
if (STRPTR_IN_SET(i->user, "0", "root"))
static int assess_bool(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_user(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_protect_home(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_protect_system(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_root_directory(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_capability_bounding_set(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_umask(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_keyring_mode(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_protect_proc(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_proc_subset(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_notify_access(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_remove_ipc(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_supplementary_groups(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_restrict_namespaces(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_system_call_architectures(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
assert(ret_badness);
assert(ret_description);
- if (strv_isempty(info->system_call_architectures)) {
+ if (set_isempty(info->system_call_architectures)) {
b = 10;
d = strdup("Service may execute system calls with all ABIs");
- } else if (strv_equal(info->system_call_architectures, STRV_MAKE("native"))) {
+ } else if (set_contains(info->system_call_architectures, "native") &&
+ set_size(info->system_call_architectures) == 1) {
b = 0;
d = strdup("Service may execute system calls only with native ABI");
} else {
#if HAVE_SECCOMP
-static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
+static bool syscall_names_in_filter(Hashmap *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
const char *syscall;
NULSTR_FOREACH(syscall, f->value) {
if (id < 0)
continue;
- if (set_contains(s, syscall) == allow_list) {
+ if (hashmap_contains(s, syscall) == allow_list) {
log_debug("Offending syscall filter item: %s", syscall);
if (ret_offending_syscall)
*ret_offending_syscall = syscall;
static int assess_system_call_filter(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
uint64_t b;
int r;
- if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) {
+ if (!info->system_call_filter_allow_list && hashmap_isempty(info->system_call_filter)) {
r = free_and_strdup(&d, "Service does not filter system calls");
b = 10;
} else {
static int assess_ip_address_allow(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_device_allow(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
static int assess_ambient_capabilities(
const struct security_assessor *a,
- const struct security_info *info,
+ const SecurityInfo *info,
const void *data,
uint64_t *ret_badness,
char **ret_description) {
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, private_devices),
+ .offset = offsetof(SecurityInfo, private_devices),
},
{
.id = "PrivateMounts=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, private_mounts),
+ .offset = offsetof(SecurityInfo, private_mounts),
},
{
.id = "PrivateNetwork=",
.weight = 2500,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, private_network),
+ .offset = offsetof(SecurityInfo, private_network),
},
{
.id = "PrivateTmp=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, private_tmp),
+ .offset = offsetof(SecurityInfo, private_tmp),
.default_dependencies_only = true,
},
{
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, private_users),
+ .offset = offsetof(SecurityInfo, private_users),
},
{
.id = "ProtectControlGroups=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, protect_control_groups),
+ .offset = offsetof(SecurityInfo, protect_control_groups),
},
{
.id = "ProtectKernelModules=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, protect_kernel_modules),
+ .offset = offsetof(SecurityInfo, protect_kernel_modules),
},
{
.id = "ProtectKernelTunables=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, protect_kernel_tunables),
+ .offset = offsetof(SecurityInfo, protect_kernel_tunables),
},
{
.id = "ProtectKernelLogs=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, protect_kernel_logs),
+ .offset = offsetof(SecurityInfo, protect_kernel_logs),
},
{
.id = "ProtectClock=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, protect_clock),
+ .offset = offsetof(SecurityInfo, protect_clock),
},
{
.id = "ProtectHome=",
.weight = 50,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, protect_hostname),
+ .offset = offsetof(SecurityInfo, protect_hostname),
},
{
.id = "ProtectSystem=",
.weight = 100,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, lock_personality),
+ .offset = offsetof(SecurityInfo, lock_personality),
},
{
.id = "MemoryDenyWriteExecute=",
.weight = 100,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, memory_deny_write_execute),
+ .offset = offsetof(SecurityInfo, memory_deny_write_execute),
},
{
.id = "NoNewPrivileges=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, no_new_privileges),
+ .offset = offsetof(SecurityInfo, no_new_privileges),
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_ADMIN",
.weight = 100,
.range = 1,
.assess = assess_remove_ipc,
- .offset = offsetof(struct security_info, remove_ipc),
+ .offset = offsetof(SecurityInfo, remove_ipc),
},
{
.id = "Delegate=",
.weight = 100,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, delegate),
+ .offset = offsetof(SecurityInfo, delegate),
.parameter = true, /* invert! */
},
{
.weight = 500,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, restrict_realtime),
+ .offset = offsetof(SecurityInfo, restrict_realtime),
},
{
.id = "RestrictSUIDSGID=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, restrict_suid_sgid),
+ .offset = offsetof(SecurityInfo, restrict_suid_sgid),
},
{
.id = "RestrictNamespaces=~CLONE_NEWUSER",
.weight = 1500,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, restrict_address_family_inet),
+ .offset = offsetof(SecurityInfo, restrict_address_family_inet),
},
{
.id = "RestrictAddressFamilies=~AF_UNIX",
.weight = 25,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, restrict_address_family_unix),
+ .offset = offsetof(SecurityInfo, restrict_address_family_unix),
},
{
.id = "RestrictAddressFamilies=~AF_NETLINK",
.weight = 200,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, restrict_address_family_netlink),
+ .offset = offsetof(SecurityInfo, restrict_address_family_netlink),
},
{
.id = "RestrictAddressFamilies=~AF_PACKET",
.weight = 1000,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, restrict_address_family_packet),
+ .offset = offsetof(SecurityInfo, restrict_address_family_packet),
},
{
.id = "RestrictAddressFamilies=~…",
.weight = 1250,
.range = 1,
.assess = assess_bool,
- .offset = offsetof(struct security_info, restrict_address_family_other),
+ .offset = offsetof(SecurityInfo, restrict_address_family_other),
},
{
.id = "SystemCallArchitectures=",
},
};
-static int assess(const struct security_info *info, Table *overview_table, AnalyzeSecurityFlags flags) {
+static int assess(const SecurityInfo *info, Table *overview_table, AnalyzeSecurityFlags flags, unsigned threshold) {
static const struct {
uint64_t exposure;
const char *name;
return table_log_add_error(r);
}
+ /* Return error when overall exposure level is over threshold */
+ if (exposure > threshold)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int property_read_restrict_namespaces(
+ sd_bus *bus,
+ const char *member,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ SecurityInfo *info = userdata;
+ int r;
+ uint64_t namespaces;
+
+ assert(bus);
+ assert(member);
+ assert(m);
+ assert(info);
+
+ r = sd_bus_message_read(m, "t", &namespaces);
+ if (r < 0)
+ return r;
+
+ info->restrict_namespaces = (unsigned long long) namespaces;
+
+ return 0;
+}
+
+static int property_read_umask(
+ sd_bus *bus,
+ const char *member,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ SecurityInfo *info = userdata;
+ int r;
+ uint32_t umask;
+
+ assert(bus);
+ assert(member);
+ assert(m);
+ assert(info);
+
+ r = sd_bus_message_read(m, "u", &umask);
+ if (r < 0)
+ return r;
+
+ info->_umask = (mode_t) umask;
+
return 0;
}
sd_bus_error *error,
void *userdata) {
- struct security_info *info = userdata;
+ SecurityInfo *info = userdata;
int allow_list, r;
assert(bus);
return sd_bus_message_exit_container(m);
}
+static int property_read_syscall_archs(
+ sd_bus *bus,
+ const char *member,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ SecurityInfo *info = userdata;
+ int r;
+
+ assert(bus);
+ assert(member);
+ assert(m);
+ assert(info);
+
+ r = sd_bus_message_enter_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *name;
+
+ r = sd_bus_message_read(m, "s", &name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = set_put_strdup(&info->system_call_architectures, name);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_exit_container(m);
+}
+
static int property_read_system_call_filter(
sd_bus *bus,
const char *member,
sd_bus_error *error,
void *userdata) {
- struct security_info *info = userdata;
+ SecurityInfo *info = userdata;
int allow_list, r;
assert(bus);
if (r == 0)
break;
- r = set_put_strdup(&info->system_call_filter, name);
+ /* The actual ExecContext stores the system call id as the map value, which we don't
+ * need. So we assign NULL to all values here. */
+ r = hashmap_put_strdup(&info->system_call_filter, name, NULL);
if (r < 0)
return r;
}
sd_bus_error *error,
void *userdata) {
- struct security_info *info = userdata;
+ SecurityInfo *info = userdata;
bool deny_ipv4 = false, deny_ipv6 = false;
int r;
sd_bus_error *error,
void *userdata) {
- struct security_info *info = userdata;
+ SecurityInfo *info = userdata;
_cleanup_(strv_freep) char **l = NULL;
int r;
if (streq(member, "IPIngressFilterPath"))
info->ip_filters_custom_ingress = !strv_isempty(l);
else if (streq(member, "IPEgressFilterPath"))
- info->ip_filters_custom_ingress = !strv_isempty(l);
+ info->ip_filters_custom_egress = !strv_isempty(l);
return 0;
}
sd_bus_error *error,
void *userdata) {
- struct security_info *info = userdata;
+ SecurityInfo *info = userdata;
size_t n = 0;
int r;
return sd_bus_message_exit_container(m);
}
-static int acquire_security_info(sd_bus *bus, const char *name, struct security_info *info, AnalyzeSecurityFlags flags) {
+static int acquire_security_info(sd_bus *bus, const char *name, SecurityInfo *info, AnalyzeSecurityFlags flags) {
static const struct bus_properties_map security_map[] = {
- { "AmbientCapabilities", "t", NULL, offsetof(struct security_info, ambient_capabilities) },
- { "CapabilityBoundingSet", "t", NULL, offsetof(struct security_info, capability_bounding_set) },
- { "DefaultDependencies", "b", NULL, offsetof(struct security_info, default_dependencies) },
- { "Delegate", "b", NULL, offsetof(struct security_info, delegate) },
- { "DeviceAllow", "a(ss)", property_read_device_allow, 0 },
- { "DevicePolicy", "s", NULL, offsetof(struct security_info, device_policy) },
- { "DynamicUser", "b", NULL, offsetof(struct security_info, dynamic_user) },
- { "FragmentPath", "s", NULL, offsetof(struct security_info, fragment_path) },
- { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow, 0 },
- { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow, 0 },
- { "IPIngressFilterPath", "as", property_read_ip_filters, 0 },
- { "IPEgressFilterPath", "as", property_read_ip_filters, 0 },
- { "Id", "s", NULL, offsetof(struct security_info, id) },
- { "KeyringMode", "s", NULL, offsetof(struct security_info, keyring_mode) },
- { "ProtectProc", "s", NULL, offsetof(struct security_info, protect_proc) },
- { "ProcSubset", "s", NULL, offsetof(struct security_info, proc_subset) },
- { "LoadState", "s", NULL, offsetof(struct security_info, load_state) },
- { "LockPersonality", "b", NULL, offsetof(struct security_info, lock_personality) },
- { "MemoryDenyWriteExecute", "b", NULL, offsetof(struct security_info, memory_deny_write_execute) },
- { "NoNewPrivileges", "b", NULL, offsetof(struct security_info, no_new_privileges) },
- { "NotifyAccess", "s", NULL, offsetof(struct security_info, notify_access) },
- { "PrivateDevices", "b", NULL, offsetof(struct security_info, private_devices) },
- { "PrivateMounts", "b", NULL, offsetof(struct security_info, private_mounts) },
- { "PrivateNetwork", "b", NULL, offsetof(struct security_info, private_network) },
- { "PrivateTmp", "b", NULL, offsetof(struct security_info, private_tmp) },
- { "PrivateUsers", "b", NULL, offsetof(struct security_info, private_users) },
- { "ProtectControlGroups", "b", NULL, offsetof(struct security_info, protect_control_groups) },
- { "ProtectHome", "s", NULL, offsetof(struct security_info, protect_home) },
- { "ProtectHostname", "b", NULL, offsetof(struct security_info, protect_hostname) },
- { "ProtectKernelModules", "b", NULL, offsetof(struct security_info, protect_kernel_modules) },
- { "ProtectKernelTunables", "b", NULL, offsetof(struct security_info, protect_kernel_tunables) },
- { "ProtectKernelLogs", "b", NULL, offsetof(struct security_info, protect_kernel_logs) },
- { "ProtectClock", "b", NULL, offsetof(struct security_info, protect_clock) },
- { "ProtectSystem", "s", NULL, offsetof(struct security_info, protect_system) },
- { "RemoveIPC", "b", NULL, offsetof(struct security_info, remove_ipc) },
- { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families, 0 },
- { "RestrictNamespaces", "t", NULL, offsetof(struct security_info, restrict_namespaces) },
- { "RestrictRealtime", "b", NULL, offsetof(struct security_info, restrict_realtime) },
- { "RestrictSUIDSGID", "b", NULL, offsetof(struct security_info, restrict_suid_sgid) },
- { "RootDirectory", "s", NULL, offsetof(struct security_info, root_directory) },
- { "RootImage", "s", NULL, offsetof(struct security_info, root_image) },
- { "SupplementaryGroups", "as", NULL, offsetof(struct security_info, supplementary_groups) },
- { "SystemCallArchitectures", "as", NULL, offsetof(struct security_info, system_call_architectures) },
- { "SystemCallFilter", "(as)", property_read_system_call_filter, 0 },
- { "Type", "s", NULL, offsetof(struct security_info, type) },
- { "UMask", "u", NULL, offsetof(struct security_info, _umask) },
- { "User", "s", NULL, offsetof(struct security_info, user) },
+ { "AmbientCapabilities", "t", NULL, offsetof(SecurityInfo, ambient_capabilities) },
+ { "CapabilityBoundingSet", "t", NULL, offsetof(SecurityInfo, capability_bounding_set) },
+ { "DefaultDependencies", "b", NULL, offsetof(SecurityInfo, default_dependencies) },
+ { "Delegate", "b", NULL, offsetof(SecurityInfo, delegate) },
+ { "DeviceAllow", "a(ss)", property_read_device_allow, 0 },
+ { "DevicePolicy", "s", NULL, offsetof(SecurityInfo, device_policy) },
+ { "DynamicUser", "b", NULL, offsetof(SecurityInfo, dynamic_user) },
+ { "FragmentPath", "s", NULL, offsetof(SecurityInfo, fragment_path) },
+ { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow, 0 },
+ { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow, 0 },
+ { "IPIngressFilterPath", "as", property_read_ip_filters, 0 },
+ { "IPEgressFilterPath", "as", property_read_ip_filters, 0 },
+ { "Id", "s", NULL, offsetof(SecurityInfo, id) },
+ { "KeyringMode", "s", NULL, offsetof(SecurityInfo, keyring_mode) },
+ { "ProtectProc", "s", NULL, offsetof(SecurityInfo, protect_proc) },
+ { "ProcSubset", "s", NULL, offsetof(SecurityInfo, proc_subset) },
+ { "LoadState", "s", NULL, offsetof(SecurityInfo, load_state) },
+ { "LockPersonality", "b", NULL, offsetof(SecurityInfo, lock_personality) },
+ { "MemoryDenyWriteExecute", "b", NULL, offsetof(SecurityInfo, memory_deny_write_execute) },
+ { "NoNewPrivileges", "b", NULL, offsetof(SecurityInfo, no_new_privileges) },
+ { "NotifyAccess", "s", NULL, offsetof(SecurityInfo, notify_access) },
+ { "PrivateDevices", "b", NULL, offsetof(SecurityInfo, private_devices) },
+ { "PrivateMounts", "b", NULL, offsetof(SecurityInfo, private_mounts) },
+ { "PrivateNetwork", "b", NULL, offsetof(SecurityInfo, private_network) },
+ { "PrivateTmp", "b", NULL, offsetof(SecurityInfo, private_tmp) },
+ { "PrivateUsers", "b", NULL, offsetof(SecurityInfo, private_users) },
+ { "ProtectControlGroups", "b", NULL, offsetof(SecurityInfo, protect_control_groups) },
+ { "ProtectHome", "s", NULL, offsetof(SecurityInfo, protect_home) },
+ { "ProtectHostname", "b", NULL, offsetof(SecurityInfo, protect_hostname) },
+ { "ProtectKernelModules", "b", NULL, offsetof(SecurityInfo, protect_kernel_modules) },
+ { "ProtectKernelTunables", "b", NULL, offsetof(SecurityInfo, protect_kernel_tunables) },
+ { "ProtectKernelLogs", "b", NULL, offsetof(SecurityInfo, protect_kernel_logs) },
+ { "ProtectClock", "b", NULL, offsetof(SecurityInfo, protect_clock) },
+ { "ProtectSystem", "s", NULL, offsetof(SecurityInfo, protect_system) },
+ { "RemoveIPC", "b", NULL, offsetof(SecurityInfo, remove_ipc) },
+ { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families, 0 },
+ { "RestrictNamespaces", "t", property_read_restrict_namespaces, 0 },
+ { "RestrictRealtime", "b", NULL, offsetof(SecurityInfo, restrict_realtime) },
+ { "RestrictSUIDSGID", "b", NULL, offsetof(SecurityInfo, restrict_suid_sgid) },
+ { "RootDirectory", "s", NULL, offsetof(SecurityInfo, root_directory) },
+ { "RootImage", "s", NULL, offsetof(SecurityInfo, root_image) },
+ { "SupplementaryGroups", "as", NULL, offsetof(SecurityInfo, supplementary_groups) },
+ { "SystemCallArchitectures", "as", property_read_syscall_archs, 0 },
+ { "SystemCallFilter", "(as)", property_read_system_call_filter, 0 },
+ { "Type", "s", NULL, offsetof(SecurityInfo, type) },
+ { "UMask", "u", property_read_umask, 0 },
+ { "User", "s", NULL, offsetof(SecurityInfo, user) },
{}
};
return 0;
}
-static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_table, AnalyzeSecurityFlags flags) {
- _cleanup_(security_info_free) struct security_info info = {
- .default_dependencies = true,
- .capability_bounding_set = UINT64_MAX,
- .restrict_namespaces = UINT64_MAX,
- ._umask = 0002,
- };
+static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_table,
+ AnalyzeSecurityFlags flags, unsigned threshold) {
+
+ _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
+ if (!info)
+ return log_oom();
+
int r;
assert(bus);
assert(name);
- r = acquire_security_info(bus, name, &info, flags);
+ r = acquire_security_info(bus, name, info, flags);
if (r == -EMEDIUMTYPE) /* Ignore this one because not loaded or Type is oneshot */
return 0;
if (r < 0)
return r;
- r = assess(&info, overview_table, flags);
+ r = assess(info, overview_table, flags, threshold);
if (r < 0)
return r;
return 0;
}
-int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags) {
+/* Refactoring SecurityInfo so that it can make use of existing struct variables instead of reading from dbus */
+static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, SecurityInfo **ret_info) {
+ assert(ret_info);
+
+ _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
+ if (!info)
+ return log_oom();
+
+ if (u) {
+ if (u->id) {
+ info->id = strdup(u->id);
+ if (!info->id)
+ return log_oom();
+ }
+ if (unit_type_to_string(u->type)) {
+ info->type = strdup(unit_type_to_string(u->type));
+ if (!info->type)
+ return log_oom();
+ }
+ if (unit_load_state_to_string(u->load_state)) {
+ info->load_state = strdup(unit_load_state_to_string(u->load_state));
+ if (!info->load_state)
+ return log_oom();
+ }
+ if (u->fragment_path) {
+ info->fragment_path = strdup(u->fragment_path);
+ if (!info->fragment_path)
+ return log_oom();
+ }
+ info->default_dependencies = u->default_dependencies;
+ if (u->type == UNIT_SERVICE && notify_access_to_string(SERVICE(u)->notify_access)) {
+ info->notify_access = strdup(notify_access_to_string(SERVICE(u)->notify_access));
+ if (!info->notify_access)
+ return log_oom();
+ }
+ }
+
+ if (c) {
+ info->ambient_capabilities = c->capability_ambient_set;
+ info->capability_bounding_set = c->capability_bounding_set;
+ if (c->user) {
+ info->user = strdup(c->user);
+ if (!info->user)
+ return log_oom();
+ }
+ if (c->supplementary_groups) {
+ info->supplementary_groups = strv_copy(c->supplementary_groups);
+ if (!info->supplementary_groups)
+ return log_oom();
+ }
+ info->dynamic_user = c->dynamic_user;
+ if (exec_keyring_mode_to_string(c->keyring_mode)) {
+ info->keyring_mode = strdup(exec_keyring_mode_to_string(c->keyring_mode));
+ if (!info->keyring_mode)
+ return log_oom();
+ }
+ if (protect_proc_to_string(c->protect_proc)) {
+ info->protect_proc = strdup(protect_proc_to_string(c->protect_proc));
+ if (!info->protect_proc)
+ return log_oom();
+ }
+ if (proc_subset_to_string(c->proc_subset)) {
+ info->proc_subset = strdup(proc_subset_to_string(c->proc_subset));
+ if (!info->proc_subset)
+ return log_oom();
+ }
+ info->lock_personality = c->lock_personality;
+ info->memory_deny_write_execute = c->memory_deny_write_execute;
+ info->no_new_privileges = c->no_new_privileges;
+ info->protect_hostname = c->protect_hostname;
+ info->private_devices = c->private_devices;
+ info->private_mounts = c->private_mounts;
+ info->private_network = c->private_network;
+ info->private_tmp = c->private_tmp;
+ info->private_users = c->private_users;
+ info->protect_control_groups = c->protect_control_groups;
+ info->protect_kernel_modules = c->protect_kernel_modules;
+ info->protect_kernel_tunables = c->protect_kernel_tunables;
+ info->protect_kernel_logs = c->protect_kernel_logs;
+ info->protect_clock = c->protect_clock;
+ if (protect_home_to_string(c->protect_home)) {
+ info->protect_home = strdup(protect_home_to_string(c->protect_home));
+ if (!info->protect_home)
+ return log_oom();
+ }
+ if (protect_system_to_string(c->protect_system)) {
+ info->protect_system = strdup(protect_system_to_string(c->protect_system));
+ if (!info->protect_system)
+ return log_oom();
+ }
+ info->remove_ipc = c->remove_ipc;
+ info->restrict_address_family_inet =
+ info->restrict_address_family_unix =
+ info->restrict_address_family_netlink =
+ info->restrict_address_family_packet =
+ info->restrict_address_family_other =
+ c->address_families_allow_list;
+
+ void *key;
+ SET_FOREACH(key, c->address_families) {
+ int family = PTR_TO_INT(key);
+ if (family == 0)
+ continue;
+ if (IN_SET(family, AF_INET, AF_INET6))
+ info->restrict_address_family_inet = !c->address_families_allow_list;
+ else if (family == AF_UNIX)
+ info->restrict_address_family_unix = !c->address_families_allow_list;
+ else if (family == AF_NETLINK)
+ info->restrict_address_family_netlink = !c->address_families_allow_list;
+ else if (family == AF_PACKET)
+ info->restrict_address_family_packet = !c->address_families_allow_list;
+ else
+ info->restrict_address_family_other = !c->address_families_allow_list;
+ }
+
+ info->restrict_namespaces = c->restrict_namespaces;
+ info->restrict_realtime = c->restrict_realtime;
+ info->restrict_suid_sgid = c->restrict_suid_sgid;
+ if (c->root_directory) {
+ info->root_directory = strdup(c->root_directory);
+ if (!info->root_directory)
+ return log_oom();
+ }
+ if (c->root_image) {
+ info->root_image = strdup(c->root_image);
+ if (!info->root_image)
+ return log_oom();
+ }
+ info->_umask = c->umask;
+ if (c->syscall_archs) {
+ info->system_call_architectures = set_copy(c->syscall_archs);
+ if (!info->system_call_architectures)
+ return log_oom();
+ }
+ info->system_call_filter_allow_list = c->syscall_allow_list;
+ if (c->syscall_filter) {
+ info->system_call_filter = hashmap_copy(c->syscall_filter);
+ if (!info->system_call_filter)
+ return log_oom();
+ }
+ }
+
+ if (g) {
+ info->delegate = g->delegate;
+ if (cgroup_device_policy_to_string(g->device_policy)) {
+ info->device_policy = strdup(cgroup_device_policy_to_string(g->device_policy));
+ if (!info->device_policy)
+ return log_oom();
+ }
+
+ IPAddressAccessItem *i;
+ bool deny_ipv4 = false, deny_ipv6 = false;
+
+ LIST_FOREACH(items, i, g->ip_address_deny) {
+ if (i->family == AF_INET && i->prefixlen == 0)
+ deny_ipv4 = true;
+ else if (i->family == AF_INET6 && i->prefixlen == 0)
+ deny_ipv6 = true;
+ }
+ info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
+
+ info->ip_address_allow_localhost = info->ip_address_allow_other = false;
+ LIST_FOREACH(items, i, g->ip_address_allow) {
+ if (in_addr_is_localhost(i->family, &i->address))
+ info->ip_address_allow_localhost = true;
+ else
+ info->ip_address_allow_other = true;
+ }
+
+ info->ip_filters_custom_ingress = !strv_isempty(g->ip_filters_ingress);
+ info->ip_filters_custom_egress = !strv_isempty(g->ip_filters_egress);
+ info->device_allow_non_empty = !LIST_IS_EMPTY(g->device_allow);
+ }
+
+ *ret_info = TAKE_PTR(info);
+
+ return 0;
+}
+
+static int offline_security_check(Unit *u, unsigned threshold) {
+ _cleanup_(table_unrefp) Table *overview_table = NULL;
+ AnalyzeSecurityFlags flags = 0;
+ _cleanup_(security_info_freep) SecurityInfo *info = NULL;
+ int r;
+
+ assert(u);
+
+ if (DEBUG_LOGGING)
+ unit_dump(u, stdout, "\t");
+
+ r = get_security_info(u, unit_get_exec_context(u), unit_get_cgroup_context(u), &info);
+ if (r < 0)
+ return r;
+
+ return assess(info, overview_table, flags, threshold);
+}
+
+static int offline_security_checks(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, unsigned threshold, const char *root) {
+ const ManagerTestRunFlags flags =
+ MANAGER_TEST_RUN_MINIMAL |
+ MANAGER_TEST_RUN_ENV_GENERATORS |
+ run_generators * MANAGER_TEST_RUN_GENERATORS;
+
+ _cleanup_(manager_freep) Manager *m = NULL;
+ Unit *units[strv_length(filenames)];
+ _cleanup_free_ char *var = NULL;
+ int r, k;
+ size_t count = 0;
+ char **filename;
+
+ if (strv_isempty(filenames))
+ return 0;
+
+ /* set the path */
+ r = verify_generate_path(&var, filenames);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit load path: %m");
+
+ assert_se(set_unit_path(var) >= 0);
+
+ r = manager_new(scope, flags, &m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize manager: %m");
+
+ log_debug("Starting manager...");
+
+ r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root);
+ if (r < 0)
+ return r;
+
+ log_debug("Loading remaining units from the command line...");
+
+ STRV_FOREACH(filename, filenames) {
+ _cleanup_free_ char *prepared = NULL;
+
+ log_debug("Handling %s...", *filename);
+
+ k = verify_prepare_filename(*filename, &prepared);
+ if (k < 0) {
+ log_warning_errno(k, "Failed to prepare filename %s: %m", *filename);
+ if (r == 0)
+ r = k;
+ continue;
+ }
+
+ k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]);
+ if (k < 0) {
+ if (r == 0)
+ r = k;
+ continue;
+ }
+
+ count++;
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ k = offline_security_check(units[i], threshold);
+ if (k < 0 && r == 0)
+ r = k;
+ }
+
+ return r;
+}
+
+int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators,
+ bool offline, unsigned threshold, const char *root, AnalyzeSecurityFlags flags) {
+
_cleanup_(table_unrefp) Table *overview_table = NULL;
int ret = 0, r;
assert(bus);
+ if (offline)
+ return offline_security_checks(units, scope, check_man, run_generators, threshold, root);
+
if (strv_length(units) != 1) {
overview_table = table_new("unit", "exposure", "predicate", "happy");
if (!overview_table)
flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING;
STRV_FOREACH(i, list) {
- r = analyze_security_one(bus, *i, overview_table, flags);
+ r = analyze_security_one(bus, *i, overview_table, flags, threshold);
if (r < 0 && ret >= 0)
ret = r;
}
} else
name = mangled;
- r = analyze_security_one(bus, name, overview_table, flags);
+ r = analyze_security_one(bus, name, overview_table, flags, threshold);
if (r < 0 && ret >= 0)
ret = r;
}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <stdbool.h>
+
#include "sd-bus.h"
+#include "unit-file.h"
+
typedef enum AnalyzeSecurityFlags {
ANALYZE_SECURITY_SHORT = 1 << 0,
ANALYZE_SECURITY_ONLY_LOADED = 1 << 1,
ANALYZE_SECURITY_ONLY_LONG_RUNNING = 1 << 2,
} AnalyzeSecurityFlags;
-int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags);
+int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators,
+ bool offline, unsigned threshold, const char *root, AnalyzeSecurityFlags flags);
#include "manager.h"
#include "pager.h"
#include "path-util.h"
+#include "string-table.h"
#include "strv.h"
#include "unit-name.h"
#include "unit-serialize.h"
-static int prepare_filename(const char *filename, char **ret) {
+static void log_syntax_callback(const char *unit, int level, void *userdata) {
+ Set **s = userdata;
+ int r;
+
+ assert(userdata);
+ assert(unit);
+
+ if (level > LOG_WARNING)
+ return;
+
+ r = set_put_strdup(s, unit);
+ if (r < 0) {
+ set_free_free(*s);
+ *s = POINTER_MAX;
+ }
+}
+
+int verify_prepare_filename(const char *filename, char **ret) {
int r;
const char *name;
_cleanup_free_ char *abspath = NULL;
return 0;
}
-static int generate_path(char **var, char **filenames) {
+int verify_generate_path(char **var, char **filenames) {
const char *old;
char **filename;
return 0;
}
-int verify_executable(Unit *u, const ExecCommand *exec) {
+int verify_executable(Unit *u, const ExecCommand *exec, const char *root) {
int r;
if (!exec)
if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE)
return 0;
- r = find_executable_full(exec->path, false, NULL, NULL);
+ r = find_executable_full(exec->path, root, false, NULL, NULL);
if (r < 0)
return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path);
return 0;
}
-static int verify_executables(Unit *u) {
+static int verify_executables(Unit *u, const char *root) {
ExecCommand *exec;
int r = 0, k;
unsigned i;
exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command :
u->type == UNIT_MOUNT ? MOUNT(u)->control_command :
u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL;
- k = verify_executable(u, exec);
+ k = verify_executable(u, exec, root);
if (k < 0 && r == 0)
r = k;
if (u->type == UNIT_SERVICE)
for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) {
- k = verify_executable(u, SERVICE(u)->exec_command[i]);
+ k = verify_executable(u, SERVICE(u)->exec_command[i], root);
if (k < 0 && r == 0)
r = k;
}
if (u->type == UNIT_SOCKET)
for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) {
- k = verify_executable(u, SOCKET(u)->exec_command[i]);
+ k = verify_executable(u, SOCKET(u)->exec_command[i], root);
if (k < 0 && r == 0)
r = k;
}
return r;
}
-static int verify_unit(Unit *u, bool check_man) {
+static int verify_unit(Unit *u, bool check_man, const char *root) {
_cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
int r, k;
if (k < 0 && r == 0)
r = k;
- k = verify_executables(u);
+ k = verify_executables(u, root);
if (k < 0 && r == 0)
r = k;
return r;
}
-int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators) {
+static void set_destroy_ignore_pointer_max(Set** s) {
+ if (*s == POINTER_MAX)
+ return;
+ set_free_free(*s);
+}
+
+int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) {
const ManagerTestRunFlags flags =
MANAGER_TEST_RUN_MINIMAL |
MANAGER_TEST_RUN_ENV_GENERATORS |
+ (recursive_errors == RECURSIVE_ERRORS_NO) * MANAGER_TEST_RUN_IGNORE_DEPENDENCIES |
run_generators * MANAGER_TEST_RUN_GENERATORS;
_cleanup_(manager_freep) Manager *m = NULL;
+ _cleanup_(set_destroy_ignore_pointer_max) Set *s = NULL;
+ _unused_ _cleanup_(clear_log_syntax_callback) dummy_t dummy;
Unit *units[strv_length(filenames)];
_cleanup_free_ char *var = NULL;
int r, k, i, count = 0;
if (strv_isempty(filenames))
return 0;
+ /* Allow systemd-analyze to hook in a callback function so that it can get
+ * all the required log data from the function itself without having to rely
+ * on a global set variable for the same */
+ set_log_syntax_callback(log_syntax_callback, &s);
+
/* set the path */
- r = generate_path(&var, filenames);
+ r = verify_generate_path(&var, filenames);
if (r < 0)
return log_error_errno(r, "Failed to generate unit load path: %m");
log_debug("Starting manager...");
- r = manager_startup(m, NULL, NULL);
+ r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root);
if (r < 0)
return r;
log_debug("Handling %s...", *filename);
- k = prepare_filename(*filename, &prepared);
+ k = verify_prepare_filename(*filename, &prepared);
if (k < 0) {
log_error_errno(k, "Failed to prepare filename %s: %m", *filename);
if (r == 0)
}
for (i = 0; i < count; i++) {
- k = verify_unit(units[i], check_man);
+ k = verify_unit(units[i], check_man, root);
if (k < 0 && r == 0)
r = k;
}
- return r;
+ if (s == POINTER_MAX)
+ return log_oom();
+
+ if (set_isempty(s) || r != 0)
+ return r;
+
+ /* If all previous verifications succeeded, then either the recursive parsing of all the
+ * associated dependencies with RECURSIVE_ERRORS_YES or the parsing of the specified unit file
+ * with RECURSIVE_ERRORS_NO must have yielded a syntax warning and hence, a non-empty set. */
+ if (IN_SET(recursive_errors, RECURSIVE_ERRORS_YES, RECURSIVE_ERRORS_NO))
+ return -ENOTRECOVERABLE;
+
+ /* If all previous verifications succeeded, then the non-empty set could have resulted from
+ * a syntax warning encountered during the recursive parsing of the specified unit file and
+ * its direct dependencies. Hence, search for any of the filenames in the set and if found,
+ * return a non-zero process exit status. */
+ if (recursive_errors == RECURSIVE_ERRORS_ONE)
+ STRV_FOREACH(filename, filenames)
+ if (set_contains(s, basename(*filename)))
+ return -ENOTRECOVERABLE;
+
+ return 0;
}
+
+static const char* const recursive_errors_table[_RECURSIVE_ERRORS_MAX] = {
+ [RECURSIVE_ERRORS_NO] = "no",
+ [RECURSIVE_ERRORS_YES] = "yes",
+ [RECURSIVE_ERRORS_ONE] = "one",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(recursive_errors, RecursiveErrors);
#include "execute.h"
#include "path-lookup.h"
-int verify_executable(Unit *u, const ExecCommand *exec);
-int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators);
+typedef enum RecursiveErrors {
+ RECURSIVE_ERRORS_YES, /* Look for errors in all associated units */
+ RECURSIVE_ERRORS_NO, /* Don't look for errors in any but the selected unit */
+ RECURSIVE_ERRORS_ONE, /* Look for errors in the selected unit and its direct dependencies */
+ _RECURSIVE_ERRORS_MAX,
+ _RECURSIVE_ERRORS_INVALID = -EINVAL,
+} RecursiveErrors;
+
+int verify_generate_path(char **var, char **filenames);
+int verify_prepare_filename(const char *filename, char **ret);
+int verify_executable(Unit *u, const ExecCommand *exec, const char *root);
+int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root);
+
+const char* recursive_errors_to_string(RecursiveErrors i) _const_;
+RecursiveErrors recursive_errors_from_string(const char *s) _pure_;
#include "locale-util.h"
#include "log.h"
#include "main-func.h"
+#include "mount-util.h"
#include "nulstr-util.h"
#include "pager.h"
#include "parse-argument.h"
#endif
#include "sort-util.h"
#include "special.h"
+#include "string-table.h"
#include "strv.h"
#include "strxcpyx.h"
#include "terminal-util.h"
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static const char *arg_host = NULL;
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
+static RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES;
static bool arg_man = true;
static bool arg_generators = false;
-static const char *arg_root = NULL;
+static char *arg_root = NULL;
+static char *arg_image = NULL;
+static bool arg_offline = false;
+static unsigned arg_threshold = 100;
static unsigned arg_iterations = 1;
static usec_t arg_base_time = USEC_INFINITY;
STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
typedef struct BootTimes {
usec_t firmware_time;
}
static int do_verify(int argc, char *argv[], void *userdata) {
- return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
+ return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
}
static int do_security(int argc, char *argv[], void *userdata) {
(void) pager_open(arg_pager_flags);
- return analyze_security(bus, strv_skip(argv, 1), 0);
+ return analyze_security(bus, strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_offline, arg_threshold, arg_root, 0);
}
static int help(int argc, char *argv[], void *userdata) {
printf("%s [OPTIONS...] COMMAND ...\n\n"
"%sProfile systemd, show unit dependencies, check unit files.%s\n"
"\nCommands:\n"
- " [time] Print time required to boot the machine\n"
- " blame Print list of running units ordered by time to init\n"
- " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
- " plot Output SVG graphic showing service initialization\n"
- " dot [UNIT...] Output dependency graph in %s format\n"
- " dump Output state serialization of service manager\n"
- " cat-config Show configuration file and drop-ins\n"
- " unit-files List files and symlinks for units\n"
- " unit-paths List load directories for units\n"
- " exit-status [STATUS...] List exit status definitions\n"
- " capability [CAP...] List capability definitions\n"
- " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
- " condition CONDITION... Evaluate conditions and asserts\n"
- " verify FILE... Check unit files for correctness\n"
- " calendar SPEC... Validate repetitive calendar time events\n"
- " timestamp TIMESTAMP... Validate a timestamp\n"
- " timespan SPAN... Validate a time span\n"
- " security [UNIT...] Analyze security of unit\n"
+ " [time] Print time required to boot the machine\n"
+ " blame Print list of running units ordered by\n"
+ " time to init\n"
+ " critical-chain [UNIT...] Print a tree of the time critical chain\n"
+ " of units\n"
+ " plot Output SVG graphic showing service\n"
+ " initialization\n"
+ " dot [UNIT...] Output dependency graph in %s format\n"
+ " dump Output state serialization of service\n"
+ " manager\n"
+ " cat-config Show configuration file and drop-ins\n"
+ " unit-files List files and symlinks for units\n"
+ " unit-paths List load directories for units\n"
+ " exit-status [STATUS...] List exit status definitions\n"
+ " capability [CAP...] List capability definitions\n"
+ " syscall-filter [NAME...] Print list of syscalls in seccomp\n"
+ " filter\n"
+ " condition CONDITION... Evaluate conditions and asserts\n"
+ " verify FILE... Check unit files for correctness\n"
+ " calendar SPEC... Validate repetitive calendar time\n"
+ " events\n"
+ " timestamp TIMESTAMP... Validate a timestamp\n"
+ " timespan SPAN... Validate a time span\n"
+ " security [UNIT...] Analyze security of unit\n"
"\nOptions:\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --no-pager Do not pipe output into a pager\n"
- " --system Operate on system systemd instance\n"
- " --user Operate on user systemd instance\n"
- " --global Operate on global user configuration\n"
- " -H --host=[USER@]HOST Operate on remote host\n"
- " -M --machine=CONTAINER Operate on local container\n"
- " --order Show only order in the graph\n"
- " --require Show only requirement in the graph\n"
- " --from-pattern=GLOB Show only origins in the graph\n"
- " --to-pattern=GLOB Show only destinations in the graph\n"
- " --fuzz=SECONDS Also print services which finished SECONDS earlier\n"
- " than the latest in the branch\n"
- " --man[=BOOL] Do [not] check for existence of man pages\n"
- " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n"
- " --iterations=N Show the specified number of iterations\n"
- " --base-time=TIMESTAMP Calculate calendar times relative to specified time\n"
+ " -h --help Show this help\n"
+ " --recursive-errors=MODE Control which units are verified\n"
+ " --offline=BOOL Perform a security review on unit file(s)\n"
+ " --threshold=N Exit with a non-zero status when overall\n"
+ " exposure level is over threshold value\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --system Operate on system systemd instance\n"
+ " --user Operate on user systemd instance\n"
+ " --global Operate on global user configuration\n"
+ " -H --host=[USER@]HOST Operate on remote host\n"
+ " -M --machine=CONTAINER Operate on local container\n"
+ " --order Show only order in the graph\n"
+ " --require Show only requirement in the graph\n"
+ " --from-pattern=GLOB Show only origins in the graph\n"
+ " --to-pattern=GLOB Show only destinations in the graph\n"
+ " --fuzz=SECONDS Also print services which finished SECONDS\n"
+ " earlier than the latest in the branch\n"
+ " --man[=BOOL] Do [not] check for existence of man pages\n"
+ " --generators[=BOOL] Do [not] run unit generators\n"
+ " (requires privileges)\n"
+ " --iterations=N Show the specified number of iterations\n"
+ " --base-time=TIMESTAMP Calculate calendar times relative to\n"
+ " specified time\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
ARG_ORDER,
ARG_REQUIRE,
ARG_ROOT,
+ ARG_IMAGE,
ARG_SYSTEM,
ARG_USER,
ARG_GLOBAL,
ARG_GENERATORS,
ARG_ITERATIONS,
ARG_BASE_TIME,
+ ARG_RECURSIVE_ERRORS,
+ ARG_OFFLINE,
+ ARG_THRESHOLD,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "order", no_argument, NULL, ARG_ORDER },
- { "require", no_argument, NULL, ARG_REQUIRE },
- { "root", required_argument, NULL, ARG_ROOT },
- { "system", no_argument, NULL, ARG_SYSTEM },
- { "user", no_argument, NULL, ARG_USER },
- { "global", no_argument, NULL, ARG_GLOBAL },
- { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
- { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
- { "fuzz", required_argument, NULL, ARG_FUZZ },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { "man", optional_argument, NULL, ARG_MAN },
- { "generators", optional_argument, NULL, ARG_GENERATORS },
- { "host", required_argument, NULL, 'H' },
- { "machine", required_argument, NULL, 'M' },
- { "iterations", required_argument, NULL, ARG_ITERATIONS },
- { "base-time", required_argument, NULL, ARG_BASE_TIME },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "order", no_argument, NULL, ARG_ORDER },
+ { "require", no_argument, NULL, ARG_REQUIRE },
+ { "root", required_argument, NULL, ARG_ROOT },
+ { "image", required_argument, NULL, ARG_IMAGE },
+ { "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
+ { "offline", required_argument, NULL, ARG_OFFLINE },
+ { "threshold", required_argument, NULL, ARG_THRESHOLD },
+ { "system", no_argument, NULL, ARG_SYSTEM },
+ { "user", no_argument, NULL, ARG_USER },
+ { "global", no_argument, NULL, ARG_GLOBAL },
+ { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
+ { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
+ { "fuzz", required_argument, NULL, ARG_FUZZ },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "man", optional_argument, NULL, ARG_MAN },
+ { "generators", optional_argument, NULL, ARG_GENERATORS },
+ { "host", required_argument, NULL, 'H' },
+ { "machine", required_argument, NULL, 'M' },
+ { "iterations", required_argument, NULL, ARG_ITERATIONS },
+ { "base-time", required_argument, NULL, ARG_BASE_TIME },
{}
};
case 'h':
return help(0, NULL, NULL);
+ case ARG_RECURSIVE_ERRORS:
+ if (streq(optarg, "help")) {
+ DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX);
+ return 0;
+ }
+ r = recursive_errors_from_string(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Unknown mode passed to --recursive-errors='%s'.", optarg);
+
+ arg_recursive_errors = r;
+ break;
+
case ARG_VERSION:
return version();
case ARG_ROOT:
- arg_root = optarg;
+ r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_IMAGE:
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
+ if (r < 0)
+ return r;
break;
case ARG_SYSTEM:
return r;
break;
+ case ARG_OFFLINE:
+ r = parse_boolean_argument("--offline", optarg, &arg_offline);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_THRESHOLD:
+ r = safe_atou(optarg, &arg_threshold);
+ if (r < 0 || arg_threshold > 100)
+ return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse threshold: %s", optarg);
+
+ break;
+
case ARG_ITERATIONS:
r = safe_atou(optarg, &arg_iterations);
if (r < 0)
return -EINVAL;
default:
- assert_not_reached("Unhandled option code.");
+ assert_not_reached();
}
+ if (arg_offline && !streq_ptr(argv[optind], "security"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --offline= is only supported for security right now.");
+
+ if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --threshold= is only supported for security right now.");
+
if (arg_scope == UNIT_FILE_GLOBAL &&
!STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --user is not supported for cat-config right now.");
- if (arg_root && !streq_ptr(argv[optind], "cat-config"))
+ if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify")) &&
+ (!(streq_ptr(argv[optind], "security") && arg_offline)))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Option --root is only supported for cat-config right now.");
+ "Options --root= and --image= are only supported for cat-config, verify and security when used with --offline= right now.");
+
+ /* Having both an image and a root is not supported by the code */
+ if (arg_root && arg_image)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
return 1; /* work to do */
}
static int run(int argc, char *argv[]) {
+ _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+ _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
if (r <= 0)
return r;
+ /* Open up and mount the image */
+ if (arg_image) {
+ assert(!arg_root);
+
+ r = mount_image_privately_interactively(
+ arg_image,
+ DISSECT_IMAGE_GENERIC_ROOT |
+ DISSECT_IMAGE_RELAX_VAR_CHECK |
+ DISSECT_IMAGE_READ_ONLY,
+ &unlink_dir,
+ &loop_device,
+ &decrypted_image);
+ if (r < 0)
+ return r;
+
+ arg_root = strdup(unlink_dir);
+ if (!arg_root)
+ return log_oom();
+ }
+
return dispatch_verb(argc, argv, verbs, NULL);
}
static void test_verify_nonexistent(void) {
/* Negative cases */
- assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/non/existent"}) == 0);
- assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/non/existent"}) < 0);
+ assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/non/existent"}, NULL) == 0);
+ assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/non/existent"}, NULL) < 0);
/* Ordinary cases */
- assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/bin/echo"}) == 0);
- assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/bin/echo"}) == 0);
+ assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/bin/echo"}, NULL) == 0);
+ assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/bin/echo"}, NULL) == 0);
}
int main(int argc, char *argv[]) {
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (isempty(emoji) || streq(emoji, "auto"))
return log_device_error_errno(device, r, "Failed to write %s: %m", saved);
} else
- assert_not_reached("Unknown verb.");
+ assert_not_reached();
return 0;
}
#define malloc0(n) (calloc(1, (n) ?: 1))
-static inline void *mfree(void *memory) {
- free(memory);
- return NULL;
-}
+#define mfree(memory) \
+ ({ \
+ free(memory); \
+ (typeof(memory)) NULL; \
+ })
#define free_and_replace(a, b) \
({ \
- free(a); \
- (a) = (b); \
- (b) = NULL; \
+ typeof(a)* _a = &(a); \
+ typeof(b)* _b = &(b); \
+ free(*_a); \
+ (*_a) = (*_b); \
+ (*_b) = NULL; \
0; \
})
if (streq(arch_map[i].machine, u.machine))
return cached = arch_map[i].arch;
- assert_not_reached("Couldn't identify architecture. You need to patch systemd.");
+ assert_not_reached();
return _ARCHITECTURE_INVALID;
}
return supported;
}
+bool cg_kill_supported(void) {
+ static thread_local int supported = -1;
+
+ if (supported >= 0)
+ return supported;
+
+ if (cg_all_unified() <= 0)
+ supported = false;
+ else if (access("/sys/fs/cgroup/init.scope/cgroup.kill", F_OK) < 0) {
+ if (errno != ENOENT)
+ log_debug_errno(errno, "Failed to check if cgroup.kill is available, assuming not: %m");
+ supported = false;
+ } else
+ supported = true;
+
+ return supported;
+}
+
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
_cleanup_free_ char *fs = NULL;
int r;
return cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.threads");
}
+int cg_kill_kernel_sigkill(const char *controller, const char *path) {
+ /* Kills the cgroup at `path` directly by writing to its cgroup.kill file.
+ * This sends SIGKILL to all processes in the cgroup and has the advantage of
+ * being completely atomic, unlike cg_kill_items. */
+ int r;
+ _cleanup_free_ char *killfile = NULL;
+
+ assert(path);
+
+ if (!cg_kill_supported())
+ return -EOPNOTSUPP;
+
+ r = cg_get_path(controller, path, "cgroup.kill", &killfile);
+ if (r < 0)
+ return r;
+
+ r = write_string_file(killfile, "1", WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
int cg_kill_recursive(
const char *controller,
const char *path,
assert(path);
assert(sig >= 0);
- if (!s) {
- s = allocated_set = set_new(NULL);
- if (!s)
- return -ENOMEM;
- }
+ if (sig == SIGKILL && cg_kill_supported() &&
+ !FLAGS_SET(flags, CGROUP_IGNORE_SELF) && !s && !log_kill) {
+ /* ignore CGROUP_SIGCONT, since this is a no-op alongside SIGKILL */
+ ret = cg_kill_kernel_sigkill(controller, path);
+ if (ret < 0)
+ return ret;
+ } else {
+ if (!s) {
+ s = allocated_set = set_new(NULL);
+ if (!s)
+ return -ENOMEM;
+ }
- ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata);
+ ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata);
- r = cg_enumerate_subgroups(controller, path, &d);
- if (r < 0) {
- if (ret >= 0 && r != -ENOENT)
- return r;
+ r = cg_enumerate_subgroups(controller, path, &d);
+ if (r < 0) {
+ if (ret >= 0 && r != -ENOENT)
+ return r;
- return ret;
- }
+ return ret;
+ }
- while ((r = cg_read_subgroup(d, &fn)) > 0) {
- _cleanup_free_ char *p = NULL;
+ while ((r = cg_read_subgroup(d, &fn)) > 0) {
+ _cleanup_free_ char *p = NULL;
- p = path_join(empty_to_root(path), fn);
- free(fn);
- if (!p)
- return -ENOMEM;
+ p = path_join(empty_to_root(path), fn);
+ free(fn);
+ if (!p)
+ return -ENOMEM;
- r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata);
- if (r != 0 && ret >= 0)
+ r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata);
+ if (r != 0 && ret >= 0)
+ ret = r;
+ }
+ if (ret >= 0 && r < 0)
ret = r;
}
- if (ret >= 0 && r < 0)
- ret = r;
- if (flags & CGROUP_REMOVE) {
+ if (FLAGS_SET(flags, CGROUP_REMOVE)) {
r = cg_rmdir(controller, path);
if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
return r;
[CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
[CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
[CGROUP_CONTROLLER_BPF_SOCKET_BIND] = "bpf-socket-bind",
+ [CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES] = "bpf-restrict-network-interfaces",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
CGROUP_CONTROLLER_BPF_DEVICES,
CGROUP_CONTROLLER_BPF_FOREIGN,
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
+ CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -EINVAL,
CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
CGROUP_MASK_BPF_SOCKET_BIND = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_SOCKET_BIND),
+ CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES),
/* All real cgroup v1 controllers */
CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
/* All cgroup v2 BPF pseudo-controllers */
- CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND,
+ CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
} CGroupMask;
typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
+int cg_kill_kernel_sigkill(const char *controller, const char *path);
int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_split_spec(const char *spec, char **ret_controller, char **ret_path);
bool cg_ns_supported(void);
bool cg_freezer_supported(void);
+bool cg_kill_supported(void);
int cg_all_unified(void);
int cg_hybrid_unified(void);
return 0;
}
-char **strv_env_merge(size_t n_lists, ...) {
- _cleanup_strv_free_ char **ret = NULL;
- size_t n = 0;
- char **l, **k;
+char** _strv_env_merge(char **first, ...) {
+ _cleanup_strv_free_ char **merged = NULL;
+ char **k;
va_list ap;
/* Merges an arbitrary number of environment sets */
- va_start(ap, n_lists);
- for (size_t i = 0; i < n_lists; i++) {
+ size_t n = strv_length(first);
+
+ va_start(ap, first);
+ for (;;) {
+ char **l;
+
l = va_arg(ap, char**);
+ if (l == POINTER_MAX)
+ break;
+
n += strv_length(l);
}
va_end(ap);
- ret = new(char*, n+1);
- if (!ret)
+ k = merged = new(char*, n + 1);
+ if (!merged)
+ return NULL;
+ merged[0] = NULL;
+
+ if (env_append(merged, &k, first) < 0)
return NULL;
- *ret = NULL;
- k = ret;
+ va_start(ap, first);
+ for (;;) {
+ char **l;
- va_start(ap, n_lists);
- for (size_t i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
- if (env_append(ret, &k, l) < 0) {
+ if (l == POINTER_MAX)
+ break;
+
+ if (env_append(merged, &k, l) < 0) {
va_end(ap);
return NULL;
}
}
va_end(ap);
- return TAKE_PTR(ret);
+ return TAKE_PTR(merged);
}
static bool env_match(const char *t, const char *pattern) {
return strv_env_replace_consume(l, p);
}
+int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
+ /* Like strv_env_replace_strdup(), but pulls the variable from the environment of
+ * the calling program, if a variable name without value is specified.
+ */
+ char *p;
+
+ if (strchr(assignment, '=')) {
+ if (!env_assignment_is_valid(assignment))
+ return -EINVAL;
+
+ p = strdup(assignment);
+ } else {
+ if (!env_name_is_valid(assignment))
+ return -EINVAL;
+
+ /* If we can't find the variable in our environment, we will use
+ * the empty string. This way "passthrough" is equivalent to passing
+ * --setenv=FOO=$FOO in the shell. */
+ p = strjoin(assignment, "=", secure_getenv(assignment));
+ }
+ if (!p)
+ return -ENOMEM;
+
+ return strv_env_replace_consume(l, p);
+}
+
int strv_env_assign(char ***l, const char *key, const char *value) {
if (!env_name_is_valid(key))
return -EINVAL;
*ret_paths = TAKE_PTR(l);
return 1;
}
+
+int unsetenv_erase(const char *name) {
+ char *p;
+
+ assert(name);
+
+ p = getenv(name);
+ if (!p)
+ return 0;
+
+ string_erase(p);
+
+ if (unsetenv(name) < 0)
+ return -errno;
+
+ return 1;
+}
bool strv_env_name_is_valid(char **l);
bool strv_env_name_or_assignment_is_valid(char **l);
-char **strv_env_merge(size_t n_lists, ...);
+char** _strv_env_merge(char **first, ...);
+#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
int strv_env_replace_consume(char ***l, char *p); /* In place ... */
int strv_env_replace_strdup(char ***l, const char *assignment);
+int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value);
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
/* Parses and does sanity checks on an environment variable containing
* PATH-like colon-separated absolute paths */
int getenv_path_list(const char *name, char ***ret_paths);
+
+int unsetenv_erase(const char *name);
}
int fd_get_path(int fd, char **ret) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int r;
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- r = readlink_malloc(procfs_path, ret);
+ r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret);
if (r == -ENOENT) {
/* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
* things debuggable and distinguish the two. */
}
int fd_reopen(int fd, int flags) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
int new_fd;
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
*
* This implicitly resets the file read index to 0. */
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- new_fd = open(procfs_path, flags);
+ new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
if (new_fd < 0) {
if (errno != ENOENT)
return -errno;
#include <sys/socket.h>
#include "macro.h"
+#include "stdio-util.h"
/* maximum length of fdname */
#define FDNAME_MAX 255
0; \
})
-
int fd_reopen(int fd, int flags);
int read_nr_open(void);
int btrfs_defrag_fd(int fd);
+
+/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
+#define PROC_FD_PATH_MAX \
+ (STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
+
+static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int fd) {
+ assert(buf);
+ assert(fd >= 0);
+ assert_se(snprintf_ok(buf, PROC_FD_PATH_MAX, "/proc/self/fd/%i", fd));
+ return buf;
+}
+
+#define FORMAT_PROC_FD_PATH(fd) \
+ format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))
if (dir_fd == AT_FDCWD)
r = sockaddr_un_set_path(&sa.un, filename);
else {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
/* If we shall operate relative to some directory, then let's use O_PATH first to
* open the socket inode, and then connect to it via /proc/self/fd/. We have to do
* this since there's not connectat() that takes a directory fd as first arg. */
if (dfd < 0)
return -errno;
- xsprintf(procfs_path, "/proc/self/fd/%i", dfd);
- r = sockaddr_un_set_path(&sa.un, procfs_path);
+ r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd));
}
if (r < 0)
return r;
#pragma once
#include <dirent.h>
+#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/stat.h>
-#include <sys/fcntl.h>
#include <sys/types.h>
#include "macro.h"
return NULL;
if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
- snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex);
+ assert(snprintf_ok(buf, IF_NAMESIZE + 1, "%%%d", ifindex));
else
- snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex);
+ assert(snprintf_ok(buf, IF_NAMESIZE + 1, "%d", ifindex));
return buf;
}
for (size_t i = 0; i < n; i++)
if (t >= table[i].factor) {
if (flag & FORMAT_BYTES_BELOW_POINT) {
- snprintf(buf, l,
- "%" PRIu64 ".%" PRIu64 "%s",
- t / table[i].factor,
- i != n - 1 ?
- (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
- (t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
- table[i].suffix);
+ (void) snprintf(buf, l,
+ "%" PRIu64 ".%" PRIu64 "%s",
+ t / table[i].factor,
+ i != n - 1 ?
+ (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
+ (t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
+ table[i].suffix);
} else
- snprintf(buf, l,
- "%" PRIu64 "%s",
- t / table[i].factor,
- table[i].suffix);
+ (void) snprintf(buf, l,
+ "%" PRIu64 "%s",
+ t / table[i].factor,
+ table[i].suffix);
goto finish;
}
- snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
+ (void) snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
finish:
buf[l-1] = 0;
}
int fchmod_opath(int fd, mode_t m) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
/* This function operates also on fd that might have been opened with
* O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
* fchownat() does. */
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- if (chmod(procfs_path, m) < 0) {
+ if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
if (errno != ENOENT)
return -errno;
}
int futimens_opath(int fd, const struct timespec ts[2]) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
/* Similar to fchmod_path() but for futimens() */
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) {
+ if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) {
if (errno != ENOENT)
return -errno;
}
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
- char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int fd = -1;
int r, ret = 0;
/* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
* ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
* something fchown(), fchmod(), futimensat() don't allow. */
- xsprintf(fdpath, "/proc/self/fd/%i", fd);
-
ret = fchmod_and_chown(fd, mode, uid, gid);
if (stamp != USEC_INFINITY) {
timespec_store(&ts[0], stamp);
ts[1] = ts[0];
- r = utimensat(AT_FDCWD, fdpath, ts, 0);
+ r = utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0);
} else
- r = utimensat(AT_FDCWD, fdpath, NULL, 0);
+ r = utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), NULL, 0);
if (r < 0 && ret >= 0)
return -errno;
}
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
- char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
int wd;
/* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
- xsprintf(path, "/proc/self/fd/%i", what);
-
- wd = inotify_add_watch(fd, path, mask);
+ wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
if (wd < 0)
return -errno;
char **ret_path,
DIR **ret_dir) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_close_ int path_fd = -1;
_cleanup_free_ char *p = NULL;
DIR *d;
return r;
assert(path_fd >= 0);
- xsprintf(procfs_path, "/proc/self/fd/%i", path_fd);
- d = opendir(procfs_path);
+ d = opendir(FORMAT_PROC_FD_PATH(path_fd));
if (!d)
return -errno;
}
int access_fd(int fd, int mode) {
- char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
-
/* Like access() but operates on an already open fd */
- xsprintf(p, "/proc/self/fd/%i", fd);
- if (access(p, mode) < 0) {
+ if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
if (errno != ENOENT)
return -errno;
if (idx < _IDX_SWAP_END)
return &bucket_at_swap(swap, idx)->p.b;
- assert_not_reached("Invalid index");
+ assert_not_reached();
}
static dib_raw_t* dib_raw_ptr(HashmapBase *h) {
return (void*) e->key;
default:
- assert_not_reached("Unknown hashmap type");
+ assert_not_reached();
}
}
r = set_merge((Set*)copy, (Set*)h);
break;
default:
- assert_not_reached("Unknown hashmap type");
+ assert_not_reached();
}
if (r < 0)
return strdup(FALLBACK_HOSTNAME);
}
-char* gethostname_malloc(void) {
+int gethostname_full(GetHostnameFlags flags, char **ret) {
+ _cleanup_free_ char *buf = NULL, *fallback = NULL;
struct utsname u;
const char *s;
- /* This call tries to return something useful, either the actual hostname
- * or it makes something up. The only reason it might fail is OOM.
- * It might even return "localhost" if that's set. */
+ assert(ret);
assert_se(uname(&u) >= 0);
s = u.nodename;
- if (isempty(s) || streq(s, "(none)"))
- return get_default_hostname();
-
- return strdup(s);
-}
-
-char* gethostname_short_malloc(void) {
- struct utsname u;
- const char *s;
- _cleanup_free_ char *f = NULL;
-
- /* Like above, but kills the FQDN part if present. */
-
- assert_se(uname(&u) >= 0);
-
- s = u.nodename;
- if (isempty(s) || streq(s, "(none)") || s[0] == '.') {
- s = f = get_default_hostname();
+ if (isempty(s) ||
+ (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_NONE) && streq(s, "(none)")) ||
+ (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) ||
+ (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
+ if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))
+ return -ENXIO;
+
+ s = fallback = get_default_hostname();
if (!s)
- return NULL;
+ return -ENOMEM;
- assert(s[0] != '.');
+ if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')
+ return -ENXIO;
}
- return strndup(s, strcspn(s, "."));
-}
-
-int gethostname_strict(char **ret) {
- struct utsname u;
- char *k;
-
- /* This call will rather fail than make up a name. It will not return "localhost" either. */
-
- assert_se(uname(&u) >= 0);
-
- if (isempty(u.nodename))
- return -ENXIO;
-
- if (streq(u.nodename, "(none)"))
- return -ENXIO;
-
- if (is_localhost(u.nodename))
- return -ENXIO;
-
- k = strdup(u.nodename);
- if (!k)
+ if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
+ buf = strndup(s, strcspn(s, "."));
+ else
+ buf = strdup(s);
+ if (!buf)
return -ENOMEM;
- *ret = k;
+ *ret = TAKE_PTR(buf);
return 0;
}
#include "macro.h"
#include "strv.h"
+typedef enum GetHostnameFlags {
+ GET_HOSTNAME_ALLOW_NONE = 1 << 0, /* accepts "(none)". */
+ GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 1, /* accepts "localhost" or friends. */
+ GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 2, /* use default hostname if no hostname is set. */
+ GET_HOSTNAME_SHORT = 1 << 3, /* kills the FQDN part if present. */
+} GetHostnameFlags;
+
+int gethostname_full(GetHostnameFlags flags, char **ret);
+static inline int gethostname_strict(char **ret) {
+ return gethostname_full(0, ret);
+}
+
+static inline char* gethostname_malloc(void) {
+ char *s;
+
+ if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0)
+ return NULL;
+
+ return s;
+}
+
+static inline char* gethostname_short_malloc(void) {
+ char *s;
+
+ if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0)
+ return NULL;
+
+ return s;
+}
+
char* get_default_hostname(void);
-char* gethostname_malloc(void);
-char* gethostname_short_malloc(void);
-int gethostname_strict(char **ret);
bool valid_ldh_char(char c) _const_;
k = 0;
break;
default:
- assert_not_reached("Invalid prefixlen mode");
+ assert_not_reached();
}
if (ret_family)
/* SPDX-License-Identifier: MIT */
-/* Copyright (C) 2016-2020 B.A.T.M.A.N. contributors:
+/* Copyright (C) B.A.T.M.A.N. contributors:
*
* Matthias Schiffer
*/
* in the TT CRC computation.
*/
enum batadv_tt_client_flags {
- /**
- * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
- */
- BATADV_TT_CLIENT_DEL = (1 << 0),
-
- /**
- * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and
- * the new update telling its new real location has not been
- * received/sent yet
- */
- BATADV_TT_CLIENT_ROAM = (1 << 1),
-
- /**
- * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi
- * interface. This information is used by the "AP Isolation" feature
- */
- BATADV_TT_CLIENT_WIFI = (1 << 4),
-
- /**
- * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
- * information is used by the Extended Isolation feature
- */
- BATADV_TT_CLIENT_ISOLA = (1 << 5),
-
- /**
- * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from
- * the table
- */
- BATADV_TT_CLIENT_NOPURGE = (1 << 8),
-
- /**
- * @BATADV_TT_CLIENT_NEW: this client has been added to the local table
- * but has not been announced yet
- */
- BATADV_TT_CLIENT_NEW = (1 << 9),
-
- /**
- * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it
- * is kept in the table for one more originator interval for consistency
- * purposes
- */
- BATADV_TT_CLIENT_PENDING = (1 << 10),
-
- /**
- * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be
- * part of the network but no node has already announced it
- */
- BATADV_TT_CLIENT_TEMP = (1 << 11),
+ /**
+ * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
+ */
+ BATADV_TT_CLIENT_DEL = (1 << 0),
+
+ /**
+ * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and
+ * the new update telling its new real location has not been
+ * received/sent yet
+ */
+ BATADV_TT_CLIENT_ROAM = (1 << 1),
+
+ /**
+ * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi
+ * interface. This information is used by the "AP Isolation" feature
+ */
+ BATADV_TT_CLIENT_WIFI = (1 << 4),
+
+ /**
+ * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
+ * information is used by the Extended Isolation feature
+ */
+ BATADV_TT_CLIENT_ISOLA = (1 << 5),
+
+ /**
+ * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from
+ * the table
+ */
+ BATADV_TT_CLIENT_NOPURGE = (1 << 8),
+
+ /**
+ * @BATADV_TT_CLIENT_NEW: this client has been added to the local table
+ * but has not been announced yet
+ */
+ BATADV_TT_CLIENT_NEW = (1 << 9),
+
+ /**
+ * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it
+ * is kept in the table for one more originator interval for consistency
+ * purposes
+ */
+ BATADV_TT_CLIENT_PENDING = (1 << 10),
+
+ /**
+ * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be
+ * part of the network but no node has already announced it
+ */
+ BATADV_TT_CLIENT_TEMP = (1 << 11),
};
/**
* related flags are undefined.
*/
enum batadv_mcast_flags_priv {
- /**
- * @BATADV_MCAST_FLAGS_BRIDGED: There is a bridge on top of the mesh
- * interface.
- */
- BATADV_MCAST_FLAGS_BRIDGED = (1 << 0),
-
- /**
- * @BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS: Whether an IGMP querier
- * exists in the mesh
- */
- BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS = (1 << 1),
-
- /**
- * @BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS: Whether an MLD querier
- * exists in the mesh
- */
- BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS = (1 << 2),
-
- /**
- * @BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING: If an IGMP querier
- * exists, whether it is potentially shadowing multicast listeners
- * (i.e. querier is behind our own bridge segment)
- */
- BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING = (1 << 3),
-
- /**
- * @BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING: If an MLD querier
- * exists, whether it is potentially shadowing multicast listeners
- * (i.e. querier is behind our own bridge segment)
- */
- BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING = (1 << 4),
+ /**
+ * @BATADV_MCAST_FLAGS_BRIDGED: There is a bridge on top of the mesh
+ * interface.
+ */
+ BATADV_MCAST_FLAGS_BRIDGED = (1 << 0),
+
+ /**
+ * @BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS: Whether an IGMP querier
+ * exists in the mesh
+ */
+ BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS = (1 << 1),
+
+ /**
+ * @BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS: Whether an MLD querier
+ * exists in the mesh
+ */
+ BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS = (1 << 2),
+
+ /**
+ * @BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING: If an IGMP querier
+ * exists, whether it is potentially shadowing multicast listeners
+ * (i.e. querier is behind our own bridge segment)
+ */
+ BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING = (1 << 3),
+
+ /**
+ * @BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING: If an MLD querier
+ * exists, whether it is potentially shadowing multicast listeners
+ * (i.e. querier is behind our own bridge segment)
+ */
+ BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING = (1 << 4),
};
/**
* enum batadv_gw_modes - gateway mode of node
*/
enum batadv_gw_modes {
- /** @BATADV_GW_MODE_OFF: gw mode disabled */
- BATADV_GW_MODE_OFF,
+ /** @BATADV_GW_MODE_OFF: gw mode disabled */
+ BATADV_GW_MODE_OFF,
- /** @BATADV_GW_MODE_CLIENT: send DHCP requests to gw servers */
- BATADV_GW_MODE_CLIENT,
+ /** @BATADV_GW_MODE_CLIENT: send DHCP requests to gw servers */
+ BATADV_GW_MODE_CLIENT,
- /** @BATADV_GW_MODE_SERVER: announce itself as gateway server */
- BATADV_GW_MODE_SERVER,
+ /** @BATADV_GW_MODE_SERVER: announce itself as gateway server */
+ BATADV_GW_MODE_SERVER,
};
/**
* enum batadv_nl_attrs - batman-adv netlink attributes
*/
enum batadv_nl_attrs {
- /**
- * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors
- */
- BATADV_ATTR_UNSPEC,
-
- /**
- * @BATADV_ATTR_VERSION: batman-adv version string
- */
- BATADV_ATTR_VERSION,
-
- /**
- * @BATADV_ATTR_ALGO_NAME: name of routing algorithm
- */
- BATADV_ATTR_ALGO_NAME,
-
- /**
- * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface
- */
- BATADV_ATTR_MESH_IFINDEX,
-
- /**
- * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface
- */
- BATADV_ATTR_MESH_IFNAME,
-
- /**
- * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface
- */
- BATADV_ATTR_MESH_ADDRESS,
-
- /**
- * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface
- */
- BATADV_ATTR_HARD_IFINDEX,
-
- /**
- * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface
- */
- BATADV_ATTR_HARD_IFNAME,
-
- /**
- * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv
- * interface
- */
- BATADV_ATTR_HARD_ADDRESS,
-
- /**
- * @BATADV_ATTR_ORIG_ADDRESS: originator mac address
- */
- BATADV_ATTR_ORIG_ADDRESS,
-
- /**
- * @BATADV_ATTR_TPMETER_RESULT: result of run (see
- * batadv_tp_meter_status)
- */
- BATADV_ATTR_TPMETER_RESULT,
-
- /**
- * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took
- */
- BATADV_ATTR_TPMETER_TEST_TIME,
-
- /**
- * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run
- */
- BATADV_ATTR_TPMETER_BYTES,
-
- /**
- * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session
- */
- BATADV_ATTR_TPMETER_COOKIE,
-
- /**
- * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment
- */
- BATADV_ATTR_PAD,
-
- /**
- * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active
- */
- BATADV_ATTR_ACTIVE,
-
- /**
- * @BATADV_ATTR_TT_ADDRESS: Client MAC address
- */
- BATADV_ATTR_TT_ADDRESS,
-
- /**
- * @BATADV_ATTR_TT_TTVN: Translation table version
- */
- BATADV_ATTR_TT_TTVN,
-
- /**
- * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version
- */
- BATADV_ATTR_TT_LAST_TTVN,
-
- /**
- * @BATADV_ATTR_TT_CRC32: CRC32 over translation table
- */
- BATADV_ATTR_TT_CRC32,
-
- /**
- * @BATADV_ATTR_TT_VID: VLAN ID
- */
- BATADV_ATTR_TT_VID,
-
- /**
- * @BATADV_ATTR_TT_FLAGS: Translation table client flags
- */
- BATADV_ATTR_TT_FLAGS,
-
- /**
- * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best
- */
- BATADV_ATTR_FLAG_BEST,
-
- /**
- * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen
- */
- BATADV_ATTR_LAST_SEEN_MSECS,
-
- /**
- * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address
- */
- BATADV_ATTR_NEIGH_ADDRESS,
-
- /**
- * @BATADV_ATTR_TQ: TQ to neighbour
- */
- BATADV_ATTR_TQ,
-
- /**
- * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour
- */
- BATADV_ATTR_THROUGHPUT,
-
- /**
- * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth
- */
- BATADV_ATTR_BANDWIDTH_UP,
-
- /**
- * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth
- */
- BATADV_ATTR_BANDWIDTH_DOWN,
-
- /**
- * @BATADV_ATTR_ROUTER: Gateway router MAC address
- */
- BATADV_ATTR_ROUTER,
-
- /**
- * @BATADV_ATTR_BLA_OWN: Flag indicating own originator
- */
- BATADV_ATTR_BLA_OWN,
-
- /**
- * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address
- */
- BATADV_ATTR_BLA_ADDRESS,
-
- /**
- * @BATADV_ATTR_BLA_VID: BLA VLAN ID
- */
- BATADV_ATTR_BLA_VID,
-
- /**
- * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address
- */
- BATADV_ATTR_BLA_BACKBONE,
-
- /**
- * @BATADV_ATTR_BLA_CRC: BLA CRC
- */
- BATADV_ATTR_BLA_CRC,
-
- /**
- * @BATADV_ATTR_DAT_CACHE_IP4ADDRESS: Client IPv4 address
- */
- BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
-
- /**
- * @BATADV_ATTR_DAT_CACHE_HWADDRESS: Client MAC address
- */
- BATADV_ATTR_DAT_CACHE_HWADDRESS,
-
- /**
- * @BATADV_ATTR_DAT_CACHE_VID: VLAN ID
- */
- BATADV_ATTR_DAT_CACHE_VID,
-
- /**
- * @BATADV_ATTR_MCAST_FLAGS: Per originator multicast flags
- */
- BATADV_ATTR_MCAST_FLAGS,
-
- /**
- * @BATADV_ATTR_MCAST_FLAGS_PRIV: Private, own multicast flags
- */
- BATADV_ATTR_MCAST_FLAGS_PRIV,
-
- /**
- * @BATADV_ATTR_VLANID: VLAN id on top of soft interface
- */
- BATADV_ATTR_VLANID,
-
- /**
- * @BATADV_ATTR_AGGREGATED_OGMS_ENABLED: whether the batman protocol
- * messages of the mesh interface shall be aggregated or not.
- */
- BATADV_ATTR_AGGREGATED_OGMS_ENABLED,
-
- /**
- * @BATADV_ATTR_AP_ISOLATION_ENABLED: whether the data traffic going
- * from a wireless client to another wireless client will be silently
- * dropped.
- */
- BATADV_ATTR_AP_ISOLATION_ENABLED,
-
- /**
- * @BATADV_ATTR_ISOLATION_MARK: the isolation mark which is used to
- * classify clients as "isolated" by the Extended Isolation feature.
- */
- BATADV_ATTR_ISOLATION_MARK,
-
- /**
- * @BATADV_ATTR_ISOLATION_MASK: the isolation (bit)mask which is used to
- * classify clients as "isolated" by the Extended Isolation feature.
- */
- BATADV_ATTR_ISOLATION_MASK,
-
- /**
- * @BATADV_ATTR_BONDING_ENABLED: whether the data traffic going through
- * the mesh will be sent using multiple interfaces at the same time.
- */
- BATADV_ATTR_BONDING_ENABLED,
-
- /**
- * @BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED: whether the bridge loop
- * avoidance feature is enabled. This feature detects and avoids loops
- * between the mesh and devices bridged with the soft interface
- */
- BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED,
-
- /**
- * @BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED: whether the distributed
- * arp table feature is enabled. This feature uses a distributed hash
- * table to answer ARP requests without flooding the request through
- * the whole mesh.
- */
- BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED,
-
- /**
- * @BATADV_ATTR_FRAGMENTATION_ENABLED: whether the data traffic going
- * through the mesh will be fragmented or silently discarded if the
- * packet size exceeds the outgoing interface MTU.
- */
- BATADV_ATTR_FRAGMENTATION_ENABLED,
-
- /**
- * @BATADV_ATTR_GW_BANDWIDTH_DOWN: defines the download bandwidth which
- * is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
- * to 'server'.
- */
- BATADV_ATTR_GW_BANDWIDTH_DOWN,
-
- /**
- * @BATADV_ATTR_GW_BANDWIDTH_UP: defines the upload bandwidth which
- * is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
- * to 'server'.
- */
- BATADV_ATTR_GW_BANDWIDTH_UP,
-
- /**
- * @BATADV_ATTR_GW_MODE: defines the state of the gateway features.
- * Possible values are specified in enum batadv_gw_modes
- */
- BATADV_ATTR_GW_MODE,
-
- /**
- * @BATADV_ATTR_GW_SEL_CLASS: defines the selection criteria this node
- * will use to choose a gateway if gw_mode was set to 'client'.
- */
- BATADV_ATTR_GW_SEL_CLASS,
-
- /**
- * @BATADV_ATTR_HOP_PENALTY: defines the penalty which will be applied
- * to an originator message's tq-field on every hop and/or per
- * hard interface
- */
- BATADV_ATTR_HOP_PENALTY,
-
- /**
- * @BATADV_ATTR_LOG_LEVEL: bitmask with to define which debug messages
- * should be send to the debug log/trace ring buffer
- */
- BATADV_ATTR_LOG_LEVEL,
-
- /**
- * @BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED: whether multicast
- * optimizations should be replaced by simple broadcast-like flooding
- * of multicast packets. If set to non-zero then all nodes in the mesh
- * are going to use classic flooding for any multicast packet with no
- * optimizations.
- */
- BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED,
-
- /**
- * @BATADV_ATTR_NETWORK_CODING_ENABLED: whether Network Coding (using
- * some magic to send fewer wifi packets but still the same content) is
- * enabled or not.
- */
- BATADV_ATTR_NETWORK_CODING_ENABLED,
-
- /**
- * @BATADV_ATTR_ORIG_INTERVAL: defines the interval in milliseconds in
- * which batman sends its protocol messages.
- */
- BATADV_ATTR_ORIG_INTERVAL,
-
- /**
- * @BATADV_ATTR_ELP_INTERVAL: defines the interval in milliseconds in
- * which batman emits probing packets for neighbor sensing (ELP).
- */
- BATADV_ATTR_ELP_INTERVAL,
-
- /**
- * @BATADV_ATTR_THROUGHPUT_OVERRIDE: defines the throughput value to be
- * used by B.A.T.M.A.N. V when estimating the link throughput using
- * this interface. If the value is set to 0 then batman-adv will try to
- * estimate the throughput by itself.
- */
- BATADV_ATTR_THROUGHPUT_OVERRIDE,
-
- /**
- * @BATADV_ATTR_MULTICAST_FANOUT: defines the maximum number of packet
- * copies that may be generated for a multicast-to-unicast conversion.
- * Once this limit is exceeded distribution will fall back to broadcast.
- */
- BATADV_ATTR_MULTICAST_FANOUT,
-
- /* add attributes above here, update the policy in netlink.c */
-
- /**
- * @__BATADV_ATTR_AFTER_LAST: internal use
- */
- __BATADV_ATTR_AFTER_LAST,
-
- /**
- * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available
- */
- NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST,
-
- /**
- * @BATADV_ATTR_MAX: highest attribute number currently defined
- */
- BATADV_ATTR_MAX = __BATADV_ATTR_AFTER_LAST - 1
+ /**
+ * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors
+ */
+ BATADV_ATTR_UNSPEC,
+
+ /**
+ * @BATADV_ATTR_VERSION: batman-adv version string
+ */
+ BATADV_ATTR_VERSION,
+
+ /**
+ * @BATADV_ATTR_ALGO_NAME: name of routing algorithm
+ */
+ BATADV_ATTR_ALGO_NAME,
+
+ /**
+ * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface
+ */
+ BATADV_ATTR_MESH_IFINDEX,
+
+ /**
+ * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface
+ */
+ BATADV_ATTR_MESH_IFNAME,
+
+ /**
+ * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface
+ */
+ BATADV_ATTR_MESH_ADDRESS,
+
+ /**
+ * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface
+ */
+ BATADV_ATTR_HARD_IFINDEX,
+
+ /**
+ * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface
+ */
+ BATADV_ATTR_HARD_IFNAME,
+
+ /**
+ * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv
+ * interface
+ */
+ BATADV_ATTR_HARD_ADDRESS,
+
+ /**
+ * @BATADV_ATTR_ORIG_ADDRESS: originator mac address
+ */
+ BATADV_ATTR_ORIG_ADDRESS,
+
+ /**
+ * @BATADV_ATTR_TPMETER_RESULT: result of run (see
+ * batadv_tp_meter_status)
+ */
+ BATADV_ATTR_TPMETER_RESULT,
+
+ /**
+ * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took
+ */
+ BATADV_ATTR_TPMETER_TEST_TIME,
+
+ /**
+ * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run
+ */
+ BATADV_ATTR_TPMETER_BYTES,
+
+ /**
+ * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session
+ */
+ BATADV_ATTR_TPMETER_COOKIE,
+
+ /**
+ * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment
+ */
+ BATADV_ATTR_PAD,
+
+ /**
+ * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active
+ */
+ BATADV_ATTR_ACTIVE,
+
+ /**
+ * @BATADV_ATTR_TT_ADDRESS: Client MAC address
+ */
+ BATADV_ATTR_TT_ADDRESS,
+
+ /**
+ * @BATADV_ATTR_TT_TTVN: Translation table version
+ */
+ BATADV_ATTR_TT_TTVN,
+
+ /**
+ * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version
+ */
+ BATADV_ATTR_TT_LAST_TTVN,
+
+ /**
+ * @BATADV_ATTR_TT_CRC32: CRC32 over translation table
+ */
+ BATADV_ATTR_TT_CRC32,
+
+ /**
+ * @BATADV_ATTR_TT_VID: VLAN ID
+ */
+ BATADV_ATTR_TT_VID,
+
+ /**
+ * @BATADV_ATTR_TT_FLAGS: Translation table client flags
+ */
+ BATADV_ATTR_TT_FLAGS,
+
+ /**
+ * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best
+ */
+ BATADV_ATTR_FLAG_BEST,
+
+ /**
+ * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen
+ */
+ BATADV_ATTR_LAST_SEEN_MSECS,
+
+ /**
+ * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address
+ */
+ BATADV_ATTR_NEIGH_ADDRESS,
+
+ /**
+ * @BATADV_ATTR_TQ: TQ to neighbour
+ */
+ BATADV_ATTR_TQ,
+
+ /**
+ * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour
+ */
+ BATADV_ATTR_THROUGHPUT,
+
+ /**
+ * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth
+ */
+ BATADV_ATTR_BANDWIDTH_UP,
+
+ /**
+ * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth
+ */
+ BATADV_ATTR_BANDWIDTH_DOWN,
+
+ /**
+ * @BATADV_ATTR_ROUTER: Gateway router MAC address
+ */
+ BATADV_ATTR_ROUTER,
+
+ /**
+ * @BATADV_ATTR_BLA_OWN: Flag indicating own originator
+ */
+ BATADV_ATTR_BLA_OWN,
+
+ /**
+ * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address
+ */
+ BATADV_ATTR_BLA_ADDRESS,
+
+ /**
+ * @BATADV_ATTR_BLA_VID: BLA VLAN ID
+ */
+ BATADV_ATTR_BLA_VID,
+
+ /**
+ * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address
+ */
+ BATADV_ATTR_BLA_BACKBONE,
+
+ /**
+ * @BATADV_ATTR_BLA_CRC: BLA CRC
+ */
+ BATADV_ATTR_BLA_CRC,
+
+ /**
+ * @BATADV_ATTR_DAT_CACHE_IP4ADDRESS: Client IPv4 address
+ */
+ BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
+
+ /**
+ * @BATADV_ATTR_DAT_CACHE_HWADDRESS: Client MAC address
+ */
+ BATADV_ATTR_DAT_CACHE_HWADDRESS,
+
+ /**
+ * @BATADV_ATTR_DAT_CACHE_VID: VLAN ID
+ */
+ BATADV_ATTR_DAT_CACHE_VID,
+
+ /**
+ * @BATADV_ATTR_MCAST_FLAGS: Per originator multicast flags
+ */
+ BATADV_ATTR_MCAST_FLAGS,
+
+ /**
+ * @BATADV_ATTR_MCAST_FLAGS_PRIV: Private, own multicast flags
+ */
+ BATADV_ATTR_MCAST_FLAGS_PRIV,
+
+ /**
+ * @BATADV_ATTR_VLANID: VLAN id on top of soft interface
+ */
+ BATADV_ATTR_VLANID,
+
+ /**
+ * @BATADV_ATTR_AGGREGATED_OGMS_ENABLED: whether the batman protocol
+ * messages of the mesh interface shall be aggregated or not.
+ */
+ BATADV_ATTR_AGGREGATED_OGMS_ENABLED,
+
+ /**
+ * @BATADV_ATTR_AP_ISOLATION_ENABLED: whether the data traffic going
+ * from a wireless client to another wireless client will be silently
+ * dropped.
+ */
+ BATADV_ATTR_AP_ISOLATION_ENABLED,
+
+ /**
+ * @BATADV_ATTR_ISOLATION_MARK: the isolation mark which is used to
+ * classify clients as "isolated" by the Extended Isolation feature.
+ */
+ BATADV_ATTR_ISOLATION_MARK,
+
+ /**
+ * @BATADV_ATTR_ISOLATION_MASK: the isolation (bit)mask which is used to
+ * classify clients as "isolated" by the Extended Isolation feature.
+ */
+ BATADV_ATTR_ISOLATION_MASK,
+
+ /**
+ * @BATADV_ATTR_BONDING_ENABLED: whether the data traffic going through
+ * the mesh will be sent using multiple interfaces at the same time.
+ */
+ BATADV_ATTR_BONDING_ENABLED,
+
+ /**
+ * @BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED: whether the bridge loop
+ * avoidance feature is enabled. This feature detects and avoids loops
+ * between the mesh and devices bridged with the soft interface
+ */
+ BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED,
+
+ /**
+ * @BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED: whether the distributed
+ * arp table feature is enabled. This feature uses a distributed hash
+ * table to answer ARP requests without flooding the request through
+ * the whole mesh.
+ */
+ BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED,
+
+ /**
+ * @BATADV_ATTR_FRAGMENTATION_ENABLED: whether the data traffic going
+ * through the mesh will be fragmented or silently discarded if the
+ * packet size exceeds the outgoing interface MTU.
+ */
+ BATADV_ATTR_FRAGMENTATION_ENABLED,
+
+ /**
+ * @BATADV_ATTR_GW_BANDWIDTH_DOWN: defines the download bandwidth which
+ * is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
+ * to 'server'.
+ */
+ BATADV_ATTR_GW_BANDWIDTH_DOWN,
+
+ /**
+ * @BATADV_ATTR_GW_BANDWIDTH_UP: defines the upload bandwidth which
+ * is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
+ * to 'server'.
+ */
+ BATADV_ATTR_GW_BANDWIDTH_UP,
+
+ /**
+ * @BATADV_ATTR_GW_MODE: defines the state of the gateway features.
+ * Possible values are specified in enum batadv_gw_modes
+ */
+ BATADV_ATTR_GW_MODE,
+
+ /**
+ * @BATADV_ATTR_GW_SEL_CLASS: defines the selection criteria this node
+ * will use to choose a gateway if gw_mode was set to 'client'.
+ */
+ BATADV_ATTR_GW_SEL_CLASS,
+
+ /**
+ * @BATADV_ATTR_HOP_PENALTY: defines the penalty which will be applied
+ * to an originator message's tq-field on every hop and/or per
+ * hard interface
+ */
+ BATADV_ATTR_HOP_PENALTY,
+
+ /**
+ * @BATADV_ATTR_LOG_LEVEL: bitmask with to define which debug messages
+ * should be send to the debug log/trace ring buffer
+ */
+ BATADV_ATTR_LOG_LEVEL,
+
+ /**
+ * @BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED: whether multicast
+ * optimizations should be replaced by simple broadcast-like flooding
+ * of multicast packets. If set to non-zero then all nodes in the mesh
+ * are going to use classic flooding for any multicast packet with no
+ * optimizations.
+ */
+ BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED,
+
+ /**
+ * @BATADV_ATTR_NETWORK_CODING_ENABLED: whether Network Coding (using
+ * some magic to send fewer wifi packets but still the same content) is
+ * enabled or not.
+ */
+ BATADV_ATTR_NETWORK_CODING_ENABLED,
+
+ /**
+ * @BATADV_ATTR_ORIG_INTERVAL: defines the interval in milliseconds in
+ * which batman sends its protocol messages.
+ */
+ BATADV_ATTR_ORIG_INTERVAL,
+
+ /**
+ * @BATADV_ATTR_ELP_INTERVAL: defines the interval in milliseconds in
+ * which batman emits probing packets for neighbor sensing (ELP).
+ */
+ BATADV_ATTR_ELP_INTERVAL,
+
+ /**
+ * @BATADV_ATTR_THROUGHPUT_OVERRIDE: defines the throughput value to be
+ * used by B.A.T.M.A.N. V when estimating the link throughput using
+ * this interface. If the value is set to 0 then batman-adv will try to
+ * estimate the throughput by itself.
+ */
+ BATADV_ATTR_THROUGHPUT_OVERRIDE,
+
+ /**
+ * @BATADV_ATTR_MULTICAST_FANOUT: defines the maximum number of packet
+ * copies that may be generated for a multicast-to-unicast conversion.
+ * Once this limit is exceeded distribution will fall back to broadcast.
+ */
+ BATADV_ATTR_MULTICAST_FANOUT,
+
+ /* add attributes above here, update the policy in netlink.c */
+
+ /**
+ * @__BATADV_ATTR_AFTER_LAST: internal use
+ */
+ __BATADV_ATTR_AFTER_LAST,
+
+ /**
+ * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available
+ */
+ NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST,
+
+ /**
+ * @BATADV_ATTR_MAX: highest attribute number currently defined
+ */
+ BATADV_ATTR_MAX = __BATADV_ATTR_AFTER_LAST - 1
};
/**
* enum batadv_nl_commands - supported batman-adv netlink commands
*/
enum batadv_nl_commands {
- /**
- * @BATADV_CMD_UNSPEC: unspecified command to catch errors
- */
- BATADV_CMD_UNSPEC,
-
- /**
- * @BATADV_CMD_GET_MESH: Get attributes from softif/mesh
- */
- BATADV_CMD_GET_MESH,
-
- /**
- * @BATADV_CMD_GET_MESH_INFO: Alias for @BATADV_CMD_GET_MESH
- */
- BATADV_CMD_GET_MESH_INFO = BATADV_CMD_GET_MESH,
-
- /**
- * @BATADV_CMD_TP_METER: Start a tp meter session
- */
- BATADV_CMD_TP_METER,
-
- /**
- * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session
- */
- BATADV_CMD_TP_METER_CANCEL,
-
- /**
- * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms.
- */
- BATADV_CMD_GET_ROUTING_ALGOS,
-
- /**
- * @BATADV_CMD_GET_HARDIF: Get attributes from a hardif of the
- * current softif
- */
- BATADV_CMD_GET_HARDIF,
-
- /**
- * @BATADV_CMD_GET_HARDIFS: Alias for @BATADV_CMD_GET_HARDIF
- */
- BATADV_CMD_GET_HARDIFS = BATADV_CMD_GET_HARDIF,
-
- /**
- * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
- */
- BATADV_CMD_GET_TRANSTABLE_LOCAL,
-
- /**
- * @BATADV_CMD_GET_TRANSTABLE_GLOBAL: Query list of global translations
- */
- BATADV_CMD_GET_TRANSTABLE_GLOBAL,
-
- /**
- * @BATADV_CMD_GET_ORIGINATORS: Query list of originators
- */
- BATADV_CMD_GET_ORIGINATORS,
-
- /**
- * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours
- */
- BATADV_CMD_GET_NEIGHBORS,
-
- /**
- * @BATADV_CMD_GET_GATEWAYS: Query list of gateways
- */
- BATADV_CMD_GET_GATEWAYS,
-
- /**
- * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims
- */
- BATADV_CMD_GET_BLA_CLAIM,
-
- /**
- * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance
- * backbones
- */
- BATADV_CMD_GET_BLA_BACKBONE,
-
- /**
- * @BATADV_CMD_GET_DAT_CACHE: Query list of DAT cache entries
- */
- BATADV_CMD_GET_DAT_CACHE,
-
- /**
- * @BATADV_CMD_GET_MCAST_FLAGS: Query list of multicast flags
- */
- BATADV_CMD_GET_MCAST_FLAGS,
-
- /**
- * @BATADV_CMD_SET_MESH: Set attributes for softif/mesh
- */
- BATADV_CMD_SET_MESH,
-
- /**
- * @BATADV_CMD_SET_HARDIF: Set attributes for hardif of the
- * current softif
- */
- BATADV_CMD_SET_HARDIF,
-
- /**
- * @BATADV_CMD_GET_VLAN: Get attributes from a VLAN of the
- * current softif
- */
- BATADV_CMD_GET_VLAN,
-
- /**
- * @BATADV_CMD_SET_VLAN: Set attributes for VLAN of the
- * current softif
- */
- BATADV_CMD_SET_VLAN,
-
- /* add new commands above here */
-
- /**
- * @__BATADV_CMD_AFTER_LAST: internal use
- */
- __BATADV_CMD_AFTER_LAST,
-
- /**
- * @BATADV_CMD_MAX: highest used command number
- */
- BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1
+ /**
+ * @BATADV_CMD_UNSPEC: unspecified command to catch errors
+ */
+ BATADV_CMD_UNSPEC,
+
+ /**
+ * @BATADV_CMD_GET_MESH: Get attributes from softif/mesh
+ */
+ BATADV_CMD_GET_MESH,
+
+ /**
+ * @BATADV_CMD_GET_MESH_INFO: Alias for @BATADV_CMD_GET_MESH
+ */
+ BATADV_CMD_GET_MESH_INFO = BATADV_CMD_GET_MESH,
+
+ /**
+ * @BATADV_CMD_TP_METER: Start a tp meter session
+ */
+ BATADV_CMD_TP_METER,
+
+ /**
+ * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session
+ */
+ BATADV_CMD_TP_METER_CANCEL,
+
+ /**
+ * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms.
+ */
+ BATADV_CMD_GET_ROUTING_ALGOS,
+
+ /**
+ * @BATADV_CMD_GET_HARDIF: Get attributes from a hardif of the
+ * current softif
+ */
+ BATADV_CMD_GET_HARDIF,
+
+ /**
+ * @BATADV_CMD_GET_HARDIFS: Alias for @BATADV_CMD_GET_HARDIF
+ */
+ BATADV_CMD_GET_HARDIFS = BATADV_CMD_GET_HARDIF,
+
+ /**
+ * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
+ */
+ BATADV_CMD_GET_TRANSTABLE_LOCAL,
+
+ /**
+ * @BATADV_CMD_GET_TRANSTABLE_GLOBAL: Query list of global translations
+ */
+ BATADV_CMD_GET_TRANSTABLE_GLOBAL,
+
+ /**
+ * @BATADV_CMD_GET_ORIGINATORS: Query list of originators
+ */
+ BATADV_CMD_GET_ORIGINATORS,
+
+ /**
+ * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours
+ */
+ BATADV_CMD_GET_NEIGHBORS,
+
+ /**
+ * @BATADV_CMD_GET_GATEWAYS: Query list of gateways
+ */
+ BATADV_CMD_GET_GATEWAYS,
+
+ /**
+ * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims
+ */
+ BATADV_CMD_GET_BLA_CLAIM,
+
+ /**
+ * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance
+ * backbones
+ */
+ BATADV_CMD_GET_BLA_BACKBONE,
+
+ /**
+ * @BATADV_CMD_GET_DAT_CACHE: Query list of DAT cache entries
+ */
+ BATADV_CMD_GET_DAT_CACHE,
+
+ /**
+ * @BATADV_CMD_GET_MCAST_FLAGS: Query list of multicast flags
+ */
+ BATADV_CMD_GET_MCAST_FLAGS,
+
+ /**
+ * @BATADV_CMD_SET_MESH: Set attributes for softif/mesh
+ */
+ BATADV_CMD_SET_MESH,
+
+ /**
+ * @BATADV_CMD_SET_HARDIF: Set attributes for hardif of the
+ * current softif
+ */
+ BATADV_CMD_SET_HARDIF,
+
+ /**
+ * @BATADV_CMD_GET_VLAN: Get attributes from a VLAN of the
+ * current softif
+ */
+ BATADV_CMD_GET_VLAN,
+
+ /**
+ * @BATADV_CMD_SET_VLAN: Set attributes for VLAN of the
+ * current softif
+ */
+ BATADV_CMD_SET_VLAN,
+
+ /* add new commands above here */
+
+ /**
+ * @__BATADV_CMD_AFTER_LAST: internal use
+ */
+ __BATADV_CMD_AFTER_LAST,
+
+ /**
+ * @BATADV_CMD_MAX: highest used command number
+ */
+ BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1
};
/**
* enum batadv_tp_meter_reason - reason of a tp meter test run stop
*/
enum batadv_tp_meter_reason {
- /**
- * @BATADV_TP_REASON_COMPLETE: sender finished tp run
- */
- BATADV_TP_REASON_COMPLETE = 3,
-
- /**
- * @BATADV_TP_REASON_CANCEL: sender was stopped during run
- */
- BATADV_TP_REASON_CANCEL = 4,
-
- /* error status >= 128 */
-
- /**
- * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or
- * didn't answer
- */
- BATADV_TP_REASON_DST_UNREACHABLE = 128,
-
- /**
- * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit
- */
- BATADV_TP_REASON_RESEND_LIMIT = 129,
-
- /**
- * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node
- * already ongoing
- */
- BATADV_TP_REASON_ALREADY_ONGOING = 130,
-
- /**
- * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory
- */
- BATADV_TP_REASON_MEMORY_ERROR = 131,
-
- /**
- * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface
- */
- BATADV_TP_REASON_CANT_SEND = 132,
-
- /**
- * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions
- */
- BATADV_TP_REASON_TOO_MANY = 133,
+ /**
+ * @BATADV_TP_REASON_COMPLETE: sender finished tp run
+ */
+ BATADV_TP_REASON_COMPLETE = 3,
+
+ /**
+ * @BATADV_TP_REASON_CANCEL: sender was stopped during run
+ */
+ BATADV_TP_REASON_CANCEL = 4,
+
+ /* error status >= 128 */
+
+ /**
+ * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or
+ * didn't answer
+ */
+ BATADV_TP_REASON_DST_UNREACHABLE = 128,
+
+ /**
+ * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit
+ */
+ BATADV_TP_REASON_RESEND_LIMIT = 129,
+
+ /**
+ * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node
+ * already ongoing
+ */
+ BATADV_TP_REASON_ALREADY_ONGOING = 130,
+
+ /**
+ * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory
+ */
+ BATADV_TP_REASON_MEMORY_ERROR = 131,
+
+ /**
+ * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface
+ */
+ BATADV_TP_REASON_CANT_SEND = 132,
+
+ /**
+ * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions
+ */
+ BATADV_TP_REASON_TOO_MANY = 133,
};
/**
* enum batadv_ifla_attrs - batman-adv ifla nested attributes
*/
enum batadv_ifla_attrs {
- /**
- * @IFLA_BATADV_UNSPEC: unspecified attribute which is not parsed by
- * rtnetlink
- */
- IFLA_BATADV_UNSPEC,
-
- /**
- * @IFLA_BATADV_ALGO_NAME: routing algorithm (name) which should be
- * used by the newly registered batadv net_device.
- */
- IFLA_BATADV_ALGO_NAME,
-
- /* add attributes above here, update the policy in soft-interface.c */
-
- /**
- * @__IFLA_BATADV_MAX: internal use
- */
- __IFLA_BATADV_MAX,
+ /**
+ * @IFLA_BATADV_UNSPEC: unspecified attribute which is not parsed by
+ * rtnetlink
+ */
+ IFLA_BATADV_UNSPEC,
+
+ /**
+ * @IFLA_BATADV_ALGO_NAME: routing algorithm (name) which should be
+ * used by the newly registered batadv net_device.
+ */
+ IFLA_BATADV_ALGO_NAME,
+
+ /* add attributes above here, update the policy in soft-interface.c */
+
+ /**
+ * @__IFLA_BATADV_MAX: internal use
+ */
+ __IFLA_BATADV_MAX,
};
#define IFLA_BATADV_MAX (__IFLA_BATADV_MAX - 1)
__u64 tree_bytes_scrubbed; /* # of tree bytes scrubbed */
__u64 read_errors; /* # of read errors encountered (EIO) */
__u64 csum_errors; /* # of failed csum checks */
- __u64 verify_errors; /* # of occurences, where the metadata
+ __u64 verify_errors; /* # of occurrences, where the metadata
* of a tree block did not match the
* expected values, like generation or
* logical */
__u64 last_physical; /* last physical address scrubbed. In
* case a scrub was aborted, this can
* be used to restart the scrub */
- __u64 unverified_errors; /* # of occurences where a read for a
+ __u64 unverified_errors; /* # of occurrences where a read for a
* full (64k) bio failed, but the re-
* check succeeded for each 4k piece.
* Intermittent error. */
#define BTRFS_FEATURE_INCOMPAT_NO_HOLES (1ULL << 9)
#define BTRFS_FEATURE_INCOMPAT_METADATA_UUID (1ULL << 10)
#define BTRFS_FEATURE_INCOMPAT_RAID1C34 (1ULL << 11)
+#define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12)
struct btrfs_ioctl_feature_flags {
__u64 compat_flags;
/* for storing balance parameters in the root tree */
#define BTRFS_BALANCE_OBJECTID -4ULL
-/* orhpan objectid for tracking unlinked/truncated files */
+/* orphan objectid for tracking unlinked/truncated files */
#define BTRFS_ORPHAN_OBJECTID -5ULL
/* does write ahead logging to speed up fsyncs */
#define BTRFS_PERSISTENT_ITEM_KEY 249
/*
- * Persistantly stores the device replace state in the device tree.
+ * Persistently stores the device replace state in the device tree.
* The key is built like this: (0, BTRFS_DEV_REPLACE_KEY, 0).
*/
#define BTRFS_DEV_REPLACE_KEY 250
*/
#define BTRFS_STRING_ITEM_KEY 253
-
+/* Maximum metadata block size (nodesize) */
+#define BTRFS_MAX_METADATA_BLOCKSIZE 65536
/* 32 bytes in various csum fields */
#define BTRFS_CSUM_SIZE 32
#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */
#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */
#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */
+#define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */
/*
* CAN device statistics
#define BOND_XMIT_POLICY_LAYER23 2 /* layer 2+3 (IP ^ MAC) */
#define BOND_XMIT_POLICY_ENCAP23 3 /* encapsulated layer 2+3 */
#define BOND_XMIT_POLICY_ENCAP34 4 /* encapsulated layer 3+4 */
+#define BOND_XMIT_POLICY_VLAN_SRCMAC 5 /* vlan + source MAC */
/* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */
#define LACP_STATE_LACP_ACTIVITY 0x1
#define BOND_3AD_STAT_MAX (__BOND_3AD_STAT_MAX - 1)
#endif /* _LINUX_IF_BONDING_H */
-
-/*
- * Local variables:
- * version-control: t
- * kept-new-versions: 5
- * c-indent-level: 8
- * c-basic-offset: 8
- * tab-width: 8
- * End:
- */
-
IFLA_BRIDGE_VLAN_INFO,
IFLA_BRIDGE_VLAN_TUNNEL_INFO,
IFLA_BRIDGE_MRP,
+ IFLA_BRIDGE_CFM,
__IFLA_BRIDGE_MAX,
};
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
__u16 in_id;
};
+enum {
+ IFLA_BRIDGE_CFM_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_CREATE,
+ IFLA_BRIDGE_CFM_MEP_DELETE,
+ IFLA_BRIDGE_CFM_MEP_CONFIG,
+ IFLA_BRIDGE_CFM_CC_CONFIG,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE,
+ IFLA_BRIDGE_CFM_CC_RDI,
+ IFLA_BRIDGE_CFM_CC_CCM_TX,
+ IFLA_BRIDGE_CFM_MEP_CREATE_INFO,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_INFO,
+ IFLA_BRIDGE_CFM_CC_CONFIG_INFO,
+ IFLA_BRIDGE_CFM_CC_RDI_INFO,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_INFO,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO,
+ IFLA_BRIDGE_CFM_MEP_STATUS_INFO,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO,
+ __IFLA_BRIDGE_CFM_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MAX (__IFLA_BRIDGE_CFM_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_MEP_CREATE_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE,
+ IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN,
+ IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION,
+ IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX,
+ __IFLA_BRIDGE_CFM_MEP_CREATE_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_CREATE_MAX (__IFLA_BRIDGE_CFM_MEP_CREATE_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_MEP_DELETE_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE,
+ __IFLA_BRIDGE_CFM_MEP_DELETE_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_DELETE_MAX (__IFLA_BRIDGE_CFM_MEP_DELETE_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_MEP_CONFIG_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL,
+ IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID,
+ __IFLA_BRIDGE_CFM_MEP_CONFIG_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_CONFIG_MAX (__IFLA_BRIDGE_CFM_MEP_CONFIG_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE,
+ IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL,
+ IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID,
+ __IFLA_BRIDGE_CFM_CC_CONFIG_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_CONFIG_MAX (__IFLA_BRIDGE_CFM_CC_CONFIG_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_PEER_MEPID,
+ __IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX (__IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_RDI_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_RDI_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_RDI_RDI,
+ __IFLA_BRIDGE_CFM_CC_RDI_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_RDI_MAX (__IFLA_BRIDGE_CFM_CC_RDI_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_CCM_TX_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV,
+ IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE,
+ __IFLA_BRIDGE_CFM_CC_CCM_TX_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_CCM_TX_MAX (__IFLA_BRIDGE_CFM_CC_CCM_TX_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_MEP_STATUS_UNSPEC,
+ IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE,
+ IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN,
+ IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN,
+ IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN,
+ __IFLA_BRIDGE_CFM_MEP_STATUS_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_STATUS_MAX (__IFLA_BRIDGE_CFM_MEP_STATUS_MAX - 1)
+
+enum {
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_UNSPEC,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN,
+ IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN,
+ __IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX (__IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX - 1)
+
struct bridge_stp_xstats {
__u64 transition_blk;
__u64 transition_fwd;
MDBA_ROUTER_PATTR_UNSPEC,
MDBA_ROUTER_PATTR_TIMER,
MDBA_ROUTER_PATTR_TYPE,
+ MDBA_ROUTER_PATTR_INET_TIMER,
+ MDBA_ROUTER_PATTR_INET6_TIMER,
__MDBA_ROUTER_PATTR_MAX
};
#define MDBA_ROUTER_PATTR_MAX (__MDBA_ROUTER_PATTR_MAX - 1)
union {
__be32 ip4;
struct in6_addr ip6;
+ unsigned char mac_addr[ETH_ALEN];
} u;
__be16 proto;
} addr;
#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
#define ETH_P_NCSI 0x88F8 /* NCSI protocol */
#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */
+#define ETH_P_CFM 0x8902 /* Connectivity Fault Management */
#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */
#define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */
#define ETH_P_TDLS 0x890D /* TDLS */
IFLA_ALT_IFNAME, /* Alternative ifname */
IFLA_PERM_ADDRESS,
IFLA_PROTO_DOWN_REASON,
+
+ /* device (sysfs) name as parent, used instead
+ * of IFLA_LINK where there's no parent netdev
+ */
+ IFLA_PARENT_DEV_NAME,
+ IFLA_PARENT_DEV_BUS_NAME,
+
__IFLA_MAX
};
IFLA_BRPORT_BACKUP_PORT,
IFLA_BRPORT_MRP_RING_OPEN,
IFLA_BRPORT_MRP_IN_OPEN,
+ IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
+ IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
__IFLA_BRPORT_MAX
};
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
};
#define MACVLAN_FLAG_NOPROMISC 1
+#define MACVLAN_FLAG_NODST 2 /* skip dst macvlan if matching src macvlan */
/* VRF section */
enum {
#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1)
#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2)
#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3)
+#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 4)
+#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 5)
enum {
IFLA_RMNET_UNSPEC,
/* Address indicating an error return. */
#define INADDR_NONE ((unsigned long int) 0xffffffff)
+/* Dummy address for src of ICMP replies if no real address is set (RFC7600). */
+#define INADDR_DUMMY ((unsigned long int) 0xc0000008)
+
/* Network number for local host loopback. */
#define IN_LOOPBACKNET 127
L2TP_ATTR_RX_ERRORS, /* u64 */
L2TP_ATTR_STATS_PAD,
L2TP_ATTR_RX_COOKIE_DISCARDS, /* u64 */
+ L2TP_ATTR_RX_INVALID, /* u64 */
__L2TP_ATTR_STATS_MAX,
};
* @NFTA_LIST_ELEM: list element (NLA_NESTED)
*/
enum nft_list_attributes {
- NFTA_LIST_UNPEC,
+ NFTA_LIST_UNSPEC,
NFTA_LIST_ELEM,
__NFTA_LIST_MAX
};
*/
enum nft_table_flags {
NFT_TABLE_F_DORMANT = 0x1,
+ NFT_TABLE_F_OWNER = 0x2,
};
+#define NFT_TABLE_F_MASK (NFT_TABLE_F_DORMANT | \
+ NFT_TABLE_F_OWNER)
/**
* enum nft_table_attributes - nf_tables table netlink attributes
* @NFTA_TABLE_NAME: name of the table (NLA_STRING)
* @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32)
* @NFTA_TABLE_USE: number of chains in this table (NLA_U32)
+ * @NFTA_TABLE_USERDATA: user data (NLA_BINARY)
+ * @NFTA_TABLE_OWNER: owner of this table through netlink portID (NLA_U32)
*/
enum nft_table_attributes {
NFTA_TABLE_UNSPEC,
NFTA_TABLE_USE,
NFTA_TABLE_HANDLE,
NFTA_TABLE_PAD,
+ NFTA_TABLE_USERDATA,
+ NFTA_TABLE_OWNER,
__NFTA_TABLE_MAX
};
#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1)
+enum nft_chain_flags {
+ NFT_CHAIN_BASE = (1 << 0),
+ NFT_CHAIN_HW_OFFLOAD = (1 << 1),
+ NFT_CHAIN_BINDING = (1 << 2),
+};
+#define NFT_CHAIN_FLAGS (NFT_CHAIN_BASE | \
+ NFT_CHAIN_HW_OFFLOAD | \
+ NFT_CHAIN_BINDING)
+
/**
* enum nft_chain_attributes - nf_tables chain netlink attributes
*
* @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING)
* @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes)
* @NFTA_CHAIN_FLAGS: chain flags
+ * @NFTA_CHAIN_ID: uniquely identifies a chain in a transaction (NLA_U32)
+ * @NFTA_CHAIN_USERDATA: user data (NLA_BINARY)
*/
enum nft_chain_attributes {
NFTA_CHAIN_UNSPEC,
NFTA_CHAIN_COUNTERS,
NFTA_CHAIN_PAD,
NFTA_CHAIN_FLAGS,
+ NFTA_CHAIN_ID,
+ NFTA_CHAIN_USERDATA,
__NFTA_CHAIN_MAX
};
#define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1)
NFTA_RULE_PAD,
NFTA_RULE_ID,
NFTA_RULE_POSITION_ID,
+ NFTA_RULE_CHAIN_ID,
__NFTA_RULE_MAX
};
#define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1)
* @NFT_SET_EVAL: set can be updated from the evaluation path
* @NFT_SET_OBJECT: set contains stateful objects
* @NFT_SET_CONCAT: set contains a concatenation
+ * @NFT_SET_EXPR: set contains expressions
*/
enum nft_set_flags {
NFT_SET_ANONYMOUS = 0x1,
NFT_SET_EVAL = 0x20,
NFT_SET_OBJECT = 0x40,
NFT_SET_CONCAT = 0x80,
+ NFT_SET_EXPR = 0x100,
};
/**
* @NFTA_SET_OBJ_TYPE: stateful object type (NLA_U32: NFT_OBJECT_*)
* @NFTA_SET_HANDLE: set handle (NLA_U64)
* @NFTA_SET_EXPR: set expression (NLA_NESTED: nft_expr_attributes)
+ * @NFTA_SET_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes)
*/
enum nft_set_attributes {
NFTA_SET_UNSPEC,
NFTA_SET_OBJ_TYPE,
NFTA_SET_HANDLE,
NFTA_SET_EXPR,
+ NFTA_SET_EXPRESSIONS,
__NFTA_SET_MAX
};
#define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
* enum nft_set_elem_flags - nf_tables set element flags
*
* @NFT_SET_ELEM_INTERVAL_END: element ends the previous interval
+ * @NFT_SET_ELEM_CATCHALL: special catch-all element
*/
enum nft_set_elem_flags {
NFT_SET_ELEM_INTERVAL_END = 0x1,
+ NFT_SET_ELEM_CATCHALL = 0x2,
};
/**
* @NFTA_SET_ELEM_EXPR: expression (NLA_NESTED: nft_expr_attributes)
* @NFTA_SET_ELEM_OBJREF: stateful object reference (NLA_STRING)
* @NFTA_SET_ELEM_KEY_END: closing key value (NLA_NESTED: nft_data)
+ * @NFTA_SET_ELEM_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes)
*/
enum nft_set_elem_attributes {
NFTA_SET_ELEM_UNSPEC,
NFTA_SET_ELEM_PAD,
NFTA_SET_ELEM_OBJREF,
NFTA_SET_ELEM_KEY_END,
+ NFTA_SET_ELEM_EXPRESSIONS,
__NFTA_SET_ELEM_MAX
};
#define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1)
*
* @NFTA_VERDICT_CODE: nf_tables verdict (NLA_U32: enum nft_verdicts)
* @NFTA_VERDICT_CHAIN: jump target chain name (NLA_STRING)
+ * @NFTA_VERDICT_CHAIN_ID: jump target chain ID (NLA_U32)
*/
enum nft_verdict_attributes {
NFTA_VERDICT_UNSPEC,
NFTA_VERDICT_CODE,
NFTA_VERDICT_CHAIN,
+ NFTA_VERDICT_CHAIN_ID,
__NFTA_VERDICT_MAX
};
#define NFTA_VERDICT_MAX (__NFTA_VERDICT_MAX - 1)
enum nft_dynset_flags {
NFT_DYNSET_F_INV = (1 << 0),
+ NFT_DYNSET_F_EXPR = (1 << 1),
};
/**
* @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64)
* @NFTA_DYNSET_EXPR: expression (NLA_NESTED: nft_expr_attributes)
* @NFTA_DYNSET_FLAGS: flags (NLA_U32)
+ * @NFTA_DYNSET_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes)
*/
enum nft_dynset_attributes {
NFTA_DYNSET_UNSPEC,
NFTA_DYNSET_EXPR,
NFTA_DYNSET_PAD,
NFTA_DYNSET_FLAGS,
+ NFTA_DYNSET_EXPRESSIONS,
__NFTA_DYNSET_MAX,
};
#define NFTA_DYNSET_MAX (__NFTA_DYNSET_MAX - 1)
*
* @NFT_PAYLOAD_CSUM_NONE: no checksumming
* @NFT_PAYLOAD_CSUM_INET: internet checksum (RFC 791)
+ * @NFT_PAYLOAD_CSUM_SCTP: CRC-32c, for use in SCTP header (RFC 3309)
*/
enum nft_payload_csum_types {
NFT_PAYLOAD_CSUM_NONE,
NFT_PAYLOAD_CSUM_INET,
+ NFT_PAYLOAD_CSUM_SCTP,
};
enum nft_payload_csum_flags {
* @NFT_EXTHDR_OP_IPV6: match against ipv6 extension headers
* @NFT_EXTHDR_OP_TCP: match against tcp options
* @NFT_EXTHDR_OP_IPV4: match against ipv4 options
+ * @NFT_EXTHDR_OP_SCTP: match against sctp chunks
*/
enum nft_exthdr_op {
NFT_EXTHDR_OP_IPV6,
NFT_EXTHDR_OP_TCPOPT,
NFT_EXTHDR_OP_IPV4,
+ NFT_EXTHDR_OP_SCTP,
__NFT_EXTHDR_OP_MAX
};
#define NFT_EXTHDR_OP_MAX (__NFT_EXTHDR_OP_MAX - 1)
*
* @NFTA_SOCKET_KEY: socket key to match
* @NFTA_SOCKET_DREG: destination register
+ * @NFTA_SOCKET_LEVEL: cgroups2 ancestor level (only for cgroupsv2)
*/
enum nft_socket_attributes {
NFTA_SOCKET_UNSPEC,
NFTA_SOCKET_KEY,
NFTA_SOCKET_DREG,
+ NFTA_SOCKET_LEVEL,
__NFTA_SOCKET_MAX
};
#define NFTA_SOCKET_MAX (__NFTA_SOCKET_MAX - 1)
*
* @NFT_SOCKET_TRANSPARENT: Value of the IP(V6)_TRANSPARENT socket option
* @NFT_SOCKET_MARK: Value of the socket mark
+ * @NFT_SOCKET_WILDCARD: Whether the socket is zero-bound (e.g. 0.0.0.0 or ::0)
+ * @NFT_SOCKET_CGROUPV2: Match on cgroups version 2
*/
enum nft_socket_keys {
NFT_SOCKET_TRANSPARENT,
NFT_SOCKET_MARK,
+ NFT_SOCKET_WILDCARD,
+ NFT_SOCKET_CGROUPV2,
__NFT_SOCKET_MAX
};
#define NFT_SOCKET_MAX (__NFT_SOCKET_MAX - 1)
};
#define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1)
+/**
+ * enum nft_last_attributes - nf_tables last expression netlink attributes
+ *
+ * @NFTA_LAST_SET: last update has been set, zero means never updated (NLA_U32)
+ * @NFTA_LAST_MSECS: milliseconds since last update (NLA_U64)
+ */
+enum nft_last_attributes {
+ NFTA_LAST_UNSPEC,
+ NFTA_LAST_SET,
+ NFTA_LAST_MSECS,
+ NFTA_LAST_PAD,
+ __NFTA_LAST_MAX
+};
+#define NFTA_LAST_MAX (__NFTA_LAST_MAX - 1)
+
/**
* enum nft_log_attributes - nf_tables log expression netlink attributes
*
* @NFTA_OBJ_DATA: stateful object data (NLA_NESTED)
* @NFTA_OBJ_USE: number of references to this expression (NLA_U32)
* @NFTA_OBJ_HANDLE: object handle (NLA_U64)
+ * @NFTA_OBJ_USERDATA: user data (NLA_BINARY)
*/
enum nft_object_attributes {
NFTA_OBJ_UNSPEC,
NFTA_OBJ_USE,
NFTA_OBJ_HANDLE,
NFTA_OBJ_PAD,
+ NFTA_OBJ_USERDATA,
__NFTA_OBJ_MAX
};
#define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1)
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _NFNETLINK_H
-#define _NFNETLINK_H
+#ifndef _UAPI_NFNETLINK_H
+#define _UAPI_NFNETLINK_H
#include <linux/types.h>
#include <linux/netfilter/nfnetlink_compat.h>
#define NFNL_SUBSYS_CTHELPER 9
#define NFNL_SUBSYS_NFTABLES 10
#define NFNL_SUBSYS_NFT_COMPAT 11
-#define NFNL_SUBSYS_COUNT 12
+#define NFNL_SUBSYS_HOOK 12
+#define NFNL_SUBSYS_COUNT 13
/* Reserved control nfnetlink messages */
#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE
};
#define NFNL_BATCH_MAX (__NFNL_BATCH_MAX - 1)
-#endif /* _NFNETLINK_H */
+#endif /* _UAPI_NFNETLINK_H */
#ifndef _UAPI__LINUX_NETLINK_H
#define _UAPI__LINUX_NETLINK_H
-#include <linux/kernel.h>
+#include <linux/const.h>
#include <linux/socket.h> /* for __kernel_sa_family_t */
#include <linux/types.h>
#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
-#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_DATA(nlh) ((void *)(((char *)nlh) + NLMSG_HDRLEN))
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
- (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+ (struct nlmsghdr *)(((char *)(nlh)) + \
+ NLMSG_ALIGN((nlh)->nlmsg_len)))
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
(nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
(nlh)->nlmsg_len <= (len))
};
enum {
- NEXTHOP_GRP_TYPE_MPATH, /* default type if not specified */
+ NEXTHOP_GRP_TYPE_MPATH, /* hash-threshold nexthop group
+ * default type if not specified
+ */
+ NEXTHOP_GRP_TYPE_RES, /* resilient nexthop group */
__NEXTHOP_GRP_TYPE_MAX,
};
NHA_FDB, /* flag; nexthop belongs to a bridge fdb */
/* if NHA_FDB is added, OIF, BLACKHOLE, ENCAP cannot be set */
+ /* nested; resilient nexthop group attributes */
+ NHA_RES_GROUP,
+ /* nested; nexthop bucket attributes */
+ NHA_RES_BUCKET,
+
__NHA_MAX,
};
#define NHA_MAX (__NHA_MAX - 1)
+
+enum {
+ NHA_RES_GROUP_UNSPEC,
+ /* Pad attribute for 64-bit alignment. */
+ NHA_RES_GROUP_PAD = NHA_RES_GROUP_UNSPEC,
+
+ /* u16; number of nexthop buckets in a resilient nexthop group */
+ NHA_RES_GROUP_BUCKETS,
+ /* clock_t as u32; nexthop bucket idle timer (per-group) */
+ NHA_RES_GROUP_IDLE_TIMER,
+ /* clock_t as u32; nexthop unbalanced timer */
+ NHA_RES_GROUP_UNBALANCED_TIMER,
+ /* clock_t as u64; nexthop unbalanced time */
+ NHA_RES_GROUP_UNBALANCED_TIME,
+
+ __NHA_RES_GROUP_MAX,
+};
+
+#define NHA_RES_GROUP_MAX (__NHA_RES_GROUP_MAX - 1)
+
+enum {
+ NHA_RES_BUCKET_UNSPEC,
+ /* Pad attribute for 64-bit alignment. */
+ NHA_RES_BUCKET_PAD = NHA_RES_BUCKET_UNSPEC,
+
+ /* u16; nexthop bucket index */
+ NHA_RES_BUCKET_INDEX,
+ /* clock_t as u64; nexthop bucket idle time */
+ NHA_RES_BUCKET_IDLE_TIME,
+ /* u32; nexthop id assigned to the nexthop bucket */
+ NHA_RES_BUCKET_NH_ID,
+
+ __NHA_RES_BUCKET_MAX,
+};
+
+#define NHA_RES_BUCKET_MAX (__NHA_RES_BUCKET_MAX - 1)
+
#endif
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
* Copyright 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2020 Intel Corporation
+ * Copyright (C) 2018-2021 Intel Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* When a security association was established on an 802.1X network using
* fast transition, this event should be followed by an
* %NL80211_CMD_PORT_AUTHORIZED event.
+ * Following a %NL80211_CMD_ROAM event userspace can issue
+ * %NL80211_CMD_GET_SCAN in order to obtain the scan information for the
+ * new BSS the card/driver roamed to.
* @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
* userspace that a connection was dropped by the AP or due to other
* reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
* of any other interfaces, and other interfaces will again take
* precedence when they are used.
*
- * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
+ * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface
+ * (no longer supported).
*
* @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform
* multicast to unicast conversion. When enabled, all multicast packets
* includes the contents of the frame. %NL80211_ATTR_ACK flag is included
* if the recipient acknowledged the frame.
*
+ * @NL80211_CMD_SET_SAR_SPECS: SAR power limitation configuration is
+ * passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to
+ * specify the wiphy index to be applied to.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS,
+ NL80211_CMD_SET_SAR_SPECS,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
* specify just a single bitrate, which is to be used for the beacon.
* The driver must also specify support for this with the extended
* features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
- * NL80211_EXT_FEATURE_BEACON_RATE_HT and
- * NL80211_EXT_FEATURE_BEACON_RATE_VHT.
+ * NL80211_EXT_FEATURE_BEACON_RATE_HT,
+ * NL80211_EXT_FEATURE_BEACON_RATE_VHT and
+ * NL80211_EXT_FEATURE_BEACON_RATE_HE.
*
* @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
* at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
* @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire
* probe-response frame. The DA field in the 802.11 header is zero-ed out,
* to be filled by the FW.
- * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable
- * this feature. Currently, only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable
+ * this feature during association. This is a flag attribute.
+ * Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_VHT: Force VHT capable interfaces to disable
+ * this feature during association. This is a flag attribute.
+ * Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_HE: Force HE capable interfaces to disable
+ * this feature during association. This is a flag attribute.
+ * Currently only supported in mac80211 drivers.
* @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
* ATTR_HT_CAPABILITY to which attention should be paid.
* Currently, only mac80211 NICs support this feature.
* until the channel switch event.
* @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
* must be blocked on the current channel (before the channel switch
- * operation).
+ * operation). Also included in the channel switch started event if quiet
+ * was requested by the AP.
* @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
* for the time while performing a channel switch.
* @NL80211_ATTR_CNTDWN_OFFS_BEACON: An array of offsets (u16) to the channel
* override mask. Used with NL80211_ATTR_S1G_CAPABILITY in
* NL80211_CMD_ASSOCIATE or NL80211_CMD_CONNECT.
*
+ * @NL80211_ATTR_SAE_PWE: Indicates the mechanism(s) allowed for SAE PWE
+ * derivation in WPA3-Personal networks which are using SAE authentication.
+ * This is a u8 attribute that encapsulates one of the values from
+ * &enum nl80211_sae_pwe_mechanism.
+ *
+ * @NL80211_ATTR_SAR_SPEC: SAR power limitation specification when
+ * used with %NL80211_CMD_SET_SAR_SPECS. The message contains fields
+ * of %nl80211_sar_attrs which specifies the sar type and related
+ * sar specs. Sar specs contains array of %nl80211_sar_specs_attrs.
+ *
+ * @NL80211_ATTR_RECONNECT_REQUESTED: flag attribute, used with deauth and
+ * disassoc events to indicate that an immediate reconnect to the AP
+ * is desired.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
NL80211_ATTR_S1G_CAPABILITY,
NL80211_ATTR_S1G_CAPABILITY_MASK,
+ NL80211_ATTR_SAE_PWE,
+
+ NL80211_ATTR_RECONNECT_REQUESTED,
+
+ NL80211_ATTR_SAR_SPEC,
+
+ NL80211_ATTR_DISABLE_HE,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
* defined
* @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
* given for all 6 GHz band channels
+ * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are
+ * advertised on this band/for this iftype (binary)
* @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
*/
enum nl80211_band_iftype_attr {
NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
+ NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
/* keep last */
__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
* @NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP: Driver/device supports
* unsolicited broadcast probe response transmission
*
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HE: Driver supports beacon rate
+ * configuration (AP/mesh) with HE rates.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_LTF: Device supports secure LTF measurement
+ * exchange protocol.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_RTT: Device supports secure RTT measurement
+ * exchange protocol.
+ *
+ * @NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE: Device supports management
+ * frame protection for all management frames exchanged during the
+ * negotiation and range measurement procedure.
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
NL80211_EXT_FEATURE_SAE_OFFLOAD_AP,
NL80211_EXT_FEATURE_FILS_DISCOVERY,
NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP,
+ NL80211_EXT_FEATURE_BEACON_RATE_HE,
+ NL80211_EXT_FEATURE_SECURE_LTF,
+ NL80211_EXT_FEATURE_SECURE_RTT,
+ NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
* @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
* @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
* @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ * @NL80211_TDLS_PEER_HE: TDLS peer is HE capable.
*/
enum nl80211_tdls_peer_capability {
NL80211_TDLS_PEER_HT = 1<<0,
NL80211_TDLS_PEER_VHT = 1<<1,
NL80211_TDLS_PEER_WMM = 1<<2,
+ NL80211_TDLS_PEER_HE = 1<<3,
};
/**
* if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
* %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
* ranging will be used.
+ * @NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK: negotiate for LMR feedback. Only
+ * valid if either %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED or
+ * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ * @NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR: optional. The BSS color of the
+ * responder. Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED
+ * or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED is set.
*
* @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
* @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC,
NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED,
NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED,
+ NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK,
+ NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR,
/* keep last */
NUM_NL80211_PMSR_FTM_REQ_ATTR,
NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX =
__NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_LAST - 1
};
+
+/**
+ * enum nl80211_sae_pwe_mechanism - The mechanism(s) allowed for SAE PWE
+ * derivation. Applicable only when WPA3-Personal SAE authentication is
+ * used.
+ *
+ * @NL80211_SAE_PWE_UNSPECIFIED: not specified, used internally to indicate that
+ * attribute is not present from userspace.
+ * @NL80211_SAE_PWE_HUNT_AND_PECK: hunting-and-pecking loop only
+ * @NL80211_SAE_PWE_HASH_TO_ELEMENT: hash-to-element only
+ * @NL80211_SAE_PWE_BOTH: both hunting-and-pecking loop and hash-to-element
+ * can be used.
+ */
+enum nl80211_sae_pwe_mechanism {
+ NL80211_SAE_PWE_UNSPECIFIED,
+ NL80211_SAE_PWE_HUNT_AND_PECK,
+ NL80211_SAE_PWE_HASH_TO_ELEMENT,
+ NL80211_SAE_PWE_BOTH,
+};
+
+/**
+ * enum nl80211_sar_type - type of SAR specs
+ *
+ * @NL80211_SAR_TYPE_POWER: power limitation specified in 0.25dBm unit
+ *
+ */
+enum nl80211_sar_type {
+ NL80211_SAR_TYPE_POWER,
+
+ /* add new type here */
+
+ /* Keep last */
+ NUM_NL80211_SAR_TYPE,
+};
+
+/**
+ * enum nl80211_sar_attrs - Attributes for SAR spec
+ *
+ * @NL80211_SAR_ATTR_TYPE: the SAR type as defined in &enum nl80211_sar_type.
+ *
+ * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power
+ * limit specifications. Each specification contains a set
+ * of %nl80211_sar_specs_attrs.
+ *
+ * For SET operation, it contains array of %NL80211_SAR_ATTR_SPECS_POWER
+ * and %NL80211_SAR_ATTR_SPECS_RANGE_INDEX.
+ *
+ * For sar_capa dump, it contains array of
+ * %NL80211_SAR_ATTR_SPECS_START_FREQ
+ * and %NL80211_SAR_ATTR_SPECS_END_FREQ.
+ *
+ * @__NL80211_SAR_ATTR_LAST: Internal
+ * @NL80211_SAR_ATTR_MAX: highest sar attribute
+ *
+ * These attributes are used with %NL80211_CMD_SET_SAR_SPEC
+ */
+enum nl80211_sar_attrs {
+ __NL80211_SAR_ATTR_INVALID,
+
+ NL80211_SAR_ATTR_TYPE,
+ NL80211_SAR_ATTR_SPECS,
+
+ __NL80211_SAR_ATTR_LAST,
+ NL80211_SAR_ATTR_MAX = __NL80211_SAR_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_sar_specs_attrs - Attributes for SAR power limit specs
+ *
+ * @NL80211_SAR_ATTR_SPECS_POWER: Required (s32)value to specify the actual
+ * power limit value in units of 0.25 dBm if type is
+ * NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm).
+ * 0 means userspace doesn't have SAR limitation on this associated range.
+ *
+ * @NL80211_SAR_ATTR_SPECS_RANGE_INDEX: Required (u32) value to specify the
+ * index of exported freq range table and the associated power limitation
+ * is applied to this range.
+ *
+ * Userspace isn't required to set all the ranges advertised by WLAN driver,
+ * and userspace can skip some certain ranges. These skipped ranges don't
+ * have SAR limitations, and they are same as setting the
+ * %NL80211_SAR_ATTR_SPECS_POWER to any unreasonable high value because any
+ * value higher than regulatory allowed value just means SAR power
+ * limitation is removed, but it's required to set at least one range.
+ * It's not allowed to set duplicated range in one SET operation.
+ *
+ * Every SET operation overwrites previous SET operation.
+ *
+ * @NL80211_SAR_ATTR_SPECS_START_FREQ: Required (u32) value to specify the start
+ * frequency of this range edge when registering SAR capability to wiphy.
+ * It's not a channel center frequency. The unit is kHz.
+ *
+ * @NL80211_SAR_ATTR_SPECS_END_FREQ: Required (u32) value to specify the end
+ * frequency of this range edge when registering SAR capability to wiphy.
+ * It's not a channel center frequency. The unit is kHz.
+ *
+ * @__NL80211_SAR_ATTR_SPECS_LAST: Internal
+ * @NL80211_SAR_ATTR_SPECS_MAX: highest sar specs attribute
+ */
+enum nl80211_sar_specs_attrs {
+ __NL80211_SAR_ATTR_SPECS_INVALID,
+
+ NL80211_SAR_ATTR_SPECS_POWER,
+ NL80211_SAR_ATTR_SPECS_RANGE_INDEX,
+ NL80211_SAR_ATTR_SPECS_START_FREQ,
+ NL80211_SAR_ATTR_SPECS_END_FREQ,
+
+ __NL80211_SAR_ATTR_SPECS_LAST,
+ NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1,
+};
+
#endif /* __LINUX_NL80211_H */
TCA_HTB_RATE64,
TCA_HTB_CEIL64,
TCA_HTB_PAD,
+ TCA_HTB_OFFLOAD,
__TCA_HTB_MAX,
};
RTM_GETVLAN,
#define RTM_GETVLAN RTM_GETVLAN
+ RTM_NEWNEXTHOPBUCKET = 116,
+#define RTM_NEWNEXTHOPBUCKET RTM_NEWNEXTHOPBUCKET
+ RTM_DELNEXTHOPBUCKET,
+#define RTM_DELNEXTHOPBUCKET RTM_DELNEXTHOPBUCKET
+ RTM_GETNEXTHOPBUCKET,
+#define RTM_GETNEXTHOPBUCKET RTM_GETNEXTHOPBUCKET
+
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
#define RTPROT_MROUTED 17 /* Multicast daemon */
#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */
#define RTPROT_BABEL 42 /* Babel daemon */
+#define RTPROT_OPENR 99 /* Open Routing (Open/R) Routes */
#define RTPROT_BGP 186 /* BGP Routes */
#define RTPROT_ISIS 187 /* ISIS Routes */
#define RTPROT_OSPF 188 /* OSPF Routes */
#define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */
#define RTM_F_OFFLOAD 0x4000 /* route is offloaded */
#define RTM_F_TRAP 0x8000 /* route is trapping packets */
+#define RTM_F_OFFLOAD_FAILED 0x20000000 /* route offload failed, this value
+ * is chosen to avoid conflicts with
+ * other flags defined in
+ * include/uapi/linux/ipv6_route.h
+ */
/* Reserved table identifiers */
#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */
#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */
#define RTNH_F_ONLINK 4 /* Gateway is forced on link */
-#define RTNH_F_OFFLOAD 8 /* offloaded route */
+#define RTNH_F_OFFLOAD 8 /* Nexthop is offloaded */
#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */
#define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */
+#define RTNH_F_TRAP 64 /* Nexthop is trapping packets */
-#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD)
+#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | \
+ RTNH_F_OFFLOAD | RTNH_F_TRAP)
/* Macros to handle hexthops */
#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg))
/* tcamsg flags stored in attribute TCA_ROOT_FLAGS
*
- * TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO
- * actions in a dump. All dump responses will contain the number of actions
- * being dumped stored in for user app's consumption in TCA_ROOT_COUNT
+ * TCA_ACT_FLAG_LARGE_DUMP_ON user->kernel to request for larger than
+ * TCA_ACT_MAX_PRIO actions in a dump. All dump responses will contain the
+ * number of actions being dumped stored in for user app's consumption in
+ * TCA_ROOT_COUNT
+ *
+ * TCA_ACT_FLAG_TERSE_DUMP user->kernel to request terse (brief) dump that only
+ * includes essential action info (kind, index, etc.)
*
*/
#define TCA_FLAG_LARGE_DUMP_ON (1 << 0)
+#define TCA_ACT_FLAG_LARGE_DUMP_ON TCA_FLAG_LARGE_DUMP_ON
+#define TCA_ACT_FLAG_TERSE_DUMP (1 << 1)
/* New extended info filters for IFLA_EXT_MASK */
#define RTEXT_FILTER_VF (1 << 0)
#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
#define RTEXT_FILTER_SKIP_STATS (1 << 3)
#define RTEXT_FILTER_MRP (1 << 4)
+#define RTEXT_FILTER_CFM_CONFIG (1 << 5)
+#define RTEXT_FILTER_CFM_STATUS (1 << 6)
/* End of information exported to user level */
#define LIST_FOREACH_SAFE(name,i,n,head) \
for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
-#define LIST_FOREACH_BEFORE(name,i,p) \
- for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
-
-#define LIST_FOREACH_AFTER(name,i,p) \
- for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
+#define LIST_FOREACH_BACKWARDS(name,i,p) \
+ for ((i) = (p); (i); (i) = (i)->name##_prev)
/* Iterate through all the members of the list p is included in, but skip over p */
#define LIST_FOREACH_OTHERS(name,i,p) \
[SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[",
[SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,",
[SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */
+ [SPECIAL_GLYPH_RECYCLING] = "~",
+ [SPECIAL_GLYPH_DOWNLOAD] = "\\",
+ [SPECIAL_GLYPH_SPARKLES] = "*",
},
/* UTF-8 */
[SPECIAL_GLYPH_LOCK_AND_KEY] = "\360\237\224\220", /* 🔐 (actually called: CLOSED LOCK WITH KEY) */
/* This emoji is a single character cell glyph in Unicode, and two in ASCII */
- [SPECIAL_GLYPH_TOUCH] = "\360\237\221\206", /* 👆 (actually called: BACKHAND INDEX POINTING UP */
+ [SPECIAL_GLYPH_TOUCH] = "\360\237\221\206", /* 👆 (actually called: BACKHAND INDEX POINTING UP) */
+
+ /* These three emojis are single character cell glyphs in Unicode and also in ASCII. */
+ [SPECIAL_GLYPH_RECYCLING] = "\u267B\uFE0F ", /* ♻️ (actually called: UNIVERSAL RECYCLNG SYMBOL) */
+ [SPECIAL_GLYPH_DOWNLOAD] = "\u2935\uFE0F ", /* ⤵️ (actually called: RIGHT ARROW CURVING DOWN) */
+ [SPECIAL_GLYPH_SPARKLES] = "\u2728", /* ✨ */
},
};
SPECIAL_GLYPH_DEPRESSED_SMILEY,
SPECIAL_GLYPH_LOCK_AND_KEY,
SPECIAL_GLYPH_TOUCH,
+ SPECIAL_GLYPH_RECYCLING,
+ SPECIAL_GLYPH_DOWNLOAD,
+ SPECIAL_GLYPH_SPARKLES,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
static inline const char *special_glyph_check_mark(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
}
+
+static inline const char *special_glyph_check_mark_space(bool b) {
+ return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " ";
+}
#define SNDBUF_SIZE (8*1024*1024)
+static log_syntax_callback_t log_syntax_callback = NULL;
+static void *log_syntax_callback_userdata = NULL;
+
static LogTarget log_target = LOG_TARGET_CONSOLE;
static int log_max_level = LOG_INFO;
static int log_facility = LOG_DAEMON;
IOVEC_MAKE_STRING(header_pid),
IOVEC_MAKE_STRING(buffer),
};
- struct msghdr msghdr = {
+ const struct msghdr msghdr = {
.msg_iov = iovec,
.msg_iovlen = ELEMENTSOF(iovec),
};
const char *buffer) {
char header[LINE_MAX];
- struct iovec iovec[4] = {};
- struct msghdr mh = {};
if (journal_fd < 0)
return 0;
log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra);
- iovec[0] = IOVEC_MAKE_STRING(header);
- iovec[1] = IOVEC_MAKE_STRING("MESSAGE=");
- iovec[2] = IOVEC_MAKE_STRING(buffer);
- iovec[3] = IOVEC_MAKE_STRING("\n");
-
- mh.msg_iov = iovec;
- mh.msg_iovlen = ELEMENTSOF(iovec);
+ struct iovec iovec[4] = {
+ IOVEC_MAKE_STRING(header),
+ IOVEC_MAKE_STRING("MESSAGE="),
+ IOVEC_MAKE_STRING(buffer),
+ IOVEC_MAKE_STRING("\n"),
+ };
+ const struct msghdr msghdr = {
+ .msg_iov = iovec,
+ .msg_iovlen = ELEMENTSOF(iovec),
+ };
- if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
+ if (sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL) < 0)
return -errno;
return 1;
}
_noreturn_ void log_assert_failed_unreachable(
- const char *text,
const char *file,
int line,
const char *func) {
- log_assert(LOG_CRIT, text, file, line, func,
- "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
+ log_assert(LOG_CRIT, "Code should not be reached", file, line, func,
+ "%s at %s:%u, function %s(). Aborting. 💥");
abort();
}
VA_FORMAT_ADVANCE(format, ap);
iovec[(*n)++] = IOVEC_MAKE_STRING(m);
-
- if (newline_separator) {
- iovec[*n] = IOVEC_MAKE((char *)&nl, 1);
- (*n)++;
- }
+ if (newline_separator)
+ iovec[(*n)++] = IOVEC_MAKE((char *)&nl, 1);
format = va_arg(ap, char *);
}
if (journal_fd >= 0) {
char header[LINE_MAX];
- struct iovec iovec[17] = {};
+ struct iovec iovec[17];
size_t n = 0;
int r;
- struct msghdr mh = {
- .msg_iov = iovec,
- };
bool fallback = false;
/* If the journal is available do structured logging.
if (r < 0)
fallback = true;
else {
- mh.msg_iovlen = n;
- (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL);
+ const struct msghdr msghdr = {
+ .msg_iov = iovec,
+ .msg_iovlen = n,
+ };
+
+ (void) sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL);
}
va_end(ap);
size_t n_input_iovec) {
PROTECT_ERRNO;
- size_t i;
- char *m;
if (_likely_(LOG_PRI(level) > log_max_level) ||
log_target == LOG_TARGET_NULL)
LOG_TARGET_JOURNAL) &&
journal_fd >= 0) {
- struct iovec iovec[1 + n_input_iovec*2];
char header[LINE_MAX];
- struct msghdr mh = {
- .msg_iov = iovec,
- .msg_iovlen = 1 + n_input_iovec*2,
- };
-
log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
- iovec[0] = IOVEC_MAKE_STRING(header);
- for (i = 0; i < n_input_iovec; i++) {
+ struct iovec iovec[1 + n_input_iovec*2];
+ iovec[0] = IOVEC_MAKE_STRING(header);
+ for (size_t i = 0; i < n_input_iovec; i++) {
iovec[1+i*2] = input_iovec[i];
iovec[1+i*2+1] = IOVEC_MAKE_STRING("\n");
}
- if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) >= 0)
+ const struct msghdr msghdr = {
+ .msg_iov = iovec,
+ .msg_iovlen = 1 + n_input_iovec*2,
+ };
+
+ if (sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL) >= 0)
return -ERRNO_VALUE(error);
}
- for (i = 0; i < n_input_iovec; i++)
- if (memory_startswith(input_iovec[i].iov_base, input_iovec[i].iov_len, "MESSAGE="))
- break;
+ for (size_t i = 0; i < n_input_iovec; i++)
+ if (memory_startswith(input_iovec[i].iov_base, input_iovec[i].iov_len, "MESSAGE=")) {
+ char *m = strndupa(input_iovec[i].iov_base + STRLEN("MESSAGE="),
+ input_iovec[i].iov_len - STRLEN("MESSAGE="));
- if (_unlikely_(i >= n_input_iovec)) /* Couldn't find MESSAGE=? */
- return -ERRNO_VALUE(error);
-
- m = strndupa(input_iovec[i].iov_base + STRLEN("MESSAGE="),
- input_iovec[i].iov_len - STRLEN("MESSAGE="));
+ return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, m);
+ }
- return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, m);
+ /* Couldn't find MESSAGE=. */
+ return -ERRNO_VALUE(error);
}
int log_set_target_from_string(const char *e) {
signal_to_string(si->ssi_signo));
}
+void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata) {
+ assert(!log_syntax_callback || !cb);
+ assert(!log_syntax_callback_userdata || !userdata);
+
+ log_syntax_callback = cb;
+ log_syntax_callback_userdata = userdata;
+}
+
int log_syntax_internal(
const char *unit,
int level,
const char *func,
const char *format, ...) {
+ if (log_syntax_callback)
+ log_syntax_callback(unit, level, log_syntax_callback_userdata);
+
PROTECT_ERRNO;
char buffer[LINE_MAX];
va_list ap;
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define ERRNO_VALUE(val) (abs(val) & 255)
+/* The callback function to be invoked when syntax warnings are seen
+ * in the unit files. */
+typedef void (*log_syntax_callback_t)(const char *unit, int level, void *userdata);
+void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata);
+
+static inline void clear_log_syntax_callback(dummy_t *dummy) {
+ set_log_syntax_callback(/* cb= */ NULL, /* userdata= */ NULL);
+}
+
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
void log_set_target(LogTarget target);
const char *func);
_noreturn_ void log_assert_failed_unreachable(
- const char *text,
const char *file,
int line,
const char *func);
#define _sentinel_ __attribute__((__sentinel__))
#define _destructor_ __attribute__((__destructor__))
#define _deprecated_ __attribute__((__deprecated__))
-#define _packed_ __attribute__((__packed__))
#define _malloc_ __attribute__((__malloc__))
#define _weak_ __attribute__((__weak__))
-#define _likely_(x) (__builtin_expect(!!(x), 1))
-#define _unlikely_(x) (__builtin_expect(!!(x), 0))
#define _public_ __attribute__((__visibility__("default")))
#define _hidden_ __attribute__((__visibility__("hidden")))
#define _weakref_(x) __attribute__((__weakref__(#x)))
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _warn_unused_result_ __attribute__((__warn_unused_result__))
-#if __GNUC__ >= 7
-#define _fallthrough_ __attribute__((__fallthrough__))
-#else
-#define _fallthrough_
-#endif
-/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
- * compiler versions */
-#ifndef _noreturn_
-#if __STDC_VERSION__ >= 201112L
-#define _noreturn_ _Noreturn
-#else
-#define _noreturn_ __attribute__((__noreturn__))
-#endif
-#endif
#if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# if defined(__has_feature)
#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
static inline size_t ALIGN_TO(size_t l, size_t ali) {
+ /* Check that alignment is exponent of 2 */
+#if SIZE_MAX == UINT_MAX
+ assert(__builtin_popcount(ali) == 1);
+#elif SIZE_MAX == ULONG_MAX
+ assert(__builtin_popcountl(ali) == 1);
+#elif SIZE_MAX == ULONGLONG_MAX
+ assert(__builtin_popcountll(ali) == 1);
+#else
+#error "Unexpected size_t"
+#endif
+
+ if (l > SIZE_MAX - (ali - 1))
+ return SIZE_MAX; /* indicate overflow */
+
return ((l + ali - 1) & ~(ali - 1));
}
return m;
}
-/*
- * STRLEN - return the length of a string literal, minus the trailing NUL byte.
- * Contrary to strlen(), this is a constant expression.
- * @x: a string literal.
- */
-#define STRLEN(x) ((unsigned) sizeof(""x"") - 1)
-
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
#define assert(expr) assert_message_se(expr, #expr)
#endif
-#define assert_not_reached(t) \
- log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
+#define assert_not_reached() \
+ log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
#define assert_return(expr, r) \
do { \
(2U+(sizeof(type) <= 1 ? 3U : \
sizeof(type) <= 2 ? 5U : \
sizeof(type) <= 4 ? 10U : \
- sizeof(type) <= 8 ? 20U : (unsigned) sizeof(int[-2*(sizeof(type) > 8)])))
+ sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)])))
#define DECIMAL_STR_WIDTH(x) \
({ \
typeof(x) _x_ = (x); \
- unsigned ans = 1; \
+ size_t ans = 1; \
while ((_x_ /= 10) != 0) \
ans++; \
ans; \
return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
}
+typedef struct {
+ int _empty[0];
+} dummy_t;
+
+assert_cc(sizeof(dummy_t) == 0);
+
#include "log.h"
return memmem(haystack, haystacklen, needle, needlelen);
}
+static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+ const uint8_t *p;
+
+ p = memmem_safe(haystack, haystacklen, needle, needlelen);
+ if (!p)
+ return NULL;
+
+ return (uint8_t*) p + needlelen;
+}
+
#if HAVE_EXPLICIT_BZERO
static inline void* explicit_bzero_safe(void *p, size_t l) {
if (l > 0)
/* Can't setns to your own userns, since then you could escalate from non-root to root in
* your own namespace, so check if namespaces are equal before attempting to enter. */
- char userns_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
- xsprintf(userns_fd_path, "/proc/self/fd/%d", userns_fd);
- r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
+ r = files_same(FORMAT_PROC_FD_PATH(userns_fd), "/proc/self/ns/user", 0);
if (r < 0)
return r;
if (r)
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
+#include "dirent-util.h"
#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "os-util.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "utf8.h"
+#include "xattr-util.h"
bool image_name_is_valid(const char *s) {
if (!filename_is_valid(s))
if (laccess(path, F_OK) < 0)
return -errno;
- /* We use /usr/lib/extension-release.d/extension-release.NAME as flag file if something is a system extension,
- * and {/etc|/usr/lib}/os-release as flag file if something is an OS (in case extension == NULL) */
+ /* We use /usr/lib/extension-release.d/extension-release[.NAME] as flag for something being a system extension,
+ * and {/etc|/usr/lib}/os-release as a flag for something being an OS (when not an extension). */
r = open_extension_release(path, extension, NULL, NULL);
if (r == -ENOENT) /* We got nothing */
return 0;
r = chase_symlinks(extension_full_path, root, CHASE_PREFIX_ROOT,
ret_path ? &q : NULL,
ret_fd ? &fd : NULL);
+ /* Cannot find the expected extension-release file? The image filename might have been
+ * mangled on deployment, so fallback to checking for any file in the extension-release.d
+ * directory, and return the first one with a user.extension-release xattr instead.
+ * The user.extension-release.strict xattr is checked to ensure the author of the image
+ * considers it OK if names do not match. */
+ if (r == -ENOENT) {
+ _cleanup_free_ char *extension_release_dir_path = NULL;
+ _cleanup_closedir_ DIR *extension_release_dir = NULL;
+
+ r = chase_symlinks_and_opendir("/usr/lib/extension-release.d/", root, CHASE_PREFIX_ROOT,
+ &extension_release_dir_path, &extension_release_dir);
+ if (r < 0)
+ return r;
+
+ r = -ENOENT;
+ struct dirent *de;
+ FOREACH_DIRENT(de, extension_release_dir, return -errno) {
+ int k;
+
+ if (!IN_SET(de->d_type, DT_REG, DT_UNKNOWN))
+ continue;
+
+ const char *image_name = startswith(de->d_name, "extension-release.");
+ if (!image_name)
+ continue;
+
+ if (!image_name_is_valid(image_name))
+ continue;
+
+ /* We already chased the directory, and checked that
+ * this is a real file, so we shouldn't fail to open it. */
+ _cleanup_close_ int extension_release_fd = openat(dirfd(extension_release_dir),
+ de->d_name,
+ O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (extension_release_fd < 0)
+ return log_debug_errno(errno,
+ "Failed to open extension-release file %s/%s: %m",
+ extension_release_dir_path,
+ de->d_name);
+
+ /* Really ensure it is a regular file after we open it. */
+ if (fd_verify_regular(extension_release_fd) < 0)
+ continue;
+
+ /* No xattr or cannot parse it? Then skip this. */
+ _cleanup_free_ char *extension_release_xattr = NULL;
+ k = fgetxattrat_fake_malloc(extension_release_fd, NULL, "user.extension-release.strict", AT_EMPTY_PATH, &extension_release_xattr);
+ if (k < 0 && !ERRNO_IS_NOT_SUPPORTED(k) && k != -ENODATA)
+ log_debug_errno(k,
+ "Failed to read 'user.extension-release.strict' extended attribute from extension-release file %s/%s: %m",
+ extension_release_dir_path,
+ de->d_name);
+ if (k < 0)
+ continue;
+
+ /* Explicitly set to request strict matching? Skip it. */
+ k = parse_boolean(extension_release_xattr);
+ if (k < 0)
+ log_debug_errno(k,
+ "Failed to parse 'user.extension-release.strict' extended attribute value from extension-release file %s/%s: %m",
+ extension_release_dir_path,
+ de->d_name);
+ if (k < 0 || k > 0)
+ continue;
+
+ /* We already found what we were looking for, but there's another candidate?
+ * We treat this as an error, as we want to enforce that there are no ambiguities
+ * in case we are in the fallback path.*/
+ if (r == 0) {
+ r = -ENOTUNIQ;
+ break;
+ }
+
+ r = 0; /* Found it! */
+
+ if (ret_fd)
+ fd = TAKE_FD(extension_release_fd);
+
+ if (ret_path) {
+ q = path_join(extension_release_dir_path, de->d_name);
+ if (!q)
+ return -ENOMEM;
+ }
+ }
+ }
} else {
const char *p;
#include <errno.h>
#include <inttypes.h>
-#include <linux/oom.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
if (r < 0)
return r;
- if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX)
+ if (!oom_score_adjust_is_valid(v))
return -ERANGE;
*ret = v;
return 0;
default:
- assert_not_reached("Hmm, unexpected scope value.");
+ assert_not_reached();
}
if (!a || !b)
return -EOPNOTSUPP;
default:
- assert_not_reached("Hmm, unexpected scope value.");
+ assert_not_reached();
}
*persistent = TAKE_PTR(a);
break;
default:
- assert_not_reached("Hmm, unexpected scope?");
+ assert_not_reached();
}
if (!add)
break;
default:
- assert_not_reached("Hmm, unexpected scope.");
+ assert_not_reached();
}
if (!add)
return 0;
}
-int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) {
+int find_executable_full(const char *name, const char *root, bool use_path_envvar, char **ret_filename, int *ret_fd) {
int last_error, r;
const char *p = NULL;
if (is_path(name)) {
_cleanup_close_ int fd = -1;
+ _cleanup_free_ char *path_name = NULL;
+
+ /* Function chase_symlinks() is invoked only when root is not NULL,
+ * as using it regardless of root value would alter the behavior
+ * of existing callers for example: /bin/sleep would become
+ * /usr/bin/sleep when find_executables is called. Hence, this function
+ * should be invoked when needed to avoid unforeseen regression or other
+ * complicated changes. */
+ if (root) {
+ r = chase_symlinks(name,
+ root,
+ CHASE_PREFIX_ROOT,
+ &path_name,
+ /* ret_fd= */ NULL); /* prefix root to name in case full paths are not specified */
+ if (r < 0)
+ return r;
+
+ name = path_name;
+ }
r = check_x_access(name, ret_fd ? &fd : NULL);
if (r < 0)
if (!path_extend(&element, name))
return -ENOMEM;
+ if (root) {
+ char *path_name;
+
+ r = chase_symlinks(element,
+ root,
+ CHASE_PREFIX_ROOT,
+ &path_name,
+ /* ret_fd= */ NULL);
+ if (r < 0) {
+ if (r != -EACCES)
+ last_error = r;
+ continue;
+ }
+
+ free_and_replace(element, path_name);
+ }
+
r = check_x_access(element, ret_fd ? &fd : NULL);
if (r < 0) {
/* PATH entries which we don't have access to are ignored, as per tradition. */
char** path_strv_resolve(char **l, const char *root);
char** path_strv_resolve_uniq(char **l, const char *root);
-int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd);
+int find_executable_full(const char *name, const char *root, bool use_path_envvar, char **ret_filename, int *ret_fd);
static inline int find_executable(const char *name, char **ret_filename) {
- return find_executable_full(name, true, ret_filename, NULL);
+ return find_executable_full(name, /* root= */ NULL, true, ret_filename, NULL);
}
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
return cached > 0;
}
+bool oom_score_adjust_is_valid(int oa) {
+ return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
+}
+
unsigned long personality_from_string(const char *p) {
int architecture;
bool is_main_thread(void);
+bool oom_score_adjust_is_valid(int oa);
+
#ifndef PERSONALITY_INVALID
/* personality(7) documents that 0xffffffffUL is used for querying the
* current personality, hence let's use that here as error
/* no set_free_free_free */
-#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS))
+#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(s) HASHMAP_DEBUG_SRC_ARGS))
int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
if (sigemptyset(&ss) < 0)
return -errno;
- /* Add first signal (if the signal is zero, we'll silently skip it, to make it easiert to build
+ /* Add first signal (if the signal is zero, we'll silently skip it, to make it easier to build
* parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles
* this.) */
if (sig > 0 && sigaddset(&ss, sig) < 0)
_len = sizeof(struct sockaddr_vm); \
break; \
default: \
- assert_not_reached("invalid socket family"); \
+ assert_not_reached(); \
} \
_len; \
})
#define SPECIAL_HIBERNATE_TARGET "hibernate.target"
#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target"
#define SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET "suspend-then-hibernate.target"
+#define SPECIAL_FACTORY_RESET_TARGET "factory-reset.target"
/* Special boot targets */
#define SPECIAL_RESCUE_TARGET "rescue.target"
#include "macro.h"
#include "memory-util.h"
-#define snprintf_ok(buf, len, fmt, ...) \
- ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len))
+#define snprintf_ok(buf, len, fmt, ...) \
+ ({ \
+ char *_buf = (buf); \
+ size_t _len = (len); \
+ int _snpf = snprintf(_buf, _len, (fmt), __VA_ARGS__); \
+ _snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \
+ })
#define xsprintf(buf, fmt, ...) \
assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough")
(void) va_arg(ap, long double); \
break; \
default: \
- assert_not_reached("Unknown format string argument."); \
+ assert_not_reached(); \
} \
} \
} while (false)
}
REENABLE_WARNING;
-/* Like startswith(), but operates on arbitrary memory blocks */
-static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
- assert(token);
-
- size_t n = strlen(token);
- if (sz < n)
- return NULL;
-
- assert(p);
-
- if (memcmp(p, token, n) != 0)
- return NULL;
-
- return (uint8_t*) p + n;
-}
-
/* Like startswith_no_case(), but operates on arbitrary memory blocks.
* It works only for ASCII strings.
*/
#define strv_free_and_replace(a, b) \
({ \
- strv_free(a); \
- (a) = (b); \
- (b) = NULL; \
+ char ***_a = &(a); \
+ char ***_b = &(b); \
+ strv_free(*_a); \
+ (*_a) = (*_b); \
+ (*_b) = NULL; \
0; \
})
usec_t years = d / USEC_PER_YEAR;
usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
- snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
- years,
- years == 1 ? "year" : "years",
- months,
- months == 1 ? "month" : "months",
- s);
+ (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+ years,
+ years == 1 ? "year" : "years",
+ months,
+ months == 1 ? "month" : "months",
+ s);
} else if (d >= USEC_PER_MONTH) {
usec_t months = d / USEC_PER_MONTH;
usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
- snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
- months,
- months == 1 ? "month" : "months",
- days,
- days == 1 ? "day" : "days",
- s);
+ (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+ months,
+ months == 1 ? "month" : "months",
+ days,
+ days == 1 ? "day" : "days",
+ s);
} else if (d >= USEC_PER_WEEK) {
usec_t weeks = d / USEC_PER_WEEK;
usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
- snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
- weeks,
- weeks == 1 ? "week" : "weeks",
- days,
- days == 1 ? "day" : "days",
- s);
+ (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+ weeks,
+ weeks == 1 ? "week" : "weeks",
+ days,
+ days == 1 ? "day" : "days",
+ s);
} else if (d >= 2*USEC_PER_DAY)
- snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
+ (void) snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
else if (d >= 25*USEC_PER_HOUR)
- snprintf(buf, l, "1 day " USEC_FMT "h %s",
- (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
+ (void) snprintf(buf, l, "1 day " USEC_FMT "h %s",
+ (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
else if (d >= 6*USEC_PER_HOUR)
- snprintf(buf, l, USEC_FMT "h %s",
- d / USEC_PER_HOUR, s);
+ (void) snprintf(buf, l, USEC_FMT "h %s",
+ d / USEC_PER_HOUR, s);
else if (d >= USEC_PER_HOUR)
- snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
- d / USEC_PER_HOUR,
- (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
+ (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
+ d / USEC_PER_HOUR,
+ (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
else if (d >= 5*USEC_PER_MINUTE)
- snprintf(buf, l, USEC_FMT "min %s",
- d / USEC_PER_MINUTE, s);
+ (void) snprintf(buf, l, USEC_FMT "min %s",
+ d / USEC_PER_MINUTE, s);
else if (d >= USEC_PER_MINUTE)
- snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
- d / USEC_PER_MINUTE,
- (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
+ (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
+ d / USEC_PER_MINUTE,
+ (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
else if (d >= USEC_PER_SEC)
- snprintf(buf, l, USEC_FMT "s %s",
- d / USEC_PER_SEC, s);
+ (void) snprintf(buf, l, USEC_FMT "s %s",
+ d / USEC_PER_SEC, s);
else if (d >= USEC_PER_MSEC)
- snprintf(buf, l, USEC_FMT "ms %s",
- d / USEC_PER_MSEC, s);
+ (void) snprintf(buf, l, USEC_FMT "ms %s",
+ d / USEC_PER_MSEC, s);
else if (d > 0)
- snprintf(buf, l, USEC_FMT"us %s",
- d, s);
+ (void) snprintf(buf, l, USEC_FMT"us %s",
+ d, s);
else
- snprintf(buf, l, "now");
+ (void) snprintf(buf, l, "now");
buf[l-1] = 0;
return buf;
if (r < 0)
return r;
} else {
- char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
-
- xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
-
- if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
+ if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
return -errno;
}
}
}
-int fgetxattrat_fake(
+/* Note: ret_fn should already be allocated for the usual xsprintf and /proc/self/fd/%i pattern. */
+static int getxattrat_fake_prepare(
int dirfd,
const char *filename,
- const char *attribute,
- void *value, size_t size,
int flags,
- size_t *ret_size) {
+ char ret_fn[static PROC_FD_PATH_MAX],
+ int *ret_fd) {
- char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
_cleanup_close_ int fd = -1;
- ssize_t l;
+ assert(ret_fn);
+ assert(ret_fd);
/* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
if (!(flags & AT_EMPTY_PATH))
return -EINVAL;
- xsprintf(fn, "/proc/self/fd/%i", dirfd);
+ assert(dirfd >= 0);
+
+ format_proc_fd_path(ret_fn, dirfd);
} else {
fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
if (fd < 0)
return -errno;
- xsprintf(fn, "/proc/self/fd/%i", fd);
+ format_proc_fd_path(ret_fn, fd);
}
+ /* Pass the FD to the caller, since in case we do openat() the filename depends on it. */
+ *ret_fd = TAKE_FD(fd);
+
+ return 0;
+}
+
+int fgetxattrat_fake(
+ int dirfd,
+ const char *filename,
+ const char *attribute,
+ void *value, size_t size,
+ int flags,
+ size_t *ret_size) {
+
+ _cleanup_close_ int fd = -1;
+ char fn[PROC_FD_PATH_MAX];
+ ssize_t l;
+ int r;
+
+ r = getxattrat_fake_prepare(dirfd, filename, flags, fn, &fd);
+ if (r < 0)
+ return r;
+
l = getxattr(fn, attribute, value, size);
if (l < 0)
return -errno;
return 0;
}
+int fgetxattrat_fake_malloc(
+ int dirfd,
+ const char *filename,
+ const char *attribute,
+ int flags,
+ char **value) {
+
+ _cleanup_close_ int fd = -1;
+ char fn[PROC_FD_PATH_MAX];
+ int r;
+
+ r = getxattrat_fake_prepare(dirfd, filename, flags, fn, &fd);
+ if (r < 0)
+ return r;
+
+ return getxattr_malloc(fn, attribute, value, false);
+}
+
static int parse_crtime(le64_t le, usec_t *usec) {
uint64_t u;
void *value, size_t size,
int flags,
size_t *ret_size);
+int fgetxattrat_fake_malloc(
+ int dirfd,
+ const char *filename,
+ const char *attribute,
+ int flags,
+ char **value);
int fd_setcrtime(int fd, usec_t usec);
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if ((arg_unregister || arg_cat_config) && argc > optind)
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
return 1;
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
return 1;
static int acquire_esp(
bool unprivileged_mode,
+ bool graceful,
uint32_t *ret_part,
uint64_t *ret_pstart,
uint64_t *ret_psize,
* this). */
r = find_esp_and_warn(arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid);
- if (r == -ENOKEY)
+ if (r == -ENOKEY) {
+ if (graceful)
+ return log_info_errno(r, "Couldn't find EFI system partition, skipping.");
+
return log_error_errno(r,
"Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
"Alternatively, use --esp-path= to specify path to mount point.");
+ }
if (r < 0)
return r;
if (buf == MAP_FAILED)
return log_error_errno(errno, "Failed to memory map EFI binary: %m");
- s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
+ s = mempmem_safe(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
if (!s)
goto finish;
- s += 17;
- e = memmem(s, st.st_size - (s - buf), " ####", 5);
+ e = memmem_safe(s, st.st_size - (s - buf), " ####", 5);
if (!e || e - s < 3) {
r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed version string.");
goto finish;
if (r < 0)
return r;
if (r == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE),
"Source file \"%s\" does not carry version information!",
from);
if (r < 0)
return r;
if (r == 0 || compare_product(a, b) != 0)
- return log_notice_errno(SYNTHETIC_ERRNO(EEXIST),
+ return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE),
"Skipping \"%s\", since it's owned by another boot loader.",
to);
- if (compare_version(a, b) < 0)
- return log_warning_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since a newer boot loader version exists already.", to);
+ r = compare_version(a, b);
+ if (r < 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since newer boot loader version in place already.", to);
+ else if (r == 0)
+ return log_info_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since same boot loader version in place already.", to);
return 0;
}
continue;
k = copy_one_file(esp_path, de->d_name, force);
+ /* Don't propagate an error code if no update necessary, installed version already equal or
+ * newer version, or other boot loader in place. */
+ if (arg_graceful && IN_SET(k, -ESTALE, -EREMOTE))
+ continue;
if (k < 0 && r == 0)
r = k;
}
static int remove_machine_id_directory(const char *root) {
sd_id128_t machine_id;
- char buf[SD_ID128_STRING_MAX];
int r;
assert(root);
if (r < 0)
return log_error_errno(r, "Failed to get machine id: %m");
- return rmdir_one(root, sd_id128_to_string(machine_id, buf));
+ return rmdir_one(root, SD_ID128_TO_STRING(machine_id));
}
static int remove_binaries(const char *esp_path) {
_cleanup_(unlink_and_freep) char *t = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -1;
- sd_id128_t machine_id;
- char machine_string[SD_ID128_STRING_MAX];
const char *p;
int r;
fprintf(f, "#timeout 3\n"
"#console-mode keep\n");
+
if (arg_make_machine_id_directory) {
+ sd_id128_t machine_id;
+
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return log_error_errno(r, "Failed to get machine id: %m");
- fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
+ fprintf(f, "default %s-*\n", SD_ID128_TO_STRING(machine_id));
}
r = fflush_sync_and_check(f);
static int install_machine_id_directory(const char *root) {
sd_id128_t machine_id;
- char buf[SD_ID128_STRING_MAX];
int r;
assert(root);
if (r < 0)
return log_error_errno(r, "Failed to get machine id: %m");
- return mkdir_one(root, sd_id128_to_string(machine_id, buf));
+ return mkdir_one(root, SD_ID128_TO_STRING(machine_id));
}
static int help(int argc, char *argv[], void *userdata) {
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
return 1;
sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL;
int r, k;
- r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &esp_uuid);
+ r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid);
if (arg_print_esp_path) {
if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
* error the find_esp_and_warn() won't log on its own) */
puts(arg_esp_path);
}
- r = acquire_xbootldr(geteuid() != 0, &xbootldr_uuid);
+ r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid);
if (arg_print_dollar_boot_path) {
if (r == -EACCES)
return log_error_errno(r, "Failed to determine XBOOTLDR location: %m");
* off logging about access errors and turn off potentially privileged device probing. Here we're interested in
* the latter but not the former, hence request the mode, and log about EACCES. */
- r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, NULL);
+ r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, NULL);
if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
return log_error_errno(r, "Failed to determine ESP: %m");
if (r < 0)
return r;
- r = acquire_xbootldr(geteuid() != 0, NULL);
+ r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL);
if (r == -EACCES)
return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
if (r < 0)
sd_id128_t uuid = SD_ID128_NULL;
uint64_t pstart = 0, psize = 0;
uint32_t part = 0;
- bool install;
+ bool install, graceful;
int r;
- r = acquire_esp(false, &part, &pstart, &psize, &uuid);
+ install = streq(argv[0], "install");
+ graceful = !install && arg_graceful; /* support graceful mode for updates */
+
+ r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid);
+ if (graceful && r == -ENOKEY)
+ return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
if (r < 0)
return r;
- r = acquire_xbootldr(false, NULL);
+ r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL);
if (r < 0)
return r;
settle_make_machine_id_directory();
- install = streq(argv[0], "install");
-
RUN_WITH_UMASK(0002) {
if (install) {
/* Don't create any of these directories when we are just updating. When we update
sd_id128_t uuid = SD_ID128_NULL;
int r, q;
- r = acquire_esp(false, NULL, NULL, NULL, &uuid);
+ r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, &uuid);
if (r < 0)
return r;
- r = acquire_xbootldr(false, NULL);
+ r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL);
if (r < 0)
return r;
_cleanup_free_ char *p = NULL;
int r;
- r = acquire_esp(false, NULL, NULL, NULL, NULL);
+ r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL);
if (r < 0)
return r;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#ifndef NDEBUG
+
+#include <efi.h>
+#include <efilib.h>
+#include "util.h"
+
+void efi_assert(const char *expr, const char *file, unsigned line, const char *function) {
+ log_error_stall(L"systemd-boot assertion '%a' failed at %a:%u, function %a(). Halting.", expr, file, line, function);
+ for (;;)
+ uefi_call_wrapper(BS->Stall, 1, 60 * 1000 * 1000);
+}
+
+#endif
#include <efilib.h>
#include "console.h"
-#include "crc32.h"
#include "disk.h"
#include "efi-loader-features.h"
#include "graphics.h"
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
#endif
+#define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111)
+
/* magic string to find in the binary image */
-static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
+static const char _used_ _section_(".sdmagic") magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
enum loader_type {
LOADER_UNDEFINED,
} Config;
static VOID cursor_left(UINTN *cursor, UINTN *first) {
+ assert(cursor);
+ assert(first);
+
if ((*cursor) > 0)
(*cursor)--;
else if ((*first) > 0)
UINTN x_max,
UINTN len) {
+ assert(cursor);
+ assert(first);
+
if ((*cursor)+1 < x_max)
(*cursor)++;
else if ((*first) + (*cursor) < len)
UINTN size, len, first, cursor, clear;
BOOLEAN exit, enter;
+ assert(line_out);
+
if (!line_in)
line_in = L"";
size = StrLen(line_in) + 1024;
len = StrLen(line);
print = AllocatePool((x_max+1) * sizeof(CHAR16));
- uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
-
first = 0;
cursor = 0;
clear = 0;
EFI_STATUS err;
UINT64 key;
UINTN j;
+ UINTN cursor_color = TEXT_ATTR_SWAP(COLOR_EDIT);
- j = len - first;
- if (j >= x_max-1)
- j = x_max-1;
+ j = MIN(len - first, x_max);
CopyMem(print, line + first, j * sizeof(CHAR16));
- while (clear > 0 && j < x_max-1) {
+ while (clear > 0 && j < x_max) {
clear--;
print[j++] = ' ';
}
print[j] = '\0';
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+ /* See comment at edit_line() call site for why we start at 1. */
+ print_at(1, y_pos, COLOR_EDIT, print);
- err = console_key_read(&key, TRUE);
- if (EFI_ERROR(err))
- continue;
+ if (!print[cursor])
+ print[cursor] = ' ';
+ print[cursor+1] = '\0';
+ do {
+ print_at(cursor + 1, y_pos, cursor_color, print + cursor);
+ cursor_color = TEXT_ATTR_SWAP(cursor_color);
+
+ err = console_key_read(&key, 750 * 1000);
+ print_at(cursor + 1, y_pos, COLOR_EDIT, print + cursor);
+ } while (EFI_ERROR(err));
switch (key) {
case KEYPRESS(0, SCAN_ESC, 0):
cursor_right(&cursor, &first, x_max, len);
while (line[first + cursor] && line[first + cursor] != ' ')
cursor_right(&cursor, &first, x_max, len);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
continue;
case KEYPRESS(0, SCAN_UP, 0):
}
while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
cursor_left(&cursor, &first);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
continue;
case KEYPRESS(0, SCAN_RIGHT, 0):
if (first + cursor == len)
continue;
cursor_right(&cursor, &first, x_max, len);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
continue;
case KEYPRESS(0, SCAN_LEFT, 0):
case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
/* backward-char */
cursor_left(&cursor, &first);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
continue;
case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
cursor_left(&cursor, &first);
clear++;
}
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
for (UINTN i = first + cursor; i + clear < len; i++)
line[i] = line[i + clear];
}
}
- uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
return enter;
}
static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
+ assert(config);
+
if (key == 0)
return -1;
_cleanup_freepool_ CHAR16 *partstr = NULL, *defaultstr = NULL;
UINTN x, y;
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ assert(config);
+ assert(loaded_image_path);
+
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, COLOR_NORMAL);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
Print(L"systemd-boot version: " GIT_VERSION "\n");
Print(L"OsIndicationsSupported: %d\n", indvar);
Print(L"\n--- press key ---\n\n");
- console_key_read(&key, TRUE);
+ console_key_read(&key, 0);
Print(L"timeout: %u\n", config->timeout_sec);
if (config->timeout_sec_efivar >= 0)
Print(L"LoaderEntryDefault: %s\n", defaultstr);
Print(L"\n--- press key ---\n\n");
- console_key_read(&key, TRUE);
+ console_key_read(&key, 0);
for (UINTN i = 0; i < config->entry_count; i++) {
ConfigEntry *entry;
entry->path, entry->next_name);
Print(L"\n--- press key ---\n\n");
- console_key_read(&key, TRUE);
+ console_key_read(&key, 0);
}
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
ConfigEntry **chosen_entry,
CHAR16 *loaded_image_path) {
+ assert(config);
+ assert(chosen_entry);
+ assert(loaded_image_path);
+
EFI_STATUS err;
UINTN visible_max;
- UINTN idx_highlight;
- UINTN idx_highlight_prev;
+ UINTN idx_highlight = config->idx_default;
+ UINTN idx_highlight_prev = 0;
UINTN idx_first;
UINTN idx_last;
- BOOLEAN refresh;
- BOOLEAN highlight;
- UINTN line_width;
+ BOOLEAN refresh = TRUE;
+ BOOLEAN highlight = FALSE;
+ UINTN line_width = 0;
+ UINTN entry_padding = 3;
CHAR16 **lines;
UINTN x_start;
UINTN y_start;
UINTN x_max;
UINTN y_max;
- CHAR16 *status;
+ CHAR16 *status = NULL;
CHAR16 *clearline;
- INTN timeout_remain;
+ UINTN timeout_remain = config->timeout_sec;
INT16 idx;
BOOLEAN exit = FALSE;
BOOLEAN run = TRUE;
- BOOLEAN wait = FALSE;
graphics_mode(FALSE);
uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, COLOR_NORMAL);
/* draw a single character to make ClearScreen work on some firmware */
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*) L" ");
+ Print(L" ");
if (config->console_mode_change != CONSOLE_MODE_KEEP) {
err = console_set_mode(&config->console_mode, config->console_mode_change);
if (EFI_ERROR(err)) {
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
- Print(L"Error switching console mode to %ld: %r.\r", (UINT64)config->console_mode, err);
+ log_error_stall(L"Error switching console mode to %lu: %r", (UINT64)config->console_mode, err);
}
} else
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
y_max = 25;
}
- /* we check 10 times per second for a keystroke */
- if (config->timeout_sec > 0)
- timeout_remain = config->timeout_sec * 10;
- else
- timeout_remain = -1;
-
- idx_highlight = config->idx_default;
- idx_highlight_prev = 0;
-
visible_max = y_max - 2;
- if ((UINTN)config->idx_default >= visible_max)
- idx_first = config->idx_default-1;
- else
+ /* Drawing entries starts at idx_first until idx_last. We want to make
+ * sure that idx_highlight is centered, but not if we are close to the
+ * beginning/end of the entry list. Otherwise we would have a half-empty
+ * screen. */
+ if (config->entry_count <= visible_max || idx_highlight <= visible_max / 2)
idx_first = 0;
-
- idx_last = idx_first + visible_max-1;
-
- refresh = TRUE;
- highlight = FALSE;
+ else if (idx_highlight >= config->entry_count - (visible_max / 2))
+ idx_first = config->entry_count - visible_max;
+ else
+ idx_first = idx_highlight - (visible_max / 2);
+ idx_last = idx_first + visible_max - 1;
/* length of the longest entry */
- line_width = 5;
- for (UINTN i = 0; i < config->entry_count; i++) {
- UINTN entry_len;
-
- entry_len = StrLen(config->entries[i]->title_show);
- if (line_width < entry_len)
- line_width = entry_len;
- }
- if (line_width > x_max-6)
- line_width = x_max-6;
+ for (UINTN i = 0; i < config->entry_count; i++)
+ line_width = MAX(line_width, StrLen(config->entries[i]->title_show));
+ line_width = MIN(line_width + 2 * entry_padding, x_max);
/* offsets to center the entries on the screen */
x_start = (x_max - (line_width)) / 2;
for (UINTN i = 0; i < config->entry_count; i++) {
UINTN j;
- lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
- for (j = 0; j < x_start; j++)
+ lines[i] = AllocatePool(((line_width + 1) * sizeof(CHAR16)));
+ UINTN padding = (line_width - MIN(StrLen(config->entries[i]->title_show), line_width)) / 2;
+
+ for (j = 0; j < padding; j++)
lines[i][j] = ' ';
- for (UINTN k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
+ for (UINTN k = 0; config->entries[i]->title_show[k] != '\0' && j < line_width; j++, k++)
lines[i][j] = config->entries[i]->title_show[k];
- for (; j < x_max; j++)
+ for (; j < line_width; j++)
lines[i][j] = ' ';
- lines[i][x_max] = '\0';
+ lines[i][line_width] = '\0';
}
- status = NULL;
clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
for (UINTN i = 0; i < x_max; i++)
clearline[i] = ' ';
for (UINTN i = 0; i < config->entry_count; i++) {
if (i < idx_first || i > idx_last)
continue;
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
- if (i == idx_highlight)
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
- EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
- else
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
- EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
- if ((INTN)i == config->idx_default_efivar) {
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*) L"=>");
- }
+ print_at(x_start, y_start + i - idx_first,
+ (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
+ lines[i]);
+ if ((INTN)i == config->idx_default_efivar)
+ print_at(x_start, y_start + i - idx_first,
+ (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
+ (CHAR16*) L"=>");
}
refresh = FALSE;
} else if (highlight) {
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
- if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*) L"=>");
- }
-
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
- if ((INTN)idx_highlight == config->idx_default_efivar) {
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*) L"=>");
- }
+ print_at(x_start, y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, lines[idx_highlight_prev]);
+ print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, lines[idx_highlight]);
+ if ((INTN)idx_highlight_prev == config->idx_default_efivar)
+ print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) L"=>");
+ if ((INTN)idx_highlight == config->idx_default_efivar)
+ print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) L"=>");
highlight = FALSE;
}
if (timeout_remain > 0) {
FreePool(status);
- status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
+ status = PoolPrint(L"Boot in %d s.", timeout_remain);
}
/* print status at last line of screen */
x = (x_max - len) / 2;
else
x = 0;
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
+ print_at(0, y_max - 1, COLOR_NORMAL, clearline + (x_max - x));
uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
}
- err = console_key_read(&key, wait);
- if (EFI_ERROR(err)) {
- /* timeout reached */
+ err = console_key_read(&key, timeout_remain > 0 ? 1000 * 1000 : 0);
+ if (err == EFI_TIMEOUT) {
+ timeout_remain--;
if (timeout_remain == 0) {
exit = TRUE;
break;
}
- /* sleep and update status */
- if (timeout_remain > 0) {
- uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
- timeout_remain--;
- continue;
- }
-
- /* timeout disabled, wait for next key */
- wait = TRUE;
+ /* update status */
continue;
- }
-
- timeout_remain = -1;
+ } else
+ timeout_remain = 0;
/* clear status after keystroke */
if (status) {
FreePool(status);
status = NULL;
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+ print_at(0, y_max - 1, COLOR_NORMAL, clearline + 1);
}
idx_highlight_prev = idx_highlight;
switch (key) {
case KEYPRESS(0, SCAN_UP, 0):
case KEYPRESS(0, 0, 'k'):
+ case KEYPRESS(0, 0, 'K'):
if (idx_highlight > 0)
idx_highlight--;
break;
case KEYPRESS(0, SCAN_DOWN, 0):
case KEYPRESS(0, 0, 'j'):
+ case KEYPRESS(0, 0, 'J'):
if (idx_highlight < config->entry_count-1)
idx_highlight++;
break;
case KEYPRESS(0, SCAN_F1, 0):
case KEYPRESS(0, 0, 'h'):
+ case KEYPRESS(0, 0, 'H'):
case KEYPRESS(0, 0, '?'):
- status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
+ /* This must stay below 80 characters! Q/v/Ctrl+l deliberately not advertised. */
+ status = StrDuplicate(L"(d)efault (t/T)timeout (e)dit (p)rint (h)elp");
break;
case KEYPRESS(0, 0, 'Q'):
break;
case KEYPRESS(0, 0, 'd'):
+ case KEYPRESS(0, 0, 'D'):
if (config->idx_default_efivar != (INTN)idx_highlight) {
/* store the selected entry in a persistent EFI variable */
efivar_set(
config->timeout_sec_efivar,
EFI_VARIABLE_NON_VOLATILE);
if (config->timeout_sec_efivar > 0)
- status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
+ status = PoolPrint(L"Menu timeout set to %d s.", config->timeout_sec_efivar);
else
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
} else if (config->timeout_sec_efivar <= 0){
efivar_set(
LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
if (config->timeout_sec_config > 0)
- status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
+ status = PoolPrint(L"Menu timeout of %d s is defined by configuration file.",
config->timeout_sec_config);
else
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
config->timeout_sec_efivar,
EFI_VARIABLE_NON_VOLATILE);
if (config->timeout_sec_efivar > 0)
- status = PoolPrint(L"Menu timeout set to %d sec.",
+ status = PoolPrint(L"Menu timeout set to %d s.",
config->timeout_sec_efivar);
else
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
break;
case KEYPRESS(0, 0, 'e'):
+ case KEYPRESS(0, 0, 'E'):
/* only the options of configured entries can be edited */
if (!config->editor || config->entries[idx_highlight]->type == LOADER_UNDEFINED)
break;
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
- if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
- exit = TRUE;
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+ /* The edit line may end up on the last line of the screen. And even though we're
+ * not telling the firmware to advance the line, it still does in this one case,
+ * causing a scroll to happen that screws with our beautiful boot loader output.
+ * Since we cannot paint the last character of the edit line, we simply start
+ * at x-offset 1 for symmetry. */
+ print_at(1, y_max - 1, COLOR_EDIT, clearline + 2);
+ exit = line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-2, y_max-1);
+ print_at(1, y_max - 1, COLOR_NORMAL, clearline + 2);
break;
case KEYPRESS(0, 0, 'v'):
ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
break;
+ case KEYPRESS(0, 0, 'p'):
case KEYPRESS(0, 0, 'P'):
print_status(config, loaded_image_path);
refresh = TRUE;
FreePool(lines);
FreePool(clearline);
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, COLOR_NORMAL);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
return run;
}
static VOID config_add_entry(Config *config, ConfigEntry *entry) {
+ assert(config);
+ assert(entry);
+
if ((config->entry_count & 15) == 0) {
UINTN i;
CHAR8 *line, *value;
UINTN linelen;
+ assert(content);
+ assert(sep);
+ assert(pos);
+ assert(key_ret);
+ assert(value_ret);
+
skip:
line = content + *pos;
if (*line == '\0')
CHAR8 *line;
UINTN pos = 0;
CHAR8 *key, *value;
+ EFI_STATUS err;
+
+ assert(config);
+ assert(content);
while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
if (strcmpa((CHAR8 *)"timeout", key) == 0) {
}
if (strcmpa((CHAR8 *)"editor", key) == 0) {
- BOOLEAN on;
-
- if (EFI_ERROR(parse_boolean(value, &on)))
- continue;
-
- config->editor = on;
+ err = parse_boolean(value, &config->editor);
+ if (EFI_ERROR(err))
+ log_error_stall(L"Error parsing 'editor' config option: %a", value);
continue;
}
if (strcmpa((CHAR8 *)"auto-entries", key) == 0) {
- BOOLEAN on;
-
- if (EFI_ERROR(parse_boolean(value, &on)))
- continue;
-
- config->auto_entries = on;
+ err = parse_boolean(value, &config->auto_entries);
+ if (EFI_ERROR(err))
+ log_error_stall(L"Error parsing 'auto-entries' config option: %a", value);
continue;
}
if (strcmpa((CHAR8 *)"auto-firmware", key) == 0) {
- BOOLEAN on;
-
- if (EFI_ERROR(parse_boolean(value, &on)))
- continue;
-
- config->auto_firmware = on;
+ err = parse_boolean(value, &config->auto_firmware);
+ if (EFI_ERROR(err))
+ log_error_stall(L"Error parsing 'auto-firmware' config option: %a", value);
continue;
}
else {
BOOLEAN on;
- if (EFI_ERROR(parse_boolean(value, &on)))
+ err = parse_boolean(value, &on);
+ if (EFI_ERROR(err)) {
+ log_error_stall(L"Error parsing 'random-seed-mode' config option: %a", value);
continue;
+ }
config->random_seed_mode = on ? RANDOM_SEED_ALWAYS : RANDOM_SEED_OFF;
}
UINTN left = UINTN_MAX, done = UINTN_MAX, factor = 1, i, next_left, next_done;
_cleanup_freepool_ CHAR16 *prefix = NULL;
+ assert(entry);
+ assert(path);
+ assert(file);
+
/*
* Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
* filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
UINTN file_info_size, a, b;
EFI_STATUS r;
+ assert(entry);
+ assert(root_dir);
+
if (entry->tries_left == UINTN_MAX)
return;
break;
if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) {
- Print(L"\nFailed to get file info for '%s': %r\n", old_path, r);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ log_error_stall(L"Failed to get file info for '%s': %r", old_path, r);
return;
}
StrCpy(file_info->FileName, entry->next_name);
r = uefi_call_wrapper(handle->SetInfo, 4, handle, &EfiFileInfoGuid, file_info_size, file_info);
if (EFI_ERROR(r)) {
- Print(L"\nFailed to rename '%s' to '%s', ignoring: %r\n", old_path, entry->next_name, r);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ log_error_stall(L"Failed to rename '%s' to '%s', ignoring: %r", old_path, entry->next_name, r);
return;
}
EFI_FILE_HANDLE handle;
_cleanup_freepool_ CHAR16 *initrd = NULL;
+ assert(config);
+ assert(device);
+ assert(root_dir);
+ assert(path);
+ assert(file);
+ assert(content);
+ assert(loaded_image_path);
+
entry = AllocatePool(sizeof(ConfigEntry));
*entry = (ConfigEntry) {
UINTN sec;
EFI_STATUS err;
+ assert(root_dir);
+
*config = (Config) {
.editor = TRUE,
.auto_entries = TRUE,
.auto_firmware = TRUE,
.random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN,
+ .idx_default_efivar = -1,
};
err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
EFI_FILE_HANDLE entries_dir;
EFI_STATUS err;
+ assert(config);
+ assert(device);
+ assert(root_dir);
+ assert(loaded_image_path);
+
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, (CHAR16*) L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
if (!EFI_ERROR(err)) {
for (;;) {
static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
INTN r;
+ assert(a);
+ assert(b);
+
/* Order entries that have no tries left to the beginning of the list */
if (a->tries_left != 0 && b->tries_left == 0)
return 1;
}
static VOID config_sort_entries(Config *config) {
+ assert(config);
+
for (UINTN i = 1; i < config->entry_count; i++) {
BOOLEAN more;
}
static INTN config_entry_find(Config *config, CHAR16 *id) {
+ assert(config);
+ assert(id);
+
for (UINTN i = 0; i < config->entry_count; i++)
if (StrCmp(config->entries[i]->id, id) == 0)
return (INTN) i;
}
static VOID config_default_entry_select(Config *config) {
- _cleanup_freepool_ CHAR16 *entry_oneshot = NULL, *entry_default = NULL;
+ _cleanup_freepool_ CHAR16 *entry_default = NULL;
EFI_STATUS err;
INTN i;
+ assert(config);
+
/*
* The EFI variable to specify a boot entry for the next, and only the
* next reboot. The variable is always cleared directly after it is read.
*/
- err = efivar_get(LOADER_GUID, L"LoaderEntryOneShot", &entry_oneshot);
+ err = efivar_get(LOADER_GUID, L"LoaderEntryOneShot", &config->entry_oneshot);
if (!EFI_ERROR(err)) {
-
- config->entry_oneshot = StrDuplicate(entry_oneshot);
efivar_set(LOADER_GUID, L"LoaderEntryOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
- i = config_entry_find(config, entry_oneshot);
+ i = config_entry_find(config, config->entry_oneshot);
if (i >= 0) {
config->idx_default = i;
return;
/*
* The EFI variable to select the default boot entry overrides the
* configured pattern. The variable can be set and cleared by pressing
- * the 'd' key in the loader selection menu, the entry is marked with
- * an '*'.
+ * the 'd' key in the loader selection menu.
*/
err = efivar_get(LOADER_GUID, L"LoaderEntryDefault", &entry_default);
if (!EFI_ERROR(err)) {
-
i = config_entry_find(config, entry_default);
if (i >= 0) {
config->idx_default = i;
return;
}
}
- config->idx_default_efivar = -1;
if (config->entry_count == 0)
return;
if (config->entry_default_pattern) {
i = config->entry_count;
while (i--) {
- if (config->entries[i]->no_autoselect)
- continue;
if (MetaiMatch(config->entries[i]->id, config->entry_default_pattern)) {
config->idx_default = i;
return;
static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
BOOLEAN non_unique = FALSE;
+ assert(entries);
+
for (UINTN i = 0; i < entry_count; i++)
entries[i]->non_unique = FALSE;
/* generate a unique title, avoiding non-distinguishable menu entries */
static VOID config_title_generate(Config *config) {
+ assert(config);
+
/* set title */
for (UINTN i = 0; i < config->entry_count; i++) {
CHAR16 *title;
ConfigEntry *entry;
+ assert(config);
+ assert(id);
+ assert(title);
+ assert(call);
+
entry = AllocatePool(sizeof(ConfigEntry));
*entry = (ConfigEntry) {
.id = StrDuplicate(id),
ConfigEntry *entry;
+ assert(config);
+ assert(device);
+ assert(id);
+ assert(title);
+ assert(loader);
+
entry = AllocatePool(sizeof(ConfigEntry));
*entry = (ConfigEntry) {
.type = type,
.title = StrDuplicate(title),
- .version = StrDuplicate(version),
+ .version = version ? StrDuplicate(version) : NULL,
.device = device,
.loader = StrDuplicate(loader),
.id = StrDuplicate(id),
return entry;
}
+static BOOLEAN is_sd_boot(EFI_FILE *root_dir, const CHAR16 *loader_path) {
+ EFI_STATUS err;
+ const CHAR8 *sections[] = {
+ (CHAR8 *)".sdmagic",
+ NULL
+ };
+ UINTN offset = 0, size = 0, read;
+ _cleanup_freepool_ CHAR8 *content = NULL;
+
+ assert(root_dir);
+ assert(loader_path);
+
+ err = pe_file_locate_sections(root_dir, loader_path, sections, &offset, &size);
+ if (EFI_ERROR(err) || size != sizeof(magic))
+ return FALSE;
+
+ err = file_read(root_dir, loader_path, offset, size, &content, &read);
+ if (EFI_ERROR(err) || size != read)
+ return FALSE;
+
+ return CompareMem(content, magic, sizeof(magic)) == 0;
+}
+
static BOOLEAN config_entry_add_loader_auto(
Config *config,
EFI_HANDLE *device,
ConfigEntry *entry;
EFI_STATUS err;
+ assert(config);
+ assert(device);
+ assert(root_dir);
+ assert(id);
+ assert(title);
+ assert(loader || loaded_image_path);
+
if (!config->auto_entries)
return FALSE;
- /* do not add an entry for ourselves */
if (loaded_image_path) {
- UINTN len;
- _cleanup_freepool_ CHAR8 *content = NULL;
-
- if (StriCmp(loader, loaded_image_path) == 0)
+ loader = L"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi";
+
+ /* We are trying to add the default EFI loader here,
+ * but we do not want to do that if that would be us.
+ *
+ * If the default loader is not us, it might be shim. It would
+ * chainload GRUBX64.EFI in that case, which might be us.*/
+ if (StriCmp(loader, loaded_image_path) == 0 ||
+ is_sd_boot(root_dir, loader) ||
+ is_sd_boot(root_dir, L"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L".EFI"))
return FALSE;
-
- /* look for systemd-boot magic string */
- err = file_read(root_dir, loader, 0, 100*1024, &content, &len);
- if (!EFI_ERROR(err))
- for (CHAR8 *start = content; start <= content + len - sizeof(magic) - 1; start++)
- if (start[0] == magic[0] && CompareMem(start, magic, sizeof(magic) - 1) == 0)
- return FALSE;
}
/* check existence */
UINTN handle_count = 0;
_cleanup_freepool_ EFI_HANDLE *handles = NULL;
+ assert(config);
+
if (!config->auto_entries)
return;
}
}
+static VOID config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir) {
+ _cleanup_freepool_ CHAR8 *bcd = NULL;
+ const CHAR16 *title = NULL;
+ EFI_STATUS err;
+ UINTN len;
+
+ assert(config);
+ assert(device);
+ assert(root_dir);
+
+ if (!config->auto_entries)
+ return;
+
+ /* Try to find a better title. */
+ err = file_read(root_dir, L"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd, &len);
+ if (!EFI_ERROR(err)) {
+ static const CHAR16 *versions[] = {
+ L"Windows 11",
+ L"Windows 10",
+ L"Windows 8.1",
+ L"Windows 8",
+ L"Windows 7",
+ L"Windows Vista",
+ };
+
+ CHAR8 *p = bcd;
+ while (!title) {
+ CHAR8 *q = mempmem_safe(p, len, versions[0], STRLEN(L"Windows "));
+ if (!q)
+ break;
+
+ len -= q - p;
+ p = q;
+
+ /* We found the prefix, now try all the version strings. */
+ for (UINTN i = 0; i < ELEMENTSOF(versions); i++) {
+ if (memory_startswith(p, len, versions[i] + STRLEN("Windows "))) {
+ title = versions[i];
+ break;
+ }
+ }
+ }
+ }
+
+ config_entry_add_loader_auto(config, device, root_dir, NULL,
+ L"auto-windows", 'w', title ?: L"Windows Boot Manager",
+ L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
+}
+
static VOID config_entry_add_linux(
Config *config,
EFI_HANDLE *device,
EFI_STATUS err;
ConfigEntry *entry;
+ assert(config);
+ assert(device);
+ assert(root_dir);
+
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, (CHAR16*) L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return;
CHAR16 buf[256];
UINTN bufsize = sizeof buf;
EFI_FILE_INFO *f;
- CHAR8 *sections[] = {
+ const CHAR8 *sections[] = {
(CHAR8 *)".osrel",
(CHAR8 *)".cmdline",
NULL
};
UINTN offs[ELEMENTSOF(sections)-1] = {};
UINTN szs[ELEMENTSOF(sections)-1] = {};
- UINTN addrs[ELEMENTSOF(sections)-1] = {};
CHAR8 *content = NULL;
CHAR8 *line;
UINTN pos = 0;
continue;
/* look for .osrel and .cmdline sections in the .efi binary */
- err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
+ err = pe_file_locate_sections(linux_dir, f->FileName, sections, offs, szs);
if (EFI_ERROR(err))
continue;
EFI_DEVICE_PATH *parent;
UINTN len;
+ assert(path);
+ assert(node);
+
len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH));
CopyMem(parent, path, len);
EFI_FILE *root_dir;
EFI_STATUS r;
+ assert(config);
+ assert(device);
+
partition_path = DevicePathFromHandle(device);
if (!partition_path)
return;
EFI_PARTITION_TABLE_HEADER gpt_header;
uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512];
} gpt_header_buffer;
- const EFI_PARTITION_TABLE_HEADER *h = &gpt_header_buffer.gpt_header;
+ EFI_PARTITION_TABLE_HEADER *h = &gpt_header_buffer.gpt_header;
UINT64 where;
UINTN sz;
- UINT32 c;
+ UINT32 crc32, crc32_saved;
if (nr == 0)
/* Read the first copy at LBA 1 */
continue;
/* Some superficial validation of the GPT header */
- c = CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature));
- if (c != 0)
+ if(CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature) != 0))
continue;
if (h->Header.HeaderSize < 92 ||
continue;
/* Calculate CRC check */
- c = ~crc32_exclude_offset(UINT32_MAX,
- (const UINT8*) &gpt_header_buffer,
- h->Header.HeaderSize,
- OFFSETOF(EFI_PARTITION_TABLE_HEADER, Header.CRC32),
- sizeof(h->Header.CRC32));
- if (c != h->Header.CRC32)
+ crc32_saved = h->Header.CRC32;
+ h->Header.CRC32 = 0;
+ r = BS->CalculateCrc32(&gpt_header_buffer, h->Header.HeaderSize, &crc32);
+ h->Header.CRC32 = crc32_saved;
+ if (EFI_ERROR(r) || crc32 != crc32_saved)
continue;
if (h->MyLBA != where)
continue;
/* Calculate CRC of entries array, too */
- c = ~crc32(UINT32_MAX, entries, sz);
- if (c != h->PartitionEntryArrayCRC32)
+ r = BS->CalculateCrc32(&entries, sz, &crc32);
+ if (EFI_ERROR(r) || crc32 != h->PartitionEntryArrayCRC32)
continue;
for (UINTN i = 0; i < h->NumberOfPartitionEntries; i++) {
CHAR16 *options;
EFI_STATUS err;
+ assert(config);
+ assert(entry);
+
path = FileDevicePath(entry->device, entry->loader);
- if (!path) {
- Print(L"Error getting device path.");
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return EFI_INVALID_PARAMETER;
- }
+ if (!path)
+ return log_error_status_stall(EFI_INVALID_PARAMETER, L"Error getting device path.");
err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
- if (EFI_ERROR(err)) {
- Print(L"Error loading %s: %r", entry->loader, err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return err;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
if (config->options_edit)
options = config->options_edit;
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) {
- Print(L"Error getting LoadedImageProtocol handle: %r", err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ log_error_stall(L"Error getting LoadedImageProtocol handle: %r", err);
goto out_unload;
}
loaded_image->LoadOptions = options;
- loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
+ loaded_image->LoadOptionsSize = StrSize(loaded_image->LoadOptions);
#if ENABLE_TPM
/* Try to log any options to the TPM, especially to catch manually edited options */
err = tpm_log_event(SD_TPM_PCR,
(EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
- if (EFI_ERROR(err)) {
- Print(L"Unable to add image options measurement: %r", err);
- uefi_call_wrapper(BS->Stall, 1, 200 * 1000);
- }
+ if (EFI_ERROR(err))
+ log_error_stall(L"Unable to add image options measurement: %r", err);
#endif
}
return err;
err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
- Print(L"Error calling ResetSystem: %r", err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return err;
+ return log_error_status_stall(err, L"Error calling ResetSystem: %r", err);
}
static VOID config_free(Config *config) {
+ assert(config);
for (UINTN i = 0; i < config->entry_count; i++)
config_entry_free(config->entries[i]);
FreePool(config->entries);
}
static VOID config_write_entries_to_variable(Config *config) {
- _cleanup_freepool_ CHAR16 *buffer = NULL;
+ _cleanup_freepool_ CHAR8 *buffer = NULL;
UINTN sz = 0;
- CHAR16 *p;
+ CHAR8 *p;
+
+ assert(config);
for (UINTN i = 0; i < config->entry_count; i++)
- sz += StrLen(config->entries[i]->id) + 1;
+ sz += StrSize(config->entries[i]->id);
- p = buffer = AllocatePool(sz * sizeof(CHAR16));
+ p = buffer = AllocatePool(sz);
for (UINTN i = 0; i < config->entry_count; i++) {
UINTN l;
- l = StrLen(config->entries[i]->id) + 1;
- CopyMem(p, config->entries[i]->id, l * sizeof(CHAR16));
+ l = StrSize(config->entries[i]->id);
+ CopyMem(p, config->entries[i]->id, l);
p += l;
}
+ assert(p == buffer + sz);
+
/* Store the full list of discovered entries. */
- (void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, (UINT8 *) p - (UINT8 *) buffer, 0);
+ (void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, sz, 0);
}
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (EFI_ERROR(err)) {
- Print(L"Error getting a LoadedImageProtocol handle: %r", err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return err;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
/* export the device path this image is started from */
if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
root_dir = LibOpenRoot(loaded_image->DeviceHandle);
- if (!root_dir) {
- Print(L"Unable to open root directory.");
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return EFI_LOAD_ERROR;
- }
+ if (!root_dir)
+ return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory.", EFI_LOAD_ERROR);
if (secure_boot_enabled() && shim_loaded()) {
err = security_policy_install();
- if (EFI_ERROR(err)) {
- Print(L"Error installing security policy: %r ", err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return err;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error installing security policy: %r", err);
}
/* the filesystem path to this image, to prevent adding ourselves to the menu */
config_sort_entries(&config);
/* if we find some well-known loaders, add them to the end of the list */
- config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL,
- L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
+ config_entry_add_osx(&config);
+ config_entry_add_windows(&config, loaded_image->DeviceHandle, root_dir);
config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL,
L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
- L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi");
- config_entry_add_osx(&config);
+ L"auto-efi-default", '\0', L"EFI Default Loader", NULL);
if (config.auto_firmware && efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind) == EFI_SUCCESS) {
if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
}
if (config.entry_count == 0) {
- Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ log_error_stall(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
goto out;
}
else {
UINT64 key;
- err = console_key_read(&key, FALSE);
-
- if (err == EFI_NOT_READY) {
- uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
- err = console_key_read(&key, FALSE);
- }
-
+ /* Block up to 100ms to give firmware time to get input working. */
+ err = console_key_read(&key, 100 * 1000);
if (!EFI_ERROR(err)) {
INT16 idx;
err = image_start(image, &config, entry);
if (EFI_ERROR(err)) {
graphics_mode(FALSE);
- Print(L"\nFailed to execute %s (%s): %r\n", entry->title, entry->loader, err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ log_error_stall(L"Failed to execute %s (%s): %r", entry->title, entry->loader, err);
goto out;
}
#define EFI_SIMPLE_TEXT_INPUT_EX_GUID &(EFI_GUID) EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID
-EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) {
+static inline void EventClosep(EFI_EVENT *event) {
+ if (!*event)
+ return;
+
+ uefi_call_wrapper(BS->CloseEvent, 1, *event);
+}
+
+/*
+ * Reading input from the console sounds like an easy task to do, but thanks to broken
+ * firmware it is actually a nightmare.
+ *
+ * There is a ConIn and TextInputEx API for this. Ideally we want to use TextInputEx,
+ * because that gives us Ctrl/Alt/Shift key state information. Unfortunately, it is not
+ * always available and sometimes just non-functional.
+ *
+ * On the other hand we have ConIn, where some firmware likes to just freeze on us
+ * if we call ReadKeyStroke on it.
+ *
+ * Therefore, we use WaitForEvent on both ConIn and TextInputEx (if available) along
+ * with a timer event. The timer ensures there is no need to call into functions
+ * that might freeze on us, while still allowing us to show a timeout counter.
+ */
+EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec) {
static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx;
static BOOLEAN checked;
UINTN index;
EFI_INPUT_KEY k;
EFI_STATUS err;
+ _cleanup_(EventClosep) EFI_EVENT timer = NULL;
+ EFI_EVENT events[3] = { ST->ConIn->WaitForKey };
+ UINTN n_events = 1;
+
+ assert(key);
if (!checked) {
err = LibLocateProtocol(EFI_SIMPLE_TEXT_INPUT_EX_GUID, (VOID **)&TextInputEx);
- if (EFI_ERROR(err))
+ if (EFI_ERROR(err) ||
+ uefi_call_wrapper(BS->CheckEvent, 1, TextInputEx->WaitForKeyEx) == EFI_INVALID_PARAMETER)
+ /* If WaitForKeyEx fails here, the firmware pretends it talks this
+ * protocol, but it really doesn't. */
TextInputEx = NULL;
+ else
+ events[n_events++] = TextInputEx->WaitForKeyEx;
checked = TRUE;
}
- /* wait until key is pressed */
- if (wait)
- uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
+ if (timeout_usec > 0) {
+ err = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL, &timer);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error creating timer event: %r", err);
+
+ /* SetTimer expects 100ns units for some reason. */
+ err = uefi_call_wrapper(BS->SetTimer, 3, timer, TimerRelative, timeout_usec * 10);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error arming timer event: %r", err);
+
+ events[n_events++] = timer;
+ }
+
+ err = uefi_call_wrapper(BS->WaitForEvent, 3, n_events, events, &index);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error waiting for events: %r", err);
+
+ if (timeout_usec > 0 && timer == events[index])
+ return EFI_TIMEOUT;
- if (TextInputEx) {
+ /* TextInputEx might be ready too even if ConIn got to signal first. */
+ if (TextInputEx && !EFI_ERROR(uefi_call_wrapper(BS->CheckEvent, 1, TextInputEx->WaitForKeyEx))) {
EFI_KEY_DATA keydata;
UINT64 keypress;
+ UINT32 shift = 0;
err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
- if (!EFI_ERROR(err)) {
- UINT32 shift = 0;
-
- /* do not distinguish between left and right keys */
- if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
- if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
- shift |= EFI_CONTROL_PRESSED;
- if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
- shift |= EFI_ALT_PRESSED;
- };
-
- /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
- keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
- if (keypress > 0) {
- *key = keypress;
- return 0;
- }
+ if (EFI_ERROR(err))
+ return err;
+
+ /* do not distinguish between left and right keys */
+ if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
+ if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
+ shift |= EFI_CONTROL_PRESSED;
+ if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
+ shift |= EFI_ALT_PRESSED;
+ };
+
+ /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
+ keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
+ if (keypress > 0) {
+ *key = keypress;
+ return EFI_SUCCESS;
}
+
+ return EFI_NOT_READY;
}
- /* fallback for firmware which does not support SimpleTextInputExProtocol
- *
- * This is also called in case ReadKeyStrokeEx did not return a key, because
- * some broken firmwares offer SimpleTextInputExProtocol, but never actually
- * handle any key. */
err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
if (EFI_ERROR(err))
return err;
*key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
- return 0;
+ return EFI_SUCCESS;
}
static EFI_STATUS change_mode(UINTN mode) {
EFI_STATUS err;
BOOLEAN keep = FALSE;
+ assert(mode);
+
err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
Info = GraphicsOutput->Mode->Info;
}
EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how) {
+ assert(mode);
+
if (how == CONSOLE_MODE_AUTO)
return mode_auto(mode);
CONSOLE_MODE_MAX,
};
-EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait);
+EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec);
EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how);
+++ /dev/null
-/* SPDX-License-Identifier: LicenseRef-crc32-no-restriction */
-/* This is copied from util-linux, which in turn copied in the version from Gary S. Brown */
-
-/*
- * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
- * code or tables extracted from it, as desired without restriction.
- *
- * First, the polynomial itself and its table of feedback terms. The
- * polynomial is
- * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- * Note that we take it "backwards" and put the highest-order term in
- * the lowest-order bit. The X^32 term is "implied"; the LSB is the
- * X^31 term, etc. The X^0 term (usually shown as "+1") results in
- * the MSB being 1.
- *
- * Note that the usual hardware shift register implementation, which
- * is what we're using (we're merely optimizing it by doing eight-bit
- * chunks at a time) shifts bits into the lowest-order term. In our
- * implementation, that means shifting towards the right. Why do we
- * do it this way? Because the calculated CRC must be transmitted in
- * order from highest-order term to lowest-order term. UARTs transmit
- * characters in order from LSB to MSB. By storing the CRC this way,
- * we hand it to the UART in the order low-byte to high-byte; the UART
- * sends each low-bit to high-bit; and the result is transmission bit
- * by bit from highest- to lowest-order term without requiring any bit
- * shuffling on our part. Reception works similarly.
- *
- * The feedback terms table consists of 256, 32-bit entries. Notes
- *
- * The table can be generated at runtime if desired; code to do so
- * is shown later. It might not be obvious, but the feedback
- * terms simply represent the results of eight shift/xor opera-
- * tions for all combinations of data and CRC register values.
- *
- * The values must be right-shifted by eight bits by the "updcrc"
- * logic; the shift must be unsigned (bring in zeroes). On some
- * hardware you could probably optimize the shift in assembler by
- * using byte-swap instructions.
- * polynomial $edb88320
- *
- */
-
-#include "crc32.h"
-
-static const UINT32 crc32_tab[] = {
- 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
- 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
- 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
- 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
- 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
- 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
- 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
- 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
- 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
- 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
- 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
- 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
- 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
- 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
- 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
- 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
- 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
- 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
- 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
- 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
- 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
- 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
- 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
- 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
- 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
- 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
- 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
- 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
- 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
- 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
- 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
- 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
- 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
- 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
- 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
- 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
- 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
- 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
- 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
- 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
- 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
- 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
- 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
- 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
- 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
- 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
- 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
- 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
- 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
- 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
- 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
- 0x2d02ef8dL
-};
-
-static inline UINT32 crc32_add_char(UINT32 crc, UINT8 c) {
- return crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8);
-}
-
-/*
- * This a generic crc32() function, it takes seed as an argument,
- * and does __not__ xor at the end. Then individual users can do
- * whatever they need.
- */
-UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len) {
- const UINT8 *p = buf;
- UINT32 crc = seed;
-
- while (len > 0) {
- crc = crc32_add_char(crc, *p++);
- len--;
- }
-
- return crc;
-}
-
-UINT32 crc32_exclude_offset(
- UINT32 seed,
- const VOID *buf,
- UINTN len,
- UINTN exclude_off,
- UINTN exclude_len) {
-
- const UINT8 *p = buf;
- UINT32 crc = seed;
-
- for (UINTN i = 0; i < len; i++) {
- UINT8 x = *p++;
-
- if (i >= exclude_off && i < exclude_off + exclude_len)
- x = 0;
-
- crc = crc32_add_char(crc, x);
- }
-
- return crc;
-}
+++ /dev/null
-/* SPDX-License-Identifier: LicenseRef-crc32-no-restriction */
-#pragma once
-
-#include <efi.h>
-#include <efilib.h>
-
-UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len);
-UINT32 crc32_exclude_offset(UINT32 seed, const VOID *buf, UINTN len, UINTN exclude_off, UINTN exclude_len);
EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[static 37]) {
EFI_DEVICE_PATH *device_path;
+ assert(handle);
+
/* export the device path this image is started from */
device_path = DevicePathFromHandle(handle);
if (device_path) {
handover_f handover;
UINTN start = (UINTN)params->hdr.code32_start;
+ assert(params);
+
#ifdef __x86_64__
asm volatile ("cli");
start += 512;
handover(image, ST, params);
}
-EFI_STATUS linux_exec(EFI_HANDLE *image,
+EFI_STATUS linux_exec(EFI_HANDLE image,
CHAR8 *cmdline, UINTN cmdline_len,
UINTN linux_addr,
UINTN initrd_addr, UINTN initrd_size) {
EFI_PHYSICAL_ADDRESS addr;
EFI_STATUS err;
+ assert(image);
+ assert(cmdline);
+
image_params = (struct boot_params *) linux_addr;
if (image_params->hdr.boot_flag != 0xAA55 ||
#pragma once
#include <efi.h>
+#include "macro-fundamental.h"
#define SETUP_MAGIC 0x53726448 /* "HdrS" */
UINT64 pref_address;
UINT32 init_size;
UINT32 handover_offset;
-} __attribute__((packed));
+} _packed_;
/* adapted from linux' bootparam.h */
struct boot_params {
UINT8 _pad8[48];
UINT8 eddbuf[6*82]; // was: struct edd_info eddbuf[EDDMAXNR]
UINT8 _pad9[276];
-} __attribute__((packed));
+} _packed_;
-EFI_STATUS linux_exec(EFI_HANDLE *image,
+EFI_STATUS linux_exec(EFI_HANDLE image,
CHAR8 *cmdline, UINTN cmdline_size,
UINTN linux_addr,
UINTN initrd_addr, UINTN initrd_size);
#include <efi.h>
#include <efilib.h>
+#include "macro-fundamental.h"
#include "measure.h"
#define EFI_TCG_GUID \
UINT16 HeaderVersion;
UINT32 PCRIndex;
UINT32 EventType;
-} __attribute__((packed)) EFI_TCG2_EVENT_HEADER;
+} _packed_ EFI_TCG2_EVENT_HEADER;
typedef struct tdEFI_TCG2_EVENT {
UINT32 Size;
EFI_TCG2_EVENT_HEADER Header;
UINT8 Event[1];
-} __attribute__((packed)) EFI_TCG2_EVENT;
+} _packed_ EFI_TCG2_EVENT;
typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_CAPABILITY) (IN EFI_TCG2_PROTOCOL * This,
IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY * ProtocolCapability);
EFI_PHYSICAL_ADDRESS event_log_last;
UINTN desc_len;
- desc_len = (StrLen(description) + 1) * sizeof(CHAR16);
+ assert(tcg);
+ assert(description);
+ desc_len = StrSize(description);
tcg_event = AllocateZeroPool(desc_len + sizeof(TCG_PCR_EVENT));
if (!tcg_event)
EFI_TCG2_EVENT *tcg_event;
UINTN desc_len;
- desc_len = StrLen(description) * sizeof(CHAR16);
+ assert(tcg);
+ assert(description);
- tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1);
+ desc_len = StrSize(description);
+ tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len);
if (!tcg_event)
return EFI_OUT_OF_RESOURCES;
- tcg_event->Size = sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1;
+ tcg_event->Size = sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len;
tcg_event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER);
tcg_event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION;
tcg_event->Header.PCRIndex = pcrindex;
EFI_TCG *tpm1;
EFI_TCG2 *tpm2;
+ assert(description);
+
tpm2 = tcg2_interface_check();
if (tpm2)
return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
efi_headers = files('''
console.h
- crc32.h
disk.h
graphics.h
linux.h
'''.split())
common_sources = '''
+ assert.c
disk.c
graphics.c
measure.c
systemd_boot_sources = '''
boot.c
console.c
- crc32.c
random-seed.c
sha256.c
shim.c
efi_libdir = get_option('efi-libdir')
if efi_libdir == ''
# New location first introduced with gnu-efi 3.0.11
- efi_libdir = join_paths('/usr/lib/gnuefi', EFI_MACHINE_TYPE_NAME)
+ efi_libdir = '/usr/lib/gnuefi' / EFI_MACHINE_TYPE_NAME
cmd = run_command(test, '-e', efi_libdir)
if cmd.returncode() != 0
# Fall back to the old approach
cmd = run_command(efi_cc + ['-print-multi-os-directory'])
if cmd.returncode() == 0
- path = join_paths('/usr/lib', cmd.stdout().strip())
+ path = '/usr/lib' / cmd.stdout().strip()
cmd = run_command(env, 'realpath', '-e', path)
if cmd.returncode() == 0
efi_libdir = cmd.stdout().strip()
efi_conf.set10('ENABLE_TPM', get_option('tpm'))
efi_conf.set('SD_TPM_PCR', get_option('tpm-pcrindex'))
+ foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit']
+ c = get_option('efi-' + ctype).split(',')
+ fg = 'EFI_' + c[0].strip().underscorify().to_upper()
+ bg = 'EFI_BACKGROUND_' + c[1].strip().underscorify().to_upper()
+ efi_conf.set(ctype.underscorify().to_upper(), '(' + fg + '|' + bg + ')')
+ endforeach
+
if get_option('sbat-distro') != ''
efi_conf.set_quoted('SBAT_PROJECT', meson.project_name())
efi_conf.set_quoted('PROJECT_VERSION', meson.project_version())
efi_location_map = [
# New locations first introduced with gnu-efi 3.0.11
- [join_paths(efi_libdir, 'efi.lds'),
- join_paths(efi_libdir, 'crt0.o')],
+ [efi_libdir / 'efi.lds',
+ efi_libdir / 'crt0.o'],
# Older locations...
- [join_paths(efi_libdir, 'gnuefi', 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)),
- join_paths(efi_libdir, 'gnuefi', 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))],
- [join_paths(efi_libdir, 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)),
- join_paths(efi_libdir, 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))]]
+ [efi_libdir / 'gnuefi' / 'elf_@0@_efi.lds'.format(gnu_efi_path_arch),
+ efi_libdir / 'gnuefi' / 'crt0-efi-@0@.o'.format(gnu_efi_path_arch)],
+ [efi_libdir / 'elf_@0@_efi.lds'.format(gnu_efi_path_arch),
+ efi_libdir / 'crt0-efi-@0@.o'.format(gnu_efi_path_arch)]]
efi_lds = ''
foreach location : efi_location_map
if efi_lds == ''
'-nostdlib',
'-std=gnu99',
'-isystem', efi_incdir,
- '-isystem', join_paths(efi_incdir, gnu_efi_path_arch),
+ '-isystem', efi_incdir / gnu_efi_path_arch,
'-I', fundamental_path,
'-DSD_BOOT',
'-include', efi_config_h,
compile_args += ['-Werror']
endif
if get_option('buildtype') == 'debug'
- compile_args += ['-ggdb', '-O0']
+ compile_args += ['-ggdb', '-O0', '-DEFI_DEBUG']
elif get_option('buildtype') == 'debugoptimized'
- compile_args += ['-ggdb', '-Og']
+ compile_args += ['-ggdb', '-Og', '-DEFI_DEBUG']
else
compile_args += ['-O2']
endif
+ if get_option('b_ndebug') == 'true' or (
+ get_option('b_ndebug') == 'if-release' and ['plain', 'release'].contains(get_option('buildtype')))
+ compile_args += ['-DNDEBUG']
+ endif
efi_ldflags = ['-T', efi_lds,
'-shared',
'-j', '.text',
'-j', '.sdata',
'-j', '.sbat',
+ '-j', '.sdmagic',
'-j', '.data',
'-j', '.dynamic',
'-j', '.dynsym',
} EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
#endif
+
+#ifndef EFI_IMAGE_MACHINE_RISCV64
+ #define EFI_IMAGE_MACHINE_RISCV64 0x5064
+#endif
#include "pe.h"
#include "util.h"
+#define DOS_FILE_MAGIC "MZ"
+#define PE_FILE_MAGIC "PE\0\0"
+#define MAX_SECTIONS 96
+
+#if defined(__i386__)
+ #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
+#elif defined(__x86_64__)
+ #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
+#elif defined(__aarch64__)
+ #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
+#elif defined(__arm__)
+ #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
+#elif defined(__riscv) && __riscv_xlen == 64
+ #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
+#else
+ #error Unknown EFI arch
+#endif
+
struct DosFileHeader {
UINT8 Magic[2];
UINT16 LastSize;
UINT16 OEMInfo;
UINT16 reserved2[10];
UINT32 ExeHeader;
-} __attribute__((packed));
+} _packed_;
-#define PE_HEADER_MACHINE_I386 0x014c
-#define PE_HEADER_MACHINE_X64 0x8664
-#define PE_HEADER_MACHINE_ARM64 0xaa64
-#define PE_HEADER_MACHINE_ARM 0x01c2
-#define PE_HEADER_MACHINE_RISCV64 0x5064
-struct PeFileHeader {
+struct CoffFileHeader {
UINT16 Machine;
UINT16 NumberOfSections;
UINT32 TimeDateStamp;
UINT32 NumberOfSymbols;
UINT16 SizeOfOptionalHeader;
UINT16 Characteristics;
-} __attribute__((packed));
+} _packed_;
-struct PeHeader {
+struct PeFileHeader {
UINT8 Magic[4];
- struct PeFileHeader FileHeader;
-} __attribute__((packed));
+ struct CoffFileHeader FileHeader;
+ /* OptionalHeader omitted */
+} _packed_;
struct PeSectionHeader {
UINT8 Name[8];
UINT16 NumberOfRelocations;
UINT16 NumberOfLinenumbers;
UINT32 Characteristics;
-} __attribute__((packed));
-
-EFI_STATUS pe_memory_locate_sections(CHAR8 *base, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
- struct DosFileHeader *dos;
- struct PeHeader *pe;
- UINTN offset;
+} _packed_;
- dos = (struct DosFileHeader *)base;
-
- if (CompareMem(dos->Magic, "MZ", 2) != 0)
- return EFI_LOAD_ERROR;
+static inline BOOLEAN verify_dos(const struct DosFileHeader *dos) {
+ assert(dos);
+ return CompareMem(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
+}
- pe = (struct PeHeader *)&base[dos->ExeHeader];
- if (CompareMem(pe->Magic, "PE\0\0", 4) != 0)
- return EFI_LOAD_ERROR;
+static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) {
+ assert(pe);
+ return CompareMem(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
+ pe->FileHeader.Machine == TARGET_MACHINE_TYPE &&
+ pe->FileHeader.NumberOfSections > 0 &&
+ pe->FileHeader.NumberOfSections <= MAX_SECTIONS;
+}
- /* PE32+ Subsystem type */
- if (pe->FileHeader.Machine != PE_HEADER_MACHINE_X64 &&
- pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM64 &&
- pe->FileHeader.Machine != PE_HEADER_MACHINE_I386 &&
- pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM &&
- pe->FileHeader.Machine != PE_HEADER_MACHINE_RISCV64)
- return EFI_LOAD_ERROR;
+static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
+ assert(dos);
+ assert(pe);
+ return dos->ExeHeader + sizeof(struct PeFileHeader) + pe->FileHeader.SizeOfOptionalHeader;
+}
- if (pe->FileHeader.NumberOfSections > 96)
- return EFI_LOAD_ERROR;
+static VOID locate_sections(
+ const struct PeSectionHeader section_table[],
+ UINTN n_table,
+ const CHAR8 **sections,
+ UINTN *addrs,
+ UINTN *offsets,
+ UINTN *sizes) {
- offset = dos->ExeHeader + sizeof(*pe) + pe->FileHeader.SizeOfOptionalHeader;
+ assert(section_table);
+ assert(sections);
+ assert(sizes);
- for (UINTN i = 0; i < pe->FileHeader.NumberOfSections; i++) {
- struct PeSectionHeader *sect;
+ for (UINTN i = 0; i < n_table; i++) {
+ const struct PeSectionHeader *sect = section_table + i;
- sect = (struct PeSectionHeader *)&base[offset];
for (UINTN j = 0; sections[j]; j++) {
if (CompareMem(sect->Name, sections[j], strlena(sections[j])) != 0)
continue;
if (addrs)
- addrs[j] = (UINTN)sect->VirtualAddress;
+ addrs[j] = sect->VirtualAddress;
if (offsets)
- offsets[j] = (UINTN)sect->PointerToRawData;
- if (sizes)
- sizes[j] = (UINTN)sect->VirtualSize;
+ offsets[j] = sect->PointerToRawData;
+ sizes[j] = sect->VirtualSize;
}
- offset += sizeof(*sect);
}
+}
+
+EFI_STATUS pe_memory_locate_sections(
+ const CHAR8 *base,
+ const CHAR8 **sections,
+ UINTN *addrs,
+ UINTN *sizes) {
+ const struct DosFileHeader *dos;
+ const struct PeFileHeader *pe;
+ UINTN offset;
+
+ assert(base);
+ assert(sections);
+ assert(addrs);
+ assert(sizes);
+
+ dos = (const struct DosFileHeader*)base;
+ if (!verify_dos(dos))
+ return EFI_LOAD_ERROR;
+
+ pe = (const struct PeFileHeader*)&base[dos->ExeHeader];
+ if (!verify_pe(pe))
+ return EFI_LOAD_ERROR;
+
+ offset = section_table_offset(dos, pe);
+ locate_sections((struct PeSectionHeader*)&base[offset], pe->FileHeader.NumberOfSections,
+ sections, addrs, NULL, sizes);
return EFI_SUCCESS;
}
-EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
- EFI_FILE_HANDLE handle;
+EFI_STATUS pe_file_locate_sections(
+ EFI_FILE *dir,
+ const CHAR16 *path,
+ const CHAR8 **sections,
+ UINTN *offsets,
+ UINTN *sizes) {
+ _cleanup_freepool_ struct PeSectionHeader *section_table = NULL;
+ _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
struct DosFileHeader dos;
- struct PeHeader pe;
- UINTN len;
- UINTN headerlen;
+ struct PeFileHeader pe;
+ UINTN len, section_table_len;
EFI_STATUS err;
- _cleanup_freepool_ CHAR8 *header = NULL;
- err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL);
+ assert(dir);
+ assert(path);
+ assert(sections);
+ assert(offsets);
+ assert(sizes);
+
+ err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*)path, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;
- /* MS-DOS stub */
len = sizeof(dos);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos);
if (EFI_ERROR(err))
- goto out;
- if (len != sizeof(dos)) {
- err = EFI_LOAD_ERROR;
- goto out;
- }
+ return err;
+ if (len != sizeof(dos) || !verify_dos(&dos))
+ return EFI_LOAD_ERROR;
err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader);
if (EFI_ERROR(err))
- goto out;
+ return err;
len = sizeof(pe);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe);
if (EFI_ERROR(err))
- goto out;
- if (len != sizeof(pe)) {
- err = EFI_LOAD_ERROR;
- goto out;
- }
+ return err;
+ if (len != sizeof(pe) || !verify_pe(&pe))
+ return EFI_LOAD_ERROR;
- headerlen = sizeof(dos) + sizeof(pe) + pe.FileHeader.SizeOfOptionalHeader + pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
- header = AllocatePool(headerlen);
- if (!header) {
- err = EFI_OUT_OF_RESOURCES;
- goto out;
- }
- len = headerlen;
- err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0);
+ section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
+ section_table = AllocatePool(section_table_len);
+ if (!section_table)
+ return EFI_OUT_OF_RESOURCES;
+
+ err = uefi_call_wrapper(handle->SetPosition, 2, handle, section_table_offset(&dos, &pe));
if (EFI_ERROR(err))
- goto out;
+ return err;
- err = uefi_call_wrapper(handle->Read, 3, handle, &len, header);
+ len = section_table_len;
+ err = uefi_call_wrapper(handle->Read, 3, handle, &len, section_table);
if (EFI_ERROR(err))
- goto out;
+ return err;
+ if (len != section_table_len)
+ return EFI_LOAD_ERROR;
- if (len != headerlen) {
- err = EFI_LOAD_ERROR;
- goto out;
- }
+ locate_sections(section_table, pe.FileHeader.NumberOfSections,
+ sections, NULL, offsets, sizes);
- err = pe_memory_locate_sections(header, sections, addrs, offsets, sizes);
-out:
- uefi_call_wrapper(handle->Close, 1, handle);
- return err;
+ return EFI_SUCCESS;
}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include <efi.h>
+#include <efidef.h>
-EFI_STATUS pe_memory_locate_sections(CHAR8 *base,
- CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
-EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path,
- CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
+EFI_STATUS pe_memory_locate_sections(
+ const CHAR8 *base,
+ const CHAR8 **sections,
+ UINTN *addrs,
+ UINTN *sizes);
+
+EFI_STATUS pe_file_locate_sections(
+ EFI_FILE *dir,
+ const CHAR16 *path,
+ const CHAR8 **sections,
+ UINTN *offsets,
+ UINTN *sizes);
EFI_RNG_PROTOCOL *rng;
EFI_STATUS err;
+ assert(ret);
+
/* Try to acquire the specified number of bytes from the UEFI RNG */
err = LibLocateProtocol(EFI_RNG_GUID, (VOID**) &rng);
return log_oom();
err = uefi_call_wrapper(rng->GetRNG, 3, rng, NULL, size, data);
- if (EFI_ERROR(err)) {
- Print(L"Failed to acquire RNG data: %r\n", err);
- return err;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to acquire RNG data: %r", err);
*ret = TAKE_PTR(data);
return EFI_SUCCESS;
struct sha256_ctx hash;
+ assert(old_seed);
+ assert(rng);
+ assert(system_token);
+
sha256_init_ctx(&hash);
sha256_process_bytes(old_seed, size, &hash);
if (rng)
_cleanup_freepool_ VOID *output = NULL;
+ assert(old_seed);
+ assert(rng);
+ assert(system_token);
+ assert(ret);
+
/* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the
* range counter_start…counter_start+n-1. */
EFI_STATUS err;
UINTN n;
+ assert(old_seed);
+ assert(rng);
+ assert(system_token);
+ assert(ret_new_seed);
+ assert(ret_for_kernel);
+
/* This takes the old seed file contents, an (optional) random number acquired from the UEFI RNG, an
* (optional) system 'token' installed once by the OS installer in an EFI variable, and hashes them
* together in counter mode, generating a new seed (to replace the file on disk) and the seed for the
EFI_STATUS err;
UINTN size;
+ assert(ret);
+ assert(ret_size);
+
err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size);
if (EFI_ERROR(err)) {
if (err != EFI_NOT_FOUND)
- Print(L"Failed to read LoaderSystemToken EFI variable: %r", err);
+ log_error_stall(L"Failed to read LoaderSystemToken EFI variable: %r", err);
return err;
}
- if (size <= 0) {
- Print(L"System token too short, ignoring.");
- return EFI_NOT_FOUND;
- }
+ if (size <= 0)
+ return log_error_status_stall(EFI_NOT_FOUND, L"System token too short, ignoring.");
*ret = TAKE_PTR(data);
*ret_size = size;
static VOID validate_sha256(void) {
-#ifndef __OPTIMIZE__
+#ifdef EFI_DEBUG
/* Let's validate our SHA256 implementation. We stole it from glibc, and converted it to UEFI
* style. We better check whether it does the right stuff. We use the simpler test vectors from the
* SHA spec. Note that we strip this out in optimization builds. */
sha256_process_bytes(array[i].string, strlena((const CHAR8*) array[i].string), &hash);
sha256_finish_ctx(&hash, result);
- if (CompareMem(result, array[i].hash, HASH_VALUE_SIZE) != 0) {
- Print(L"SHA256 failed validation.\n");
- uefi_call_wrapper(BS->Stall, 1, 120 * 1000 * 1000);
- return;
- }
+ assert(CompareMem(result, array[i].hash, HASH_VALUE_SIZE) == 0);
}
- Print(L"SHA256 validated\n");
#endif
}
_cleanup_freepool_ EFI_FILE_INFO *info = NULL;
EFI_STATUS err;
+ assert(root_dir);
+
validate_sha256();
if (mode == RANDOM_SEED_OFF)
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, (CHAR16*) L"\\loader\\random-seed", EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
if (EFI_ERROR(err)) {
- if (err != EFI_NOT_FOUND)
- Print(L"Failed to open random seed file: %r\n", err);
+ if (err != EFI_NOT_FOUND && err != EFI_WRITE_PROTECTED)
+ log_error_stall(L"Failed to open random seed file: %r", err);
return err;
}
return log_oom();
size = info->FileSize;
- if (size < RANDOM_MAX_SIZE_MIN) {
- Print(L"Random seed file is too short?\n");
- return EFI_INVALID_PARAMETER;
- }
+ if (size < RANDOM_MAX_SIZE_MIN)
+ return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too short.");
- if (size > RANDOM_MAX_SIZE_MAX) {
- Print(L"Random seed file is too large?\n");
- return EFI_INVALID_PARAMETER;
- }
+ if (size > RANDOM_MAX_SIZE_MAX)
+ return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
seed = AllocatePool(size);
if (!seed)
rsize = size;
err = uefi_call_wrapper(handle->Read, 3, handle, &rsize, seed);
- if (EFI_ERROR(err)) {
- Print(L"Failed to read random seed file: %r\n", err);
- return err;
- }
- if (rsize != size) {
- Print(L"Short read on random seed file\n");
- return EFI_PROTOCOL_ERROR;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to read random seed file: %r", err);
+ if (rsize != size)
+ return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short read on random seed file.");
err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0);
- if (EFI_ERROR(err)) {
- Print(L"Failed to seek to beginning of random seed file: %r\n", err);
- return err;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
/* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
* idea to use it because it helps us for cases where users mistakenly include a random seed in
/* Update the random seed on disk before we use it */
wsize = size;
err = uefi_call_wrapper(handle->Write, 3, handle, &wsize, new_seed);
- if (EFI_ERROR(err)) {
- Print(L"Failed to write random seed file: %r\n", err);
- return err;
- }
- if (wsize != size) {
- Print(L"Short write on random seed file\n");
- return EFI_PROTOCOL_ERROR;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
+ if (wsize != size)
+ return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
err = uefi_call_wrapper(handle->Flush, 1, handle);
- if (EFI_ERROR(err)) {
- Print(L"Failed to flush random seed file: %r\n");
- return err;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
/* We are good to go */
err = efivar_set_raw(LOADER_GUID, L"LoaderRandomSeed", for_kernel, size, 0);
- if (EFI_ERROR(err)) {
- Print(L"Failed to write random seed to EFI variable: %r\n", err);
- return err;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Failed to write random seed to EFI variable: %r", err);
return EFI_SUCCESS;
}
/* Written by Ulrich Drepper <drepper@redhat.com>, 2007. */
+#include "macro-fundamental.h"
#include "sha256.h"
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
/* Initialize structure containing state of computation.
(FIPS 180-2:5.3.2) */
void sha256_init_ctx(struct sha256_ctx *ctx) {
+ assert(ctx);
+
ctx->H[0] = 0x6a09e667;
ctx->H[1] = 0xbb67ae85;
ctx->H[2] = 0x3c6ef372;
UINT32 bytes = ctx->buflen;
UINTN pad;
+ assert(ctx);
+ assert(resbuf);
+
/* Now count remaining bytes. */
ctx->total64 += bytes;
}
void sha256_process_bytes(const void *buffer, UINTN len, struct sha256_ctx *ctx) {
+ assert(buffer);
+ assert(ctx);
+
/* When we already have some bits in our internal buffer concatenate
both inputs first. */
static void sha256_process_block(const void *buffer, UINTN len, struct sha256_ctx *ctx) {
const UINT32 *words = buffer;
UINTN nwords = len / sizeof (UINT32);
+
+ assert(buffer);
+ assert(ctx);
+
UINT32 a = ctx->H[0];
UINT32 b = ctx->H[1];
UINT32 c = ctx->H[2];
VOID *file_buffer, UINTN file_size, BOOLEAN boot_policy) {
EFI_STATUS status;
+ assert(this);
+ assert(device_path);
+ assert(file_buffer);
+
/* Chain original security policy */
status = uefi_call_wrapper(es2fa, 5, this, device_path, file_buffer, file_size, boot_policy);
_cleanup_freepool_ CHAR8 *file_buffer = NULL;
UINTN file_size;
+ assert(this);
+ assert(device_path_const);
+
if (!device_path_const)
return EFI_INVALID_PARAMETER;
UINT32 size;
UINT16 reserved[2];
UINT32 offset;
-} __attribute__((packed));
+} _packed_;
/* we require at least BITMAPINFOHEADER, later versions are
accepted, but their features ignored */
INT32 y_pixel_meter;
UINT32 colors_used;
UINT32 colors_important;
-} __attribute__((packed));
+} _packed_;
struct bmp_map {
UINT8 blue;
UINT8 green;
UINT8 red;
UINT8 reserved;
-} __attribute__((packed));
+} _packed_;
static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib,
struct bmp_map **ret_map, UINT8 **pixmap) {
struct bmp_map *map;
UINTN row_size;
+ assert(bmp);
+ assert(ret_dib);
+ assert(ret_map);
+ assert(pixmap);
+
if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib))
return EFI_INVALID_PARAMETER;
static VOID pixel_blend(UINT32 *dst, const UINT32 source) {
UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
+ assert(dst);
+
alpha = (source & 0xff);
/* convert src from RGBA to XRGB */
UINT8 *pixmap) {
UINT8 *in;
+ assert(buf);
+ assert(dib);
+ assert(map);
+ assert(pixmap);
+
/* transform and copy pixels */
in = pixmap;
for (UINTN y = 0; y < dib->y; y++) {
UINTN y_pos = 0;
EFI_STATUS err;
+ assert(content);
+
if (!background) {
if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) {
pixel.Red = 0xc0;
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
EFI_LOADED_IMAGE *loaded_image;
- CHAR8 *sections[] = {
+ const CHAR8 *sections[] = {
(CHAR8 *)".cmdline",
(CHAR8 *)".linux",
(CHAR8 *)".initrd",
NULL
};
UINTN addrs[ELEMENTSOF(sections)-1] = {};
- UINTN offs[ELEMENTSOF(sections)-1] = {};
UINTN szs[ELEMENTSOF(sections)-1] = {};
CHAR8 *cmdline = NULL;
UINTN cmdline_len;
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (EFI_ERROR(err)) {
- Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return err;
- }
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
- err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, offs, szs);
- if (EFI_ERROR(err)) {
- Print(L"Unable to locate embedded .linux section: %r ", err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return err;
- }
+ err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, szs);
+ if (EFI_ERROR(err))
+ return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
if (szs[0] > 0)
cmdline = (CHAR8 *)(loaded_image->ImageBase) + addrs[0];
err = tpm_log_event(SD_TPM_PCR,
(EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
- if (EFI_ERROR(err)) {
- Print(L"Unable to add image options measurement: %r", err);
- uefi_call_wrapper(BS->Stall, 1, 200 * 1000);
- }
+ if (EFI_ERROR(err))
+ log_error_stall(L"Unable to add image options measurement: %r", err);
#endif
}
(UINTN)loaded_image->ImageBase + addrs[2], szs[2]);
graphics_mode(FALSE);
- Print(L"Execution of embedded linux image failed: %r\n", err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return err;
+ return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
}
__asm__ volatile ("rdtsc" : "=A" (val));
return val;
}
+#elif defined(__aarch64__)
+UINT64 ticks_read(VOID) {
+ UINT64 val;
+ __asm__ volatile ("mrs %0, cntpct_el0" : "=r" (val));
+ return val;
+}
#else
UINT64 ticks_read(VOID) {
UINT64 val = 1;
}
#endif
+#if defined(__aarch64__)
+UINT64 ticks_freq(VOID) {
+ UINT64 freq;
+ __asm__ volatile ("mrs %0, cntfrq_el0": "=r" (freq));
+ return freq;
+}
+#else
/* count TSC ticks during a millisecond delay */
UINT64 ticks_freq(VOID) {
UINT64 ticks_start, ticks_end;
return (ticks_end - ticks_start) * 1000UL;
}
+#endif
UINT64 time_usec(VOID) {
UINT64 ticks;
}
EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
+ assert(b);
+
if (!v)
return EFI_INVALID_PARAMETER;
if (strcmpa(v, (CHAR8 *)"1") == 0 ||
strcmpa(v, (CHAR8 *)"yes") == 0 ||
strcmpa(v, (CHAR8 *)"y") == 0 ||
- strcmpa(v, (CHAR8 *)"true") == 0) {
+ strcmpa(v, (CHAR8 *)"true") == 0 ||
+ strcmpa(v, (CHAR8 *)"t") == 0 ||
+ strcmpa(v, (CHAR8 *)"on") == 0) {
*b = TRUE;
return EFI_SUCCESS;
}
if (strcmpa(v, (CHAR8 *)"0") == 0 ||
strcmpa(v, (CHAR8 *)"no") == 0 ||
strcmpa(v, (CHAR8 *)"n") == 0 ||
- strcmpa(v, (CHAR8 *)"false") == 0) {
+ strcmpa(v, (CHAR8 *)"false") == 0 ||
+ strcmpa(v, (CHAR8 *)"f") == 0 ||
+ strcmpa(v, (CHAR8 *)"off") == 0) {
*b = FALSE;
return EFI_SUCCESS;
}
}
EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, UINT32 flags) {
+ assert(vendor);
+ assert(name);
+ assert(buf || size == 0);
+
flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
}
EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) {
- return efivar_set_raw(vendor, name, value, value ? (StrLen(value) + 1) * sizeof(CHAR16) : 0, flags);
+ assert(vendor);
+ assert(name);
+
+ return efivar_set_raw(vendor, name, value, value ? StrSize(value) : 0, flags);
}
EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) {
CHAR16 str[32];
- SPrint(str, 32, L"%u", i);
+ assert(vendor);
+ assert(name);
+
+ SPrint(str, ELEMENTSOF(str), L"%u", i);
return efivar_set(vendor, name, str, flags);
}
EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 value, UINT32 flags) {
UINT8 buf[4];
+ assert(vendor);
+ assert(name);
+
buf[0] = (UINT8)(value >> 0U & 0xFF);
buf[1] = (UINT8)(value >> 8U & 0xFF);
buf[2] = (UINT8)(value >> 16U & 0xFF);
EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 value, UINT32 flags) {
UINT8 buf[8];
+ assert(vendor);
+ assert(name);
+
buf[0] = (UINT8)(value >> 0U & 0xFF);
buf[1] = (UINT8)(value >> 8U & 0xFF);
buf[2] = (UINT8)(value >> 16U & 0xFF);
}
EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value) {
- _cleanup_freepool_ CHAR8 *buf = NULL;
+ _cleanup_freepool_ CHAR16 *buf = NULL;
EFI_STATUS err;
CHAR16 *val;
UINTN size;
- err = efivar_get_raw(vendor, name, &buf, &size);
+ assert(vendor);
+ assert(name);
+
+ err = efivar_get_raw(vendor, name, (CHAR8**)&buf, &size);
if (EFI_ERROR(err))
return err;
/* Make sure there are no incomplete characters in the buffer */
- if ((size % 2) != 0)
+ if ((size % sizeof(CHAR16)) != 0)
return EFI_INVALID_PARAMETER;
if (!value)
return EFI_SUCCESS;
/* Return buffer directly if it happens to be NUL terminated already */
- if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) {
- *value = (CHAR16*) TAKE_PTR(buf);
+ if (size >= sizeof(CHAR16) && buf[size/sizeof(CHAR16)] == 0) {
+ *value = TAKE_PTR(buf);
return EFI_SUCCESS;
}
/* Make sure a terminating NUL is available at the end */
- val = AllocatePool(size + 2);
+ val = AllocatePool(size + sizeof(CHAR16));
if (!val)
return EFI_OUT_OF_RESOURCES;
CopyMem(val, buf, size);
- val[size/2] = 0; /* NUL terminate */
+ val[size / sizeof(CHAR16)] = 0; /* NUL terminate */
*value = val;
return EFI_SUCCESS;
_cleanup_freepool_ CHAR16 *val = NULL;
EFI_STATUS err;
+ assert(vendor);
+ assert(name);
+ assert(i);
+
err = efivar_get(vendor, name, &val);
- if (!EFI_ERROR(err) && i)
+ if (!EFI_ERROR(err))
*i = Atoi(val);
return err;
UINTN size;
EFI_STATUS err;
+ assert(vendor);
+ assert(name);
+
err = efivar_get_raw(vendor, name, &buf, &size);
if (!EFI_ERROR(err) && ret) {
if (size != sizeof(UINT32))
UINTN size;
EFI_STATUS err;
+ assert(vendor);
+ assert(name);
+
err = efivar_get_raw(vendor, name, &buf, &size);
if (!EFI_ERROR(err) && ret) {
if (size != sizeof(UINT64))
UINTN l;
EFI_STATUS err;
+ assert(vendor);
+ assert(name);
+
l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
buf = AllocatePool(l);
if (!buf)
UINTN size;
EFI_STATUS err;
+ assert(vendor);
+ assert(name);
+ assert(ret);
+
err = efivar_get_raw(vendor, name, &b, &size);
if (!EFI_ERROR(err))
*ret = *b > 0;
VOID efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) {
CHAR16 str[32];
+ assert(vendor);
+ assert(name);
+
if (usec == 0)
usec = time_usec();
if (usec == 0)
return;
- SPrint(str, 32, L"%ld", usec);
+ SPrint(str, ELEMENTSOF(str), L"%ld", usec);
efivar_set(vendor, name, str, 0);
}
CHAR16 unichar;
UINTN len;
+ assert(stra);
+ assert(c);
+
if (!(stra[0] & 0x80))
len = 1;
else if ((stra[0] & 0xe0) == 0xc0)
UINTN i;
CHAR16 *str;
+ assert(stra);
+
len = strlena(stra);
str = AllocatePool((len + 1) * sizeof(CHAR16));
UINTN len;
UINTN i;
+ assert(stra);
+
len = strlena(stra);
str = AllocatePool((len + 2) * sizeof(CHAR16));
}
CHAR8 *strchra(const CHAR8 *s, CHAR8 c) {
+ assert(s);
do {
if (*s == c)
return (CHAR8*) s;
_cleanup_freepool_ CHAR8 *buf = NULL;
EFI_STATUS err;
+ assert(name);
+ assert(ret);
+
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;
return err;
}
+VOID log_error_stall(const CHAR16 *fmt, ...) {
+ va_list args;
+
+ assert(fmt);
+
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
+
+ Print(L"\n");
+ va_start(args, fmt);
+ VPrint(fmt, args);
+ va_end(args);
+ Print(L"\n");
+
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+}
+
EFI_STATUS log_oom(void) {
- Print(L"Out of memory.");
- (void) uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ log_error_stall(L"Out of memory.");
return EFI_OUT_OF_RESOURCES;
}
+
+VOID *memmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len) {
+ assert(haystack || haystack_len == 0);
+ assert(needle || needle_len == 0);
+
+ if (needle_len == 0)
+ return (VOID*)haystack;
+
+ for (const CHAR8 *h = haystack, *n = needle; haystack_len >= needle_len; h++, haystack_len--)
+ if (*h == *n && CompareMem(h + 1, n + 1, needle_len - 1) == 0)
+ return (VOID*)h;
+
+ return NULL;
+}
+
+VOID print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str) {
+ assert(str);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x, y);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, attr);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*)str);
+}
#define UINT64_MAX ((UINT64) -1)
#endif
+VOID log_error_stall(const CHAR16 *fmt, ...);
EFI_STATUS log_oom(void);
+
+/* This works just like log_error_errno() from userspace, but requires you
+ * to provide err a second time if you want to use %r in the message! */
+#define log_error_status_stall(err, fmt, ...) \
+ ({ \
+ log_error_stall(fmt, ##__VA_ARGS__); \
+ err; \
+ })
+
+VOID *memmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len);
+
+static inline VOID *mempmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len) {
+ CHAR8 *p = memmem_safe(haystack, haystack_len, needle, needle_len);
+ return p ? p + needle_len : NULL;
+}
+
+VOID print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str);
break;
default:
- assert_not_reached("Bad state");
+ assert_not_reached();
}
}
}
break;
default:
- assert_not_reached("Hmm, unknown transport type.");
+ assert_not_reached();
}
}
if (r < 0)
if (r < 0)
log_debug_errno(r, "Failed to acquire credentials of service %s, ignoring: %m", k);
else {
- char m[SD_ID128_STRING_MAX];
-
- r = table_add_cell(table, NULL, TABLE_STRING, sd_id128_to_string(mid, m));
+ r = table_add_cell(table, NULL, TABLE_ID128, &mid);
if (r < 0)
return table_log_add_error(r);
break;
default:
- assert_not_reached("Unknown basic type.");
+ assert_not_reached();
}
needs_space = true;
break;
default:
- assert_not_reached("Unexpected element type");
+ assert_not_reached();
}
*ret = TAKE_PTR(v);
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_machine && arg_show_unit != SHOW_UNIT_NONE)
static const char *maybe_format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
if (arg_raw) {
- snprintf(buf, l, USEC_FMT, t);
+ (void) snprintf(buf, l, USEC_FMT, t);
return buf;
}
return format_timespan(buf, l, t, accuracy);
if (!is_valid)
return "-";
if (arg_raw) {
- snprintf(buf, l, "%" PRIu64, t);
+ (void) snprintf(buf, l, "%" PRIu64, t);
return buf;
}
return format_bytes(buf, l, t);
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind == argc - 1)
#include "fileio.h"
#include "nulstr-util.h"
#include "parse-util.h"
+#include "path-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
r = bpf_program_cgroup_attach(prog, BPF_CGROUP_DEVICE, controller_path, BPF_F_ALLOW_MULTI);
if (r < 0)
return log_error_errno(r, "Attaching device control BPF program to cgroup %s failed: %m",
- cgroup_path);
+ empty_to_root(cgroup_path));
finish:
/* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+
+if conf.get('BPF_FRAMEWORK') == 1
+ restrict_ifaces_skel_h = custom_target(
+ 'restrict-ifaces.skel.h',
+ input : 'restrict-ifaces.bpf.c',
+ output : 'restrict-ifaces.skel.h',
+ command : [build_bpf_skel_py,
+ '--clang_exec', clang.path(),
+ '--llvm_strip_exec', llvm_strip.path(),
+ '--bpftool_exec', bpftool.path(),
+ '--arch', host_machine.cpu_family(),
+ '@INPUT@', '@OUTPUT@'])
+endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/* <linux/bpf.h> must precede <bpf/bpf_helpers.h> due to integer types
+ * in bpf helpers signatures.
+ */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+const volatile __u8 is_allow_list = 0;
+
+/* Map containing the network interfaces indexes.
+ * The interpretation of the map depends on the value of is_allow_list.
+ */
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __type(key, __u32);
+ __type(value, __u8);
+} sd_restrictif SEC(".maps");
+
+#define DROP 0
+#define PASS 1
+
+static inline int restrict_network_interfaces_impl(const struct __sk_buff *sk) {
+ __u32 zero = 0, ifindex;
+ __u8 *lookup_result;
+
+ ifindex = sk->ifindex;
+ lookup_result = bpf_map_lookup_elem(&sd_restrictif, &ifindex);
+ if (is_allow_list) {
+ /* allow-list: let the packet pass if iface in the list */
+ if (lookup_result)
+ return PASS;
+ } else {
+ /* deny-list: let the packet pass if iface *not* in the list */
+ if (!lookup_result)
+ return PASS;
+ }
+
+ return DROP;
+}
+
+SEC("cgroup_skb/egress")
+int sd_restrictif_e(const struct __sk_buff *sk) {
+ return restrict_network_interfaces_impl(sk);
+}
+
+SEC("cgroup_skb/ingress")
+int sd_restrictif_i(const struct __sk_buff *sk) {
+ return restrict_network_interfaces_impl(sk);
+}
+
+static const char _license[] SEC("license") = "LGPL-2.1-or-later";
#include "percent-util.h"
#include "process-util.h"
#include "procfs-util.h"
+#include "restrict-ifaces.h"
#include "special.h"
#include "stat-util.h"
#include "stdio-util.h"
r = cg_set_attribute(controller, u->cgroup_path, attribute, value);
if (r < 0)
log_unit_full_errno(u, LOG_LEVEL_CGROUP_WRITE(r), r, "Failed to set '%s' attribute on '%s' to '%.*s': %m",
- strna(attribute), isempty(u->cgroup_path) ? "/" : u->cgroup_path, (int) strcspn(value, NEWLINE), value);
+ strna(attribute), empty_to_root(u->cgroup_path), (int) strcspn(value, NEWLINE), value);
return r;
}
while (c->bpf_foreign_programs)
cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
+ c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
+
cpu_set_reset(&c->cpuset_cpus);
cpu_set_reset(&c->cpuset_mems);
}
}
if (r < 0) {
- snprintf(buf, l, " (error getting kernel value: %s)", strerror_safe(r));
+ (void) snprintf(buf, l, " (error getting kernel value: %s)", strerror_safe(r));
return buf;
}
- snprintf(buf, l, " (different value in kernel: %" PRIu64 ")", kval);
+ (void) snprintf(buf, l, " (different value in kernel: %" PRIu64 ")", kval);
return buf;
}
cgroup_context_dump_socket_bind_item(bi, f);
fputc('\n', f);
}
+
+ if (c->restrict_network_interfaces) {
+ char *iface;
+ SET_FOREACH(iface, c->restrict_network_interfaces)
+ fprintf(f, "%sRestrictNetworkInterfaces: %s\n", prefix, iface);
+ }
}
void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE *f) {
if (c->moom_preference == MANAGED_OOM_PREFERENCE_OMIT) {
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_omit", "1", 1, 0);
if (r < 0)
- log_unit_debug_errno(u, r, "Failed to set oomd_omit flag on control group %s, ignoring: %m", cgroup_path);
+ log_unit_debug_errno(u, r, "Failed to set oomd_omit flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
}
if (c->moom_preference == MANAGED_OOM_PREFERENCE_AVOID) {
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_avoid", "1", 1, 0);
if (r < 0)
- log_unit_debug_errno(u, r, "Failed to set oomd_avoid flag on control group %s, ignoring: %m", cgroup_path);
+ log_unit_debug_errno(u, r, "Failed to set oomd_avoid flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
}
if (c->moom_preference != MANAGED_OOM_PREFERENCE_AVOID) {
r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_avoid");
- if (r != -ENODATA)
- log_unit_debug_errno(u, r, "Failed to remove oomd_avoid flag on control group %s, ignoring: %m", cgroup_path);
+ if (r < 0 && r != -ENODATA)
+ log_unit_debug_errno(u, r, "Failed to remove oomd_avoid flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
}
if (c->moom_preference != MANAGED_OOM_PREFERENCE_OMIT) {
r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_omit");
- if (r != -ENODATA)
- log_unit_debug_errno(u, r, "Failed to remove oomd_omit flag on control group %s, ignoring: %m", cgroup_path);
+ if (r < 0 && r != -ENODATA)
+ log_unit_debug_errno(u, r, "Failed to remove oomd_omit flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
}
}
static void cgroup_xattr_apply(Unit *u) {
- char ids[SD_ID128_STRING_MAX];
int r;
assert(u);
if (!sd_id128_is_null(u->invocation_id)) {
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
"trusted.invocation_id",
- sd_id128_to_string(u->invocation_id, ids), 32,
+ SD_ID128_TO_STRING(u->invocation_id), 32,
0);
if (r < 0)
- log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", u->cgroup_path);
+ log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
}
if (unit_cgroup_delegate(u)) {
"1", 1,
0);
if (r < 0)
- log_unit_debug_errno(u, r, "Failed to set delegate flag on control group %s, ignoring: %m", u->cgroup_path);
+ log_unit_debug_errno(u, r, "Failed to set delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
} else {
r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "trusted.delegate");
- if (r != -ENODATA)
- log_unit_debug_errno(u, r, "Failed to remove delegate flag on control group %s, ignoring: %m", u->cgroup_path);
+ if (r < 0 && r != -ENODATA)
+ log_unit_debug_errno(u, r, "Failed to remove delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
}
cgroup_oomd_xattr_apply(u, u->cgroup_path);
(void) bpf_socket_bind_install(u);
}
+static void cgroup_apply_restrict_network_interfaces(Unit *u) {
+ assert(u);
+
+ (void) restrict_network_interfaces_install(u);
+}
+
static int cgroup_apply_devices(Unit *u) {
_cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
const char *path;
if (apply_mask & CGROUP_MASK_BPF_SOCKET_BIND)
cgroup_apply_socket_bind(u);
+
+ if (apply_mask & CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES)
+ cgroup_apply_restrict_network_interfaces(u);
}
static bool unit_get_needs_bpf_firewall(Unit *u) {
return c->socket_bind_allow || c->socket_bind_deny;
}
+static bool unit_get_needs_restrict_network_interfaces(Unit *u) {
+ CGroupContext *c;
+ assert(u);
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return false;
+
+ return !set_isempty(c->restrict_network_interfaces);
+}
+
static CGroupMask unit_get_cgroup_mask(Unit *u) {
CGroupMask mask = 0;
CGroupContext *c;
if (unit_get_needs_socket_bind(u))
mask |= CGROUP_MASK_BPF_SOCKET_BIND;
+ if (unit_get_needs_restrict_network_interfaces(u))
+ mask |= CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES;
+
return mask;
}
* is not an error */
return 0;
- return log_unit_error_errno(u, errno, "Failed to add control inotify watch descriptor for control group %s: %m", u->cgroup_path);
+ return log_unit_error_errno(u, errno, "Failed to add control inotify watch descriptor for control group %s: %m", empty_to_root(u->cgroup_path));
}
r = hashmap_put(u->manager->cgroup_control_inotify_wd_unit, INT_TO_PTR(u->cgroup_control_inotify_wd), u);
if (r < 0)
- return log_unit_error_errno(u, r, "Failed to add control inotify watch descriptor to hash map: %m");
+ return log_unit_error_errno(u, r, "Failed to add control inotify watch descriptor for control group %s to hash map: %m", empty_to_root(u->cgroup_path));
return 0;
}
* is not an error */
return 0;
- return log_unit_error_errno(u, errno, "Failed to add memory inotify watch descriptor for control group %s: %m", u->cgroup_path);
+ return log_unit_error_errno(u, errno, "Failed to add memory inotify watch descriptor for control group %s: %m", empty_to_root(u->cgroup_path));
}
r = hashmap_put(u->manager->cgroup_memory_inotify_wd_unit, INT_TO_PTR(u->cgroup_memory_inotify_wd), u);
if (r < 0)
- return log_unit_error_errno(u, r, "Failed to add memory inotify watch descriptor to hash map: %m");
+ return log_unit_error_errno(u, r, "Failed to add memory inotify watch descriptor for control group %s to hash map: %m", empty_to_root(u->cgroup_path));
return 0;
}
r = unit_set_cgroup_path(u, path);
if (r == -EEXIST)
- return log_unit_error_errno(u, r, "Control group %s exists already.", path);
+ return log_unit_error_errno(u, r, "Control group %s exists already.", empty_to_root(path));
if (r < 0)
- return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", path);
+ return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", empty_to_root(path));
return 0;
}
/* First, create our own group */
r = cg_create_everywhere(u->manager->cgroup_supported, target_mask, u->cgroup_path);
if (r < 0)
- return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", u->cgroup_path);
+ return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", empty_to_root(u->cgroup_path));
created = r;
/* Start watching it */
/* Enable all controllers we need */
r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path, &result_mask);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enable/disable controllers on cgroup %s, ignoring: %m", u->cgroup_path);
+ log_unit_warning_errno(u, r, "Failed to enable/disable controllers on cgroup %s, ignoring: %m", empty_to_root(u->cgroup_path));
/* Remember what's actually enabled now */
u->cgroup_enabled_mask = result_mask;
if (cg_all_unified() == 0) {
r = cg_migrate_v1_controllers(u->manager->cgroup_supported, migrate_mask, u->cgroup_path, migrate_callback, u);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to migrate controller cgroups from %s, ignoring: %m", u->cgroup_path);
+ log_unit_warning_errno(u, r, "Failed to migrate controller cgroups from %s, ignoring: %m", empty_to_root(u->cgroup_path));
is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
r = cg_trim_v1_controllers(u->manager->cgroup_supported, ~target_mask, u->cgroup_path, !is_root_slice);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to delete controller cgroups %s, ignoring: %m", u->cgroup_path);
+ log_unit_warning_errno(u, r, "Failed to delete controller cgroups %s, ignoring: %m", empty_to_root(u->cgroup_path));
}
/* Set attributes */
log_unit_full_errno(u, again ? LOG_DEBUG : LOG_INFO, q,
"Couldn't move process "PID_FMT" to%s requested cgroup '%s': %m",
- pid, again ? " directly" : "", p);
+ pid, again ? " directly" : "", empty_to_root(p));
if (again) {
int z;
z = unit_attach_pid_to_cgroup_via_bus(u, pid, suffix_path);
if (z < 0)
- log_unit_info_errno(u, z, "Couldn't move process "PID_FMT" to requested cgroup '%s' (directly or via the system bus): %m", pid, p);
+ log_unit_info_errno(u, z, "Couldn't move process "PID_FMT" to requested cgroup '%s' (directly or via the system bus): %m", pid, empty_to_root(p));
else
continue; /* When the bus thing worked via the bus we are fully done for this PID. */
}
continue; /* Success! */
log_unit_debug_errno(u, q, "Failed to attach PID " PID_FMT " to requested cgroup %s in controller %s, falling back to unit's cgroup: %m",
- pid, p, cgroup_controller_to_string(c));
+ pid, empty_to_root(p), cgroup_controller_to_string(c));
}
/* So this controller is either not delegate or realized, or something else weird happened. In
* the containing slice is stopped. So even if we failed now, this unit shouldn't assume
* that the cgroup is still realized the next time it is started. Do not return early
* on error, continue cleanup. */
- log_unit_full_errno(u, r == -EBUSY ? LOG_DEBUG : LOG_WARNING, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path);
+ log_unit_full_errno(u, r == -EBUSY ? LOG_DEBUG : LOG_WARNING, r, "Failed to destroy cgroup %s, ignoring: %m", empty_to_root(u->cgroup_path));
if (is_root_slice)
return;
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
if (r < 0) {
- log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", u->cgroup_path);
+ log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", empty_to_root(u->cgroup_path));
return;
}
if (r == 0)
assert(u);
+ if (!u->cgroup_path)
+ return 0;
+
r = cg_get_keyed_attribute_graceful(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events",
STRV_MAKE("populated", "frozen"), values);
if (r < 0)
if (r > 0)
mask |= CGROUP_MASK_BPF_SOCKET_BIND;
+ /* BPF-based cgroup_skb/{egress|ingress} hooks */
+ r = restrict_network_interfaces_supported();
+ if (r > 0)
+ mask |= CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES;
+
*ret = mask;
return 0;
}
}
}
+void unit_cgroup_catchup(Unit *u) {
+ assert(u);
+
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return;
+
+ /* We dropped the inotify watch during reexec/reload, so we need to
+ * check these as they may have changed.
+ * Note that (currently) the kernel doesn't actually update cgroup
+ * file modification times, so we can't just serialize and then check
+ * the mtime for file(s) we are interested in. */
+ (void) unit_check_cgroup_events(u);
+ unit_add_to_cgroup_oom_queue(u);
+}
+
bool unit_cgroup_delegate(Unit *u) {
CGroupContext *c;
char **ip_filters_egress;
LIST_HEAD(CGroupBPFForeignProgram, bpf_foreign_programs);
+ Set *restrict_network_interfaces;
+ bool restrict_network_interfaces_is_allow_list;
+
/* For legacy hierarchies */
uint64_t cpu_shares;
uint64_t startup_cpu_shares;
const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_;
CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_;
+void unit_cgroup_catchup(Unit *u);
+
bool unit_cgroup_delegate(Unit *u);
int compare_job_priority(const void *a, const void *b);
#include "parse-util.h"
#include "path-util.h"
#include "percent-util.h"
+#include "socket-util.h"
BUS_DEFINE_PROPERTY_GET(bus_property_get_tasks_max, "t", TasksMax, tasks_max_resolve);
return sd_bus_message_close_container(reply);
}
+static int property_get_restrict_network_interfaces(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ int r;
+ CGroupContext *c = userdata;
+ char *iface;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ r = sd_bus_message_open_container(reply, 'r', "bas");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "b", c->restrict_network_interfaces_is_allow_list);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "s");
+ if (r < 0)
+ return r;
+
+ SET_FOREACH(iface, c->restrict_network_interfaces) {
+ r = sd_bus_message_append(reply, "s", iface);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_close_container(reply);
+}
+
const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
SD_BUS_PROPERTY("BPFProgram", "a(ss)", property_get_bpf_foreign_program, 0, 0),
SD_BUS_PROPERTY("SocketBindAllow", "a(iiqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_allow), 0),
SD_BUS_PROPERTY("SocketBindDeny", "a(iiqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_deny), 0),
+ SD_BUS_PROPERTY("RestrictNetworkInterfaces", "(bas)", property_get_restrict_network_interfaces, 0, 0),
SD_BUS_VTABLE_END
};
return 1;
}
+ if (streq(name, "RestrictNetworkInterfaces")) {
+ int is_allow_list;
+ _cleanup_strv_free_ char **l = NULL;
+
+ r = sd_bus_message_enter_container(message, 'r', "bas");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "b", &is_allow_list);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ _cleanup_free_ char *joined = NULL;
+ char **s;
+
+ if (strv_isempty(l)) {
+ c->restrict_network_interfaces_is_allow_list = false;
+ c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
+
+ unit_write_settingf(u, flags, name, "%s=", name);
+ return 1;
+ }
+
+ if (set_isempty(c->restrict_network_interfaces))
+ c->restrict_network_interfaces_is_allow_list = is_allow_list;
+
+ STRV_FOREACH(s, l) {
+ if (!ifname_valid(*s)) {
+ log_full(LOG_WARNING, "Invalid interface name, ignoring: %s", *s);
+ continue;
+ }
+ if (c->restrict_network_interfaces_is_allow_list != (bool) is_allow_list)
+ free(set_remove(c->restrict_network_interfaces, *s));
+ else {
+ r = set_put_strdup(&c->restrict_network_interfaces, *s);
+ if (r < 0)
+ return log_oom();
+ }
+ }
+
+ joined = strv_join(l, " ");
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_settingf(u, flags, name, "%s=%s%s", name, is_allow_list ? "" : "~", joined);
+ }
+
+ return 1;
+ }
if (streq(name, "DisableControllers") || (u->transient && u->load_state == UNIT_STUB))
return bus_cgroup_set_transient_property(u, c, name, message, flags, error);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <linux/oom.h>
#include <sys/mount.h>
#include <sys/prctl.h>
return sd_bus_message_close_container(reply);
}
-static bool oom_score_adjust_is_valid(int oa) {
- return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
-}
-
static int property_get_oom_score_adjust(
sd_bus *bus,
const char *path,
if (!joined)
return -ENOMEM;
- e = strv_env_merge(2, c->environment, l);
+ e = strv_env_merge(c->environment, l);
if (!e)
return -ENOMEM;
if (!joined)
return -ENOMEM;
- e = strv_env_merge(2, c->unset_environment, l);
+ e = strv_env_merge(c->unset_environment, l);
if (!e)
return -ENOMEM;
break;
default:
- assert_not_reached("Unknown socket type");
+ assert_not_reached();
}
r = sd_bus_message_append(reply, "(ss)", socket_port_type_to_string(p), a);
SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("OnSuccesJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("OnSuccesJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* deprecated */
+ SD_BUS_PROPERTY("OnSuccessJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
return NULL;
/* Make everybody follow the unit that's named after the sysfs path */
- LIST_FOREACH_AFTER(same_sysfs, other, d)
+ LIST_FOREACH(same_sysfs, other, d->same_sysfs_next)
if (startswith(UNIT(other)->id, "sys-"))
return UNIT(other);
- LIST_FOREACH_BEFORE(same_sysfs, other, d) {
+ LIST_FOREACH_BACKWARDS(same_sysfs, other, d->same_sysfs_prev) {
if (startswith(UNIT(other)->id, "sys-"))
return UNIT(other);
if (!set)
return -ENOMEM;
- LIST_FOREACH_AFTER(same_sysfs, other, d) {
+ LIST_FOREACH(same_sysfs, other, d->same_sysfs_next) {
r = set_put(set, other);
if (r < 0)
return r;
}
- LIST_FOREACH_BEFORE(same_sysfs, other, d) {
+ LIST_FOREACH_BACKWARDS(same_sysfs, other, d->same_sysfs_prev) {
r = set_put(set, other);
if (r < 0)
return r;
break;
default:
- assert_not_reached("unknown phase");
+ assert_not_reached();
}
/* Make sure whatever we picked here actually is in the right range */
break;
default:
- assert_not_reached("Unknown emergency action");
+ assert_not_reached();
}
}
}
default:
- assert_not_reached("Unknown input type");
+ assert_not_reached();
}
}
}
default:
- assert_not_reached("Unknown error type");
+ assert_not_reached();
}
}
r = CONFIRM_EXECUTE;
break;
default:
- assert_not_reached("Unhandled choice");
+ assert_not_reached();
}
break;
}
return log_oom();
}
- accum_env = strv_env_merge(5,
- params->environment,
+ accum_env = strv_env_merge(params->environment,
our_env,
pass_env,
context->environment,
_cleanup_free_ char *executable = NULL;
_cleanup_close_ int executable_fd = -1;
- r = find_executable_full(command->path, false, &executable, &executable_fd);
+ r = find_executable_full(command->path, /* root= */ NULL, false, &executable, &executable_fd);
if (r < 0) {
if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
log_unit_struct_errno(unit, LOG_INFO, r,
else {
char **m;
- m = strv_env_merge(2, r, p);
+ m = strv_env_merge(r, p);
strv_free(r);
strv_free(p);
if (!m)
return true;
default:
- assert_not_reached("Invalid job type");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Invalid job type");
+ assert_not_reached();
}
/* Log if the job still exists and the start/stop/reload function actually did something. Note that this means
break;
default:
- assert_not_reached("Unknown job type");
+ assert_not_reached();
}
if (j) {
{{type}}.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof({{type}}, cgroup_context)
{{type}}.SocketBindAllow, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_allow)
{{type}}.SocketBindDeny, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_deny)
+{{type}}.RestrictNetworkInterfaces, config_parse_restrict_network_interfaces, 0, offsetof({{type}}, cgroup_context)
{%- endmacro -%}
%{
return 0;
}
+int config_parse_restrict_network_interfaces(
+ 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) {
+ CGroupContext *c = data;
+ bool is_allow_rule = true;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
+ return 0;
+ }
+
+ if (rvalue[0] == '~') {
+ is_allow_rule = false;
+ rvalue++;
+ }
+
+ if (set_isempty(c->restrict_network_interfaces))
+ /* Only initialize this when creating the set */
+ c->restrict_network_interfaces_is_allow_list = is_allow_rule;
+
+ for (const char *p = rvalue;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
+ break;
+ }
+
+ if (!ifname_valid(word)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", word);
+ continue;
+ }
+
+ if (c->restrict_network_interfaces_is_allow_list != is_allow_rule)
+ free(set_remove(c->restrict_network_interfaces, word));
+ else {
+ r = set_put_strdup(&c->restrict_network_interfaces, word);
+ if (r < 0)
+ return log_oom();
+ }
+ }
+
+ return 0;
+}
+
static int merge_by_names(Unit **u, Set *names, const char *id) {
char *k;
int r;
CONFIG_PARSER_PROTOTYPE(config_parse_extension_images);
CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program);
CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_socket_bind);
+CONFIG_PARSER_PROTOTYPE(config_parse_restrict_network_interfaces);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
else {
char **merged;
- merged = strv_env_merge(2, *environment, add);
+ merged = strv_env_merge(*environment, add);
if (!merged)
return -ENOMEM;
#include "switch-root.h"
#include "sysctl-util.h"
#include "terminal-util.h"
+#include "time-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
pid = raw_getpid();
(void) kill(pid, sig); /* raise() would kill the parent */
- assert_not_reached("We shouldn't be here...");
+ assert_not_reached();
_exit(EXIT_EXCEPTION);
} else {
siginfo_t status;
return 0;
default:
- assert_not_reached("Unhandled option code.");
+ assert_not_reached();
}
if (optind < argc && getpid_cached() != 1)
*/
(void) clock_reset_timewarp();
- r = clock_apply_epoch();
- if (r < 0)
- log_error_errno(r, "Current system time is before build time, but cannot correct: %m");
- else if (r > 0)
+ ClockChangeDirection change_dir;
+ r = clock_apply_epoch(&change_dir);
+ if (r > 0 && change_dir == CLOCK_CHANGE_FORWARD)
log_info("System time before build time, advancing clock.");
+ else if (r > 0 && change_dir == CLOCK_CHANGE_BACKWARD)
+ log_info("System time is further ahead than %s after build time, resetting clock to build time.",
+ FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY));
+ else if (r < 0 && change_dir == CLOCK_CHANGE_FORWARD)
+ log_error_errno(r, "Current system time is before build time, but cannot correct: %m");
+ else if (r < 0 && change_dir == CLOCK_CHANGE_BACKWARD)
+ log_error_errno(r, "Current system time is further ahead %s after build time, but cannot correct: %m",
+ FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY));
}
static void apply_clock_update(void) {
log_warning_errno(r, "Failed to set NUMA memory policy: %m");
}
-static void filter_args(const char* dst[], unsigned *pos, char **src, int argc) {
+static void filter_args(
+ const char* dst[],
+ size_t *dst_index,
+ char **src,
+ int argc) {
+
assert(dst);
- assert(pos);
+ assert(dst_index);
/* Copy some filtered arguments into the dst array from src. */
for (int i = 1; i < argc; i++) {
continue;
/* Seems we have a good old option. Let's pass it over to the new instance. */
- dst[*pos] = src[i];
- (*pos)++;
+ dst[(*dst_index)++] = src[i];
}
}
const char *switch_root_init,
const char **ret_error_message) {
- unsigned i, args_size;
+ size_t i, args_size;
const char **args;
int r;
+ assert(argc >= 0);
assert(saved_rlimit_nofile);
assert(saved_rlimit_memlock);
assert(ret_error_message);
}
default:
- assert_not_reached("Unknown or unexpected manager objective.");
+ assert_not_reached();
}
}
}
before_startup = now(CLOCK_MONOTONIC);
- r = manager_startup(m, arg_serialization, fds);
+ r = manager_startup(m, arg_serialization, fds, /* root= */ NULL);
if (r < 0) {
error_message = "Failed to start up manager";
goto finish;
SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */
SIGRTMIN+23, /* systemd: set log level to LOG_INFO */
SIGRTMIN+24, /* systemd: Immediate exit (--user only) */
-
- /* .. one free signal here ... */
+ SIGRTMIN+25, /* systemd: reexecute manager */
/* Apparently Linux on hppa had fewer RT signals until v3.18,
* SIGRTMAX was SIGRTMIN+25, and then SIGRTMIN was lowered,
assert(!m->cleanup_queue);
assert(!m->gc_unit_queue);
assert(!m->gc_job_queue);
+ assert(!m->cgroup_realize_queue);
+ assert(!m->cgroup_empty_queue);
+ assert(!m->cgroup_oom_queue);
+ assert(!m->target_deps_queue);
assert(!m->stop_when_unneeded_queue);
assert(!m->start_when_upheld_queue);
assert(!m->stop_when_bound_queue);
static void manager_enumerate_perpetual(Manager *m) {
assert(m);
- if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
+ if (FLAGS_SET(m->test_run_flags, MANAGER_TEST_RUN_MINIMAL))
return;
/* Let's ask every type to load all units from disk/kernel that it might know */
static void manager_enumerate(Manager *m) {
assert(m);
- if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
+ if (FLAGS_SET(m->test_run_flags, MANAGER_TEST_RUN_MINIMAL))
return;
/* Let's ask every type to load all units from disk/kernel that it might know */
}
}
-int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
+int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *root) {
int r;
assert(m);
* but we should not touch the real generator directories. */
r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope,
MANAGER_IS_TEST_RUN(m) ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0,
- NULL);
+ root);
if (r < 0)
return log_error_errno(r, "Failed to initialize path lookup table: %m");
/* This is a nop on init */
break;
+ case 25:
+ m->objective = MANAGER_REEXECUTE;
+ break;
+
case 26:
case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */
manager_restore_original_log_target(m);
if (strv_isempty(plus))
return 0;
- a = strv_env_merge(2, m->transient_environment, plus);
+ a = strv_env_merge(m->transient_environment, plus);
if (!a)
return log_oom();
}
if (!strv_isempty(plus)) {
- b = strv_env_merge(2, l, plus);
+ b = strv_env_merge(l, plus);
if (!b) {
strv_free(a);
return -ENOMEM;
assert(m);
assert(ret);
- l = strv_env_merge(2, m->transient_environment, m->client_environment);
+ l = strv_env_merge(m->transient_environment, m->client_environment);
if (!l)
return -ENOMEM;
#include "unit-name.h"
typedef enum ManagerTestRunFlags {
- MANAGER_TEST_NORMAL = 0, /* run normally */
- MANAGER_TEST_RUN_MINIMAL = 1 << 0, /* create basic data structures */
- MANAGER_TEST_RUN_BASIC = 1 << 1, /* interact with the environment */
- MANAGER_TEST_RUN_ENV_GENERATORS = 1 << 2, /* also run env generators */
- MANAGER_TEST_RUN_GENERATORS = 1 << 3, /* also run unit generators */
+ MANAGER_TEST_NORMAL = 0, /* run normally */
+ MANAGER_TEST_RUN_MINIMAL = 1 << 0, /* create basic data structures */
+ MANAGER_TEST_RUN_BASIC = 1 << 1, /* interact with the environment */
+ MANAGER_TEST_RUN_ENV_GENERATORS = 1 << 2, /* also run env generators */
+ MANAGER_TEST_RUN_GENERATORS = 1 << 3, /* also run unit generators */
+ MANAGER_TEST_RUN_IGNORE_DEPENDENCIES = 1 << 4, /* run while ignoring dependencies */
MANAGER_TEST_FULL = MANAGER_TEST_RUN_BASIC | MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_GENERATORS,
} ManagerTestRunFlags;
Manager* manager_free(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
-int manager_startup(Manager *m, FILE *serialization, FDSet *fds);
+int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *root);
Job *manager_get_job(Manager *m, uint32_t id);
Unit *manager_get_unit(Manager *m, const char *name);
namespace.h
path.c
path.h
+ restrict-ifaces.c
+ restrict-ifaces.h
scope.c
scope.h
selinux-access.c
libcore_sources += [socket_bind_skel_h]
endif
+subdir('bpf/restrict_ifaces')
+if conf.get('BPF_FRAMEWORK') == 1
+ libcore_sources += [restrict_ifaces_skel_h]
+endif
+
load_fragment_gperf_gperf = custom_target(
'load-fragment-gperf.gperf',
input : 'load-fragment-gperf.gperf.in',
meson.add_install_script('sh', '-c', mkdir_p.format(usergeneratordir))
if install_sysconfdir
- meson.add_install_script('sh', '-c', mkdir_p.format(join_paths(pkgsysconfdir, 'system')))
- meson.add_install_script('sh', '-c', mkdir_p.format(join_paths(pkgsysconfdir, 'user')))
- meson.add_install_script('sh', '-c', mkdir_p.format(join_paths(sysconfdir, 'xdg/systemd')))
+ meson.add_install_script('sh', '-c', mkdir_p.format(pkgsysconfdir / 'system'))
+ meson.add_install_script('sh', '-c', mkdir_p.format(pkgsysconfdir / 'user'))
+ meson.add_install_script('sh', '-c', mkdir_p.format(sysconfdir / 'xdg/systemd'))
endif
############################################################
return 0;
default:
- assert_not_reached("Unexpected state.");
+ assert_not_reached();
}
}
else if (code == CLD_DUMPED)
f = MOUNT_FAILURE_CORE_DUMP;
else
- assert_not_reached("Unknown code");
+ assert_not_reached();
if (IN_SET(m->state, MOUNT_REMOUNTING, MOUNT_REMOUNTING_SIGKILL, MOUNT_REMOUNTING_SIGTERM))
mount_set_reload_result(m, f);
break;
default:
- assert_not_reached("Uh, control process died at wrong time.");
+ assert_not_reached();
}
/* Notify clients about changed exit status */
break;
default:
- assert_not_reached("Timeout at wrong time.");
+ assert_not_reached();
}
return 0;
return append_static_mounts(p, protect_home_yes_table, ELEMENTSOF(protect_home_yes_table), ignore_protect);
default:
- assert_not_reached("Unexpected ProtectHome= value");
+ assert_not_reached();
}
}
return append_static_mounts(p, protect_system_full_table, ELEMENTSOF(protect_system_full_table), ignore_protect);
default:
- assert_not_reached("Unexpected ProtectSystem= value");
+ assert_not_reached();
}
}
return mount_overlay(m);
default:
- assert_not_reached("Unknown mode");
+ assert_not_reached();
}
assert(what);
static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, char **tmp_path) {
_cleanup_free_ char *x = NULL;
_cleanup_free_ char *y = NULL;
- char bid[SD_ID128_STRING_MAX];
sd_id128_t boot_id;
bool rw = true;
int r;
if (r < 0)
return r;
- x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX");
+ x = strjoin(prefix, "/systemd-private-", SD_ID128_TO_STRING(boot_id), "-", id, "-XXXXXX");
if (!x)
return -ENOMEM;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "restrict-ifaces.h"
+#include "netlink-util.h"
+
+#if BPF_FRAMEWORK
+/* libbpf, clang and llc compile time dependencies are satisfied */
+
+#include "bpf-dlopen.h"
+#include "bpf-link.h"
+
+#include "bpf/restrict_ifaces/restrict-ifaces.skel.h"
+
+static struct restrict_ifaces_bpf *restrict_ifaces_bpf_free(struct restrict_ifaces_bpf *obj) {
+ restrict_ifaces_bpf__destroy(obj);
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct restrict_ifaces_bpf *, restrict_ifaces_bpf_free);
+
+static int prepare_restrict_ifaces_bpf(Unit* u, bool is_allow_list,
+ const Set *restrict_network_interfaces,
+ struct restrict_ifaces_bpf **ret_object) {
+ _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ char *iface;
+ int r, map_fd;
+
+ assert(ret_object);
+
+ obj = restrict_ifaces_bpf__open();
+ if (!obj)
+ return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOMEM), "Failed to open BPF object");
+
+ r = sym_bpf_map__resize(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u));
+ if (r != 0)
+ return log_unit_error_errno(u, r,
+ "Failed to resize BPF map '%s': %m",
+ sym_bpf_map__name(obj->maps.sd_restrictif));
+
+ obj->rodata->is_allow_list = is_allow_list;
+
+ r = restrict_ifaces_bpf__load(obj);
+ if (r != 0)
+ return log_unit_error_errno(u, r, "Failed to load BPF object: %m");
+
+ map_fd = sym_bpf_map__fd(obj->maps.sd_restrictif);
+
+ SET_FOREACH(iface, restrict_network_interfaces) {
+ uint8_t dummy = 0;
+ int ifindex;
+ ifindex = rtnl_resolve_interface(&rtnl, iface);
+ if (ifindex < 0) {
+ log_unit_warning_errno(u, ifindex, "Couldn't find index of network interface: %m. Ignoring '%s'", iface);
+ continue;
+ }
+
+ if (sym_bpf_map_update_elem(map_fd, &ifindex, &dummy, BPF_ANY))
+ return log_unit_error_errno(u, errno, "Failed to update BPF map '%s' fd: %m", sym_bpf_map__name(obj->maps.sd_restrictif));
+ }
+
+ *ret_object = TAKE_PTR(obj);
+ return 0;
+}
+
+int restrict_network_interfaces_supported(void) {
+ _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
+ int r;
+ static int supported = -1;
+
+ if (supported >= 0)
+ return supported;
+
+ r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
+ if (r < 0) {
+ log_warning_errno(r, "Can't determine whether the unified hierarchy is used: %m");
+ supported = 0;
+ return supported;
+ }
+ if (r == 0) {
+ log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Not running with unified cgroup hierarchy, BPF is not supported");
+ supported = 0;
+ return supported;
+ }
+
+ if (dlopen_bpf() < 0)
+ return false;
+
+ if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*ifindex=*/0)) {
+ log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "BPF program type cgroup_skb is not supported");
+ supported = 0;
+ return supported;
+ }
+
+ r = prepare_restrict_ifaces_bpf(NULL, true, NULL, &obj);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to load BPF object: %m");
+
+ supported = bpf_can_link_program(obj->progs.sd_restrictif_i);
+ return supported;
+}
+
+static int restrict_network_interfaces_install_impl(Unit *u) {
+ _cleanup_(bpf_link_freep) struct bpf_link *egress_link = NULL, *ingress_link = NULL;
+ _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
+ _cleanup_free_ char *cgroup_path = NULL;
+ _cleanup_close_ int cgroup_fd = -1;
+ CGroupContext *cc;
+ int r;
+
+ cc = unit_get_cgroup_context(u);
+ if (!cc)
+ return 0;
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to get cgroup path: %m");
+
+ if (!cc->restrict_network_interfaces)
+ return 0;
+
+ r = prepare_restrict_ifaces_bpf(u,
+ cc->restrict_network_interfaces_is_allow_list,
+ cc->restrict_network_interfaces,
+ &obj);
+ if (r < 0)
+ return r;
+
+ cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY, 0);
+ if (cgroup_fd < 0)
+ return -errno;
+
+ ingress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_i, cgroup_fd);
+ r = sym_libbpf_get_error(ingress_link);
+ if (r != 0)
+ return log_unit_error_errno(u, r, "Failed to create ingress cgroup link: %m");
+
+ egress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_e, cgroup_fd);
+ r = sym_libbpf_get_error(egress_link);
+ if (r != 0)
+ return log_unit_error_errno(u, r, "Failed to create egress cgroup link: %m");
+
+ u->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link);
+ u->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link);
+
+ return 0;
+}
+
+int restrict_network_interfaces_install(Unit *u) {
+ int r = restrict_network_interfaces_install_impl(u);
+ fdset_close(u->initial_restric_ifaces_link_fds);
+ return r;
+}
+
+int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) {
+ int r;
+
+ assert(u);
+
+ r = bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_ingress_bpf_link);
+ if (r < 0)
+ return r;
+
+ return bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_egress_bpf_link);
+}
+
+int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) {
+ int r;
+
+ assert(u);
+
+ if (!u->initial_restric_ifaces_link_fds) {
+ u->initial_restric_ifaces_link_fds = fdset_new();
+ if (!u->initial_restric_ifaces_link_fds)
+ return log_oom();
+ }
+
+ r = fdset_put(u->initial_restric_ifaces_link_fds, fd);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to put restrict-ifaces-bpf-fd %d to restored fdset: %m", fd);
+
+ return 0;
+}
+
+#else /* ! BPF_FRAMEWORK */
+int restrict_network_interfaces_supported(void) {
+ return 0;
+}
+
+int restrict_network_interfaces_install(Unit *u) {
+ return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Failed to install RestrictInterfaces: BPF programs built from source code are not supported: %m");
+}
+
+int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) {
+ return 0;
+}
+
+int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) {
+ return 0;
+}
+#endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "fdset.h"
+#include "unit.h"
+
+typedef struct Unit Unit;
+
+int restrict_network_interfaces_supported(void);
+int restrict_network_interfaces_install(Unit *u);
+
+int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds);
+
+/* Add BPF link fd created before daemon-reload or daemon-reexec.
+ * FDs will be closed at the end of restrict_network_interfaces_install. */
+int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd);
break;
default:
- assert_not_reached("Timeout at wrong time.");
+ assert_not_reached();
}
return 0;
if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
xsprintf(gid_buf, GID_FMT, gid);
- snprintf(msgbuf, msgbufsize,
- "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
- login_uid_buf, uid_buf, gid_buf,
- audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
- audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
+ (void) snprintf(msgbuf, msgbufsize,
+ "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
+ login_uid_buf, uid_buf, gid_buf,
+ audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
+ audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
return 0;
}
}
static int service_load_pid_file(Service *s, bool may_warn) {
- char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
bool questionable_pid_file = false;
_cleanup_free_ char *k = NULL;
_cleanup_close_ int fd = -1;
/* Let's read the PID file now that we chased it down. But we need to convert the O_PATH fd
* chase_symlinks() returned us into a proper fd first. */
- xsprintf(procfs, "/proc/self/fd/%i", fd);
- r = read_one_line_file(procfs, &k);
+ r = read_one_line_file(FORMAT_PROC_FD_PATH(fd), &k);
if (r < 0)
return log_unit_error_errno(UNIT(s), r,
"Can't convert PID files %s O_PATH file descriptor to proper file descriptor: %m",
if (r < 0)
return r;
- final_env = strv_env_merge(2, exec_params.environment, our_env, NULL);
+ final_env = strv_env_merge(exec_params.environment, our_env);
if (!final_env)
return -ENOMEM;
return IN_SET(s->result, SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP);
default:
- assert_not_reached("unknown restart setting");
+ assert_not_reached();
}
}
service_set_main_pid(s, pid);
service_set_state(s, SERVICE_START);
} else
- assert_not_reached("Unknown service type");
+ assert_not_reached();
return;
return -ENOMEM;
break;
default:
- assert_not_reached("Logic error in exec command deserialization");
+ assert_not_reached();
}
}
else if (code == CLD_DUMPED)
f = SERVICE_FAILURE_CORE_DUMP;
else
- assert_not_reached("Unknown code");
+ assert_not_reached();
if (s->main_pid == pid) {
/* Clean up the exec_fd event source. We want to do this here, not later in
break;
default:
- assert_not_reached("Uh, main process died at wrong time.");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Uh, control process died at wrong time.");
+ assert_not_reached();
}
}
} else /* Neither control nor main PID? If so, don't notify about anything */
break;
default:
- assert_not_reached("unknown timeout mode");
+ assert_not_reached();
}
break;
break;
default:
- assert_not_reached("unknown timeout mode");
+ assert_not_reached();
}
break;
break;
default:
- assert_not_reached("unknown timeout mode");
+ assert_not_reached();
}
break;
break;
default:
- assert_not_reached("Timeout at wrong time.");
+ assert_not_reached();
}
return 0;
else if (s->peer.sa.sa_family == AF_VSOCK)
siphash24_compress(&s->peer.vm.svm_cid, sizeof(s->peer.vm.svm_cid), state);
else
- assert_not_reached("Unknown address family.");
+ assert_not_reached();
}
static int peer_address_compare_func(const SocketPeer *x, const SocketPeer *y) {
case AF_VSOCK:
return CMP(x->peer.vm.svm_cid, y->peer.vm.svm_cid);
}
- assert_not_reached("Black sheep in the family!");
+ assert_not_reached();
}
DEFINE_PRIVATE_HASH_OPS(peer_address_hash_ops, SocketPeer, peer_address_hash_func, peer_address_compare_func);
else if (type == SOCK_SEQPACKET)
return "ListenSequentialPacket";
- assert_not_reached("Unknown socket type");
+ assert_not_reached();
return NULL;
}
break;
default:
- assert_not_reached("Unhandled socket type.");
+ assert_not_reached();
}
*instance = r;
break;
}
default:
- assert_not_reached("Unknown port type");
+ assert_not_reached();
}
}
else if (code == CLD_DUMPED)
f = SOCKET_FAILURE_CORE_DUMP;
else
- assert_not_reached("Unknown sigchld code");
+ assert_not_reached();
if (s->control_command) {
exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
break;
default:
- assert_not_reached("Uh, control process died at wrong time.");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Timeout at wrong time.");
+ assert_not_reached();
}
return 0;
return 0;
default:
- assert_not_reached("Unexpected state.");
+ assert_not_reached();
}
}
else if (code == CLD_DUMPED)
f = SWAP_FAILURE_CORE_DUMP;
else
- assert_not_reached("Unknown code");
+ assert_not_reached();
if (s->result == SWAP_SUCCESS)
s->result = f;
break;
default:
- assert_not_reached("Uh, control process died at wrong time.");
+ assert_not_reached();
}
/* Notify clients about changed exit status */
break;
default:
- assert_not_reached("Timeout at wrong time.");
+ assert_not_reached();
}
return 0;
if (streq_ptr(s->what, s->devnode))
return NULL;
- LIST_FOREACH_AFTER(same_devnode, other, s)
+ LIST_FOREACH(same_devnode, other, s->same_devnode_next)
if (streq_ptr(other->what, other->devnode))
return UNIT(other);
- LIST_FOREACH_BEFORE(same_devnode, other, s) {
+ LIST_FOREACH_BACKWARDS(same_devnode, other, s->same_devnode_prev) {
if (streq_ptr(other->what, other->devnode))
return UNIT(other);
break;
default:
- assert_not_reached("Unknown timer base");
+ assert_not_reached();
}
v->next_elapse = usec_add(usec_shift_clock(base, CLOCK_MONOTONIC, TIMER_MONOTONIC_CLOCK(t)), v->value);
break;
default:
- assert_not_reached("Unknown timer state");
+ assert_not_reached();
}
}
#include "fileio.h"
#include "format-util.h"
#include "parse-util.h"
+#include "restrict-ifaces.h"
#include "serialize.h"
#include "string-table.h"
#include "unit-serialize.h"
(void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-ingress-installed", u->ip_bpf_custom_ingress_installed);
(void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-egress-installed", u->ip_bpf_custom_egress_installed);
+ (void) serialize_restrict_network_interfaces(u, f, fds);
+
if (uid_is_valid(u->ref_uid))
(void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
if (gid_is_valid(u->ref_gid))
(void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_egress_installed);
continue;
+ } else if (streq(l, "restrict-ifaces-bpf-fd")) {
+ int fd;
+
+ if (safe_atoi(v, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) {
+ log_unit_debug(u, "Failed to parse restrict-ifaces-bpf-fd value: %s", v);
+ continue;
+ }
+ if (fdset_remove(fds, fd) < 0) {
+ log_unit_debug(u, "Failed to remove restrict-ifaces-bpf-fd %d from fdset", fd);
+ continue;
+ }
+
+ (void) restrict_network_interfaces_add_initial_link_fd(u, fd);
+ continue;
+
} else if (streq(l, "ref-uid")) {
uid_t uid;
break;
default:
- assert_not_reached("Unknown garbage collection mode");
+ assert_not_reached();
}
if (u->cgroup_path) {
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
if (r < 0)
- log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", u->cgroup_path);
+ log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", empty_to_root(u->cgroup_path));
if (r <= 0)
return false;
}
if (u->in_dbus_queue)
LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
+ if (u->in_cleanup_queue)
+ LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u);
+
if (u->in_gc_queue)
LIST_REMOVE(gc_queue, u->manager->gc_unit_queue, u);
if (u->in_cgroup_empty_queue)
LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u);
- if (u->in_cleanup_queue)
- LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u);
+ if (u->in_cgroup_oom_queue)
+ LIST_REMOVE(cgroup_oom_queue, u->manager->cgroup_oom_queue, u);
if (u->in_target_deps_queue)
LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
bpf_program_unref(u->bpf_device_control_installed);
+#if BPF_FRAMEWORK
+ bpf_link_free(u->restrict_ifaces_ingress_bpf_link);
+ bpf_link_free(u->restrict_ifaces_egress_bpf_link);
+#endif
+ fdset_free(u->initial_restric_ifaces_link_fds);
+
condition_free_list(u->conditions);
condition_free_list(u->asserts);
break;
default:
- assert_not_reached("Job type unknown");
+ assert_not_reached();
}
return unexpected;
return unit_can_reload(u) && unit_can_start(u);
default:
- assert_not_reached("Invalid job type");
+ assert_not_reached();
}
}
return 0;
}
+ if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
+ return 0;
+
/* Note that ordering a device unit after a unit is permitted since it allows to start its job
* running timeout at a specific time. */
if (FLAGS_SET(a, UNIT_ATOM_BEFORE) && other->type == UNIT_DEVICE) {
if (r < 0)
return r;
+ if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
+ return 0;
+
r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0)
return r;
if (r < 0)
return r;
+ if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
+ return 0;
+
r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0)
return r;
assert(u);
+ if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
+ return 0;
+
if (UNIT_GET_SLICE(u))
return 0;
int unit_coldplug(Unit *u) {
int r = 0, q;
char **i;
- Job *uj;
assert(u);
r = q;
}
- uj = u->job ?: u->nop_job;
- if (uj) {
- q = job_coldplug(uj);
+ if (u->job) {
+ q = job_coldplug(u->job);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
+ if (u->nop_job) {
+ q = job_coldplug(u->nop_job);
if (q < 0 && r >= 0)
r = q;
}
if (UNIT_VTABLE(u)->catchup)
UNIT_VTABLE(u)->catchup(u);
+
+ unit_cgroup_catchup(u);
}
static bool fragment_mtime_newer(const char *path, usec_t mtime, bool path_masked) {
return c->watchdog_signal;
default:
- assert_not_reached("KillOperation unknown");
+ assert_not_reached();
}
}
log_func, u);
if (r < 0) {
if (!IN_SET(r, -EAGAIN, -ESRCH, -ENOENT))
- log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path);
+ log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
} else if (r > 0) {
if (u->cgroup_path) {
r = cg_attach_everywhere(u->manager->cgroup_supported, u->cgroup_path, 0, NULL, NULL);
if (r < 0) {
- log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", u->cgroup_path);
+ log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", empty_to_root(u->cgroup_path));
_exit(EXIT_CGROUP);
}
}
/* Returns the file system path to use for MAC access decisions, i.e. the file to read the SELinux label off
* when validating access checks. */
+ if (IN_SET(u->load_state, UNIT_MASKED, UNIT_NOT_FOUND, UNIT_MERGED))
+ return NULL; /* Shortcut things if we know there is no real, relevant unit file around */
+
p = u->source_path ?: u->fragment_path;
if (!p)
return NULL;
+ if (IN_SET(u->load_state, UNIT_LOADED, UNIT_BAD_SETTING, UNIT_ERROR))
+ return p; /* Shortcut things, if we successfully loaded at least some stuff from the unit file */
+
+ /* Not loaded yet, we need to go to disk */
+ assert(u->load_state == UNIT_STUB);
+
/* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense */
if (null_or_empty_path(p) > 0)
return NULL;
struct bpf_link *ipv6_socket_bind_link;
#endif
+ FDSet *initial_restric_ifaces_link_fds;
+#if BPF_FRAMEWORK
+ struct bpf_link *restrict_ifaces_ingress_bpf_link;
+ struct bpf_link *restrict_ifaces_egress_bpf_link;
+#endif
+
/* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new
* ones which might have appeared. */
sd_event_source *rewatch_pids_event_source;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
program_header->p_offset,
program_header->p_filesz,
ELF_T_NHDR);
+ if (!data)
+ continue;
while (note_offset < data->d_size &&
(note_offset = gelf_getnote(data, note_offset, ¬e_header, &name_offset, &desc_offset)) > 0) {
return r;
default:
- assert_not_reached("Unexpected transcoding mode");
+ assert_not_reached();
}
}
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
#include "ask-password-api.h"
#include "cryptenroll-password.h"
+#include "env-util.h"
#include "escape.h"
#include "memory-util.h"
#include "pwquality-util.h"
if (!new_password)
return log_oom();
- string_erase(e);
- assert_se(unsetenv("NEWPASSWORD") == 0);
+ assert_se(unsetenv_erase("NEWPASSWORD") >= 0);
} else {
_cleanup_free_ char *disk_path = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
size_t secret_size, secret2_size, blob_size, hash_size;
_cleanup_free_ void *blob = NULL, *hash = NULL;
+ uint16_t pcr_bank;
const char *node;
int r, keyslot;
assert_se(node = crypt_get_device_name(cd));
- r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size);
+ r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank);
if (r < 0)
return r;
/* Quick verification that everything is in order, we are not in a hurry after all. */
log_debug("Unsealing for verification...");
- r = tpm2_unseal(device, pcr_mask, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
+ r = tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
if (r < 0)
return r;
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
- r = tpm2_make_luks2_json(keyslot, pcr_mask, blob, blob_size, hash, hash_size, &v);
+ r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v);
if (r < 0)
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
break;
default:
- assert_not_reached("Unexpected wipe scope");
+ assert_not_reached();
}
/* Then add all slots that match a token type */
#include "cryptenroll-wipe.h"
#include "cryptenroll.h"
#include "cryptsetup-util.h"
+#include "env-util.h"
#include "escape.h"
#include "libfido2-util.h"
#include "main-func.h"
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
if (!password)
return log_oom();
- string_erase(e);
- assert_se(unsetenv("PASSWORD") >= 0);
+ assert_se(unsetenv_erase("PASSWORD") >= 0);
r = crypt_volume_key_get(
cd,
#include "ask-password-api.h"
#include "cryptsetup-fido2.h"
+#include "env-util.h"
#include "fileio.h"
#include "hexdecoct.h"
#include "json.h"
if (!pins)
return log_oom();
- string_erase(e);
- if (unsetenv("PIN") < 0)
- return log_error_errno(errno, "Failed to unset $PIN: %m");
+ assert_se(unsetenv_erase("PIN") >= 0);
}
for (;;) {
#include "stat-util.h"
#include "strv.h"
-struct pkcs11_callback_data {
- const char *friendly_name;
- usec_t until;
- void *encrypted_key;
- size_t encrypted_key_size;
- void *decrypted_key;
- size_t decrypted_key_size;
- bool free_encrypted_key;
- bool headless;
-};
-
-static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
- erase_and_free(data->decrypted_key);
-
- if (data->free_encrypted_key)
- free(data->encrypted_key);
-}
-
-static int pkcs11_callback(
- CK_FUNCTION_LIST *m,
- CK_SESSION_HANDLE session,
- CK_SLOT_ID slot_id,
- const CK_SLOT_INFO *slot_info,
- const CK_TOKEN_INFO *token_info,
- P11KitUri *uri,
- void *userdata) {
-
- struct pkcs11_callback_data *data = userdata;
- CK_OBJECT_HANDLE object;
- int r;
-
- assert(m);
- assert(slot_info);
- assert(token_info);
- assert(uri);
- assert(data);
-
- /* Called for every token matching our URI */
-
- r = pkcs11_token_login(
- m,
- session,
- slot_id,
- token_info,
- data->friendly_name,
- "drive-harddisk",
- "pkcs11-pin",
- "cryptsetup.pkcs11-pin",
- data->until,
- data->headless,
- NULL);
- if (r < 0)
- return r;
-
- /* We are likely called during early boot, where entropy is scarce. Mix some data from the PKCS#11
- * token, if it supports that. It should be cheap, given that we already are talking to it anyway and
- * shouldn't hurt. */
- (void) pkcs11_token_acquire_rng(m, session);
-
- r = pkcs11_token_find_private_key(m, session, uri, &object);
- if (r < 0)
- return r;
-
- r = pkcs11_token_decrypt_data(
- m,
- session,
- object,
- data->encrypted_key,
- data->encrypted_key_size,
- &data->decrypted_key,
- &data->decrypted_key_size);
- if (r < 0)
- return r;
-
- return 0;
-}
-
int decrypt_pkcs11_key(
const char *volume_name,
const char *friendly_name,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
- _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
+ _cleanup_(pkcs11_crypt_device_callback_data_release) pkcs11_crypt_device_callback_data data = {
.friendly_name = friendly_name,
.until = until,
.headless = headless,
data.free_encrypted_key = true;
}
- r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data);
+ r = pkcs11_find_token(pkcs11_uri, pkcs11_crypt_device_callback, &data);
if (r < 0)
return r;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <libcryptsetup.h>
+#include <string.h>
+
+#include "cryptsetup-token.h"
+#include "cryptsetup-token-util.h"
+#include "hexdecoct.h"
+#include "json.h"
+#include "luks2-fido2.h"
+#include "memory-util.h"
+#include "version.h"
+
+#define TOKEN_NAME "systemd-fido2"
+#define TOKEN_VERSION_MAJOR "1"
+#define TOKEN_VERSION_MINOR "0"
+
+/* for libcryptsetup debug purpose */
+_public_ const char *cryptsetup_token_version(void) {
+ return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")";
+}
+
+_public_ int cryptsetup_token_open_pin(
+ struct crypt_device *cd, /* is always LUKS2 context */
+ int token /* is always >= 0 */,
+ const char *pin,
+ size_t pin_size,
+ char **password, /* freed by cryptsetup_token_buffer_free */
+ size_t *password_len,
+ void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+ int r;
+ const char *json;
+ _cleanup_(erase_and_freep) char *pin_string = NULL;
+
+ assert(!pin || pin_size);
+ assert(token >= 0);
+
+ /* This must not fail at this moment (internal error) */
+ r = crypt_token_json_get(cd, token, &json);
+ assert(token == r);
+ assert(json);
+
+ if (pin && memchr(pin, 0, pin_size - 1))
+ return crypt_log_error_errno(cd, ENOANO, "PIN must be characters string.");
+
+ /* pin was passed as pin = pin, pin_size = strlen(pin). We need to add terminating
+ * NULL byte to addressable memory*/
+ if (pin && pin[pin_size-1] != '\0') {
+ pin_string = strndup(pin, pin_size);
+ if (!pin_string)
+ return crypt_log_oom(cd);
+ }
+
+ return acquire_luks2_key(cd, json, (const char *)usrptr, pin_string ?: pin, password, password_len);
+}
+
+/*
+ * This function is called from within following libcryptsetup calls
+ * provided conditions further below are met:
+ *
+ * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-fido2'):
+ *
+ * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
+ * (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
+ * and token is assigned to at least single keyslot).
+ *
+ * - if plugin defines validate funtion (see cryptsetup_token_validate below) it must have
+ * passed the check (aka return 0)
+ */
+_public_ int cryptsetup_token_open(
+ struct crypt_device *cd, /* is always LUKS2 context */
+ int token /* is always >= 0 */,
+ char **password, /* freed by cryptsetup_token_buffer_free */
+ size_t *password_len,
+ void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+ return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr);
+}
+
+/*
+ * libcryptsetup callback for memory deallocation of 'password' parameter passed in
+ * any crypt_token_open_* plugin function
+ */
+_public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) {
+ erase_and_free(buffer);
+}
+
+/*
+ * prints systemd-fido2 token content in crypt_dump().
+ * 'type' and 'keyslots' fields are printed by libcryptsetup
+ */
+_public_ void cryptsetup_token_dump(
+ struct crypt_device *cd /* is always LUKS2 context */,
+ const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
+
+ int r;
+ Fido2EnrollFlags required;
+ size_t cid_size, salt_size;
+ const char *client_pin_req_str, *up_req_str, *uv_req_str;
+ _cleanup_free_ void *cid = NULL, *salt = NULL;
+ _cleanup_free_ char *rp_id = NULL, *cid_str = NULL, *salt_str = NULL;
+
+ assert(json);
+
+ r = parse_luks2_fido2_data(cd, json, &rp_id, &salt, &salt_size, &cid, &cid_size, &required);
+ if (r < 0)
+ return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
+
+ r = crypt_dump_buffer_to_hex_string(cid, cid_size, &cid_str);
+ if (r < 0)
+ return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
+
+ r = crypt_dump_buffer_to_hex_string(salt, salt_size, &salt_str);
+ if (r < 0)
+ return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
+
+ if (required & FIDO2ENROLL_PIN)
+ client_pin_req_str = "true";
+ else if (required & FIDO2ENROLL_PIN_IF_NEEDED)
+ client_pin_req_str = NULL;
+ else
+ client_pin_req_str = "false";
+
+ if (required & FIDO2ENROLL_UP)
+ up_req_str = "true";
+ else if (required & FIDO2ENROLL_UP_IF_NEEDED)
+ up_req_str = NULL;
+ else
+ up_req_str = "false";
+
+ if (required & FIDO2ENROLL_UV)
+ uv_req_str = "true";
+ else if (required & FIDO2ENROLL_UV_OMIT)
+ uv_req_str = NULL;
+ else
+ uv_req_str = "false";
+
+ crypt_log(cd, "\tfido2-credential:" CRYPT_DUMP_LINE_SEP "%s\n", cid_str);
+ crypt_log(cd, "\tfido2-salt: %s\n", salt_str);
+
+ /* optional fields */
+ if (rp_id)
+ crypt_log(cd, "\tfido2-rp: %s\n", rp_id);
+ if (client_pin_req_str)
+ crypt_log(cd, "\tfido2-clientPin-required:" CRYPT_DUMP_LINE_SEP "%s\n",
+ client_pin_req_str);
+ if (up_req_str)
+ crypt_log(cd, "\tfido2-up-required:" CRYPT_DUMP_LINE_SEP "%s\n", up_req_str);
+ if (uv_req_str)
+ crypt_log(cd, "\tfido2-uv-required:" CRYPT_DUMP_LINE_SEP "%s\n", uv_req_str);
+}
+
+/*
+ * Note:
+ * If plugin is available in library path, it's called in before following libcryptsetup calls:
+ *
+ * crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour
+ */
+_public_ int cryptsetup_token_validate(
+ struct crypt_device *cd, /* is always LUKS2 context */
+ const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-tpm2' */) {
+
+ int r;
+ JsonVariant *w;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ assert(json);
+
+ r = json_parse(json, 0, &v, NULL, NULL);
+ if (r < 0)
+ return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
+
+ w = json_variant_by_key(v, "fido2-credential");
+ if (!w || !json_variant_is_string(w)) {
+ crypt_log_debug(cd, "FIDO2 token data lacks 'fido2-credential' field.");
+ return 1;
+ }
+
+ r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+ if (r < 0)
+ return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'fido2-credential' field: %m");
+
+ w = json_variant_by_key(v, "fido2-salt");
+ if (!w || !json_variant_is_string(w)) {
+ crypt_log_debug(cd, "FIDO2 token data lacks 'fido2-salt' field.");
+ return 1;
+ }
+
+ r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+ if (r < 0)
+ return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded salt: %m.");
+
+ /* The "rp" field is optional. */
+ w = json_variant_by_key(v, "fido2-rp");
+ if (w && !json_variant_is_string(w)) {
+ crypt_log_debug(cd, "FIDO2 token data's 'fido2-rp' field is not a string.");
+ return 1;
+ }
+
+ /* The "fido2-clientPin-required" field is optional. */
+ w = json_variant_by_key(v, "fido2-clientPin-required");
+ if (w && !json_variant_is_boolean(w)) {
+ crypt_log_debug(cd, "FIDO2 token data's 'fido2-clientPin-required' field is not a boolean.");
+ return 1;
+ }
+
+ /* The "fido2-up-required" field is optional. */
+ w = json_variant_by_key(v, "fido2-up-required");
+ if (w && !json_variant_is_boolean(w)) {
+ crypt_log_debug(cd, "FIDO2 token data's 'fido2-up-required' field is not a boolean.");
+ return 1;
+ }
+
+ /* The "fido2-uv-required" field is optional. */
+ w = json_variant_by_key(v, "fido2-uv-required");
+ if (w && !json_variant_is_boolean(w)) {
+ crypt_log_debug(cd, "FIDO2 token data's 'fido2-uv-required' field is not a boolean.");
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <libcryptsetup.h>
+
+#include "cryptsetup-token.h"
+#include "cryptsetup-token-util.h"
+#include "hexdecoct.h"
+#include "json.h"
+#include "luks2-pkcs11.h"
+#include "memory-util.h"
+#include "pkcs11-util.h"
+#include "version.h"
+
+#define TOKEN_NAME "systemd-pkcs11"
+#define TOKEN_VERSION_MAJOR "1"
+#define TOKEN_VERSION_MINOR "0"
+
+/* for libcryptsetup debug purpose */
+_public_ const char *cryptsetup_token_version(void) {
+ return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")";
+}
+
+_public_ int cryptsetup_token_open_pin(
+ struct crypt_device *cd, /* is always LUKS2 context */
+ int token /* is always >= 0 */,
+ const char *pin,
+ size_t pin_size,
+ char **password, /* freed by cryptsetup_token_buffer_free */
+ size_t *password_len,
+ void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+ const char *json;
+ int r;
+
+ assert(!pin || pin_size);
+ assert(token >= 0);
+
+ /* This must not fail at this moment (internal error) */
+ r = crypt_token_json_get(cd, token, &json);
+ assert(token == r);
+ assert(json);
+
+ return acquire_luks2_key(cd, json, usrptr, pin, pin_size, password, password_len);
+}
+
+/*
+ * This function is called from within following libcryptsetup calls
+ * provided conditions further below are met:
+ *
+ * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-pkcs11'):
+ *
+ * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
+ * (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
+ * and token is assigned to at least single keyslot).
+ *
+ * - if plugin defines validate funtion (see cryptsetup_token_validate below) it must have
+ * passed the check (aka return 0)
+ */
+_public_ int cryptsetup_token_open(
+ struct crypt_device *cd, /* is always LUKS2 context */
+ int token /* is always >= 0 */,
+ char **password, /* freed by cryptsetup_token_buffer_free */
+ size_t *password_len,
+ void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+ return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr);
+}
+
+/*
+ * libcryptsetup callback for memory deallocation of 'password' parameter passed in
+ * any crypt_token_open_* plugin function
+ */
+_public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) {
+ erase_and_free(buffer);
+}
+
+/*
+ * prints systemd-pkcs11 token content in crypt_dump().
+ * 'type' and 'keyslots' fields are printed by libcryptsetup
+ */
+_public_ void cryptsetup_token_dump(
+ struct crypt_device *cd /* is always LUKS2 context */,
+ const char *json /* validated 'systemd-pkcs11' token if cryptsetup_token_validate is defined */) {
+
+ int r;
+ size_t pkcs11_key_size;
+ _cleanup_free_ char *pkcs11_uri = NULL, *key_str = NULL;
+ _cleanup_free_ void *pkcs11_key = NULL;
+
+ r = parse_luks2_pkcs11_data(cd, json, &pkcs11_uri, &pkcs11_key, &pkcs11_key_size);
+ if (r < 0)
+ return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
+
+ r = crypt_dump_buffer_to_hex_string(pkcs11_key, pkcs11_key_size, &key_str);
+ if (r < 0)
+ return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
+
+ crypt_log(cd, "\tpkcs11-uri: %s\n", pkcs11_uri);
+ crypt_log(cd, "\tpkcs11-key: %s\n", key_str);
+}
+
+/*
+ * Note:
+ * If plugin is available in library path, it's called in before following libcryptsetup calls:
+ *
+ * crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour
+ */
+_public_ int cryptsetup_token_validate(
+ struct crypt_device *cd, /* is always LUKS2 context */
+ const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-pkcs11' */) {
+
+ int r;
+ JsonVariant *w;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ r = json_parse(json, 0, &v, NULL, NULL);
+ if (r < 0)
+ return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
+
+ w = json_variant_by_key(v, "pkcs11-uri");
+ if (!w || !json_variant_is_string(w)) {
+ crypt_log_debug(cd, "PKCS#11 token data lacks 'pkcs11-uri' field.");
+ return 1;
+ }
+
+ if (!pkcs11_uri_valid(json_variant_string(w))) {
+ crypt_log_debug(cd, "PKCS#11 token data contains invalid PKCS#11 URI.");
+ return 1;
+ }
+
+ w = json_variant_by_key(v, "pkcs11-key");
+ if (!w || !json_variant_is_string(w)) {
+ crypt_log_debug(cd, "PKCS#11 token data lacks 'pkcs11-key' field.");
+ return 1;
+ }
+
+ r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+ if (r < 0)
+ return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m.");
+
+ return 0;
+}
* (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
* and token is assigned to at least single keyslot).
*
- * - if plugin defines validate funtion (see cryptsetup_token_validate below) it must have
+ * - if plugin defines validate function (see cryptsetup_token_validate below) it must have
* passed the check (aka return 0)
*/
_public_ int cryptsetup_token_open(
const char *json;
size_t blob_size, policy_hash_size, decrypted_key_size;
uint32_t pcr_mask;
+ uint16_t pcr_bank;
systemd_tpm2_plugin_params params = {
.search_pcr_mask = UINT32_MAX
};
if (usrptr)
params = *(systemd_tpm2_plugin_params *)usrptr;
- r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &base64_blob, &hex_policy_hash);
+ r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &base64_blob, &hex_policy_hash);
if (r < 0)
return log_debug_open_error(cd, r);
r = acquire_luks2_key(
pcr_mask,
+ pcr_bank,
params.device,
blob,
blob_size,
if (r < 0)
return log_debug_open_error(cd, r);
- /* free'd automaticaly by libcryptsetup */
+ /* free'd automatically by libcryptsetup */
*password_len = strlen(base64_encoded);
*password = TAKE_PTR(base64_encoded);
const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
int r;
- uint32_t i, pcr_mask;
+ uint32_t pcr_mask;
+ uint16_t pcr_bank;
size_t decoded_blob_size;
_cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL,
*pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL;
assert(json);
- r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &base64_blob, &hex_policy_hash);
+ r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &base64_blob, &hex_policy_hash);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
- for (i = 0; i < TPM2_PCRS_MAX; i++) {
+ for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) {
if ((pcr_mask & (UINT32_C(1) << i)) &&
((r = strextendf_with_separator(&pcrs_str, ", ", "%" PRIu32, i)) < 0))
return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
- crypt_log(cd, "\ttpm2-pcrs: %s\n", pcrs_str ?: "");
+ crypt_log(cd, "\ttpm2-pcrs: %s\n", strna(pcrs_str));
+ crypt_log(cd, "\ttpm2-bank: %s\n", strna(tpm2_pcr_bank_to_string(pcr_bank)));
crypt_log(cd, "\ttmp2-blob: %s\n", blob_str);
crypt_log(cd, "\ttmp2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
}
}
}
+ /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded to SHA256 */
+ w = json_variant_by_key(v, "tpm2-pcr-bank");
+ if (w) {
+ /* The PCR bank field is optional */
+
+ if (!json_variant_is_string(w)) {
+ crypt_log_debug(cd, "TPM2 PCR bank is not a string.");
+ return 1;
+ }
+
+ if (tpm2_pcr_bank_from_string(json_variant_string(w)) < 0) {
+ crypt_log_debug(cd, "TPM2 PCR bank invalid or not supported: %s.", json_variant_string(w));
+ return 1;
+ }
+ }
+
w = json_variant_by_key(v, "tpm2-blob");
if (!w || !json_variant_is_string(w)) {
crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-blob' field.");
#pragma once
+#include <stdbool.h>
#include <stddef.h>
+#include <libcryptsetup.h>
/* crypt_dump() internal indentation magic */
#define CRYPT_DUMP_LINE_SEP "\n\t "
-#define crypt_log_debug(cd, ...) crypt_logf(cd, CRYPT_LOG_DEBUG, __VA_ARGS__)
-#define crypt_log_error(cd, ...) crypt_logf(cd, CRYPT_LOG_ERROR, __VA_ARGS__)
-#define crypt_log(cd, ...) crypt_logf(cd, CRYPT_LOG_NORMAL, __VA_ARGS__)
+#define crypt_log_debug(cd, ...) crypt_logf(cd, CRYPT_LOG_DEBUG, __VA_ARGS__)
+#define crypt_log_error(cd, ...) crypt_logf(cd, CRYPT_LOG_ERROR, __VA_ARGS__)
+#define crypt_log_verbose(cd, ...) crypt_logf(cd, CRYPT_LOG_VERBOSE, __VA_ARGS__)
+#define crypt_log(cd, ...) crypt_logf(cd, CRYPT_LOG_NORMAL, __VA_ARGS__)
-#define crypt_log_debug_errno(cd, e, ...) ({ \
+#define crypt_log_full_errno(cd, e, lvl, ...) ({ \
int _e = abs(e), _s = errno; \
errno = _e; \
- crypt_logf(cd, CRYPT_LOG_DEBUG, __VA_ARGS__); \
+ crypt_logf(cd, lvl, __VA_ARGS__); \
errno = _s; \
-_e; \
})
+#define crypt_log_debug_errno(cd, e, ...) \
+ crypt_log_full_errno(cd, e, CRYPT_LOG_DEBUG, __VA_ARGS__)
+
+#define crypt_log_error_errno(cd, e, ...) \
+ crypt_log_full_errno(cd, e, CRYPT_LOG_ERROR, __VA_ARGS__)
+
+#define crypt_log_oom(cd) crypt_log_error_errno(cd, ENOMEM, "Not enough memory.")
+
int crypt_dump_buffer_to_hex_string(
const char *buf,
size_t buf_size,
int cryptsetup_token_open(struct crypt_device *cd, int token,
char **password, size_t *password_len, void *usrptr);
+int cryptsetup_token_open_pin(struct crypt_device *cd, int token,
+ const char *pin, size_t pin_size,
+ char **password, size_t *password_len, void *usrptr);
+
void cryptsetup_token_dump(struct crypt_device *cd, const char *json);
int cryptsetup_token_validate(struct crypt_device *cd, const char *json);
CRYPTSETUP_TOKEN_1.0 {
global:
cryptsetup_token_open;
+ cryptsetup_token_open_pin;
cryptsetup_token_buffer_free;
cryptsetup_token_validate;
cryptsetup_token_dump;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <libcryptsetup.h>
+
+#include "cryptsetup-token-util.h"
+#include "hexdecoct.h"
+#include "json.h"
+#include "luks2-fido2.h"
+#include "memory-util.h"
+#include "strv.h"
+
+int acquire_luks2_key(
+ struct crypt_device *cd,
+ const char *json,
+ const char *device,
+ const char *pin,
+ char **ret_keyslot_passphrase,
+ size_t *ret_keyslot_passphrase_size) {
+
+ int r;
+ Fido2EnrollFlags required;
+ size_t cid_size, salt_size, decrypted_key_size;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_free_ void *cid = NULL, *salt = NULL;
+ _cleanup_free_ char *rp_id = NULL;
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+ _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+ _cleanup_strv_free_erase_ char **pins = NULL;
+
+ assert(ret_keyslot_passphrase);
+ assert(ret_keyslot_passphrase_size);
+
+ r = parse_luks2_fido2_data(cd, json, &rp_id, &salt, &salt_size, &cid, &cid_size, &required);
+ if (r < 0)
+ return r;
+
+ if (pin) {
+ pins = strv_new(pin);
+ if (!pins)
+ return crypt_log_oom(cd);
+ }
+
+ /* configured to use pin but none was provided */
+ if ((required & FIDO2ENROLL_PIN) && strv_isempty(pins))
+ return -ENOANO;
+
+ r = fido2_use_hmac_hash(
+ device,
+ rp_id ?: "io.systemd.cryptsetup",
+ salt, salt_size,
+ cid, cid_size,
+ pins,
+ required,
+ &decrypted_key,
+ &decrypted_key_size);
+ if (r == -ENOLCK) /* libcryptsetup returns -ENOANO also on wrong pin */
+ r = -ENOANO;
+ if (r < 0)
+ return r;
+
+ /* Before using this key as passphrase we base64 encode it, for compat with homed */
+ r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (r < 0)
+ return crypt_log_error_errno(cd, r, "Can not base64 encode key: %m");
+
+ *ret_keyslot_passphrase = TAKE_PTR(base64_encoded);
+ *ret_keyslot_passphrase_size = strlen(*ret_keyslot_passphrase);
+
+ return 0;
+}
+
+/* this function expects valid "systemd-fido2" in json */
+int parse_luks2_fido2_data(
+ struct crypt_device *cd,
+ const char *json,
+ char **ret_rp_id,
+ void **ret_salt,
+ size_t *ret_salt_size,
+ void **ret_cid,
+ size_t *ret_cid_size,
+ Fido2EnrollFlags *ret_required) {
+
+ _cleanup_free_ void *cid = NULL, *salt = NULL;
+ size_t cid_size = 0, salt_size = 0;
+ _cleanup_free_ char *rp = NULL;
+ int r;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ JsonVariant *w;
+ Fido2EnrollFlags required = 0;
+
+ assert(json);
+ assert(ret_rp_id);
+ assert(ret_salt);
+ assert(ret_salt_size);
+ assert(ret_cid);
+ assert(ret_cid_size);
+ assert(ret_required);
+
+ r = json_parse(json, 0, &v, NULL, NULL);
+ if (r < 0)
+ return crypt_log_error_errno(cd, r, "Failed to parse JSON token data: %m");
+
+ w = json_variant_by_key(v, "fido2-credential");
+ if (!w)
+ return -EINVAL;
+
+ r = unbase64mem(json_variant_string(w), SIZE_MAX, &cid, &cid_size);
+ if (r < 0)
+ return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-credentials' field: %m");
+
+ w = json_variant_by_key(v, "fido2-salt");
+ if (!w)
+ return -EINVAL;
+
+ r = unbase64mem(json_variant_string(w), SIZE_MAX, &salt, &salt_size);
+ if (r < 0)
+ return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-salt' field: %m");
+
+ w = json_variant_by_key(v, "fido2-rp");
+ if (w) {
+ /* The "rp" field is optional. */
+ rp = strdup(json_variant_string(w));
+ if (!rp) {
+ crypt_log_error(cd, "Not enough memory.");
+ return -ENOMEM;
+ }
+ }
+
+ w = json_variant_by_key(v, "fido2-clientPin-required");
+ if (w)
+ /* The "fido2-clientPin-required" field is optional. */
+ SET_FLAG(required, FIDO2ENROLL_PIN, json_variant_boolean(w));
+ else
+ required |= FIDO2ENROLL_PIN_IF_NEEDED; /* compat with 248, where the field was unset */
+
+ w = json_variant_by_key(v, "fido2-up-required");
+ if (w)
+ /* The "fido2-up-required" field is optional. */
+ SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w));
+ else
+ required |= FIDO2ENROLL_UP_IF_NEEDED; /* compat with 248 */
+
+ w = json_variant_by_key(v, "fido2-uv-required");
+ if (w)
+ /* The "fido2-uv-required" field is optional. */
+ SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w));
+ else
+ required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */
+
+ *ret_rp_id = TAKE_PTR(rp);
+ *ret_cid = TAKE_PTR(cid);
+ *ret_cid_size = cid_size;
+ *ret_salt = TAKE_PTR(salt);
+ *ret_salt_size = salt_size;
+ *ret_required = required;
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "libfido2-util.h"
+
+struct crypt_device;
+
+int acquire_luks2_key(
+ struct crypt_device *cd,
+ const char *json,
+ const char *device,
+ const char *pin,
+ char **ret_keyslot_passphrase,
+ size_t *ret_keyslot_passphrase_size);
+
+int parse_luks2_fido2_data(
+ struct crypt_device *cd,
+ const char *json,
+ char **ret_rp_id,
+ void **ret_salt,
+ size_t *ret_salt_size,
+ void **ret_cid,
+ size_t *ret_cid_size,
+ Fido2EnrollFlags *ret_required);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <p11-kit/p11-kit.h>
+#include <p11-kit/uri.h>
+
+#include "cryptsetup-token-util.h"
+#include "escape.h"
+#include "hexdecoct.h"
+#include "json.h"
+#include "luks2-pkcs11.h"
+#include "memory-util.h"
+#include "pkcs11-util.h"
+#include "time-util.h"
+
+struct luks2_pkcs11_callback_data {
+ struct crypt_device *cd;
+ const char *pin;
+ size_t pin_size;
+ void *encrypted_key;
+ size_t encrypted_key_size;
+ void *decrypted_key;
+ size_t decrypted_key_size;
+};
+
+static int luks2_pkcs11_callback(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_SLOT_ID slot_id,
+ const CK_SLOT_INFO *slot_info,
+ const CK_TOKEN_INFO *token_info,
+ P11KitUri *uri,
+ void *userdata) {
+
+ CK_OBJECT_HANDLE object;
+ CK_RV rv;
+ CK_TOKEN_INFO updated_token_info;
+ int r;
+ _cleanup_free_ char *token_label = NULL;
+ struct luks2_pkcs11_callback_data *data = userdata;
+
+ assert(m);
+ assert(slot_info);
+ assert(token_info);
+ assert(uri);
+ assert(data);
+
+ token_label = pkcs11_token_label(token_info);
+ if (!token_label)
+ return -ENOMEM;
+
+ /* Called for every token matching our URI */
+ r = pkcs11_token_login_by_pin(m, session, token_info, token_label, data->pin, data->pin_size);
+ if (r == -ENOLCK) {
+ /* Referesh the token info, so that we can prompt knowing the new flags if they changed. */
+ rv = m->C_GetTokenInfo(slot_id, &updated_token_info);
+ if (rv != CKR_OK) {
+ crypt_log_error(data->cd,
+ "Failed to acquire updated security token information for slot %lu: %s",
+ slot_id, p11_kit_strerror(rv));
+ return -EIO;
+ }
+ token_info = &updated_token_info;
+ r = -ENOANO;
+ }
+
+ if (r == -ENOANO) {
+ if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
+ crypt_log_error(data->cd, "Please enter correct PIN for security token "
+ "'%s' in order to unlock it (final try).", token_label);
+ else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW))
+ crypt_log_error(data->cd, "PIN has been entered incorrectly previously, "
+ "please enter correct PIN for security token '%s' in order to unlock it.",
+ token_label);
+ }
+
+ if (r == -EPERM) /* pin is locked, but map it to -ENOANO anyway */
+ r = -ENOANO;
+
+ if (r < 0)
+ return r;
+
+ r = pkcs11_token_find_private_key(m, session, uri, &object);
+ if (r < 0)
+ return r;
+
+ r = pkcs11_token_decrypt_data(
+ m,
+ session,
+ object,
+ data->encrypted_key,
+ data->encrypted_key_size,
+ &data->decrypted_key,
+ &data->decrypted_key_size);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static void luks2_pkcs11_callback_data_release(struct luks2_pkcs11_callback_data *data) {
+ erase_and_free(data->decrypted_key);
+}
+
+static int acquire_luks2_key_by_pin(
+ struct crypt_device *cd,
+ const char *pkcs11_uri,
+ const void *pin,
+ size_t pin_size,
+ void *encrypted_key,
+ size_t encrypted_key_size,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size) {
+
+ int r;
+ _cleanup_(luks2_pkcs11_callback_data_release) struct luks2_pkcs11_callback_data data = {
+ .cd = cd,
+ .pin = pin,
+ .pin_size = pin_size,
+ .encrypted_key = encrypted_key,
+ .encrypted_key_size = encrypted_key_size,
+ };
+
+ assert(pkcs11_uri);
+ assert(encrypted_key);
+ assert(ret_decrypted_key);
+ assert(ret_decrypted_key_size);
+
+ r = pkcs11_find_token(pkcs11_uri, luks2_pkcs11_callback, &data);
+ if (r < 0)
+ return r;
+
+ *ret_decrypted_key = TAKE_PTR(data.decrypted_key);
+ *ret_decrypted_key_size = data.decrypted_key_size;
+
+ return 0;
+}
+
+/* called from within systemd utilities */
+static int acquire_luks2_key_systemd(
+ const char *pkcs11_uri,
+ systemd_pkcs11_plugin_params *params,
+ void *encrypted_key,
+ size_t encrypted_key_size,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size) {
+
+ int r;
+ _cleanup_(pkcs11_crypt_device_callback_data_release) pkcs11_crypt_device_callback_data data = {
+ .encrypted_key = encrypted_key,
+ .encrypted_key_size = encrypted_key_size,
+ .free_encrypted_key = false
+ };
+
+ assert(pkcs11_uri);
+ assert(encrypted_key);
+ assert(ret_decrypted_key);
+ assert(ret_decrypted_key_size);
+ assert(params);
+
+ data.friendly_name = params->friendly_name;
+ data.headless = params->headless;
+ data.until = params->until;
+
+ /* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
+ r = pkcs11_find_token(pkcs11_uri, pkcs11_crypt_device_callback, &data);
+ if (r < 0)
+ return r;
+
+ *ret_decrypted_key = TAKE_PTR(data.decrypted_key);
+ *ret_decrypted_key_size = data.decrypted_key_size;
+
+ return 0;
+}
+
+int acquire_luks2_key(
+ struct crypt_device *cd,
+ const char *json,
+ void *userdata,
+ const void *pin,
+ size_t pin_size,
+ char **ret_password,
+ size_t *ret_password_size) {
+
+ int r;
+ size_t decrypted_key_size, encrypted_key_size;
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+ _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+ _cleanup_free_ char *pkcs11_uri = NULL;
+ _cleanup_free_ void *encrypted_key = NULL;
+ systemd_pkcs11_plugin_params *pkcs11_params = userdata;
+
+ assert(json);
+ assert(ret_password);
+ assert(ret_password_size);
+
+ r = parse_luks2_pkcs11_data(cd, json, &pkcs11_uri, &encrypted_key, &encrypted_key_size);
+ if (r < 0)
+ return r;
+
+ if (pkcs11_params && pin)
+ crypt_log_verbose(cd, "PIN parameter ignored in interactive mode.");
+
+ if (pkcs11_params) /* systemd based activation with interactive pin query callbacks */
+ r = acquire_luks2_key_systemd(
+ pkcs11_uri,
+ pkcs11_params,
+ encrypted_key, encrypted_key_size,
+ &decrypted_key, &decrypted_key_size);
+ else /* default activation that provides single PIN if needed */
+ r = acquire_luks2_key_by_pin(
+ cd, pkcs11_uri, pin, pin_size,
+ encrypted_key, encrypted_key_size,
+ &decrypted_key, &decrypted_key_size);
+ if (r < 0)
+ return r;
+
+ r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (r < 0)
+ return crypt_log_error_errno(cd, r, "Can not base64 encode key: %m");
+
+ *ret_password = TAKE_PTR(base64_encoded);
+ *ret_password_size = strlen(*ret_password);
+
+ return 0;
+}
+
+int parse_luks2_pkcs11_data(
+ struct crypt_device *cd,
+ const char *json,
+ char **ret_uri,
+ void **ret_encrypted_key,
+ size_t *ret_encrypted_key_size) {
+
+ int r;
+ size_t key_size;
+ _cleanup_free_ char *uri = NULL;
+ _cleanup_free_ void *key = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ JsonVariant *w;
+
+ assert(json);
+ assert(ret_uri);
+ assert(ret_encrypted_key);
+ assert(ret_encrypted_key_size);
+
+ r = json_parse(json, 0, &v, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ w = json_variant_by_key(v, "pkcs11-uri");
+ if (!w)
+ return -EINVAL;
+
+ uri = strdup(json_variant_string(w));
+ if (!uri)
+ return -ENOMEM;
+
+ w = json_variant_by_key(v, "pkcs11-key");
+ if (!w)
+ return -EINVAL;
+
+ r = unbase64mem(json_variant_string(w), SIZE_MAX, &key, &key_size);
+ if (r < 0)
+ return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m.");
+
+ *ret_uri = TAKE_PTR(uri);
+ *ret_encrypted_key = TAKE_PTR(key);
+ *ret_encrypted_key_size = key_size;
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#pragma once
+
+struct crypt_device;
+
+int acquire_luks2_key(
+ struct crypt_device *cd,
+ const char *json,
+ void *userdata,
+ const void *pin,
+ size_t pin_size,
+ char **password,
+ size_t *password_size);
+
+int parse_luks2_pkcs11_data(
+ struct crypt_device *cd,
+ const char *json,
+ char **ret_uri,
+ void **ret_encrypted_key,
+ size_t *ret_encrypted_key_size);
int acquire_luks2_key(
uint32_t pcr_mask,
+ uint16_t pcr_bank,
const char *device,
const void *key_data,
size_t key_data_size,
device = auto_device;
}
- return tpm2_unseal(device, pcr_mask, key_data, key_data_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
+ return tpm2_unseal(
+ device,
+ pcr_mask, pcr_bank,
+ key_data, key_data_size,
+ policy_hash, policy_hash_size,
+ ret_decrypted_key, ret_decrypted_key_size);
}
/* this function expects valid "systemd-tpm2" in json */
const char *json,
uint32_t search_pcr_mask,
uint32_t *ret_pcr_mask,
+ uint16_t *ret_pcr_bank,
char **ret_base64_blob,
char **ret_hex_policy_hash) {
int r;
JsonVariant *w, *e;
uint32_t pcr_mask = 0;
+ uint16_t pcr_bank = UINT16_MAX;
_cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
assert(json);
+ assert(ret_pcr_mask);
+ assert(ret_pcr_bank);
assert(ret_base64_blob);
assert(ret_hex_policy_hash);
- assert(ret_pcr_mask);
r = json_parse(json, 0, &v, NULL, NULL);
if (r < 0)
search_pcr_mask != pcr_mask)
return -ENXIO;
+ w = json_variant_by_key(v, "tpm2-pcr-bank");
+ if (w) {
+ /* The PCR bank field is optional */
+
+ if (!json_variant_is_string(w))
+ return -EINVAL;
+
+ r = tpm2_pcr_bank_from_string(json_variant_string(w));
+ if (r < 0)
+ return r;
+
+ pcr_bank = r;
+ }
+
w = json_variant_by_key(v, "tpm2-blob");
if (!w || !json_variant_is_string(w))
return -EINVAL;
return -ENOMEM;
*ret_pcr_mask = pcr_mask;
+ *ret_pcr_bank = pcr_bank;
*ret_base64_blob = TAKE_PTR(base64_blob);
*ret_hex_policy_hash = TAKE_PTR(hex_policy_hash);
int acquire_luks2_key(
uint32_t pcr_mask,
+ uint16_t pcr_bank,
const char *device,
const void *key_data,
size_t key_data_size,
const char *json,
uint32_t search_pcr_mask,
uint32_t *ret_pcr_mask,
+ uint16_t *ret_pcr_bank,
char **ret_base64_blob,
char **ret_hex_policy_hash);
cryptsetup_token_c_args = ['-fvisibility=hidden']
cryptsetup_token_sym = files('cryptsetup-token.sym')
-cryptsetup_token_sym_path = join_paths(meson.current_source_dir(), 'cryptsetup-token.sym')
+cryptsetup_token_sym_path = meson.current_source_dir() / 'cryptsetup-token.sym'
if conf.get('HAVE_TPM2') == 1
cryptsetup_token_systemd_tpm2_sources = files('''
'cryptsetup-token-systemd-tpm2_static',
cryptsetup_token_systemd_tpm2_sources,
include_directories : includes,
- dependencies : libshared_deps + [libcryptsetup],
+ dependencies : libshared_deps + [libcryptsetup, versiondep],
+ c_args : cryptsetup_token_c_args)
+endif
+
+if conf.get('HAVE_LIBFIDO2') == 1
+ cryptsetup_token_systemd_fido2_sources = files('''
+ cryptsetup-token-systemd-fido2.c
+ cryptsetup-token.h
+ cryptsetup-token-util.h
+ cryptsetup-token-util.c
+ luks2-fido2.c
+ luks2-fido2.h
+ '''.split())
+
+ cryptsetup_token_systemd_fido2_static = static_library(
+ 'cryptsetup-token-systemd-fido2_static',
+ cryptsetup_token_systemd_fido2_sources,
+ include_directories : includes,
+ dependencies : libshared_deps + [libcryptsetup, versiondep],
+ c_args : cryptsetup_token_c_args)
+endif
+
+if conf.get('HAVE_P11KIT') == 1
+ cryptsetup_token_systemd_pkcs11_sources = files('''
+ cryptsetup-token-systemd-pkcs11.c
+ cryptsetup-token.h
+ cryptsetup-token-util.h
+ cryptsetup-token-util.c
+ luks2-pkcs11.c
+ luks2-pkcs11.h
+ '''.split())
+
+ cryptsetup_token_systemd_pkcs11_static = static_library(
+ 'cryptsetup-token-systemd-pkcs11_static',
+ cryptsetup_token_systemd_pkcs11_sources,
+ include_directories : includes,
+ dependencies : libshared_deps + [libcryptsetup, versiondep],
c_args : cryptsetup_token_c_args)
endif
const char *volume_name,
const char *device,
uint32_t pcr_mask,
+ uint16_t pcr_bank,
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
blob = loaded_blob;
}
- return tpm2_unseal(device, pcr_mask, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
+ return tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
}
int find_tpm2_auto_data(
uint32_t search_pcr_mask,
int start_token,
uint32_t *ret_pcr_mask,
+ uint16_t *ret_pcr_bank,
void **ret_blob,
size_t *ret_blob_size,
void **ret_policy_hash,
size_t blob_size = 0, policy_hash_size = 0;
int r, keyslot = -1, token = -1;
uint32_t pcr_mask = 0;
+ uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
assert(cd);
search_pcr_mask != pcr_mask) /* PCR mask doesn't match what is configured, ignore this entry */
continue;
+ /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded to SHA256 */
+ assert(pcr_bank == UINT16_MAX);
+ w = json_variant_by_key(v, "tpm2-pcr-bank");
+ if (w) {
+ /* The PCR bank field is optional */
+
+ if (!json_variant_is_string(w))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "TPM2 PCR bank is not a string.");
+
+ r = tpm2_pcr_bank_from_string(json_variant_string(w));
+ if (r < 0)
+ return log_error_errno(r, "TPM2 PCR bank invalid or not supported: %s", json_variant_string(w));
+
+ pcr_bank = r;
+ }
+
assert(!blob);
w = json_variant_by_key(v, "tpm2-blob");
if (!w || !json_variant_is_string(w))
*ret_policy_hash_size = policy_hash_size;
*ret_keyslot = keyslot;
*ret_token = token;
+ *ret_pcr_bank = pcr_bank;
return 0;
}
const char *volume_name,
const char *device,
uint32_t pcr_mask,
+ uint16_t pcr_bank,
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
uint32_t search_pcr_mask,
int start_token,
uint32_t *ret_pcr_mask,
+ uint16_t *ret_pcr_bank,
void **ret_blob,
size_t *ret_blob_size,
void **ret_policy_hash,
const char *volume_name,
const char *device,
uint32_t pcr_mask,
+ uint16_t pcr_bank,
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
uint32_t search_pcr_mask,
int start_token,
uint32_t *ret_pcr_mask,
+ uint16_t *ret_pcr_bank,
void **ret_blob,
size_t *ret_blob_size,
void **ret_policy_hash,
return 0;
}
+static bool libcryptsetup_plugins_support(void) {
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+ return crypt_token_external_path() != NULL;
+#else
+ return false;
+#endif
+}
+
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+static int acquire_pins_from_env_variable(char ***ret_pins) {
+ char *e;
+ _cleanup_strv_free_erase_ char **pins = NULL;
+
+ assert(ret_pins);
+
+ e = getenv("PIN");
+ if (e) {
+ pins = strv_new(e);
+ if (!pins)
+ return log_oom();
+
+ string_erase(e);
+ if (unsetenv("PIN") < 0)
+ return log_error_errno(errno, "Failed to unset $PIN: %m");
+ }
+
+ *ret_pins = TAKE_PTR(pins);
+
+ return 0;
+}
+#endif
+
+static int attach_luks2_by_fido2(
+ struct crypt_device *cd,
+ const char *name,
+ usec_t until,
+ bool headless,
+ void *usrptr,
+ uint32_t activation_flags) {
+
+ int r = -EOPNOTSUPP;
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+ char **p;
+ _cleanup_strv_free_erase_ char **pins = NULL;
+ AskPasswordFlags flags = ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
+
+ r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags);
+ if (r > 0) /* returns unlocked keyslot id on success */
+ r = 0;
+ if (r != -ENOANO) /* needs pin or pin is wrong */
+ return r;
+
+ r = acquire_pins_from_env_variable(&pins);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, pins) {
+ r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
+ if (r > 0) /* returns unlocked keyslot id on success */
+ r = 0;
+ if (r != -ENOANO) /* needs pin or pin is wrong */
+ return r;
+ }
+
+ if (headless)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
+
+ pins = strv_free_erase(pins);
+ r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, pins) {
+ r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
+ if (r > 0) /* returns unlocked keyslot id on success */
+ r = 0;
+ if (r != -ENOANO) /* needs pin or pin is wrong */
+ return r;
+ }
+
+ flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
+ for (;;) {
+ pins = strv_free_erase(pins);
+ r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, pins) {
+ r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
+ if (r > 0) /* returns unlocked keyslot id on success */
+ r = 0;
+ if (r != -ENOANO) /* needs pin or pin is wrong */
+ return r;
+ }
+ }
+#endif
+ return r;
+}
+
static int attach_luks_or_plain_or_bitlk_by_fido2(
struct crypt_device *cd,
const char *name,
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_free_ void *discovered_salt = NULL, *discovered_cid = NULL;
- size_t discovered_salt_size, discovered_cid_size, cid_size, decrypted_key_size;
+ size_t discovered_salt_size, discovered_cid_size, decrypted_key_size, cid_size = 0;
_cleanup_free_ char *friendly = NULL, *discovered_rp_id = NULL;
int keyslot = arg_key_slot, r;
- const char *rp_id;
- const void *cid;
+ const char *rp_id = NULL;
+ const void *cid = NULL;
Fido2EnrollFlags required;
+ bool use_libcryptsetup_plugin = libcryptsetup_plugins_support();
assert(cd);
assert(name);
* use PIN + UP when needed, and do not configure UV at all. Eventually, we should make this
* explicitly configurable. */
required = FIDO2ENROLL_PIN_IF_NEEDED | FIDO2ENROLL_UP_IF_NEEDED | FIDO2ENROLL_UV_OMIT;
- } else {
+ } else if (!use_libcryptsetup_plugin) {
r = find_fido2_auto_data(
cd,
&discovered_rp_id,
for (;;) {
bool processed = false;
- r = acquire_fido2_key(
- name,
- friendly,
- arg_fido2_device,
- rp_id,
- cid, cid_size,
- key_file, arg_keyfile_size, arg_keyfile_offset,
- key_data, key_data_size,
- until,
- arg_headless,
- required,
- &decrypted_key, &decrypted_key_size,
- arg_ask_password_flags);
- if (r >= 0)
- break;
+ if (use_libcryptsetup_plugin && !arg_fido2_cid) {
+ r = attach_luks2_by_fido2(cd, name, until, arg_headless, arg_fido2_device, flags);
+ if (IN_SET(r, -ENOTUNIQ, -ENXIO, -ENOENT))
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
+
+ } else {
+ r = acquire_fido2_key(
+ name,
+ friendly,
+ arg_fido2_device,
+ rp_id,
+ cid, cid_size,
+ key_file, arg_keyfile_size, arg_keyfile_offset,
+ key_data, key_data_size,
+ until,
+ arg_headless,
+ required,
+ &decrypted_key, &decrypted_key_size,
+ arg_ask_password_flags);
+ if (r >= 0)
+ break;
+ }
+
if (r != -EAGAIN) /* EAGAIN means: token not found */
return r;
return 0;
}
+static int attach_luks2_by_pkcs11(
+ struct crypt_device *cd,
+ const char *name,
+ const char *friendly_name,
+ usec_t until,
+ bool headless,
+ uint32_t flags) {
+
+ int r = -ENOTSUP;
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+ if (!crypt_get_type(cd) || strcmp(crypt_get_type(cd), CRYPT_LUKS2))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Automatic PKCS#11 metadata requires LUKS2 device.");
+
+ systemd_pkcs11_plugin_params params = {
+ .friendly_name = friendly_name,
+ .until = until,
+ .headless = headless
+ };
+
+ r = crypt_activate_by_token_pin(cd, name, "systemd-pkcs11", CRYPT_ANY_TOKEN, NULL, 0, ¶ms, flags);
+ if (r > 0) /* returns unlocked keyslot id on success */
+ r = 0;
+#endif
+ return r;
+}
+
static int attach_luks_or_plain_or_bitlk_by_pkcs11(
struct crypt_device *cd,
const char *name,
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_free_ void *discovered_key = NULL;
int keyslot = arg_key_slot, r;
- const char *uri;
+ const char *uri = NULL;
+ bool use_libcryptsetup_plugin = libcryptsetup_plugins_support();
assert(cd);
assert(name);
assert(arg_pkcs11_uri || arg_pkcs11_uri_auto);
if (arg_pkcs11_uri_auto) {
- r = find_pkcs11_auto_data(cd, &discovered_uri, &discovered_key, &discovered_key_size, &keyslot);
- if (IN_SET(r, -ENOTUNIQ, -ENXIO))
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "Automatic PKCS#11 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
- if (r < 0)
- return r;
+ if (!use_libcryptsetup_plugin) {
+ r = find_pkcs11_auto_data(cd, &discovered_uri, &discovered_key, &discovered_key_size, &keyslot);
+ if (IN_SET(r, -ENOTUNIQ, -ENXIO))
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "Automatic PKCS#11 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
+ if (r < 0)
+ return r;
- uri = discovered_uri;
- key_data = discovered_key;
- key_data_size = discovered_key_size;
+ uri = discovered_uri;
+ key_data = discovered_key;
+ key_data_size = discovered_key_size;
+ }
} else {
uri = arg_pkcs11_uri;
for (;;) {
bool processed = false;
- r = decrypt_pkcs11_key(
- name,
- friendly,
- uri,
- key_file, arg_keyfile_size, arg_keyfile_offset,
- key_data, key_data_size,
- until,
- arg_headless,
- &decrypted_key, &decrypted_key_size);
- if (r >= 0)
- break;
+ if (use_libcryptsetup_plugin && arg_pkcs11_uri_auto)
+ r = attach_luks2_by_pkcs11(cd, name, friendly, until, arg_headless, flags);
+ else {
+ r = decrypt_pkcs11_key(
+ name,
+ friendly,
+ uri,
+ key_file, arg_keyfile_size, arg_keyfile_offset,
+ key_data, key_data_size,
+ until,
+ arg_headless,
+ &decrypted_key, &decrypted_key_size);
+ if (r >= 0)
+ break;
+ }
+
if (r != -EAGAIN) /* EAGAIN means: token not found */
return r;
log_debug("Got one or more potentially relevant udev events, rescanning PKCS#11...");
}
+ assert(decrypted_key);
if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
name,
arg_tpm2_device,
arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask,
+ UINT16_MAX,
key_file, arg_keyfile_size, arg_keyfile_offset,
key_data, key_data_size,
NULL, 0, /* we don't know the policy hash */
for (;;) {
uint32_t pcr_mask;
+ uint16_t pcr_bank;
r = find_tpm2_auto_data(
cd,
arg_tpm2_pcr_mask, /* if != UINT32_MAX we'll only look for tokens with this PCR mask */
token, /* search for the token with this index, or any later index than this */
&pcr_mask,
+ &pcr_bank,
&blob, &blob_size,
&policy_hash, &policy_hash_size,
&keyslot,
name,
arg_tpm2_device,
pcr_mask,
+ pcr_bank,
NULL, 0, 0, /* no key file */
blob, blob_size,
policy_hash, policy_hash_size,
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind < argc)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Unknown action.");
+ assert_not_reached();
}
return 1;
if (r < 0)
return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target);
- (void) copy_xattr(source_fd, target_fd);
+ (void) copy_xattr(source_fd, target_fd, 0);
(void) copy_access(source_fd, target_fd);
(void) copy_times(source_fd, target_fd, 0);
if (r < 0)
return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
- (void) copy_xattr(source_fd, target_fd);
+ (void) copy_xattr(source_fd, target_fd, 0);
(void) copy_access(source_fd, target_fd);
(void) copy_times(source_fd, target_fd, 0);
break;
default:
- assert_not_reached("Unknown action.");
+ assert_not_reached();
}
return r;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind >= argc)
static int process_machine_id(void) {
const char *etc_machine_id;
- char id[SD_ID128_STRING_MAX];
int r;
etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
if (sd_id128_is_null(arg_machine_id))
return 0;
- r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id),
+ r = write_string_file(etc_machine_id, SD_ID128_TO_STRING(arg_machine_id),
WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
(arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
if (r < 0)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
/* We check if the specified locale strings are valid down here, so that we can take --root= into
/* Order the mount unit we generate relative to the post unit, so that DefaultDependencies= on the
* target unit won't affect us. */
- if (post && !FLAGS_SET(flags, MOUNT_AUTOMOUNT) && !FLAGS_SET(flags, MOUNT_NOAUTO))
+ if (post && !FLAGS_SET(flags, MOUNT_AUTOMOUNT) && !FLAGS_SET(flags, MOUNT_NOAUTO) &&
+ !FLAGS_SET(flags, MOUNT_NOFAIL))
fprintf(f, "Before=%s\n", post);
if (passno != 0) {
"\n"
"[Mount]\n");
+ r = write_what(f, what);
+ if (r < 0)
+ return r;
+
if (original_where)
fprintf(f, "# Canonicalized from %s\n", original_where);
return log_oom();
fprintf(f, "Where=%s\n", where_escaped);
- r = write_what(f, what);
- if (r < 0)
- return r;
-
if (!isempty(fstype) && !streq(fstype, "auto")) {
_cleanup_free_ char *t = NULL;
#define _const_ __attribute__((__const__))
#define _pure_ __attribute__((__pure__))
#define _section_(x) __attribute__((__section__(x)))
+#define _packed_ __attribute__((__packed__))
#define _used_ __attribute__((__used__))
#define _unused_ __attribute__((__unused__))
#define _cleanup_(x) __attribute__((__cleanup__(x)))
+#define _likely_(x) (__builtin_expect(!!(x), 1))
+#define _unlikely_(x) (__builtin_expect(!!(x), 0))
+#if __GNUC__ >= 7
+#define _fallthrough_ __attribute__((__fallthrough__))
+#else
+#define _fallthrough_
+#endif
+/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
+ * compiler versions */
+#ifndef _noreturn_
+#if __STDC_VERSION__ >= 201112L
+#define _noreturn_ _Noreturn
+#else
+#define _noreturn_ __attribute__((__noreturn__))
+#endif
+#endif
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
#define CONCATENATE(x, y) XCONCATENATE(x, y)
#ifdef SD_BOOT
-#define assert(expr) do {} while (false)
+ #ifdef NDEBUG
+ #define assert(expr)
+ #define assert_not_reached() __builtin_unreachable()
+ #else
+ void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
+ #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
+ #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+ #endif
#endif
#if defined(static_assert)
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
-/* evaluates to (void) if _A or _B are not constant or of different types */
+#define IS_UNSIGNED_INTEGER_TYPE(type) \
+ (__builtin_types_compatible_p(typeof(type), unsigned char) || \
+ __builtin_types_compatible_p(typeof(type), unsigned short) || \
+ __builtin_types_compatible_p(typeof(type), unsigned) || \
+ __builtin_types_compatible_p(typeof(type), unsigned long) || \
+ __builtin_types_compatible_p(typeof(type), unsigned long long))
+
+#define IS_SIGNED_INTEGER_TYPE(type) \
+ (__builtin_types_compatible_p(typeof(type), signed char) || \
+ __builtin_types_compatible_p(typeof(type), signed short) || \
+ __builtin_types_compatible_p(typeof(type), signed) || \
+ __builtin_types_compatible_p(typeof(type), signed long) || \
+ __builtin_types_compatible_p(typeof(type), signed long long))
+
+/* Evaluates to (void) if _A or _B are not constant or of different types (being integers of different sizes
+ * is also OK as long as the signedness matches) */
#define CONST_MAX(_A, _B) \
(__builtin_choose_expr( \
__builtin_constant_p(_A) && \
__builtin_constant_p(_B) && \
- __builtin_types_compatible_p(typeof(_A), typeof(_B)), \
+ (__builtin_types_compatible_p(typeof(_A), typeof(_B)) || \
+ (IS_UNSIGNED_INTEGER_TYPE(_A) && IS_UNSIGNED_INTEGER_TYPE(_B)) || \
+ (IS_SIGNED_INTEGER_TYPE(_A) && IS_SIGNED_INTEGER_TYPE(_B))), \
((_A) > (_B)) ? (_A) : (_B), \
VOID_0))
(ptr) = NULL; \
_ptr_; \
})
+
+/*
+ * STRLEN - return the length of a string literal, minus the trailing NUL byte.
+ * Contrary to strlen(), this is a constant expression.
+ * @x: a string literal.
+ */
+#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
# for sd-boot
fundamental_source_paths = []
-foreach s : sources
- fundamental_source_paths += join_paths(meson.current_source_dir(), s)
+foreach source : sources
+ fundamental_source_paths += meson.current_source_dir() / source
endforeach
# for libbasic
#define strncmp(a, b, n) StrnCmp((a), (b), (n))
#define strcasecmp(a, b) StriCmp((a), (b))
#define STR_C(str) (L ## str)
+#define memcmp(a, b, n) CompareMem(a, b, n)
#else
#define STR_C(str) (str)
#endif
}
sd_int strverscmp_improved(const sd_char *a, const sd_char *b);
+
+/* Like startswith(), but operates on arbitrary memory blocks */
+static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) {
+ assert(token);
+
+ sd_size_t n = strlen(token) * sizeof(sd_char);
+ if (sz < n)
+ return NULL;
+
+ assert(p);
+
+ if (memcmp(p, token, n) != 0)
+ return NULL;
+
+ return (uint8_t*) p + n;
+}
if (r < 0)
return log_error_errno(r, "Failed to store password: %m");
- string_erase(e);
- assert_se(unsetenv("PASSWORD") == 0);
-
+ assert_se(unsetenv_erase("PASSWORD") >= 0);
return 1;
}
if (r < 0)
return log_error_errno(r, "Failed to store token PIN: %m");
- string_erase(e);
- assert_se(unsetenv("PIN") == 0);
-
+ assert_se(unsetenv_erase("PIN") >= 0);
return 1;
}
if (arg_identity_extra_this_machine || !strv_isempty(arg_identity_filter)) {
_cleanup_(json_variant_unrefp) JsonVariant *per_machine = NULL, *mmid = NULL;
- char mids[SD_ID128_STRING_MAX];
sd_id128_t mid;
r = sd_id128_get_machine(&mid);
if (r < 0)
return log_error_errno(r, "Failed to acquire machine ID: %m");
- r = json_variant_new_string(&mmid, sd_id128_to_string(mid, mids));
+ r = json_variant_new_string(&mmid, SD_ID128_TO_STRING(mid));
if (r < 0)
return log_error_errno(r, "Failed to allocate matchMachineId object: %m");
if (r < 0)
return log_error_errno(r, "Failed to store password: %m");
- string_erase(e);
- assert_se(unsetenv("NEWPASSWORD") == 0);
+ assert_se(unsetenv_erase("NEWPASSWORD") >= 0);
if (ret)
*ret = TAKE_PTR(copy);
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
"%2$sCreate, manipulate or inspect home directories.%3$s\n"
"\n%4$sCommands:%5$s\n"
- " list List home areas\n"
- " activate USER… Activate a home area\n"
- " deactivate USER… Deactivate a home area\n"
- " inspect USER… Inspect a home area\n"
- " authenticate USER… Authenticate a home area\n"
- " create USER Create a home area\n"
- " remove USER… Remove a home area\n"
- " update USER Update a home area\n"
- " passwd USER Change password of a home area\n"
- " resize USER SIZE Resize a home area\n"
- " lock USER… Temporarily lock an active home area\n"
- " unlock USER… Unlock a temporarily locked home area\n"
- " lock-all Lock all suitable home areas\n"
- " deactivate-all Deactivate all active home areas\n"
- " with USER [COMMAND…] Run shell or command with access to a home area\n"
+ " list List home areas\n"
+ " activate USER… Activate a home area\n"
+ " deactivate USER… Deactivate a home area\n"
+ " inspect USER… Inspect a home area\n"
+ " authenticate USER… Authenticate a home area\n"
+ " create USER Create a home area\n"
+ " remove USER… Remove a home area\n"
+ " update USER Update a home area\n"
+ " passwd USER Change password of a home area\n"
+ " resize USER SIZE Resize a home area\n"
+ " lock USER… Temporarily lock an active home area\n"
+ " unlock USER… Unlock a temporarily locked home area\n"
+ " lock-all Lock all suitable home areas\n"
+ " deactivate-all Deactivate all active home areas\n"
+ " with USER [COMMAND…] Run shell or command with access to a home area\n"
"\n%4$sOptions:%5$s\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --no-pager Do not pipe output into a pager\n"
- " --no-legend Do not show the headers and footers\n"
- " --no-ask-password Do not ask for system passwords\n"
- " -H --host=[USER@]HOST Operate on remote host\n"
- " -M --machine=CONTAINER Operate on local container\n"
- " --identity=PATH Read JSON identity from file\n"
- " --json=FORMAT Output inspection data in JSON (takes one of\n"
- " pretty, short, off)\n"
- " -j Equivalent to --json=pretty (on TTY) or\n"
- " --json=short (otherwise)\n"
- " --export-format= Strip JSON inspection data (full, stripped,\n"
- " minimal)\n"
- " -E When specified once equals -j --export-format=\n"
- " stripped, when specified twice equals\n"
- " -j --export-format=minimal\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-legend Do not show the headers and footers\n"
+ " --no-ask-password Do not ask for system passwords\n"
+ " -H --host=[USER@]HOST Operate on remote host\n"
+ " -M --machine=CONTAINER Operate on local container\n"
+ " --identity=PATH Read JSON identity from file\n"
+ " --json=FORMAT Output inspection data in JSON (takes one of\n"
+ " pretty, short, off)\n"
+ " -j Equivalent to --json=pretty (on TTY) or\n"
+ " --json=short (otherwise)\n"
+ " --export-format= Strip JSON inspection data (full, stripped,\n"
+ " minimal)\n"
+ " -E When specified once equals -j --export-format=\n"
+ " stripped, when specified twice equals\n"
+ " -j --export-format=minimal\n"
"\n%4$sGeneral User Record Properties:%5$s\n"
- " -c --real-name=REALNAME Real name for user\n"
- " --realm=REALM Realm to create user in\n"
- " --email-address=EMAIL Email address for user\n"
- " --location=LOCATION Set location of user on earth\n"
- " --icon-name=NAME Icon name for user\n"
- " -d --home-dir=PATH Home directory\n"
- " -u --uid=UID Numeric UID for user\n"
- " -G --member-of=GROUP Add user to group\n"
- " --skel=PATH Skeleton directory to use\n"
- " --shell=PATH Shell for account\n"
- " --setenv=VARIABLE=VALUE Set an environment variable at log-in\n"
- " --timezone=TIMEZONE Set a time-zone\n"
- " --language=LOCALE Set preferred language\n"
+ " -c --real-name=REALNAME Real name for user\n"
+ " --realm=REALM Realm to create user in\n"
+ " --email-address=EMAIL Email address for user\n"
+ " --location=LOCATION Set location of user on earth\n"
+ " --icon-name=NAME Icon name for user\n"
+ " -d --home-dir=PATH Home directory\n"
+ " -u --uid=UID Numeric UID for user\n"
+ " -G --member-of=GROUP Add user to group\n"
+ " --skel=PATH Skeleton directory to use\n"
+ " --shell=PATH Shell for account\n"
+ " --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
+ " --timezone=TIMEZONE Set a time-zone\n"
+ " --language=LOCALE Set preferred language\n"
" --ssh-authorized-keys=KEYS\n"
- " Specify SSH public keys\n"
- " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
- " private key and matching X.509 certificate\n"
- " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
- " extension\n"
+ " Specify SSH public keys\n"
+ " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
+ " private key and matching X.509 certificate\n"
+ " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n"
+ " extension\n"
" --fido2-with-client-pin=BOOL\n"
- " Whether to require entering a PIN to unlock the\n"
- " account\n"
+ " Whether to require entering a PIN to unlock the\n"
+ " account\n"
" --fido2-with-user-presence=BOOL\n"
- " Whether to require user presence to unlock the\n"
- " account\n"
+ " Whether to require user presence to unlock the\n"
+ " account\n"
" --fido2-with-user-verification=BOOL\n"
- " Whether to require user verification to unlock the\n"
- " account\n"
- " --recovery-key=BOOL Add a recovery key\n"
- "\n%4$sAccount Management User Record Properties:%5$s\n"
- " --locked=BOOL Set locked account state\n"
- " --not-before=TIMESTAMP Do not allow logins before\n"
- " --not-after=TIMESTAMP Do not allow logins after\n"
+ " Whether to require user verification to unlock\n"
+ " the account\n"
+ " --recovery-key=BOOL Add a recovery key\n"
+ "\n%4$sAccount Management User Record Properties:%5$s\n"
+ " --locked=BOOL Set locked account state\n"
+ " --not-before=TIMESTAMP Do not allow logins before\n"
+ " --not-after=TIMESTAMP Do not allow logins after\n"
" --rate-limit-interval=SECS\n"
- " Login rate-limit interval in seconds\n"
+ " Login rate-limit interval in seconds\n"
" --rate-limit-burst=NUMBER\n"
- " Login rate-limit attempts per interval\n"
+ " Login rate-limit attempts per interval\n"
"\n%4$sPassword Policy User Record Properties:%5$s\n"
- " --password-hint=HINT Set Password hint\n"
+ " --password-hint=HINT Set Password hint\n"
" --enforce-password-policy=BOOL\n"
- " Control whether to enforce system's password\n"
- " policy for this user\n"
- " -P Equivalent to --enforce-password-password=no\n"
+ " Control whether to enforce system's password\n"
+ " policy for this user\n"
+ " -P Same as --enforce-password-password=no\n"
" --password-change-now=BOOL\n"
- " Require the password to be changed on next login\n"
+ " Require the password to be changed on next login\n"
" --password-change-min=TIME\n"
- " Require minimum time between password changes\n"
+ " Require minimum time between password changes\n"
" --password-change-max=TIME\n"
- " Require maximum time between password changes\n"
+ " Require maximum time between password changes\n"
" --password-change-warn=TIME\n"
- " How much time to warn before password expiry\n"
+ " How much time to warn before password expiry\n"
" --password-change-inactive=TIME\n"
- " How much time to block password after expiry\n"
+ " How much time to block password after expiry\n"
"\n%4$sResource Management User Record Properties:%5$s\n"
- " --disk-size=BYTES Size to assign the user on disk\n"
- " --access-mode=MODE User home directory access mode\n"
- " --umask=MODE Umask for user when logging in\n"
- " --nice=NICE Nice level for user\n"
+ " --disk-size=BYTES Size to assign the user on disk\n"
+ " --access-mode=MODE User home directory access mode\n"
+ " --umask=MODE Umask for user when logging in\n"
+ " --nice=NICE Nice level for user\n"
" --rlimit=LIMIT=VALUE[:VALUE]\n"
- " Set resource limits\n"
- " --tasks-max=MAX Set maximum number of per-user tasks\n"
- " --memory-high=BYTES Set high memory threshold in bytes\n"
- " --memory-max=BYTES Set maximum memory limit\n"
- " --cpu-weight=WEIGHT Set CPU weight\n"
- " --io-weight=WEIGHT Set IO weight\n"
+ " Set resource limits\n"
+ " --tasks-max=MAX Set maximum number of per-user tasks\n"
+ " --memory-high=BYTES Set high memory threshold in bytes\n"
+ " --memory-max=BYTES Set maximum memory limit\n"
+ " --cpu-weight=WEIGHT Set CPU weight\n"
+ " --io-weight=WEIGHT Set IO weight\n"
"\n%4$sStorage User Record Properties:%5$s\n"
- " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
- " subvolume, cifs)\n"
- " --image-path=PATH Path to image file/directory\n"
+ " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
+ " subvolume, cifs)\n"
+ " --image-path=PATH Path to image file/directory\n"
"\n%4$sLUKS Storage User Record Properties:%5$s\n"
- " --fs-type=TYPE File system type to use in case of luks\n"
- " storage (btrfs, ext4, xfs)\n"
- " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
- " when activated (mounted)\n"
+ " --fs-type=TYPE File system type to use in case of luks\n"
+ " storage (btrfs, ext4, xfs)\n"
+ " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
+ " when activated (mounted)\n"
" --luks-offline-discard=BOOL\n"
- " Whether to trim file on logout\n"
- " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
- " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
+ " Whether to trim file on logout\n"
+ " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
+ " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
" --luks-volume-key-size=BITS\n"
- " Volume key size to use for LUKS encryption\n"
- " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
+ " Volume key size to use for LUKS encryption\n"
+ " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
" --luks-pbkdf-hash-algorithm=ALGORITHM\n"
- " PBKDF hash algorithm to use\n"
+ " PBKDF hash algorithm to use\n"
" --luks-pbkdf-time-cost=SECS\n"
- " Time cost for PBKDF in seconds\n"
+ " Time cost for PBKDF in seconds\n"
" --luks-pbkdf-memory-cost=BYTES\n"
- " Memory cost for PBKDF in bytes\n"
+ " Memory cost for PBKDF in bytes\n"
" --luks-pbkdf-parallel-threads=NUMBER\n"
- " Number of parallel threads for PKBDF\n"
+ " Number of parallel threads for PKBDF\n"
"\n%4$sMounting User Record Properties:%5$s\n"
- " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
- " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
- " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
+ " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
+ " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
+ " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
"\n%4$sCIFS User Record Properties:%5$s\n"
- " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
- " --cifs-user-name=USER CIFS (Windows) user name\n"
- " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
+ " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
+ " --cifs-user-name=USER CIFS (Windows) user name\n"
+ " --cifs-service=SERVICE CIFS (Windows) service to mount as home area\n"
"\n%4$sLogin Behaviour User Record Properties:%5$s\n"
- " --stop-delay=SECS How long to leave user services running after\n"
- " logout\n"
- " --kill-processes=BOOL Whether to kill user processes when sessions\n"
- " terminate\n"
- " --auto-login=BOOL Try to log this user in automatically\n"
+ " --stop-delay=SECS How long to leave user services running after\n"
+ " logout\n"
+ " --kill-processes=BOOL Whether to kill user processes when sessions\n"
+ " terminate\n"
+ " --auto-login=BOOL Try to log this user in automatically\n"
"\nSee the %6$s for details.\n",
program_invocation_short_name,
ansi_highlight(),
break;
}
- if (!env_assignment_is_valid(optarg))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Environment assignment '%s' not valid.", optarg);
-
e = json_variant_by_key(arg_identity_extra, "environment");
if (e) {
r = json_variant_strv(e, &l);
return log_error_errno(r, "Failed to parse JSON environment field: %m");
}
- r = strv_env_replace_strdup(&l, optarg);
+ r = strv_env_replace_strdup_passthrough(&l, optarg);
if (r < 0)
- return log_error_errno(r, "Failed to replace JSON environment field: %m");
+ return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
strv_sort(l);
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Unexpected state after worker exited");
+ assert_not_reached();
}
return 0;
uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX, disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX;
_cleanup_(json_variant_unrefp) JsonVariant *j = NULL, *v = NULL, *m = NULL, *status = NULL;
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
- char ids[SD_ID128_STRING_MAX];
HomeState state;
sd_id128_t id;
int r;
j = json_variant_ref(h->record->json);
v = json_variant_ref(json_variant_by_key(j, "status"));
- m = json_variant_ref(json_variant_by_key(v, sd_id128_to_string(id, ids)));
+ m = json_variant_ref(json_variant_by_key(v, SD_ID128_TO_STRING(id)));
r = json_variant_filter(&m, STRV_MAKE("diskSize", "diskUsage", "diskFree", "diskCeiling", "diskFloor", "signedLocally"));
if (r < 0)
if (r < 0)
return r;
- r = json_variant_set_field(&v, ids, m);
+ r = json_variant_set_field(&v, SD_ID128_TO_STRING(id), m);
if (r < 0)
return r;
break;
default:
- assert_not_reached("unknown phase");
+ assert_not_reached();
}
other = hashmap_get(m->homes_by_uid, UID_TO_PTR(candidate));
break;
default:
- assert_not_reached("unexpected storage");
+ assert_not_reached();
}
temporary = TAKE_PTR(d); /* Needs to be destroyed now */
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(erase_and_freep) void *volume_key = NULL;
struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
- char suuid[ID128_UUID_STRING_MAX], **pp;
_cleanup_free_ char *text = NULL;
size_t volume_key_size;
int slot = 0, r;
+ char **pp;
assert(node);
assert(dm_name);
build_good_pbkdf(&good_pbkdf, hr);
build_minimal_pbkdf(&minimal_pbkdf, hr);
- r = sym_crypt_format(cd,
- CRYPT_LUKS2,
- user_record_luks_cipher(hr),
- user_record_luks_cipher_mode(hr),
- id128_to_uuid_string(uuid, suuid),
- volume_key,
- volume_key_size,
- &(struct crypt_params_luks2) {
- .label = label,
- .subsystem = "systemd-home",
- .sector_size = 512U,
- .pbkdf = &good_pbkdf,
- });
+ r = sym_crypt_format(
+ cd,
+ CRYPT_LUKS2,
+ user_record_luks_cipher(hr),
+ user_record_luks_cipher_mode(hr),
+ ID128_TO_UUID_STRING(uuid),
+ volume_key,
+ volume_key_size,
+ &(struct crypt_params_luks2) {
+ .label = label,
+ .subsystem = "systemd-home",
+ .sector_size = 512U,
+ .pbkdf = &good_pbkdf,
+ });
if (r < 0)
return log_error_errno(r, "Failed to format LUKS image: %m");
_cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
uint64_t offset, size;
sd_id128_t disk_uuid;
- char uuids[ID128_UUID_STRING_MAX];
int r;
assert(fd >= 0);
if (r < 0)
return log_error_errno(r, "Failed to set partition name: %m");
- r = fdisk_partition_set_uuid(p, id128_to_uuid_string(uuid, uuids));
+ r = fdisk_partition_set_uuid(p, ID128_TO_UUID_STRING(uuid));
if (r < 0)
return log_error_errno(r, "Failed to set partition UUID: %m");
break;
default:
- assert_not_reached("unexpected type");
+ assert_not_reached();
}
/* Note that the returned object might either be a reference to an updated version of the existing
break;
default:
- assert_not_reached("unknown storage type");
+ assert_not_reached();
}
if (hd) {
}
default:
- assert_not_reached("unexpected storage type");
+ assert_not_reached();
}
return has_mount; /* return true if the home record is already active */
gid_t gid) {
_cleanup_free_ char *hd = NULL, *un = NULL, *ip = NULL, *rr = NULL, *user_name_and_realm = NULL;
- char smid[SD_ID128_STRING_MAX];
sd_id128_t mid;
int r;
JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(realm)),
JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("regular")),
JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path)),
JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING(hd)),
JSON_BUILD_PAIR("storage", JSON_BUILD_STRING(user_storage_to_string(storage))),
int group_record_synthesize(GroupRecord *g, UserRecord *h) {
_cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL, *description = NULL;
- char smid[SD_ID128_STRING_MAX];
sd_id128_t mid;
int r;
JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(rr)),
JSON_BUILD_PAIR("description", JSON_BUILD_STRING(description)),
JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h))))))),
JSON_BUILD_PAIR_CONDITION(h->disposition >= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h)))),
JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
if (r < 0)
return r;
gid_t gid) {
_cleanup_(json_variant_unrefp) JsonVariant *new_binding_entry = NULL, *binding = NULL;
- char smid[SD_ID128_STRING_MAX], partition_uuids[ID128_UUID_STRING_MAX], luks_uuids[ID128_UUID_STRING_MAX], fs_uuids[ID128_UUID_STRING_MAX];
_cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
sd_id128_t mid;
int r;
r = sd_id128_get_machine(&mid);
if (r < 0)
return r;
- sd_id128_to_string(mid, smid);
if (image_path) {
ip = strdup(image_path);
r = json_build(&new_binding_entry,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
- JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(id128_to_uuid_string(partition_uuid, partition_uuids))),
- JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(id128_to_uuid_string(luks_uuid, luks_uuids))),
- JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(id128_to_uuid_string(fs_uuid, fs_uuids))),
+ JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(partition_uuid))),
+ JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(luks_uuid))),
+ JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(fs_uuid))),
JSON_BUILD_PAIR_CONDITION(!!luks_cipher, "luksCipher", JSON_BUILD_STRING(luks_cipher)),
JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode)),
JSON_BUILD_PAIR_CONDITION(luks_volume_key_size != UINT64_MAX, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size)),
_cleanup_(json_variant_unrefp) JsonVariant *be = NULL;
/* Merge the new entry with an old one, if that exists */
- be = json_variant_ref(json_variant_by_key(binding, smid));
+ be = json_variant_ref(json_variant_by_key(binding, SD_ID128_TO_STRING(mid)));
if (be) {
r = json_variant_merge(&be, new_binding_entry);
if (r < 0)
}
}
- r = json_variant_set_field(&binding, smid, new_binding_entry);
+ r = json_variant_set_field(&binding, SD_ID128_TO_STRING(mid), new_binding_entry);
if (r < 0)
return r;
return -ENOTDIR;
default:
- assert_not_reached("Unexpected record type");
+ assert_not_reached();
}
}
int user_record_set_disk_size(UserRecord *h, uint64_t disk_size) {
_cleanup_(json_variant_unrefp) JsonVariant *new_per_machine = NULL, *midv = NULL, *midav = NULL, *ne = NULL;
_cleanup_free_ JsonVariant **array = NULL;
- char smid[SD_ID128_STRING_MAX];
size_t idx = SIZE_MAX, n;
JsonVariant *per_machine;
sd_id128_t mid;
if (r < 0)
return r;
- sd_id128_to_string(mid, smid);
-
- r = json_variant_new_string(&midv, smid);
+ r = json_variant_new_string(&midv, SD_ID128_TO_STRING(mid));
if (r < 0)
return r;
int user_record_good_authentication(UserRecord *h) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
- char buf[SD_ID128_STRING_MAX];
uint64_t counter, usec;
sd_id128_t mid;
int r;
v = json_variant_ref(h->json);
w = json_variant_ref(json_variant_by_key(v, "status"));
- z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+ z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
r = json_variant_set_field_unsigned(&z, "goodAuthenticationCounter", counter);
if (r < 0)
if (r < 0)
return r;
- r = json_variant_set_field(&w, buf, z);
+ r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
if (r < 0)
return r;
int user_record_bad_authentication(UserRecord *h) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
- char buf[SD_ID128_STRING_MAX];
uint64_t counter, usec;
sd_id128_t mid;
int r;
v = json_variant_ref(h->json);
w = json_variant_ref(json_variant_by_key(v, "status"));
- z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+ z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
r = json_variant_set_field_unsigned(&z, "badAuthenticationCounter", counter);
if (r < 0)
if (r < 0)
return r;
- r = json_variant_set_field(&w, buf, z);
+ r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
if (r < 0)
return r;
int user_record_ratelimit(UserRecord *h) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
usec_t usec, new_ratelimit_begin_usec, new_ratelimit_count;
- char buf[SD_ID128_STRING_MAX];
sd_id128_t mid;
int r;
v = json_variant_ref(h->json);
w = json_variant_ref(json_variant_by_key(v, "status"));
- z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+ z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
r = json_variant_set_field_unsigned(&z, "rateLimitBeginUSec", new_ratelimit_begin_usec);
if (r < 0)
if (r < 0)
return r;
- r = json_variant_set_field(&w, buf, z);
+ r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
if (r < 0)
return r;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
}
static void context_determine_hostname_source(Context *c) {
- char hostname[HOST_NAME_MAX + 1] = {};
- _cleanup_free_ char *fallback = NULL;
+ _cleanup_free_ char *hostname = NULL;
int r;
assert(c);
if (c->hostname_source >= 0)
return;
- (void) get_hostname_filtered(hostname);
+ (void) gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &hostname);
if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME]))
c->hostname_source = HOSTNAME_STATIC;
else {
+ _cleanup_free_ char *fallback = NULL;
+
/* If the hostname was not set by us, try to figure out where it came from. If we set it to
* the default hostname, the file will tell us. We compare the string because it is possible
* that the hostname was set by an older version that had a different fallback, in the
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
return 1;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 30L) != CURLE_OK)
return -EIO;
+ if (curl_easy_setopt(c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_FILE) != CURLE_OK)
+ return -EIO;
+
*ret = TAKE_PTR(c);
return 0;
}
finish:
if (r >= 0) {
(void) copy_times(e->input_fd, e->output_fd, COPY_CRTIME);
- (void) copy_xattr(e->input_fd, e->output_fd);
+ (void) copy_xattr(e->input_fd, e->output_fd, 0);
}
if (e->on_finished)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "hostname-util.h"
#include "import-common.h"
#include "os-util.h"
#include "process-util.h"
#include "tmpfile-util.h"
#include "util.h"
-int import_make_read_only_fd(int fd) {
- struct stat st;
- int r;
-
- assert(fd >= 0);
-
- /* First, let's make this a read-only subvolume if it refers
- * to a subvolume */
- r = btrfs_subvol_set_read_only_fd(fd, true);
- if (r >= 0)
- return 0;
-
- if (!ERRNO_IS_NOT_SUPPORTED(r) && !IN_SET(r, -ENOTDIR, -EINVAL))
- return log_error_errno(r, "Failed to make subvolume read-only: %m");
-
- /* This doesn't refer to a subvolume, or the file system isn't even btrfs. In that, case fall back to
- * chmod()ing */
-
- r = fstat(fd, &st);
- if (r < 0)
- return log_error_errno(errno, "Failed to stat image: %m");
-
- if (S_ISDIR(st.st_mode)) {
- /* For directories set the immutable flag on the dir itself */
-
- r = chattr_fd(fd, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to set +i attribute on directory image: %m");
-
- } else if (S_ISREG(st.st_mode)) {
- /* For regular files drop "w" flags */
-
- if ((st.st_mode & 0222) != 0)
- if (fchmod(fd, st.st_mode & 07555) < 0)
- return log_error_errno(errno, "Failed to chmod() image: %m");
- } else
- return log_error_errno(SYNTHETIC_ERRNO(EBADFD), "Image of unexpected type");
-
- return 0;
-}
-
-int import_make_read_only(const char *path) {
- _cleanup_close_ int fd = 1;
-
- fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open %s: %m", path);
-
- return import_make_read_only_fd(fd);
-}
-
int import_fork_tar_x(const char *path, pid_t *ret) {
_cleanup_close_pair_ int pipefd[2] = { -1, -1 };
bool use_selinux;
return 0;
}
+
+bool import_validate_local(const char *name, ImportFlags flags) {
+
+ /* By default we insist on a valid hostname for naming images. But optionally we relax that, in which
+ * case it can be any path name */
+
+ if (FLAGS_SET(flags, IMPORT_DIRECT))
+ return path_is_valid(name);
+
+ return hostname_is_valid(name, 0);
+}
+
+static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ log_notice("Transfer aborted.");
+ sd_event_exit(sd_event_source_get_event(s), EINTR);
+ return 0;
+}
+
+int import_allocate_event_with_signals(sd_event **ret) {
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ int r;
+
+ assert(ret);
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
+ (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
+ (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+
+ *ret = TAKE_PTR(event);
+ return 0;
+}
#include <sys/types.h>
+#include "sd-event.h"
+
typedef enum ImportFlags {
- IMPORT_FORCE = 1 << 0, /* replace existing image */
- IMPORT_READ_ONLY = 1 << 1, /* make generated image read-only */
+ IMPORT_FORCE = 1 << 0, /* replace existing image */
+ IMPORT_READ_ONLY = 1 << 1, /* make generated image read-only */
+ IMPORT_BTRFS_SUBVOL = 1 << 2, /* tar: preferably create images as btrfs subvols */
+ IMPORT_BTRFS_QUOTA = 1 << 3, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
+ IMPORT_CONVERT_QCOW2 = 1 << 4, /* raw: if we detect a qcow2 image, unpack it */
+ IMPORT_DIRECT = 1 << 5, /* import without rename games */
+ IMPORT_SYNC = 1 << 6, /* fsync() right before we are done */
- IMPORT_FLAGS_MASK = IMPORT_FORCE|IMPORT_READ_ONLY,
+ IMPORT_FLAGS_MASK_TAR = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_BTRFS_SUBVOL|IMPORT_BTRFS_QUOTA|IMPORT_DIRECT|IMPORT_SYNC,
+ IMPORT_FLAGS_MASK_RAW = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_CONVERT_QCOW2|IMPORT_DIRECT|IMPORT_SYNC,
} ImportFlags;
-int import_make_read_only_fd(int fd);
-int import_make_read_only(const char *path);
-
int import_fork_tar_c(const char *path, pid_t *ret);
int import_fork_tar_x(const char *path, pid_t *ret);
int import_mangle_os_tree(const char *path);
+
+bool import_validate_local(const char *name, ImportFlags flags);
+
+int import_allocate_event_with_signals(sd_event **ret);
#endif
default:
- assert_not_reached("Unknown compression");
+ assert_not_reached();
}
return 1;
#include "hostname-util.h"
#include "import-common.h"
#include "import-util.h"
+#include "install-file.h"
+#include "main-func.h"
#include "mkdir.h"
+#include "parse-argument.h"
#include "ratelimit.h"
#include "rm-rf.h"
+#include "signal-util.h"
#include "string-util.h"
+#include "terminal-util.h"
#include "tmpfile-util.h"
#include "verbs.h"
static bool arg_force = false;
static bool arg_read_only = false;
+static bool arg_btrfs_subvol = true;
+static bool arg_btrfs_quota = true;
+static bool arg_sync = true;
+static bool arg_direct = false;
static const char *arg_image_root = "/var/lib/machines";
typedef struct ProgressInfo {
bool logged_incomplete;
} ProgressInfo;
-static volatile sig_atomic_t cancelled = false;
-
-static void sigterm_sigint(int sig) {
- cancelled = true;
-}
-
static void progress_info_free(ProgressInfo *p) {
free(p->path);
}
/* Mention the list is incomplete before showing first output. */
if (!p->logged_incomplete) {
- log_notice("(Note, file list shown below is incomplete, and is intended as sporadic progress report only.)");
+ log_notice("(Note: file list shown below is incomplete, and is intended as sporadic progress report only.)");
p->logged_incomplete = true;
}
assert(p);
- if (cancelled)
- return -EOWNERDEAD;
-
r = free_and_strdup(&p->path, path);
if (r < 0)
return r;
assert(p);
assert(p->size != UINT64_MAX);
- if (cancelled)
- return -EOWNERDEAD;
-
p->size += nbytes;
progress_show(p);
static int import_fs(int argc, char *argv[], void *userdata) {
_cleanup_(rm_rf_subvolume_and_freep) char *temp_path = NULL;
_cleanup_(progress_info_free) ProgressInfo progress = {};
- const char *path = NULL, *local = NULL, *final_path;
+ _cleanup_free_ char *l = NULL, *final_path = NULL;
+ const char *path = NULL, *local = NULL, *dest = NULL;
_cleanup_close_ int open_fd = -1;
- struct sigaction old_sigint_sa, old_sigterm_sa;
- static const struct sigaction sa = {
- .sa_handler = sigterm_sigint,
- .sa_flags = SA_RESTART,
- };
int r, fd;
if (argc >= 2)
- path = argv[1];
- path = empty_or_dash_to_null(path);
+ path = empty_or_dash_to_null(argv[1]);
if (argc >= 3)
- local = argv[2];
- else if (path)
- local = basename(path);
- local = empty_or_dash_to_null(local);
+ local = empty_or_dash_to_null(argv[2]);
+ else if (path) {
+ r = path_extract_filename(path, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
- if (local) {
- if (!hostname_is_valid(local, 0))
+ local = l;
+ }
+
+ if (arg_direct) {
+ if (!local)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No local path specified.");
+
+ if (path_is_absolute(local))
+ final_path = strdup(local);
+ else
+ final_path = path_join(arg_image_root, local);
+ if (!final_path)
+ return log_oom();
+
+ if (!path_is_valid(final_path))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local image name '%s' is not valid.",
- local);
+ "Local path name '%s' is not valid.", final_path);
+ } else {
+ if (local) {
+ if (!hostname_is_valid(local, 0))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Local image name '%s' is not valid.", local);
+ } else
+ local = "imported";
+
+ final_path = path_join(arg_image_root, local);
+ if (!final_path)
+ return log_oom();
if (!arg_force) {
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
- } else {
+ } else
return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "Image '%s' already exists.",
- local);
- }
+ "Image '%s' already exists.", local);
}
- } else
- local = "imported";
+ }
if (path) {
open_fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
}
- final_path = prefix_roota(arg_image_root, local);
+ if (!arg_sync)
+ log_info("File system synchronization on completion is off.");
- r = tempfn_random(final_path, NULL, &temp_path);
- if (r < 0)
- return log_oom();
+ if (arg_direct) {
+ if (arg_force)
+ (void) rm_rf(final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
- (void) mkdir_parents_label(temp_path, 0700);
-
- progress.limit = (RateLimit) { 200*USEC_PER_MSEC, 1 };
+ dest = final_path;
+ } else {
+ r = tempfn_random(final_path, NULL, &temp_path);
+ if (r < 0)
+ return log_oom();
- /* Hook into SIGINT/SIGTERM, so that we can cancel things then */
- assert_se(sigaction(SIGINT, &sa, &old_sigint_sa) >= 0);
- assert_se(sigaction(SIGTERM, &sa, &old_sigterm_sa) >= 0);
-
- r = btrfs_subvol_snapshot_fd_full(
- fd,
- temp_path,
- BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|BTRFS_SNAPSHOT_QUOTA,
- progress_path,
- progress_bytes,
- &progress);
- if (r == -EOWNERDEAD) { /* SIGINT + SIGTERM cause this, see signal handler above */
- log_error("Copy cancelled.");
- goto finish;
- }
- if (r < 0) {
- log_error_errno(r, "Failed to copy directory: %m");
- goto finish;
+ dest = temp_path;
}
- r = import_mangle_os_tree(temp_path);
- if (r < 0)
- goto finish;
+ (void) mkdir_parents_label(dest, 0700);
- (void) import_assign_pool_quota_and_warn(arg_image_root);
- (void) import_assign_pool_quota_and_warn(temp_path);
+ progress.limit = (RateLimit) { 200*USEC_PER_MSEC, 1 };
- if (arg_read_only) {
- r = import_make_read_only(temp_path);
- if (r < 0) {
- log_error_errno(r, "Failed to make directory read-only: %m");
- goto finish;
- }
+ {
+ BLOCK_SIGNALS(SIGINT, SIGTERM);
+
+ if (arg_btrfs_subvol)
+ r = btrfs_subvol_snapshot_fd_full(
+ fd,
+ dest,
+ BTRFS_SNAPSHOT_FALLBACK_COPY|
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
+ BTRFS_SNAPSHOT_RECURSIVE|
+ BTRFS_SNAPSHOT_SIGINT|
+ BTRFS_SNAPSHOT_SIGTERM,
+ progress_path,
+ progress_bytes,
+ &progress);
+ else
+ r = copy_directory_fd_full(
+ fd,
+ dest,
+ COPY_REFLINK|
+ COPY_SAME_MOUNT|
+ COPY_HARDLINKS|
+ COPY_SIGINT|
+ COPY_SIGTERM|
+ (arg_direct ? COPY_MERGE_EMPTY : 0),
+ progress_path,
+ progress_bytes,
+ &progress);
+ if (r == -EINTR) /* SIGINT/SIGTERM hit */
+ return log_error_errno(r, "Copy cancelled.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to copy directory: %m");
}
- if (arg_force)
- (void) rm_rf(final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ r = import_mangle_os_tree(dest);
+ if (r < 0)
+ return r;
- r = rename_noreplace(AT_FDCWD, temp_path, AT_FDCWD, final_path);
- if (r < 0) {
- log_error_errno(r, "Failed to move image into place: %m");
- goto finish;
+ if (arg_btrfs_quota) {
+ if (!arg_direct)
+ (void) import_assign_pool_quota_and_warn(arg_image_root);
+ (void) import_assign_pool_quota_and_warn(dest);
}
- temp_path = mfree(temp_path);
-
- log_info("Exiting.");
+ r = install_file(AT_FDCWD, dest,
+ AT_FDCWD, arg_direct ? NULL : final_path, /* pass NULL as target in case of direct
+ * mode since file is already in place */
+ (arg_force ? INSTALL_REPLACE : 0) |
+ (arg_read_only ? INSTALL_READ_ONLY : 0) |
+ (arg_sync ? INSTALL_SYNCFS : 0));
+ if (r < 0)
+ return log_error_errno(r, "Failed install directory as '%s': %m", final_path);
-finish:
- /* Put old signal handlers into place */
- assert_se(sigaction(SIGINT, &old_sigint_sa, NULL) >= 0);
- assert_se(sigaction(SIGTERM, &old_sigterm_sa, NULL) >= 0);
+ temp_path = mfree(temp_path);
+ log_info("Directory '%s successfully installed. Exiting.", final_path);
return 0;
}
static int help(int argc, char *argv[], void *userdata) {
- printf("%s [OPTIONS...] {COMMAND} ...\n\n"
- "Import container images from a file system.\n\n"
+ printf("%1$s [OPTIONS...] {COMMAND} ...\n"
+ "\n%4$sImport container images from a file system directories.%5$s\n"
+ "\n%2$sCommands:%3$s\n"
+ " run DIRECTORY [NAME] Import a directory\n"
+ "\n%2$sOptions:%3$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --force Force creation of image\n"
" --image-root=PATH Image root directory\n"
- " --read-only Create a read-only image\n\n"
- "Commands:\n"
- " run DIRECTORY [NAME] Import a directory\n",
- program_invocation_short_name);
+ " --read-only Create a read-only image\n"
+ " --direct Import directly to specified directory\n"
+ " --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
+ " instead of a directory\n"
+ " --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
+ " subvolume\n"
+ " --sync=BOOL Controls whether to sync() before completing\n",
+ program_invocation_short_name,
+ ansi_underline(),
+ ansi_normal(),
+ ansi_highlight(),
+ ansi_normal());
return 0;
}
ARG_FORCE,
ARG_IMAGE_ROOT,
ARG_READ_ONLY,
+ ARG_DIRECT,
+ ARG_BTRFS_SUBVOL,
+ ARG_BTRFS_QUOTA,
+ ARG_SYNC,
};
static const struct option options[] = {
{ "force", no_argument, NULL, ARG_FORCE },
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
+ { "direct", no_argument, NULL, ARG_DIRECT },
+ { "btrfs-subvol", required_argument, NULL, ARG_BTRFS_SUBVOL },
+ { "btrfs-quota", required_argument, NULL, ARG_BTRFS_QUOTA },
+ { "sync", required_argument, NULL, ARG_SYNC },
{}
};
- int c;
+ int c, r;
assert(argc >= 0);
assert(argv);
arg_read_only = true;
break;
+ case ARG_DIRECT:
+ arg_direct = true;
+ break;
+
+ case ARG_BTRFS_SUBVOL:
+ r = parse_boolean_argument("--btrfs-subvol=", optarg, &arg_btrfs_subvol);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case ARG_BTRFS_QUOTA:
+ r = parse_boolean_argument("--btrfs-quota=", optarg, &arg_btrfs_quota);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case ARG_SYNC:
+ r = parse_boolean_argument("--sync=", optarg, &arg_sync);
+ if (r < 0)
+ return r;
+
+ break;
+
case '?':
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
return dispatch_verb(argc, argv, verbs, NULL);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r;
setlocale(LC_ALL, "");
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
-
- r = import_fs_main(argc, argv);
+ return r;
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return import_fs_main(argc, argv);
}
+
+DEFINE_MAIN_FUNCTION(run);
#include "btrfs-util.h"
#include "copy.h"
#include "fd-util.h"
+#include "format-util.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "import-common.h"
#include "import-compress.h"
#include "import-raw.h"
+#include "install-file.h"
#include "io-util.h"
#include "machine-pool.h"
#include "mkdir.h"
uint64_t written_compressed;
uint64_t written_uncompressed;
- struct stat st;
+ struct stat input_stat;
+ struct stat output_stat;
unsigned last_percent;
RateLimit progress_ratelimit;
+
+ uint64_t offset;
+ uint64_t size_max;
};
RawImport* raw_import_unref(RawImport *i) {
if (!i)
return NULL;
- sd_event_unref(i->event);
+ sd_event_source_unref(i->input_event_source);
unlink_and_free(i->temp_path);
import_compress_free(&i->compress);
- sd_event_source_unref(i->input_event_source);
+ sd_event_unref(i->event);
safe_close(i->output_fd);
.last_percent = UINT_MAX,
.image_root = TAKE_PTR(root),
.progress_ratelimit = { 100 * USEC_PER_MSEC, 1 },
+ .offset = UINT64_MAX,
+ .size_max = UINT64_MAX,
};
if (event)
}
*ret = TAKE_PTR(i);
-
return 0;
}
assert(i);
/* We have no size information, unless the source is a regular file */
- if (!S_ISREG(i->st.st_mode))
+ if (!S_ISREG(i->input_stat.st_mode))
return;
- if (i->written_compressed >= (uint64_t) i->st.st_size)
+ if (i->written_compressed >= (uint64_t) i->input_stat.st_size)
percent = 100;
else
- percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
+ percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->input_stat.st_size);
if (percent == i->last_percent)
return;
static int raw_import_maybe_convert_qcow2(RawImport *i) {
_cleanup_close_ int converted_fd = -1;
- _cleanup_free_ char *t = NULL;
+ _cleanup_(unlink_and_freep) char *t = NULL;
+ _cleanup_free_ char *f = NULL;
int r;
assert(i);
+ /* Do QCOW2 conversion if enabled and not in direct mode */
+ if ((i->flags & (IMPORT_CONVERT_QCOW2|IMPORT_DIRECT)) != IMPORT_CONVERT_QCOW2)
+ return 0;
+
+ assert(i->final_path);
+
r = qcow2_detect(i->output_fd);
if (r < 0)
return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
return 0;
/* This is a QCOW2 image, let's convert it */
- r = tempfn_random(i->final_path, NULL, &t);
+ r = tempfn_random(i->final_path, NULL, &f);
if (r < 0)
return log_oom();
- converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+ converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (converted_fd < 0)
- return log_error_errno(errno, "Failed to create %s: %m", t);
+ return log_error_errno(errno, "Failed to create %s: %m", f);
+
+ t = TAKE_PTR(f);
(void) import_set_nocow_and_log(converted_fd, t);
log_info("Unpacking QCOW2 file.");
r = qcow2_convert(i->output_fd, converted_fd);
- if (r < 0) {
- (void) unlink(t);
+ if (r < 0)
return log_error_errno(r, "Failed to convert qcow2 image: %m");
- }
- (void) unlink(i->temp_path);
- free_and_replace(i->temp_path, t);
+ unlink_and_free(i->temp_path);
+ i->temp_path = TAKE_PTR(t);
CLOSE_AND_REPLACE(i->output_fd, converted_fd);
return 1;
assert(i);
assert(i->output_fd >= 0);
- assert(i->temp_path);
- assert(i->final_path);
- /* In case this was a sparse file, make sure the file system is right */
- if (i->written_uncompressed > 0) {
- if (ftruncate(i->output_fd, i->written_uncompressed) < 0)
- return log_error_errno(errno, "Failed to truncate file: %m");
- }
+ /* Nothing of what is below applies to block devices */
+ if (S_ISBLK(i->output_stat.st_mode)) {
- r = raw_import_maybe_convert_qcow2(i);
- if (r < 0)
- return r;
+ if (i->flags & IMPORT_SYNC) {
+ if (fsync(i->output_fd) < 0)
+ return log_error_errno(errno, "Failed to synchronize block device: %m");
+ }
- if (S_ISREG(i->st.st_mode)) {
- (void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME);
- (void) copy_xattr(i->input_fd, i->output_fd);
+ return 0;
}
- if (i->flags & IMPORT_READ_ONLY) {
- r = import_make_read_only_fd(i->output_fd);
+ assert(S_ISREG(i->output_stat.st_mode));
+
+ /* If an offset is specified we only are supposed to affect part of an existing output file or block
+ * device, thus don't manipulate file properties in that case */
+
+ if (i->offset == UINT64_MAX) {
+ /* In case this was a sparse file, make sure the file size is right */
+ if (i->written_uncompressed > 0) {
+ if (ftruncate(i->output_fd, i->written_uncompressed) < 0)
+ return log_error_errno(errno, "Failed to truncate file: %m");
+ }
+
+ r = raw_import_maybe_convert_qcow2(i);
if (r < 0)
return r;
- }
- if (i->flags & IMPORT_FORCE)
- (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ if (S_ISREG(i->input_stat.st_mode)) {
+ (void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME);
+ (void) copy_xattr(i->input_fd, i->output_fd, 0);
+ }
+ }
- r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ r = install_file(AT_FDCWD, i->temp_path ?: i->local,
+ AT_FDCWD, i->final_path,
+ (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
+ (i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
+ (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 0)
return log_error_errno(r, "Failed to move image into place: %m");
i->temp_path = mfree(i->temp_path);
+ log_info("Wrote %s.", FORMAT_BYTES(i->written_uncompressed));
+
return 0;
}
int r;
assert(i);
-
+ assert(i->local);
assert(!i->final_path);
assert(!i->temp_path);
assert(i->output_fd < 0);
- i->final_path = strjoin(i->image_root, "/", i->local, ".raw");
- if (!i->final_path)
- return log_oom();
+ if (i->flags & IMPORT_DIRECT) {
+ (void) mkdir_parents_label(i->local, 0700);
- r = tempfn_random(i->final_path, NULL, &i->temp_path);
- if (r < 0)
- return log_oom();
+ /* In direct mode we just open/create the local path and truncate it (like shell >
+ * redirection would do it) — except if an offset was passed, in which case we are supposed
+ * to operate on a section of the file only, in which case we apparently work on an some
+ * existing thing (i.e. are not the sole thing stored in the file), in which case we will
+ * neither truncate nor create. */
+
+ i->output_fd = open(i->local, O_RDWR|O_NOCTTY|O_CLOEXEC|(i->offset == UINT64_MAX ? O_TRUNC|O_CREAT : 0), 0664);
+ if (i->output_fd < 0)
+ return log_error_errno(errno, "Failed to open destination '%s': %m", i->local);
- (void) mkdir_parents_label(i->temp_path, 0700);
+ if (i->offset == UINT64_MAX)
+ (void) import_set_nocow_and_log(i->output_fd, i->local);
+ } else {
+ i->final_path = strjoin(i->image_root, "/", i->local, ".raw");
+ if (!i->final_path)
+ return log_oom();
- i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
- if (i->output_fd < 0)
- return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
+ r = tempfn_random(i->final_path, NULL, &i->temp_path);
+ if (r < 0)
+ return log_oom();
+
+ (void) mkdir_parents_label(i->temp_path, 0700);
+
+ i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+ if (i->output_fd < 0)
+ return log_error_errno(errno, "Failed to open destination '%s': %m", i->temp_path);
+
+ (void) import_set_nocow_and_log(i->output_fd, i->temp_path);
+ }
+
+ if (fstat(i->output_fd, &i->output_stat) < 0)
+ return log_error_errno(errno, "Failed to stat() output file: %m");
+
+ if (!S_ISREG(i->output_stat.st_mode) && !S_ISBLK(i->output_stat.st_mode))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADFD),
+ "Target file is not a regular file or block device");
+
+ if (i->offset != UINT64_MAX) {
+ if (lseek(i->output_fd, i->offset, SEEK_SET) == (off_t) -1)
+ return log_error_errno(errno, "Failed to seek to offset: %m");
+ }
- (void) import_set_nocow_and_log(i->output_fd, i->temp_path);
return 0;
}
if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED)
return 0;
- if (!S_ISREG(i->st.st_mode))
+ if (i->offset != UINT64_MAX || i->size_max != UINT64_MAX)
+ return 0;
+
+ if (!S_ISREG(i->input_stat.st_mode) || !S_ISREG(i->output_stat.st_mode))
return 0;
p = lseek(i->input_fd, 0, SEEK_CUR);
if (r >= 0)
return 1;
+ log_debug_errno(r, "Couldn't establish reflink, using copy: %m");
return 0;
}
static int raw_import_write(const void *p, size_t sz, void *userdata) {
RawImport *i = userdata;
- ssize_t n;
+ bool too_much = false;
+ int r;
+
+ assert(i);
+ assert(p);
+ assert(sz > 0);
+
+ if (i->written_uncompressed >= UINT64_MAX - sz)
+ return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow");
- n = sparse_write(i->output_fd, p, sz, 64);
- if (n < 0)
- return (int) n;
- if ((size_t) n < sz)
- return -EIO;
+ if (i->size_max != UINT64_MAX) {
+ if (i->written_uncompressed >= i->size_max) {
+ too_much = true;
+ goto finish;
+ }
+
+ if (i->written_uncompressed + sz > i->size_max) {
+ too_much = true;
+ sz = i->size_max - i->written_uncompressed; /* since we have the data in memory
+ * already, we might as well write it to
+ * disk to the max */
+ }
+ }
+
+ /* Generate sparse file if we created/truncated the file */
+ if (S_ISREG(i->output_stat.st_mode) && i->offset == UINT64_MAX) {
+ ssize_t n;
+
+ n = sparse_write(i->output_fd, p, sz, 64);
+ if (n < 0)
+ return log_error_errno((int) n, "Failed to write file: %m");
+ if ((size_t) n < sz)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
+ } else {
+ r = loop_write(i->output_fd, p, sz, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file: %m");
+ }
i->written_uncompressed += sz;
+finish:
+ if (too_much)
+ return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "File too large");
+
return 0;
}
return raw_import_process(i);
}
-int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags) {
+int raw_import_start(
+ RawImport *i,
+ int fd,
+ const char *local,
+ uint64_t offset,
+ uint64_t size_max,
+ ImportFlags flags) {
int r;
assert(i);
assert(fd >= 0);
assert(local);
- assert(!(flags & ~IMPORT_FLAGS_MASK));
+ assert(!(flags & ~IMPORT_FLAGS_MASK_RAW));
+ assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT));
- if (!hostname_is_valid(local, 0))
+ if (!import_validate_local(local, flags))
return -EINVAL;
if (i->input_fd >= 0)
return r;
i->flags = flags;
+ i->offset = offset;
+ i->size_max = size_max;
- if (fstat(fd, &i->st) < 0)
+ if (fstat(fd, &i->input_stat) < 0)
return -errno;
r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i);
return r;
i->input_fd = fd;
- return r;
+ return 0;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(RawImport*, raw_import_unref);
-int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags);
+int raw_import_start(RawImport *i, int fd, const char *local, uint64_t offset, uint64_t size_max, ImportFlags flags);
#include "import-common.h"
#include "import-compress.h"
#include "import-tar.h"
+#include "install-file.h"
#include "io-util.h"
#include "machine-pool.h"
#include "mkdir.h"
uint64_t written_compressed;
uint64_t written_uncompressed;
- struct stat st;
+ struct stat input_stat;
pid_t tar_pid;
assert(i);
/* We have no size information, unless the source is a regular file */
- if (!S_ISREG(i->st.st_mode))
+ if (!S_ISREG(i->input_stat.st_mode))
return;
- if (i->written_compressed >= (uint64_t) i->st.st_size)
+ if (i->written_compressed >= (uint64_t) i->input_stat.st_size)
percent = 100;
else
- percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
+ percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->input_stat.st_size);
if (percent == i->last_percent)
return;
}
static int tar_import_finish(TarImport *i) {
+ const char *d;
int r;
assert(i);
assert(i->tar_fd >= 0);
- assert(i->temp_path);
- assert(i->final_path);
i->tar_fd = safe_close(i->tar_fd);
return -EPROTO;
}
- r = import_mangle_os_tree(i->temp_path);
+ assert_se(d = i->temp_path ?: i->local);
+
+ r = import_mangle_os_tree(d);
if (r < 0)
return r;
- if (i->flags & IMPORT_READ_ONLY) {
- r = import_make_read_only(i->temp_path);
- if (r < 0)
- return r;
- }
-
- if (i->flags & IMPORT_FORCE)
- (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
-
- r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ r = install_file(
+ AT_FDCWD, d,
+ AT_FDCWD, i->final_path,
+ (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
+ (i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
+ (i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
if (r < 0)
- return log_error_errno(r, "Failed to move image into place: %m");
+ return log_error_errno(r, "Failed to move '%s' into place: %m", i->final_path ?: i->local);
i->temp_path = mfree(i->temp_path);
}
static int tar_import_fork_tar(TarImport *i) {
+ const char *d, *root;
int r;
assert(i);
-
+ assert(i->local);
assert(!i->final_path);
assert(!i->temp_path);
assert(i->tar_fd < 0);
- i->final_path = path_join(i->image_root, i->local);
- if (!i->final_path)
- return log_oom();
+ if (i->flags & IMPORT_DIRECT) {
+ d = i->local;
+ root = NULL;
+ } else {
+ i->final_path = path_join(i->image_root, i->local);
+ if (!i->final_path)
+ return log_oom();
- r = tempfn_random(i->final_path, NULL, &i->temp_path);
- if (r < 0)
- return log_oom();
+ r = tempfn_random(i->final_path, NULL, &i->temp_path);
+ if (r < 0)
+ return log_oom();
+
+ d = i->temp_path;
+ root = i->image_root;
+ }
+
+ assert(d);
- (void) mkdir_parents_label(i->temp_path, 0700);
+ (void) mkdir_parents_label(d, 0700);
- r = btrfs_subvol_make_fallback(i->temp_path, 0755);
+ if (FLAGS_SET(i->flags, IMPORT_DIRECT|IMPORT_FORCE))
+ (void) rm_rf(d, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+
+ if (i->flags & IMPORT_BTRFS_SUBVOL)
+ r = btrfs_subvol_make_fallback(d, 0755);
+ else
+ r = mkdir(d, 0755) < 0 ? -errno : 0;
+ if (r == -EEXIST && (i->flags & IMPORT_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
+ * because in that case our temporary path collided */
+ r = 0;
if (r < 0)
- return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path);
- if (r > 0) { /* actually btrfs subvol */
- (void) import_assign_pool_quota_and_warn(i->image_root);
- (void) import_assign_pool_quota_and_warn(i->temp_path);
+ return log_error_errno(r, "Failed to create directory/subvolume %s: %m", d);
+ if (r > 0 && (i->flags & IMPORT_BTRFS_QUOTA)) { /* actually btrfs subvol */
+ if (!(i->flags & IMPORT_DIRECT))
+ (void) import_assign_pool_quota_and_warn(root);
+ (void) import_assign_pool_quota_and_warn(d);
}
- i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
+ i->tar_fd = import_fork_tar_x(d, &i->tar_pid);
if (i->tar_fd < 0)
return i->tar_fd;
assert(i);
assert(fd >= 0);
assert(local);
- assert(!(flags & ~IMPORT_FLAGS_MASK));
+ assert(!(flags & ~IMPORT_FLAGS_MASK_TAR));
- if (!hostname_is_valid(local, 0))
+ if (!import_validate_local(local, flags))
return -EINVAL;
if (i->input_fd >= 0)
i->flags = flags;
- if (fstat(fd, &i->st) < 0)
+ if (fstat(fd, &i->input_stat) < 0)
return -errno;
r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
return r;
i->input_fd = fd;
- return r;
+ return 0;
}
#include "alloc-util.h"
#include "discover-image.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "import-raw.h"
#include "import-tar.h"
#include "import-util.h"
+#include "io-util.h"
#include "main-func.h"
+#include "parse-argument.h"
+#include "parse-util.h"
#include "signal-util.h"
#include "string-util.h"
+#include "terminal-util.h"
#include "verbs.h"
static const char *arg_image_root = "/var/lib/machines";
-static ImportFlags arg_import_flags = 0;
+static ImportFlags arg_import_flags = IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC;
+static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
-static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- log_notice("Transfer aborted.");
- sd_event_exit(sd_event_source_get_event(s), EINTR);
+static int normalize_local(const char *local, char **ret) {
+ _cleanup_free_ char *ll = NULL;
+ int r;
+
+ assert(ret);
+
+ if (arg_import_flags & IMPORT_DIRECT) {
+
+ if (!local)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No local path specified.");
+
+ if (!path_is_absolute(local)) {
+ ll = path_join(arg_image_root, local);
+ if (!ll)
+ return log_oom();
+
+ local = ll;
+ }
+
+ if (!path_is_valid(local))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Local path name '%s' is not valid.", local);
+ } else {
+ if (local) {
+ if (!hostname_is_valid(local, 0))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Local image name '%s' is not valid.",
+ local);
+ } else
+ local = "imported";
+
+ if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
+ r = image_find(IMAGE_MACHINE, local, NULL, NULL);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Image '%s' already exists.",
+ local);
+ }
+ }
+
+ if (!ll) {
+ ll = strdup(local);
+ if (!ll)
+ return log_oom();
+ }
+
+ *ret = TAKE_PTR(ll);
return 0;
}
+static int open_source(const char *path, const char *local, int *ret_open_fd) {
+ _cleanup_close_ int open_fd = -1;
+ int retval;
+
+ assert(local);
+ assert(ret_open_fd);
+
+ if (path) {
+ open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (open_fd < 0)
+ return log_error_errno(errno, "Failed to open raw image to import: %m");
+
+ retval = open_fd;
+
+ if (arg_offset != UINT64_MAX)
+ log_info("Importing '%s', saving at offset %" PRIu64 " in '%s'.", path, arg_offset, local);
+ else
+ log_info("Importing '%s', saving as '%s'.", path, local);
+ } else {
+ _cleanup_free_ char *pretty = NULL;
+
+ retval = STDIN_FILENO;
+
+ (void) fd_get_path(STDIN_FILENO, &pretty);
+
+ if (arg_offset != UINT64_MAX)
+ log_info("Importing '%s', saving at offset %" PRIu64 " in '%s'.", strempty(pretty), arg_offset, local);
+ else
+ log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
+ }
+
+ *ret_open_fd = TAKE_FD(open_fd);
+ return retval;
+}
+
static void on_tar_finished(TarImport *import, int error, void *userdata) {
sd_event *event = userdata;
assert(import);
static int import_tar(int argc, char *argv[], void *userdata) {
_cleanup_(tar_import_unrefp) TarImport *import = NULL;
+ _cleanup_free_ char *ll = NULL, *normalized = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
const char *path = NULL, *local = NULL;
- _cleanup_free_ char *ll = NULL;
_cleanup_close_ int open_fd = -1;
int r, fd;
if (argc >= 3)
local = empty_or_dash_to_null(argv[2]);
- else if (path)
- local = basename(path);
+ else if (path) {
+ _cleanup_free_ char *l = NULL;
- if (local) {
- r = tar_strip_suffixes(local, &ll);
+ r = path_extract_filename(path, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
+
+ r = tar_strip_suffixes(l, &ll);
if (r < 0)
return log_oom();
local = ll;
+ }
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local image name '%s' is not valid.",
- local);
-
- if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
- r = image_find(IMAGE_MACHINE, local, NULL, NULL);
- if (r < 0) {
- if (r != -ENOENT)
- return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
- } else
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "Image '%s' already exists.",
- local);
- }
- } else
- local = "imported";
-
- if (path) {
- open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (open_fd < 0)
- return log_error_errno(errno, "Failed to open tar image to import: %m");
-
- fd = open_fd;
-
- log_info("Importing '%s', saving as '%s'.", path, local);
- } else {
- _cleanup_free_ char *pretty = NULL;
+ r = normalize_local(local, &normalized);
+ if (r < 0)
+ return r;
- fd = STDIN_FILENO;
+ fd = open_source(path, normalized, &open_fd);
+ if (fd < 0)
+ return r;
- (void) fd_get_path(fd, &pretty);
- log_info("Importing '%s', saving as '%s'.", strna(pretty), local);
- }
-
- r = sd_event_default(&event);
+ r = import_allocate_event_with_signals(&event);
if (r < 0)
- return log_error_errno(r, "Failed to allocate event loop: %m");
+ return r;
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
- (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
- (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+ if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
+ log_info("File system synchronization on completion is off.");
r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate importer: %m");
- r = tar_import_start(import, fd, local, arg_import_flags);
+ r = tar_import_start(
+ import,
+ fd,
+ normalized,
+ arg_import_flags & IMPORT_FLAGS_MASK_TAR);
if (r < 0)
return log_error_errno(r, "Failed to import image: %m");
static int import_raw(int argc, char *argv[], void *userdata) {
_cleanup_(raw_import_unrefp) RawImport *import = NULL;
+ _cleanup_free_ char *ll = NULL, *normalized = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
const char *path = NULL, *local = NULL;
- _cleanup_free_ char *ll = NULL;
_cleanup_close_ int open_fd = -1;
int r, fd;
if (argc >= 3)
local = empty_or_dash_to_null(argv[2]);
- else if (path)
- local = basename(path);
+ else if (path) {
+ _cleanup_free_ char *l = NULL;
+
+ r = path_extract_filename(path, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
- if (local) {
- r = raw_strip_suffixes(local, &ll);
+ r = raw_strip_suffixes(l, &ll);
if (r < 0)
return log_oom();
local = ll;
+ }
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local image name '%s' is not valid.",
- local);
-
- if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
- r = image_find(IMAGE_MACHINE, local, NULL, NULL);
- if (r < 0) {
- if (r != -ENOENT)
- return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
- } else
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "Image '%s' already exists.",
- local);
- }
- } else
- local = "imported";
-
- if (path) {
- open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (open_fd < 0)
- return log_error_errno(errno, "Failed to open raw image to import: %m");
-
- fd = open_fd;
-
- log_info("Importing '%s', saving as '%s'.", path, local);
- } else {
- _cleanup_free_ char *pretty = NULL;
-
- fd = STDIN_FILENO;
+ r = normalize_local(local, &normalized);
+ if (r < 0)
+ return r;
- (void) fd_get_path(fd, &pretty);
- log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
- }
+ fd = open_source(path, normalized, &open_fd);
+ if (fd < 0)
+ return fd;
- r = sd_event_default(&event);
+ r = import_allocate_event_with_signals(&event);
if (r < 0)
- return log_error_errno(r, "Failed to allocate event loop: %m");
+ return r;
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
- (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
- (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+ if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
+ log_info("File system synchronization on completion is off.");
r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate importer: %m");
- r = raw_import_start(import, fd, local, arg_import_flags);
+ r = raw_import_start(
+ import,
+ fd,
+ normalized,
+ arg_offset,
+ arg_size_max,
+ arg_import_flags & IMPORT_FLAGS_MASK_RAW);
if (r < 0)
return log_error_errno(r, "Failed to import image: %m");
static int help(int argc, char *argv[], void *userdata) {
- printf("%s [OPTIONS...] {COMMAND} ...\n\n"
- "Import container or virtual machine images.\n\n"
+ printf("%1$s [OPTIONS...] {COMMAND} ...\n"
+ "\n%4$sImport container or virtual machine images.%5$s\n"
+ "\n%2$sCommands:%3$s\n"
+ " tar FILE [NAME] Import a TAR image\n"
+ " raw FILE [NAME] Import a RAW image\n"
+ "\n%2$sOptions:%3$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --force Force creation of image\n"
" --image-root=PATH Image root directory\n"
- " --read-only Create a read-only image\n\n"
- "Commands:\n"
- " tar FILE [NAME] Import a TAR image\n"
- " raw FILE [NAME] Import a RAW image\n",
- program_invocation_short_name);
+ " --read-only Create a read-only image\n"
+ " --direct Import directly to specified file\n"
+ " --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
+ " instead of a directory\n"
+ " --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
+ " subvolume\n"
+ " --convert-qcow2=BOOL Controls whether to convert QCOW2 images to\n"
+ " regular disk images\n"
+ " --sync=BOOL Controls whether to sync() before completing\n"
+ " --offset=BYTES Offset to seek to in destination\n"
+ " --size-max=BYTES Maximum number of bytes to write to destination\n",
+ program_invocation_short_name,
+ ansi_underline(),
+ ansi_normal(),
+ ansi_highlight(),
+ ansi_normal());
return 0;
}
ARG_FORCE,
ARG_IMAGE_ROOT,
ARG_READ_ONLY,
+ ARG_DIRECT,
+ ARG_BTRFS_SUBVOL,
+ ARG_BTRFS_QUOTA,
+ ARG_CONVERT_QCOW2,
+ ARG_SYNC,
+ ARG_OFFSET,
+ ARG_SIZE_MAX,
};
static const struct option options[] = {
{ "force", no_argument, NULL, ARG_FORCE },
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
+ { "direct", no_argument, NULL, ARG_DIRECT },
+ { "btrfs-subvol", required_argument, NULL, ARG_BTRFS_SUBVOL },
+ { "btrfs-quota", required_argument, NULL, ARG_BTRFS_QUOTA },
+ { "convert-qcow2", required_argument, NULL, ARG_CONVERT_QCOW2 },
+ { "sync", required_argument, NULL, ARG_SYNC },
+ { "offset", required_argument, NULL, ARG_OFFSET },
+ { "size-max", required_argument, NULL, ARG_SIZE_MAX },
{}
};
- int c;
+ int r, c;
assert(argc >= 0);
assert(argv);
arg_import_flags |= IMPORT_READ_ONLY;
break;
+ case ARG_DIRECT:
+ arg_import_flags |= IMPORT_DIRECT;
+ break;
+
+ case ARG_BTRFS_SUBVOL:
+ r = parse_boolean_argument("--btrfs-subvol=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
+ break;
+
+ case ARG_BTRFS_QUOTA:
+ r = parse_boolean_argument("--btrfs-quota=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
+ break;
+
+ case ARG_CONVERT_QCOW2:
+ r = parse_boolean_argument("--convert-qcow2=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_import_flags, IMPORT_CONVERT_QCOW2, r);
+ break;
+
+ case ARG_SYNC:
+ r = parse_boolean_argument("--sync=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
+ break;
+
+ case ARG_OFFSET: {
+ uint64_t u;
+
+ r = safe_atou64(optarg, &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --offset= argument: %s", optarg);
+ if (!FILE_SIZE_VALID(u))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --offset= switch too large: %s", optarg);
+
+ arg_offset = u;
+ break;
+ }
+
+ case ARG_SIZE_MAX: {
+ uint64_t u;
+
+ r = parse_size(optarg, 1024, &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --size-max= argument: %s", optarg);
+ if (!FILE_SIZE_VALID(u))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --size-max= switch too large: %s", optarg);
+
+ arg_size_max = u;
+ break;
+ }
+
case '?':
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
+ /* Make sure offset+size is still in the valid range if both set */
+ if (arg_offset != UINT64_MAX && arg_size_max != UINT64_MAX &&
+ ((arg_size_max > (UINT64_MAX - arg_offset)) ||
+ !FILE_SIZE_VALID(arg_offset + arg_size_max)))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset und maximum size out of range.");
+
+ if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
+
return 1;
}
return dispatch_verb(argc, argv, verbs, NULL);
}
+static void parse_env(void) {
+ int r;
+
+ /* Let's make these relatively low-level settings also controllable via env vars. User can then set
+ * them to systemd-import if they like to tweak behaviour */
+
+ r = getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
+ if (r >= 0)
+ SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
+ else if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
+
+ r = getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
+ if (r >= 0)
+ SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
+ else if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
+
+ r = getenv_bool("SYSTEMD_IMPORT_SYNC");
+ if (r >= 0)
+ SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
+ else if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_SYNC: %m");
+}
+
static int run(int argc, char *argv[]) {
int r;
log_parse_environment();
log_open();
+ parse_env();
+
r = parse_argv(argc, argv);
if (r <= 0)
- return 0;
+ return r;
(void) ignore_signals(SIGPIPE);
break;
default:
- assert_not_reached("Unexpected transfer type");
+ assert_not_reached();
}
switch (t->type) {
if (r < 0)
return r;
- if (!http_url_is_valid(remote))
+ if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"URL %s is invalid", remote);
#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
+#include "hostname-util.h"
#include "io-util.h"
+#include "memory-util.h"
#include "path-util.h"
#include "process-util.h"
#include "pull-common.h"
return 0;
}
-int pull_make_local_copy(const char *final, const char *image_root, const char *local, PullFlags flags) {
- const char *p;
- int r;
-
- assert(final);
- assert(local);
-
- if (!image_root)
- image_root = "/var/lib/machines";
-
- p = prefix_roota(image_root, local);
-
- if (FLAGS_SET(flags, PULL_FORCE))
- (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
-
- r = btrfs_subvol_snapshot(final, p,
- BTRFS_SNAPSHOT_QUOTA|
- BTRFS_SNAPSHOT_FALLBACK_COPY|
- BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
- BTRFS_SNAPSHOT_RECURSIVE);
- if (r < 0)
- return log_error_errno(r, "Failed to create local image: %m");
-
- log_info("Created new local image '%s'.", local);
-
- return 0;
-}
-
static int hash_url(const char *url, char **ret) {
uint64_t h;
static const sd_id128_t k = SD_ID128_ARRAY(df,89,16,87,01,cc,42,30,98,ab,4a,19,a6,a5,63,4f);
const char *url,
int (*strip_suffixes)(const char *name, char **ret),
const char *suffix,
+ ImportVerify verify,
CurlGlue *glue,
+ PullJobOpenDisk on_open_disk,
PullJobFinished on_finished,
void *userdata) {
if (r < 0)
return r;
+ job->on_open_disk = on_open_disk;
job->on_finished = on_finished;
job->compressed_max = job->uncompressed_max = 1ULL * 1024ULL * 1024ULL;
+ job->calc_checksum = IN_SET(verify, IMPORT_VERIFY_CHECKSUM, IMPORT_VERIFY_SIGNATURE);
*ret = TAKE_PTR(job);
-
return 0;
}
+static bool is_checksum_file(const char *fn) {
+ /* Returns true if the specified filename refers to a checksum file we grok */
+
+ if (!fn)
+ return false;
+
+ return streq(fn, "SHA256SUMS") || endswith(fn, ".sha256");
+}
+
+static bool is_signature_file(const char *fn) {
+ /* Returns true if the specified filename refers to a signature file we grok (reminder:
+ * suse-style .sha256 files are inline signed) */
+
+ if (!fn)
+ return false;
+
+ return streq(fn, "SHA256SUMS.gpg") || endswith(fn, ".sha256");
+}
+
int pull_make_verification_jobs(
PullJob **ret_checksum_job,
PullJob **ret_signature_job,
ImportVerify verify,
+ const char *checksum, /* set if literal checksum verification is requested, in which case 'verify' is set to _IMPORT_VERIFY_INVALID */
const char *url,
CurlGlue *glue,
PullJobFinished on_finished,
void *userdata) {
_cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL;
+ _cleanup_free_ char *fn = NULL;
int r;
assert(ret_checksum_job);
assert(ret_signature_job);
- assert(verify >= 0);
- assert(verify < _IMPORT_VERIFY_MAX);
+ assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
+ assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
+ assert((verify < 0) || !checksum);
assert(url);
assert(glue);
- if (verify != IMPORT_VERIFY_NO) {
- _cleanup_free_ char *checksum_url = NULL, *fn = NULL;
- const char *chksums = NULL;
+ /* If verification is turned off, or if the checksum to validate is already specified we don't need
+ * to download a checksum file or signature, hence shortcut things */
+ if (verify == IMPORT_VERIFY_NO || checksum) {
+ *ret_checksum_job = *ret_signature_job = NULL;
+ return 0;
+ }
+
+ r = import_url_last_component(url, &fn);
+ if (r < 0 && r != -EADDRNOTAVAIL) /* EADDRNOTAVAIL means there was no last component, which is OK for
+ * us, we'll just assume it's not a checksum/signature file */
+ return r;
+
+ /* Acquire the checksum file if verification or signature verification is requested and the main file
+ * to acquire isn't a checksum or signature file anyway */
+ if (verify != IMPORT_VERIFY_NO && !is_checksum_file(fn) && !is_signature_file(fn)) {
+ _cleanup_free_ char *checksum_url = NULL;
+ const char *suffixed = NULL;
/* Queue jobs for the checksum file for the image. */
- r = import_url_last_component(url, &fn);
- if (r < 0)
- return r;
- chksums = strjoina(fn, ".sha256");
+ if (fn)
+ suffixed = strjoina(fn, ".sha256"); /* Start with the suse-style checksum (if there's a base filename) */
+ else
+ suffixed = "SHA256SUMS";
- r = import_url_change_last_component(url, chksums, &checksum_url);
+ r = import_url_change_last_component(url, suffixed, &checksum_url);
if (r < 0)
return r;
checksum_job->on_finished = on_finished;
checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
+ checksum_job->on_not_found = pull_job_restart_with_sha256sum; /* if this fails, look for ubuntu-style checksum */
}
- if (verify == IMPORT_VERIFY_SIGNATURE) {
+ if (verify == IMPORT_VERIFY_SIGNATURE && !is_signature_file(fn)) {
_cleanup_free_ char *signature_url = NULL;
/* Queue job for the SHA256SUMS.gpg file for the image. */
*ret_checksum_job = TAKE_PTR(checksum_job);
*ret_signature_job = TAKE_PTR(signature_job);
-
return 0;
}
r = import_url_last_component(job->url, &fn);
if (r < 0)
- return log_oom();
+ return log_error_errno(r, "Failed to extract filename from URL '%s': %m", job->url);
if (!filename_is_valid(fn))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
"Cannot verify checksum, could not determine server-side file name.");
- line = strjoina(job->checksum, " *", fn, "\n");
-
- p = memmem(checksum_job->payload,
- checksum_job->payload_size,
- line,
- strlen(line));
-
- if (!p) {
- line = strjoina(job->checksum, " ", fn, "\n");
+ if (is_checksum_file(fn) || is_signature_file(fn)) /* We cannot verify checksum files or signature files with a checksum file */
+ return log_error_errno(SYNTHETIC_ERRNO(ELOOP),
+ "Cannot verify checksum/signature files via themselves.");
- p = memmem(checksum_job->payload,
+ line = strjoina(job->checksum, " *", fn, "\n"); /* string for binary mode */
+ p = memmem_safe(checksum_job->payload,
checksum_job->payload_size,
line,
strlen(line));
+ if (!p) {
+ line = strjoina(job->checksum, " ", fn, "\n"); /* string for text mode */
+ p = memmem_safe(checksum_job->payload,
+ checksum_job->payload_size,
+ line,
+ strlen(line));
}
+ /* Only counts if found at beginning of a line */
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n'))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
- "DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn);
+ "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", fn);
log_info("SHA256 checksum of %s is valid.", job->url);
return 1;
pid = 0;
if (r < 0)
goto finish;
- if (r != EXIT_SUCCESS) {
- log_error("DOWNLOAD INVALID: Signature verification failed.");
- r = -EBADMSG;
- } else {
+ if (r != EXIT_SUCCESS)
+ r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "DOWNLOAD INVALID: Signature verification failed.");
+ else {
log_info("Signature verification succeeded.");
r = 0;
}
}
int pull_verify(ImportVerify verify,
+ const char *checksum, /* Verify with literal checksum */
PullJob *main_job,
PullJob *checksum_job,
PullJob *signature_job,
PullJob *roothash_signature_job,
PullJob *verity_job) {
+ _cleanup_free_ char *fn = NULL;
VerificationStyle style;
- PullJob *j;
+ PullJob *verify_job;
int r;
+ assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
+ assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
+ assert((verify < 0) || !checksum);
assert(main_job);
assert(main_job->state == PULL_JOB_DONE);
- if (verify == IMPORT_VERIFY_NO)
+ if (verify == IMPORT_VERIFY_NO) /* verification turned off */
return 0;
- assert(main_job->calc_checksum);
- assert(main_job->checksum);
- assert(checksum_job);
- assert(checksum_job->state == PULL_JOB_DONE);
+ if (checksum) {
+ /* Verification by literal checksum */
+ assert(!checksum_job);
+ assert(!signature_job);
+ assert(!settings_job);
+ assert(!roothash_job);
+ assert(!roothash_signature_job);
+ assert(!verity_job);
- if (!checksum_job->payload || checksum_job->payload_size <= 0)
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
- "Checksum is empty, cannot verify.");
+ assert(main_job->calc_checksum);
+ assert(main_job->checksum);
- FOREACH_POINTER(j, main_job, settings_job, roothash_job, roothash_signature_job, verity_job) {
- r = verify_one(checksum_job, j);
- if (r < 0)
- return r;
+ if (!strcaseeq(checksum, main_job->checksum))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.",
+ main_job->url);
+
+ return 0;
+ }
+
+ r = import_url_last_component(main_job->url, &fn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from URL '%s': %m", main_job->url);
+
+ if (is_signature_file(fn))
+ return log_error_errno(SYNTHETIC_ERRNO(ELOOP),
+ "Main download is a signature file, can't verify it.");
+
+ if (is_checksum_file(fn)) {
+ log_debug("Main download is a checksum file, can't validate its checksum with itself, skipping.");
+ verify_job = main_job;
+ } else {
+ PullJob *j;
+ assert(main_job->calc_checksum);
+ assert(main_job->checksum);
+ assert(checksum_job);
+ assert(checksum_job->state == PULL_JOB_DONE);
+
+ if (!checksum_job->payload || checksum_job->payload_size <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Checksum is empty, cannot verify.");
+
+ FOREACH_POINTER(j, main_job, settings_job, roothash_job, roothash_signature_job, verity_job) {
+ r = verify_one(checksum_job, j);
+ if (r < 0)
+ return r;
+ }
+
+ verify_job = checksum_job;
}
- if (verify == IMPORT_VERIFY_CHECKSUM)
+ if (verify != IMPORT_VERIFY_SIGNATURE)
return 0;
- r = verification_style_from_url(checksum_job->url, &style);
+ assert(verify_job);
+
+ r = verification_style_from_url(verify_job->url, &style);
if (r < 0)
- return log_error_errno(r, "Failed to determine verification style from URL '%s': %m", checksum_job->url);
+ return log_error_errno(r, "Failed to determine verification style from URL '%s': %m", verify_job->url);
if (style == VERIFICATION_PER_DIRECTORY) {
assert(signature_job);
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
"Signature is empty, cannot verify.");
- return verify_gpg(checksum_job->payload, checksum_job->payload_size, signature_job->payload, signature_job->payload_size);
+ return verify_gpg(verify_job->payload, verify_job->payload_size, signature_job->payload, signature_job->payload_size);
} else
- return verify_gpg(checksum_job->payload, checksum_job->payload_size, NULL, 0);
+ return verify_gpg(verify_job->payload, verify_job->payload_size, NULL, 0);
}
int verification_style_from_url(const char *url, VerificationStyle *ret) {
return 1;
}
+
+bool pull_validate_local(const char *name, PullFlags flags) {
+
+ if (FLAGS_SET(flags, PULL_DIRECT))
+ return path_is_valid(name);
+
+ return hostname_is_valid(name, 0);
+}
+
+int pull_url_needs_checksum(const char *url) {
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ /* Returns true if we need to validate this resource via a hash value. This returns true for all
+ * files — except for gpg signature files and SHA256SUMS files and the like, which are validated with
+ * a validation tool like gpg. */
+
+ r = import_url_last_component(url, &fn);
+ if (r == -EADDRNOTAVAIL) /* no last component? then let's assume it's not a signature/checksum file */
+ return false;
+ if (r < 0)
+ return r;
+
+ return !is_checksum_file(fn) && !is_signature_file(fn);
+}
#include "pull-job.h"
typedef enum PullFlags {
- PULL_FORCE = 1 << 0, /* replace existing image */
- PULL_SETTINGS = 1 << 1, /* .nspawn settings file */
- PULL_ROOTHASH = 1 << 2, /* only for raw: .roothash file for verity */
- PULL_ROOTHASH_SIGNATURE = 1 << 3, /* only for raw: .roothash.p7s file for verity */
- PULL_VERITY = 1 << 4, /* only for raw: .verity file for verity */
+ PULL_FORCE = 1 << 0, /* replace existing image */
+ PULL_READ_ONLY = 1 << 1, /* make generated image read-only */
+ PULL_SETTINGS = 1 << 1, /* download .nspawn settings file */
+ PULL_ROOTHASH = 1 << 2, /* only for raw: download .roothash file for verity */
+ PULL_ROOTHASH_SIGNATURE = 1 << 3, /* only for raw: download .roothash.p7s file for verity */
+ PULL_VERITY = 1 << 4, /* only for raw: download .verity file for verity */
+ PULL_BTRFS_SUBVOL = 1 << 2, /* tar: preferably create images as btrfs subvols */
+ PULL_BTRFS_QUOTA = 1 << 3, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
+ PULL_CONVERT_QCOW2 = 1 << 4, /* raw: if we detect a qcow2 image, unpack it */
+ PULL_DIRECT = 1 << 5, /* download without rename games */
+ PULL_SYNC = 1 << 6, /* fsync() right before we are done */
/* The supported flags for the tar and the raw pulling */
- PULL_FLAGS_MASK_TAR = PULL_FORCE|PULL_SETTINGS,
- PULL_FLAGS_MASK_RAW = PULL_FORCE|PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY,
+ PULL_FLAGS_MASK_TAR = PULL_FORCE|PULL_READ_ONLY|PULL_SETTINGS|PULL_BTRFS_SUBVOL|PULL_BTRFS_QUOTA|PULL_DIRECT|PULL_SYNC,
+ PULL_FLAGS_MASK_RAW = PULL_FORCE|PULL_READ_ONLY|PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY|PULL_CONVERT_QCOW2|PULL_DIRECT|PULL_SYNC,
} PullFlags;
-int pull_make_local_copy(const char *final, const char *root, const char *local, PullFlags flags);
-
int pull_find_old_etags(const char *url, const char *root, int dt, const char *prefix, const char *suffix, char ***etags);
int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret);
-int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
-int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
+int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, ImportVerify verify, CurlGlue *glue, PullJobOpenDisk on_open_disk, PullJobFinished on_finished, void *userdata);
+int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *checksum, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
-int pull_verify(ImportVerify verify, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job);
+int pull_verify(ImportVerify verify, const char *checksum, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job);
typedef enum VerificationStyle {
VERIFICATION_PER_FILE, /* SuSE-style ".sha256" files with inline gpg signature */
int verification_style_from_url(const char *url, VerificationStyle *style);
int pull_job_restart_with_sha256sum(PullJob *job, char **ret);
+
+bool pull_validate_local(const char *name, PullFlags flags);
+
+int pull_url_needs_checksum(const char *url);
#include "alloc-util.h"
#include "fd-util.h"
#include "format-util.h"
+#include "fs-util.h"
#include "gcrypt-util.h"
#include "hexdecoct.h"
#include "import-util.h"
#include "strv.h"
#include "xattr-util.h"
+void pull_job_close_disk_fd(PullJob *j) {
+ if (!j)
+ return;
+
+ if (j->close_disk_fd)
+ safe_close(j->disk_fd);
+
+ j->disk_fd = -1;
+}
+
PullJob* pull_job_unref(PullJob *j) {
if (!j)
return NULL;
+ pull_job_close_disk_fd(j);
+
curl_glue_remove_and_free(j->glue, j->curl);
curl_slist_free_all(j->request_header);
- safe_close(j->disk_fd);
-
import_compress_free(&j->compress);
if (j->checksum_context)
void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
PullJob *j = NULL;
CURLcode code;
- long status;
+ long protocol;
int r;
if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK)
return;
if (result != CURLE_OK) {
- log_error("Transfer failed: %s", curl_easy_strerror(result));
- r = -EIO;
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Transfer failed: %s", curl_easy_strerror(result));
goto finish;
}
- code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
+ code = curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol);
if (code != CURLE_OK) {
- log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
- r = -EIO;
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code));
goto finish;
- } else if (status == 304) {
- log_info("Image already downloaded. Skipping download.");
- j->etag_exists = true;
- r = 0;
- goto finish;
- } else if (status >= 300) {
+ }
- if (status == 404 && j->on_not_found) {
- _cleanup_free_ char *new_url = NULL;
+ if (IN_SET(protocol, CURLPROTO_HTTP, CURLPROTO_HTTPS)) {
+ long status;
- /* This resource wasn't found, but the implementor wants to maybe let us know a new URL, query for it. */
- r = j->on_not_found(j, &new_url);
- if (r < 0)
- goto finish;
+ code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
+ if (code != CURLE_OK) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code));
+ goto finish;
+ }
+
+ if (status == 304) {
+ log_info("Image already downloaded. Skipping download.");
+ j->etag_exists = true;
+ r = 0;
+ goto finish;
+ } else if (status >= 300) {
- if (r > 0) { /* A new url to use */
- assert(new_url);
+ if (status == 404 && j->on_not_found) {
+ _cleanup_free_ char *new_url = NULL;
- r = pull_job_restart(j, new_url);
+ /* This resource wasn't found, but the implementor wants to maybe let us know a new URL, query for it. */
+ r = j->on_not_found(j, &new_url);
if (r < 0)
goto finish;
- code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
- if (code != CURLE_OK) {
- log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
- r = -EIO;
- goto finish;
- }
+ if (r > 0) { /* A new url to use */
+ assert(new_url);
+
+ r = pull_job_restart(j, new_url);
+ if (r < 0)
+ goto finish;
+
+ code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
+ if (code != CURLE_OK) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code));
+ goto finish;
+ }
- if (status == 0)
- return;
+ if (status == 0)
+ return;
+ }
}
- }
- log_error("HTTP request to %s failed with code %li.", j->url, status);
- r = -EIO;
- goto finish;
- } else if (status < 200) {
- log_error("HTTP request to %s finished with unexpected code %li.", j->url, status);
- r = -EIO;
- goto finish;
+ r = log_error_errno(
+ status == 404 ? SYNTHETIC_ERRNO(ENOMEDIUM) : SYNTHETIC_ERRNO(EIO), /* Make the most common error recognizable */
+ "HTTP request to %s failed with code %li.", j->url, status);
+ goto finish;
+ } else if (status < 200) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "HTTP request to %s finished with unexpected code %li.", j->url, status);
+ goto finish;
+ }
}
if (j->state != PULL_JOB_RUNNING) {
- log_error("Premature connection termination.");
- r = -EIO;
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Premature connection termination.");
goto finish;
}
if (j->content_length != UINT64_MAX &&
j->content_length != j->written_compressed) {
- log_error("Download truncated.");
- r = -EIO;
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Download truncated.");
goto finish;
}
k = gcry_md_read(j->checksum_context, GCRY_MD_SHA256);
if (!k) {
- log_error("Failed to get checksum.");
- r = -EIO;
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum.");
goto finish;
}
log_debug("SHA256 of %s is %s.", j->url, j->checksum);
}
- if (j->disk_fd >= 0 && j->allow_sparse) {
- /* Make sure the file size is right, in case the file was
- * sparse and we just seeked for the last part */
+ /* Do a couple of finishing disk operations, but only if we are the sole owner of the file (i.e. no
+ * offset is specified, which indicates we only own the file partially) */
- if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) {
- r = log_error_errno(errno, "Failed to truncate file: %m");
- goto finish;
- }
+ if (j->disk_fd >= 0) {
- if (j->etag)
- (void) fsetxattr(j->disk_fd, "user.source_etag", j->etag, strlen(j->etag), 0);
- if (j->url)
- (void) fsetxattr(j->disk_fd, "user.source_url", j->url, strlen(j->url), 0);
+ if (S_ISREG(j->disk_stat.st_mode)) {
- if (j->mtime != 0) {
- struct timespec ut[2];
+ if (j->offset == UINT64_MAX) {
+
+ if (j->written_compressed > 0) {
+ /* Make sure the file size is right, in case the file was sparse and we just seeked
+ * for the last part */
+ if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) {
+ r = log_error_errno(errno, "Failed to truncate file: %m");
+ goto finish;
+ }
+ }
- timespec_store(&ut[0], j->mtime);
- ut[1] = ut[0];
- (void) futimens(j->disk_fd, ut);
+ if (j->etag)
+ (void) fsetxattr(j->disk_fd, "user.source_etag", j->etag, strlen(j->etag), 0);
+ if (j->url)
+ (void) fsetxattr(j->disk_fd, "user.source_url", j->url, strlen(j->url), 0);
- (void) fd_setcrtime(j->disk_fd, j->mtime);
+ if (j->mtime != 0) {
+ struct timespec ut;
+
+ timespec_store(&ut, j->mtime);
+
+ if (futimens(j->disk_fd, (struct timespec[]) { ut, ut }) < 0)
+ log_debug_errno(errno, "Failed to adjust atime/mtime of created image, ignoring: %m");
+
+ r = fd_setcrtime(j->disk_fd, j->mtime);
+ if (r < 0)
+ log_debug_errno(r, "Failed to adjust crtime of created image, ignoring: %m");
+ }
+ }
+
+ if (j->sync) {
+ r = fsync_full(j->disk_fd);
+ if (r < 0) {
+ log_error_errno(r, "Failed to synchronize file to disk: %m");
+ goto finish;
+ }
+ }
+
+ } else if (S_ISBLK(j->disk_stat.st_mode) && j->sync) {
+
+ if (fsync(j->disk_fd) < 0) {
+ r = log_error_errno(errno, "Failed to synchronize block device: %m");
+ goto finish;
+ }
}
}
+ log_info("Acquired %s.", FORMAT_BYTES(j->written_uncompressed));
+
r = 0;
finish:
static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) {
PullJob *j = userdata;
- ssize_t n;
+ bool too_much = false;
+ int r;
assert(j);
assert(p);
+ assert(sz > 0);
- if (sz <= 0)
- return 0;
+ if (j->written_uncompressed > UINT64_MAX - sz)
+ return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow");
- if (j->written_uncompressed + sz < j->written_uncompressed)
- return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW),
- "File too large, overflow");
+ if (j->written_uncompressed >= j->uncompressed_max) {
+ too_much = true;
+ goto finish;
+ }
- if (j->written_uncompressed + sz > j->uncompressed_max)
- return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
- "File overly large, refusing");
+ if (j->written_uncompressed + sz > j->uncompressed_max) {
+ too_much = true;
+ sz = j->uncompressed_max - j->written_uncompressed; /* since we have the data in memory
+ * already, we might as well write it to
+ * disk to the max */
+ }
if (j->disk_fd >= 0) {
- if (j->allow_sparse)
+ if (S_ISREG(j->disk_stat.st_mode) && j->offset == UINT64_MAX) {
+ ssize_t n;
+
n = sparse_write(j->disk_fd, p, sz, 64);
- else {
- n = write(j->disk_fd, p, sz);
if (n < 0)
- n = -errno;
+ return log_error_errno((int) n, "Failed to write file: %m");
+ if ((size_t) n < sz)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
+ } else {
+ r = loop_write(j->disk_fd, p, sz, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file: %m");
}
- if (n < 0)
- return log_error_errno((int) n, "Failed to write file: %m");
- if ((size_t) n < sz)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
- } else {
+ }
+ if (j->disk_fd < 0 || j->force_memory) {
if (!GREEDY_REALLOC(j->payload, j->payload_size + sz))
return log_oom();
j->written_uncompressed += sz;
+finish:
+ if (too_much)
+ return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing.");
+
return 0;
}
}
if (j->disk_fd >= 0) {
- /* Check if we can do sparse files */
+ if (fstat(j->disk_fd, &j->disk_stat) < 0)
+ return log_error_errno(errno, "Failed to stat disk file: %m");
- if (lseek(j->disk_fd, SEEK_SET, 0) == 0)
- j->allow_sparse = true;
- else {
- if (errno != ESPIPE)
+ if (j->offset != UINT64_MAX) {
+ if (lseek(j->disk_fd, j->offset, SEEK_SET) == (off_t) -1)
return log_error_errno(errno, "Failed to seek on file descriptor: %m");
-
- j->allow_sparse = false;
}
}
goto fail;
default:
- assert_not_reached("Impossible state.");
+ assert_not_reached();
}
return sz;
code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
if (code != CURLE_OK) {
- log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
- r = -EIO;
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code));
goto fail;
}
return 0;
}
-int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) {
+int pull_job_new(
+ PullJob **ret,
+ const char *url,
+ CurlGlue *glue,
+ void *userdata) {
+
_cleanup_(pull_job_unrefp) PullJob *j = NULL;
_cleanup_free_ char *u = NULL;
*j = (PullJob) {
.state = PULL_JOB_INIT,
.disk_fd = -1,
+ .close_disk_fd = true,
.userdata = userdata,
.glue = glue,
.content_length = UINT64_MAX,
.compressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
.uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
.url = TAKE_PTR(u),
+ .offset = UINT64_MAX,
+ .sync = true,
};
*ret = TAKE_PTR(j);
#pragma once
#include <gcrypt.h>
+#include <sys/stat.h>
#include "curl-util.h"
#include "import-compress.h"
#include "macro.h"
+#include "pull-common.h"
typedef struct PullJob PullJob;
uint64_t content_length;
uint64_t written_compressed;
uint64_t written_uncompressed;
+ uint64_t offset;
uint64_t uncompressed_max;
uint64_t compressed_max;
size_t payload_size;
int disk_fd;
+ bool close_disk_fd;
+ struct stat disk_stat;
usec_t mtime;
usec_t start_usec;
usec_t last_status_usec;
- bool allow_sparse;
-
bool calc_checksum;
gcry_md_hd_t checksum_context;
char *checksum;
+ bool sync;
+ bool force_memory;
};
int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata);
void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result);
+void pull_job_close_disk_fd(PullJob *j);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(PullJob*, pull_job_unref);
#include "hostname-util.h"
#include "import-common.h"
#include "import-util.h"
+#include "install-file.h"
#include "macro.h"
#include "mkdir.h"
#include "path-util.h"
ImportVerify verify;
char *image_root;
+ uint64_t offset;
+
PullJob *raw_job;
PullJob *checksum_job;
PullJob *signature_job;
RawPullFinished on_finished;
void *userdata;
- char *local;
+ char *local; /* In PULL_DIRECT mode the path we are supposed to place things in, otherwise the
+ * machine name of the final copy we make */
char *final_path;
char *temp_path;
char *verity_path;
char *verity_temp_path;
+
+ char *checksum;
};
RawPull* raw_pull_unref(RawPull *i) {
free(i->verity_path);
free(i->image_root);
free(i->local);
+ free(i->checksum);
return mfree(i);
}
.image_root = TAKE_PTR(root),
.event = TAKE_PTR(e),
.glue = TAKE_PTR(g),
+ .offset = UINT64_MAX,
};
i->glue->on_finished = pull_job_curl_on_finished;
break;
default:
- assert_not_reached("Unknown progress state");
+ assert_not_reached();
}
sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
}
static int raw_pull_maybe_convert_qcow2(RawPull *i) {
+ _cleanup_(unlink_and_freep) char *t = NULL;
_cleanup_close_ int converted_fd = -1;
- _cleanup_free_ char *t = NULL;
+ _cleanup_free_ char *f = NULL;
int r;
assert(i);
assert(i->raw_job);
+ assert(!FLAGS_SET(i->flags, PULL_DIRECT));
+
+ if (!FLAGS_SET(i->flags, PULL_CONVERT_QCOW2))
+ return 0;
+
+ assert(i->final_path);
+ assert(i->raw_job->close_disk_fd);
r = qcow2_detect(i->raw_job->disk_fd);
if (r < 0)
return 0;
/* This is a QCOW2 image, let's convert it */
- r = tempfn_random(i->final_path, NULL, &t);
+ r = tempfn_random(i->final_path, NULL, &f);
if (r < 0)
return log_oom();
- converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+ converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (converted_fd < 0)
- return log_error_errno(errno, "Failed to create %s: %m", t);
+ return log_error_errno(errno, "Failed to create %s: %m", f);
+
+ t = TAKE_PTR(f);
(void) import_set_nocow_and_log(converted_fd, t);
log_info("Unpacking QCOW2 file.");
r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
- if (r < 0) {
- (void) unlink(t);
+ if (r < 0)
return log_error_errno(r, "Failed to convert qcow2 image: %m");
- }
- (void) unlink(i->temp_path);
- free_and_replace(i->temp_path, t);
+ unlink_and_free(i->temp_path);
+ i->temp_path = TAKE_PTR(t);
CLOSE_AND_REPLACE(i->raw_job->disk_fd, converted_fd);
return 1;
}
-static int raw_pull_determine_path(RawPull *i, const char *suffix, char **field) {
+static int raw_pull_determine_path(
+ RawPull *i,
+ const char *suffix,
+ char **field /* input + output (!) */) {
int r;
assert(i);
static int raw_pull_copy_auxiliary_file(
RawPull *i,
const char *suffix,
- char **path) {
+ char **path /* input + output (!) */) {
const char *local;
int r;
local = strjoina(i->image_root, "/", i->local, suffix);
- r = copy_file_atomic(*path, local, 0644, 0, 0, COPY_REFLINK | (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0));
+ r = copy_file_atomic(
+ *path,
+ local,
+ 0644,
+ 0, 0,
+ COPY_REFLINK |
+ (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) |
+ (FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 0));
if (r == -EEXIST)
log_warning_errno(r, "File %s already exists, not replacing.", local);
else if (r == -ENOENT)
}
static int raw_pull_make_local_copy(RawPull *i) {
- _cleanup_free_ char *tp = NULL;
+ _cleanup_(unlink_and_freep) char *tp = NULL;
+ _cleanup_free_ char *f = NULL;
_cleanup_close_ int dfd = -1;
const char *p;
int r;
assert(i);
assert(i->raw_job);
+ assert(!FLAGS_SET(i->flags, PULL_DIRECT));
if (!i->local)
return 0;
/* We freshly downloaded the image, use it */
assert(i->raw_job->disk_fd >= 0);
+ assert(i->offset == UINT64_MAX);
if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
p = strjoina(i->image_root, "/", i->local, ".raw");
- if (FLAGS_SET(i->flags, PULL_FORCE))
- (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
-
- r = tempfn_random(p, NULL, &tp);
+ r = tempfn_random(p, NULL, &f);
if (r < 0)
return log_oom();
- dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+ dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (dfd < 0)
return log_error_errno(errno, "Failed to create writable copy of image: %m");
+ tp = TAKE_PTR(f);
+
/* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
* since it reduces fragmentation caused by not allowing in-place writes. */
(void) import_set_nocow_and_log(dfd, tp);
r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
- if (r < 0) {
- (void) unlink(tp);
+ if (r < 0)
return log_error_errno(r, "Failed to make writable copy of image: %m");
- }
(void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
- (void) copy_xattr(i->raw_job->disk_fd, dfd);
+ (void) copy_xattr(i->raw_job->disk_fd, dfd, 0);
dfd = safe_close(dfd);
- r = rename(tp, p);
- if (r < 0) {
- r = log_error_errno(errno, "Failed to move writable image into place: %m");
- (void) unlink(tp);
- return r;
- }
+ r = install_file(AT_FDCWD, tp,
+ AT_FDCWD, p,
+ (i->flags & PULL_FORCE ? INSTALL_REPLACE : 0) |
+ (i->flags & PULL_READ_ONLY ? INSTALL_READ_ONLY : 0) |
+ (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
+ if (r < 0)
+ return log_error_errno(errno, "Failed to move local image into place '%s': %m", p);
+
+ tp = mfree(tp);
log_info("Created new local image '%s'.", i->local);
int r;
assert(i);
+ assert(path);
assert(temp_path);
+ assert(*temp_path);
assert(suffix);
- assert(path);
/* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should
* incorporate it in the file name if we can */
if (r < 0)
return r;
- r = import_make_read_only(*temp_path);
+ r = install_file(
+ AT_FDCWD, *temp_path,
+ AT_FDCWD, *path,
+ INSTALL_READ_ONLY|
+ (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 0)
- return r;
-
- r = rename_noreplace(AT_FDCWD, *temp_path, AT_FDCWD, *path);
- if (r < 0)
- return log_error_errno(r, "Failed to rename file %s to %s: %m", *temp_path, *path);
+ return log_error_errno(r, "Failed to move '%s' into place: %m", *path);
*temp_path = mfree(*temp_path);
-
return 1;
}
static void raw_pull_job_on_finished(PullJob *j) {
RawPull *i;
+ PullJob *jj;
int r;
assert(j);
assert(j->userdata);
i = j->userdata;
- if (j == i->settings_job) {
- if (j->error != 0)
+
+ if (j->error != 0) {
+ /* Only the main job and the checksum job are fatal if they fail. The other fails are just
+ * "decoration", that we'll download if we can. The signature job isn't fatal here because we
+ * might not actually need it in case Suse style signatures are used, that are inline in the
+ * checksum file. */
+
+ if (j == i->raw_job) {
+ if (j->error == ENOMEDIUM) /* HTTP 404 */
+ r = log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
+ else
+ r = log_error_errno(j->error, "Failed to retrieve image file.");
+ goto finish;
+ } else if (j == i->checksum_job) {
+ r = log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
+ goto finish;
+ } else if (j == i->signature_job)
+ log_debug_errno(j->error, "Signature job for %s failed, proceeding for now.", j->url);
+ else if (j == i->settings_job)
log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
- } else if (j == i->roothash_job) {
- if (j->error != 0)
+ else if (j == i->roothash_job)
log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without.");
- } else if (j == i->roothash_signature_job) {
- if (j->error != 0)
+ else if (j == i->roothash_signature_job)
log_info_errno(j->error, "Root hash signature file could not be retrieved, proceeding without.");
- } else if (j == i->verity_job) {
- if (j->error != 0)
- log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without. %s", j->url);
- } else if (j->error != 0 && j != i->signature_job) {
- if (j == i->checksum_job)
- log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
+ else if (j == i->verity_job)
+ log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without.");
else
- log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
-
- r = j->error;
- goto finish;
+ assert_not_reached();
}
/* This is invoked if either the download completed successfully, or the download was skipped because
* we already have the etag. In this case ->etag_exists is true.
*
- * We only do something when we got all three files */
+ * We only do something when we got all files */
if (!raw_pull_is_done(i))
return;
if (i->signature_job && i->signature_job->error != 0) {
VerificationStyle style;
+ PullJob *verify_job;
+
+ /* The signature job failed. Let's see if we actually need it */
+
+ verify_job = i->checksum_job ?: i->raw_job; /* if the checksum job doesn't exist this must be
+ * because the main job is the checksum file
+ * itself */
+
+ assert(verify_job);
- r = verification_style_from_url(i->checksum_job->url, &style);
+ r = verification_style_from_url(verify_job->url, &style);
if (r < 0) {
log_error_errno(r, "Failed to determine verification style from checksum URL: %m");
goto finish;
* in per-directory verification mode, since only
* then the signature is detached, and thus a file
* of its own. */
- log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
- r = i->signature_job->error;
+ r = log_error_errno(i->signature_job->error,
+ "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
goto finish;
}
}
- if (i->settings_job)
- i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
- if (i->roothash_job)
- i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd);
- if (i->roothash_signature_job)
- i->roothash_signature_job->disk_fd = safe_close(i->roothash_signature_job->disk_fd);
- if (i->verity_job)
- i->verity_job->disk_fd = safe_close(i->verity_job->disk_fd);
-
- r = raw_pull_determine_path(i, ".raw", &i->final_path);
- if (r < 0)
- goto finish;
+ /* Let's close these auxiliary files now, we don't need access to them anymore. */
+ FOREACH_POINTER(jj, i->settings_job, i->roothash_job, i->roothash_signature_job, i->verity_job)
+ pull_job_close_disk_fd(jj);
if (!i->raw_job->etag_exists) {
- /* This is a new download, verify it, and move it into place */
- assert(i->raw_job->disk_fd >= 0);
-
raw_pull_report_progress(i, RAW_VERIFYING);
r = pull_verify(i->verify,
+ i->checksum,
i->raw_job,
i->checksum_job,
i->signature_job,
i->verity_job);
if (r < 0)
goto finish;
+ }
- raw_pull_report_progress(i, RAW_UNPACKING);
+ if (i->flags & PULL_DIRECT) {
+ assert(!i->settings_job);
+ assert(!i->roothash_job);
+ assert(!i->roothash_signature_job);
+ assert(!i->verity_job);
+
+ raw_pull_report_progress(i, RAW_FINALIZING);
- r = raw_pull_maybe_convert_qcow2(i);
+ if (i->local) {
+ r = install_file(AT_FDCWD, i->local,
+ AT_FDCWD, NULL,
+ ((i->flags & PULL_READ_ONLY) && i->offset == UINT64_MAX ? INSTALL_READ_ONLY : 0) |
+ (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
+ if (r < 0) {
+ log_error_errno(r, "Failed to finalize raw file to '%s': %m", i->local);
+ goto finish;
+ }
+ }
+ } else {
+ r = raw_pull_determine_path(i, ".raw", &i->final_path);
if (r < 0)
goto finish;
- raw_pull_report_progress(i, RAW_FINALIZING);
+ if (!i->raw_job->etag_exists) {
+ /* This is a new download, verify it, and move it into place */
+
+ assert(i->temp_path);
+ assert(i->final_path);
- if (i->raw_job->etag) {
- /* Only make a read-only copy if ETag header is set. */
- r = import_make_read_only_fd(i->raw_job->disk_fd);
+ raw_pull_report_progress(i, RAW_UNPACKING);
+
+ r = raw_pull_maybe_convert_qcow2(i);
if (r < 0)
goto finish;
- r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ raw_pull_report_progress(i, RAW_FINALIZING);
+
+ r = install_file(AT_FDCWD, i->temp_path,
+ AT_FDCWD, i->final_path,
+ INSTALL_READ_ONLY|
+ (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 0) {
- log_error_errno(r, "Failed to rename raw file to %s: %m", i->final_path);
+ log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);
goto finish;
}
- }
- i->temp_path = mfree(i->temp_path);
+ i->temp_path = mfree(i->temp_path);
- if (i->roothash_job &&
- i->roothash_job->error == 0) {
- r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path);
- if (r < 0)
- goto finish;
- }
+ if (i->settings_job &&
+ i->settings_job->error == 0) {
+ r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path);
+ if (r < 0)
+ goto finish;
+ }
- if (i->settings_job &&
- i->settings_job->error == 0) {
- r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path);
- if (r < 0)
- goto finish;
+ if (i->roothash_job &&
+ i->roothash_job->error == 0) {
+ r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path);
+ if (r < 0)
+ goto finish;
+ }
+
+ if (i->roothash_signature_job &&
+ i->roothash_signature_job->error == 0) {
+ r = raw_pull_rename_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_temp_path, &i->roothash_signature_path);
+ if (r < 0)
+ goto finish;
+ }
+
+ if (i->verity_job &&
+ i->verity_job->error == 0) {
+ r = raw_pull_rename_auxiliary_file(i, ".verity", &i->verity_temp_path, &i->verity_path);
+ if (r < 0)
+ goto finish;
+ }
}
- }
- raw_pull_report_progress(i, RAW_COPYING);
+ raw_pull_report_progress(i, RAW_COPYING);
- r = raw_pull_make_local_copy(i);
- if (r < 0)
- goto finish;
+ r = raw_pull_make_local_copy(i);
+ if (r < 0)
+ goto finish;
+ }
r = 0;
RawPull *i,
PullJob *j,
const char *extra,
- char **temp_path) {
+ char **temp_path /* input + output */) {
int r;
assert(extra);
assert(temp_path);
+ assert(!FLAGS_SET(i->flags, PULL_DIRECT));
+
if (!*temp_path) {
r = tempfn_random_child(i->image_root, extra, temp_path);
if (r < 0)
i = j->userdata;
assert(i->raw_job == j);
+ assert(j->disk_fd < 0);
- r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
- if (r < 0)
- return r;
+ if (i->flags & PULL_DIRECT) {
+
+ if (!i->local) { /* If no local name specified, the pull job will write its data to stdout */
+ j->disk_fd = STDOUT_FILENO;
+ j->close_disk_fd = false;
+ return 0;
+ }
+
+ (void) mkdir_parents_label(i->local, 0700);
+
+ j->disk_fd = open(i->local, O_RDWR|O_NOCTTY|O_CLOEXEC|(i->offset == UINT64_MAX ? O_TRUNC|O_CREAT : 0), 0664);
+ if (j->disk_fd < 0)
+ return log_error_errno(errno, "Failed to open destination '%s': %m", i->local);
+
+ if (i->offset == UINT64_MAX)
+ (void) import_set_nocow_and_log(j->disk_fd, i->local);
+
+ } else {
+ r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
+ if (r < 0)
+ return r;
+
+ assert(i->offset == UINT64_MAX);
+ (void) import_set_nocow_and_log(j->disk_fd, i->temp_path);
+ }
- (void) import_set_nocow_and_log(j->disk_fd, i->temp_path);
return 0;
}
RawPull *i,
const char *url,
const char *local,
+ uint64_t offset,
+ uint64_t size_max,
PullFlags flags,
- ImportVerify verify) {
+ ImportVerify verify,
+ const char *checksum) {
+ PullJob *j;
int r;
assert(i);
- assert(verify < _IMPORT_VERIFY_MAX);
- assert(verify >= 0);
+ assert(url);
+ assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
+ assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
+ assert((verify < 0) || !checksum);
assert(!(flags & ~PULL_FLAGS_MASK_RAW));
+ assert(offset == UINT64_MAX || FLAGS_SET(flags, PULL_DIRECT));
+ assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !(flags & PULL_DIRECT));
+ assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !checksum);
- if (!http_url_is_valid(url))
+ if (!http_url_is_valid(url) && !file_url_is_valid(url))
return -EINVAL;
- if (local && !hostname_is_valid(local, 0))
+ if (local && !pull_validate_local(local, flags))
return -EINVAL;
if (i->raw_job)
if (r < 0)
return r;
+ r = free_and_strdup(&i->checksum, checksum);
+ if (r < 0)
+ return r;
+
i->flags = flags;
i->verify = verify;
i->raw_job->on_finished = raw_pull_job_on_finished;
i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
- i->raw_job->on_progress = raw_pull_job_on_progress;
- i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
-
- r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
- if (r < 0)
- return r;
-
- r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i);
- if (r < 0)
- return r;
-
- if (FLAGS_SET(flags, PULL_SETTINGS)) {
- r = pull_make_auxiliary_job(&i->settings_job, url, raw_strip_suffixes, ".nspawn", i->glue, raw_pull_job_on_finished, i);
- if (r < 0)
- return r;
- i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings;
- i->settings_job->on_progress = raw_pull_job_on_progress;
- i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
- }
+ if (checksum)
+ i->raw_job->calc_checksum = true;
+ else if (verify != IMPORT_VERIFY_NO) {
+ /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its
+ * signature, which we let gpg verify instead. */
- if (FLAGS_SET(flags, PULL_ROOTHASH)) {
- r = pull_make_auxiliary_job(&i->roothash_job, url, raw_strip_suffixes, ".roothash", i->glue, raw_pull_job_on_finished, i);
+ r = pull_url_needs_checksum(url);
if (r < 0)
return r;
- i->roothash_job->on_open_disk = raw_pull_job_on_open_disk_roothash;
- i->roothash_job->on_progress = raw_pull_job_on_progress;
- i->roothash_job->calc_checksum = verify != IMPORT_VERIFY_NO;
+ i->raw_job->calc_checksum = r;
+ i->raw_job->force_memory = true; /* make sure this is both written to disk if that's
+ * requested and into memory, since we need to verify it */
}
- if (FLAGS_SET(flags, PULL_ROOTHASH_SIGNATURE)) {
- r = pull_make_auxiliary_job(&i->roothash_signature_job, url, raw_strip_suffixes, ".roothash.p7s", i->glue, raw_pull_job_on_finished, i);
- if (r < 0)
- return r;
-
- i->roothash_signature_job->on_open_disk = raw_pull_job_on_open_disk_roothash_signature;
- i->roothash_signature_job->on_progress = raw_pull_job_on_progress;
- i->roothash_signature_job->calc_checksum = verify != IMPORT_VERIFY_NO;
- }
+ if (size_max != UINT64_MAX)
+ i->raw_job->uncompressed_max = size_max;
+ if (offset != UINT64_MAX)
+ i->raw_job->offset = i->offset = offset;
- if (FLAGS_SET(flags, PULL_VERITY)) {
- r = pull_make_auxiliary_job(&i->verity_job, url, raw_strip_suffixes, ".verity", i->glue, raw_pull_job_on_finished, i);
+ if (!FLAGS_SET(flags, PULL_DIRECT)) {
+ r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
if (r < 0)
return r;
-
- i->verity_job->on_open_disk = raw_pull_job_on_open_disk_verity;
- i->verity_job->on_progress = raw_pull_job_on_progress;
- i->verity_job->calc_checksum = verify != IMPORT_VERIFY_NO;
}
- r = pull_job_begin(i->raw_job);
+ r = pull_make_verification_jobs(
+ &i->checksum_job,
+ &i->signature_job,
+ verify,
+ i->checksum,
+ url,
+ i->glue,
+ raw_pull_job_on_finished,
+ i);
if (r < 0)
return r;
- if (i->checksum_job) {
- i->checksum_job->on_progress = raw_pull_job_on_progress;
- i->checksum_job->on_not_found = pull_job_restart_with_sha256sum;
-
- r = pull_job_begin(i->checksum_job);
+ if (FLAGS_SET(flags, PULL_SETTINGS)) {
+ r = pull_make_auxiliary_job(
+ &i->settings_job,
+ url,
+ raw_strip_suffixes,
+ ".nspawn",
+ verify,
+ i->glue,
+ raw_pull_job_on_open_disk_settings,
+ raw_pull_job_on_finished,
+ i);
if (r < 0)
return r;
}
- if (i->signature_job) {
- i->signature_job->on_progress = raw_pull_job_on_progress;
-
- r = pull_job_begin(i->signature_job);
+ if (FLAGS_SET(flags, PULL_ROOTHASH)) {
+ r = pull_make_auxiliary_job(
+ &i->roothash_job,
+ url,
+ raw_strip_suffixes,
+ ".roothash",
+ verify,
+ i->glue,
+ raw_pull_job_on_open_disk_roothash,
+ raw_pull_job_on_finished,
+ i);
if (r < 0)
return r;
}
- if (i->settings_job) {
- r = pull_job_begin(i->settings_job);
+ if (FLAGS_SET(flags, PULL_ROOTHASH_SIGNATURE)) {
+ r = pull_make_auxiliary_job(
+ &i->roothash_signature_job,
+ url,
+ raw_strip_suffixes,
+ ".roothash.p7s",
+ verify,
+ i->glue,
+ raw_pull_job_on_open_disk_roothash_signature,
+ raw_pull_job_on_finished,
+ i);
if (r < 0)
return r;
}
- if (i->roothash_job) {
- r = pull_job_begin(i->roothash_job);
+ if (FLAGS_SET(flags, PULL_VERITY)) {
+ r = pull_make_auxiliary_job(
+ &i->verity_job,
+ url,
+ raw_strip_suffixes,
+ ".verity",
+ verify,
+ i->glue,
+ raw_pull_job_on_open_disk_verity,
+ raw_pull_job_on_finished,
+ i);
if (r < 0)
return r;
}
- if (i->roothash_signature_job) {
- r = pull_job_begin(i->roothash_signature_job);
- if (r < 0)
- return r;
- }
+ FOREACH_POINTER(j,
+ i->raw_job,
+ i->checksum_job,
+ i->signature_job,
+ i->settings_job,
+ i->roothash_job,
+ i->roothash_signature_job,
+ i->verity_job) {
+
+ if (!j)
+ continue;
+
+ j->on_progress = raw_pull_job_on_progress;
+ j->sync = FLAGS_SET(flags, PULL_SYNC);
- if (i->verity_job) {
- r = pull_job_begin(i->verity_job);
+ r = pull_job_begin(j);
if (r < 0)
return r;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref);
-int raw_pull_start(RawPull *pull, const char *url, const char *local, PullFlags flags, ImportVerify verify);
+int raw_pull_start(RawPull *pull, const char *url, const char *local, uint64_t offset, uint64_t size_max, PullFlags flags, ImportVerify verify, const char *checksum);
#include "hostname-util.h"
#include "import-common.h"
#include "import-util.h"
+#include "install-file.h"
#include "macro.h"
#include "mkdir.h"
#include "path-util.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
+#include "user-util.h"
#include "utf8.h"
#include "util.h"
#include "web-util.h"
char *settings_path;
char *settings_temp_path;
+
+ char *checksum;
};
TarPull* tar_pull_unref(TarPull *i) {
free(i->settings_path);
free(i->image_root);
free(i->local);
+ free(i->checksum);
return mfree(i);
}
break;
default:
- assert_not_reached("Unknown progress state");
+ assert_not_reached();
}
sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
log_debug("Combined progress %u%%", percent);
}
-static int tar_pull_determine_path(TarPull *i, const char *suffix, char **field) {
+static int tar_pull_determine_path(
+ TarPull *i,
+ const char *suffix,
+ char **field /* input + output (!) */) {
int r;
assert(i);
}
static int tar_pull_make_local_copy(TarPull *i) {
+ _cleanup_(rm_rf_subvolume_and_freep) char *t = NULL;
+ const char *p;
int r;
assert(i);
if (!i->local)
return 0;
- r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->flags);
+ assert(i->final_path);
+
+ p = prefix_roota(i->image_root, i->local);
+
+ r = tempfn_random(p, NULL, &t);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
+
+ if (i->flags & PULL_BTRFS_SUBVOL)
+ r = btrfs_subvol_snapshot(
+ i->final_path,
+ t,
+ (i->flags & PULL_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
+ BTRFS_SNAPSHOT_FALLBACK_COPY|
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
+ BTRFS_SNAPSHOT_RECURSIVE);
+ else
+ r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create local image: %m");
+
+ r = install_file(AT_FDCWD, t,
+ AT_FDCWD, p,
+ (i->flags & PULL_FORCE ? INSTALL_REPLACE : 0) |
+ (i->flags & PULL_READ_ONLY ? INSTALL_READ_ONLY : 0) |
+ (i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0));
+ if (r < 0)
+ return log_error_errno(r, "Failed to install local image '%s': %m", p);
+
+ t = mfree(t);
+
+ log_info("Created new local image '%s'.", i->local);
if (FLAGS_SET(i->flags, PULL_SETTINGS)) {
const char *local_settings;
local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
- r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, 0, COPY_REFLINK | (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0));
+ r = copy_file_atomic(
+ i->settings_path,
+ local_settings,
+ 0664,
+ 0, 0,
+ COPY_REFLINK |
+ (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) |
+ (FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 0));
if (r == -EEXIST)
log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
else if (r == -ENOENT)
i = j->userdata;
- if (j == i->settings_job) {
- if (j->error != 0)
+ if (j->error != 0) {
+ if (j == i->tar_job) {
+ if (j->error == ENOMEDIUM) /* HTTP 404 */
+ r = log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
+ else
+ r = log_error_errno(j->error, "Failed to retrieve image file.");
+ goto finish;
+ } else if (j == i->checksum_job) {
+ r = log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
+ goto finish;
+ } else if (j == i->signature_job)
+ log_debug_errno(j->error, "Signature job for %s failed, proceeding for now.", j->url);
+ else if (j == i->settings_job)
log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
- } else if (j->error != 0 && j != i->signature_job) {
- if (j == i->checksum_job)
- log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
else
- log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
-
- r = j->error;
- goto finish;
+ assert("unexpected job");
}
/* This is invoked if either the download completed successfully, or the download was skipped because
if (i->signature_job && i->signature_job->error != 0) {
VerificationStyle style;
+ assert(i->checksum_job);
+
r = verification_style_from_url(i->checksum_job->url, &style);
if (r < 0) {
log_error_errno(r, "Failed to determine verification style from checksum URL: %m");
* in per-directory verification mode, since only
* then the signature is detached, and thus a file
* of its own. */
- log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
- r = i->signature_job->error;
+ r = log_error_errno(i->signature_job->error,
+ "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
goto finish;
}
}
- i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd);
- if (i->settings_job)
- i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
-
- r = tar_pull_determine_path(i, NULL, &i->final_path);
- if (r < 0)
- goto finish;
+ pull_job_close_disk_fd(i->tar_job);
+ pull_job_close_disk_fd(i->settings_job);
if (i->tar_pid > 0) {
r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG);
tar_pull_report_progress(i, TAR_VERIFYING);
r = pull_verify(i->verify,
+ i->checksum,
i->tar_job,
i->checksum_job,
i->signature_job,
/* verity_job = */ NULL);
if (r < 0)
goto finish;
+ }
- tar_pull_report_progress(i, TAR_FINALIZING);
+ if (i->flags & PULL_DIRECT) {
+ assert(!i->settings_job);
+ assert(i->local);
+ assert(!i->temp_path);
- r = import_mangle_os_tree(i->temp_path);
- if (r < 0)
- goto finish;
+ tar_pull_report_progress(i, TAR_FINALIZING);
- r = import_make_read_only(i->temp_path);
+ r = import_mangle_os_tree(i->local);
if (r < 0)
goto finish;
- r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ r = install_file(
+ AT_FDCWD, i->local,
+ AT_FDCWD, NULL,
+ (i->flags & PULL_READ_ONLY) ? INSTALL_READ_ONLY : 0 |
+ (i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0));
if (r < 0) {
- log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path);
+ log_error_errno(r, "Failed to finalize '%s': %m", i->local);
goto finish;
}
+ } else {
+ r = tar_pull_determine_path(i, NULL, &i->final_path);
+ if (r < 0)
+ goto finish;
- i->temp_path = mfree(i->temp_path);
-
- if (i->settings_job &&
- i->settings_job->error == 0) {
+ if (!i->tar_job->etag_exists) {
+ /* This is a new download, verify it, and move it into place */
- /* Also move the settings file into place, if it exists. Note that we do so only if we also
- * moved the tar file in place, to keep things strictly in sync. */
- assert(i->settings_temp_path);
+ assert(i->temp_path);
+ assert(i->final_path);
- /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and
- * we should incorporate it in the file name if we can */
- i->settings_path = mfree(i->settings_path);
+ tar_pull_report_progress(i, TAR_FINALIZING);
- r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
+ r = import_mangle_os_tree(i->temp_path);
if (r < 0)
goto finish;
- r = import_make_read_only(i->settings_temp_path);
- if (r < 0)
- goto finish;
-
- r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path);
+ r = install_file(
+ AT_FDCWD, i->temp_path,
+ AT_FDCWD, i->final_path,
+ INSTALL_READ_ONLY|
+ (i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0));
if (r < 0) {
- log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path);
+ log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path);
goto finish;
}
- i->settings_temp_path = mfree(i->settings_temp_path);
+ i->temp_path = mfree(i->temp_path);
+
+ if (i->settings_job &&
+ i->settings_job->error == 0) {
+
+ /* Also move the settings file into place, if it exists. Note that we do so only if we also
+ * moved the tar file in place, to keep things strictly in sync. */
+ assert(i->settings_temp_path);
+
+ /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and
+ * we should incorporate it in the file name if we can */
+ i->settings_path = mfree(i->settings_path);
+
+ r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
+ if (r < 0)
+ goto finish;
+
+ r = install_file(
+ AT_FDCWD, i->settings_temp_path,
+ AT_FDCWD, i->settings_path,
+ INSTALL_READ_ONLY|
+ (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
+ if (r < 0) {
+ log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path);
+ goto finish;
+ }
+
+ i->settings_temp_path = mfree(i->settings_temp_path);
+ }
}
- }
- tar_pull_report_progress(i, TAR_COPYING);
+ tar_pull_report_progress(i, TAR_COPYING);
- r = tar_pull_make_local_copy(i);
- if (r < 0)
- goto finish;
+ r = tar_pull_make_local_copy(i);
+ if (r < 0)
+ goto finish;
+ }
r = 0;
}
static int tar_pull_job_on_open_disk_tar(PullJob *j) {
+ const char *where;
TarPull *i;
int r;
assert(i->tar_job == j);
assert(i->tar_pid <= 0);
- if (!i->temp_path) {
- r = tempfn_random_child(i->image_root, "tar", &i->temp_path);
- if (r < 0)
- return log_oom();
+ if (i->flags & PULL_DIRECT)
+ where = i->local;
+ else {
+ if (!i->temp_path) {
+ r = tempfn_random_child(i->image_root, "tar", &i->temp_path);
+ if (r < 0)
+ return log_oom();
+ }
+
+ where = i->temp_path;
}
- mkdir_parents_label(i->temp_path, 0700);
+ (void) mkdir_parents_label(where, 0700);
+
+ if (FLAGS_SET(i->flags, PULL_DIRECT|PULL_FORCE))
+ (void) rm_rf(where, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
- r = btrfs_subvol_make_fallback(i->temp_path, 0755);
+ if (i->flags & PULL_BTRFS_SUBVOL)
+ r = btrfs_subvol_make_fallback(where, 0755);
+ else
+ r = mkdir(where, 0755) < 0 ? -errno : 0;
+ if (r == -EEXIST && (i->flags & PULL_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
+ * because in that case our temporary path collided */
+ r = 0;
if (r < 0)
- return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path);
- if (r > 0) { /* actually btrfs subvol */
- (void) import_assign_pool_quota_and_warn(i->image_root);
- (void) import_assign_pool_quota_and_warn(i->temp_path);
+ return log_error_errno(r, "Failed to create directory/subvolume %s: %m", where);
+ if (r > 0 && (i->flags & PULL_BTRFS_QUOTA)) { /* actually btrfs subvol */
+ if (!(i->flags & PULL_DIRECT))
+ (void) import_assign_pool_quota_and_warn(i->image_root);
+ (void) import_assign_pool_quota_and_warn(where);
}
- j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
+ j->disk_fd = import_fork_tar_x(where, &i->tar_pid);
if (j->disk_fd < 0)
return j->disk_fd;
return log_oom();
}
- mkdir_parents_label(i->settings_temp_path, 0700);
+ (void) mkdir_parents_label(i->settings_temp_path, 0700);
j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (j->disk_fd < 0)
const char *url,
const char *local,
PullFlags flags,
- ImportVerify verify) {
+ ImportVerify verify,
+ const char *checksum) {
+ PullJob *j;
int r;
assert(i);
- assert(verify < _IMPORT_VERIFY_MAX);
- assert(verify >= 0);
+ assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
+ assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
+ assert((verify < 0) || !checksum);
assert(!(flags & ~PULL_FLAGS_MASK_TAR));
+ assert(!(flags & PULL_SETTINGS) || !(flags & PULL_DIRECT));
+ assert(!(flags & PULL_SETTINGS) || !checksum);
- if (!http_url_is_valid(url))
+ if (!http_url_is_valid(url) && !file_url_is_valid(url))
return -EINVAL;
- if (local && !hostname_is_valid(local, 0))
+ if (local && !pull_validate_local(local, flags))
return -EINVAL;
if (i->tar_job)
if (r < 0)
return r;
+ r = free_and_strdup(&i->checksum, checksum);
+ if (r < 0)
+ return r;
+
i->flags = flags;
i->verify = verify;
i->tar_job->on_finished = tar_pull_job_on_finished;
i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar;
- i->tar_job->on_progress = tar_pull_job_on_progress;
- i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
+ i->tar_job->calc_checksum = checksum || IN_SET(verify, IMPORT_VERIFY_CHECKSUM, IMPORT_VERIFY_SIGNATURE);
- r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
- if (r < 0)
- return r;
+ if (!FLAGS_SET(flags, PULL_DIRECT)) {
+ r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
+ if (r < 0)
+ return r;
+ }
/* Set up download of checksum/signature files */
- r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
+ r = pull_make_verification_jobs(
+ &i->checksum_job,
+ &i->signature_job,
+ verify,
+ checksum,
+ url,
+ i->glue,
+ tar_pull_job_on_finished,
+ i);
if (r < 0)
return r;
/* Set up download job for the settings file (.nspawn) */
if (FLAGS_SET(flags, PULL_SETTINGS)) {
- r = pull_make_auxiliary_job(&i->settings_job, url, tar_strip_suffixes, ".nspawn", i->glue, tar_pull_job_on_finished, i);
+ r = pull_make_auxiliary_job(
+ &i->settings_job,
+ url,
+ tar_strip_suffixes,
+ ".nspawn",
+ verify,
+ i->glue,
+ tar_pull_job_on_open_disk_settings,
+ tar_pull_job_on_finished,
+ i);
if (r < 0)
return r;
-
- i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings;
- i->settings_job->on_progress = tar_pull_job_on_progress;
- i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
}
- r = pull_job_begin(i->tar_job);
- if (r < 0)
- return r;
-
- if (i->checksum_job) {
- i->checksum_job->on_progress = tar_pull_job_on_progress;
- i->checksum_job->on_not_found = pull_job_restart_with_sha256sum;
-
- r = pull_job_begin(i->checksum_job);
- if (r < 0)
- return r;
- }
+ FOREACH_POINTER(j,
+ i->tar_job,
+ i->checksum_job,
+ i->signature_job,
+ i->settings_job) {
- if (i->signature_job) {
- i->signature_job->on_progress = tar_pull_job_on_progress;
+ if (!j)
+ continue;
- r = pull_job_begin(i->signature_job);
- if (r < 0)
- return r;
- }
+ j->on_progress = tar_pull_job_on_progress;
+ j->sync = FLAGS_SET(flags, PULL_SYNC);
- if (i->settings_job) {
- r = pull_job_begin(i->settings_job);
+ r = pull_job_begin(j);
if (r < 0)
return r;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref);
-int tar_pull_start(TarPull *pull, const char *url, const char *local, PullFlags flags, ImportVerify verify);
+int tar_pull_start(TarPull *pull, const char *url, const char *local, PullFlags flags, ImportVerify verify, const char *checksum);
#include "alloc-util.h"
#include "discover-image.h"
+#include "env-util.h"
+#include "hexdecoct.h"
#include "hostname-util.h"
+#include "import-common.h"
#include "import-util.h"
+#include "io-util.h"
#include "main-func.h"
+#include "parse-argument.h"
#include "parse-util.h"
#include "pull-raw.h"
#include "pull-tar.h"
#include "signal-util.h"
#include "string-util.h"
+#include "terminal-util.h"
#include "verbs.h"
#include "web-util.h"
static const char *arg_image_root = "/var/lib/machines";
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
-static PullFlags arg_pull_flags = PULL_SETTINGS | PULL_ROOTHASH | PULL_ROOTHASH_SIGNATURE | PULL_VERITY;
+static PullFlags arg_pull_flags = PULL_SETTINGS | PULL_ROOTHASH | PULL_ROOTHASH_SIGNATURE | PULL_VERITY | PULL_BTRFS_SUBVOL | PULL_BTRFS_QUOTA | PULL_CONVERT_QCOW2 | PULL_SYNC;
+static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
+static char *arg_checksum = NULL;
-static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- log_notice("Transfer aborted.");
- sd_event_exit(sd_event_source_get_event(s), EINTR);
+STATIC_DESTRUCTOR_REGISTER(arg_checksum, freep);
+
+static int normalize_local(const char *local, const char *url, char **ret) {
+ _cleanup_free_ char *ll = NULL;
+ int r;
+
+ if (arg_pull_flags & PULL_DIRECT) {
+
+ if (!local)
+ log_debug("Writing downloaded data to STDOUT.");
+ else {
+ if (!path_is_absolute(local)) {
+ ll = path_join(arg_image_root, local);
+ if (!ll)
+ return log_oom();
+
+ local = ll;
+ }
+
+ if (!path_is_valid(local))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Local path name '%s' is not valid.", local);
+ }
+
+ } else if (local) {
+
+ if (!hostname_is_valid(local, 0))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Local image name '%s' is not valid.",
+ local);
+
+ if (!FLAGS_SET(arg_pull_flags, PULL_FORCE)) {
+ r = image_find(IMAGE_MACHINE, local, NULL, NULL);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Image '%s' already exists.",
+ local);
+ }
+ }
+
+ if (!ll && local) {
+ ll = strdup(local);
+ if (!ll)
+ return log_oom();
+ }
+
+ if (ll) {
+ if (arg_offset != UINT64_MAX)
+ log_info("Pulling '%s', saving at offset %" PRIu64 " in '%s'.", url, arg_offset, ll);
+ else
+ log_info("Pulling '%s', saving as '%s'.", url, ll);
+ } else
+ log_info("Pulling '%s'.", url);
+
+ *ret = TAKE_PTR(ll);
return 0;
}
}
static int pull_tar(int argc, char *argv[], void *userdata) {
- _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
+ _cleanup_free_ char *ll = NULL, *normalized = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
const char *url, *local;
- _cleanup_free_ char *l = NULL, *ll = NULL;
int r;
url = argv[1];
- if (!http_url_is_valid(url))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "URL '%s' is not valid.", url);
+ if (!http_url_is_valid(url) && !file_url_is_valid(url))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "URL '%s' is not valid.", url);
if (argc >= 3)
- local = argv[2];
+ local = empty_or_dash_to_null(argv[2]);
else {
+ _cleanup_free_ char *l = NULL;
+
r = import_url_last_component(url, &l);
if (r < 0)
- return log_error_errno(r, "Failed get final component of URL: %m");
-
- local = l;
- }
+ return log_error_errno(r, "Failed to get final component of URL: %m");
- local = empty_or_dash_to_null(local);
-
- if (local) {
- r = tar_strip_suffixes(local, &ll);
+ r = tar_strip_suffixes(l, &ll);
if (r < 0)
return log_oom();
local = ll;
+ }
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local image name '%s' is not valid.",
- local);
-
- if (!FLAGS_SET(arg_pull_flags, PULL_FORCE)) {
- r = image_find(IMAGE_MACHINE, local, NULL, NULL);
- if (r < 0) {
- if (r != -ENOENT)
- return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
- } else {
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "Image '%s' already exists.",
- local);
- }
- }
+ if (!local && FLAGS_SET(arg_pull_flags, PULL_DIRECT))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Pulling tar images to STDOUT is not supported.");
- log_info("Pulling '%s', saving as '%s'.", url, local);
- } else
- log_info("Pulling '%s'.", url);
+ r = normalize_local(local, url, &normalized);
+ if (r < 0)
+ return r;
- r = sd_event_default(&event);
+ r = import_allocate_event_with_signals(&event);
if (r < 0)
- return log_error_errno(r, "Failed to allocate event loop: %m");
+ return r;
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
- (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
- (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+ if (!FLAGS_SET(arg_pull_flags, PULL_SYNC))
+ log_info("File system synchronization on completion is off.");
r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate puller: %m");
- r = tar_pull_start(pull, url, local, arg_pull_flags & PULL_FLAGS_MASK_TAR, arg_verify);
+ r = tar_pull_start(
+ pull,
+ url,
+ normalized,
+ arg_pull_flags & PULL_FLAGS_MASK_TAR,
+ arg_verify,
+ arg_checksum);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
}
static int pull_raw(int argc, char *argv[], void *userdata) {
- _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
+ _cleanup_free_ char *ll = NULL, *normalized = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
const char *url, *local;
- _cleanup_free_ char *l = NULL, *ll = NULL;
int r;
url = argv[1];
- if (!http_url_is_valid(url))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "URL '%s' is not valid.", url);
+ if (!http_url_is_valid(url) && !file_url_is_valid(url))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "URL '%s' is not valid.", url);
if (argc >= 3)
- local = argv[2];
+ local = empty_or_dash_to_null(argv[2]);
else {
+ _cleanup_free_ char *l = NULL;
+
r = import_url_last_component(url, &l);
if (r < 0)
- return log_error_errno(r, "Failed get final component of URL: %m");
-
- local = l;
- }
+ return log_error_errno(r, "Failed to get final component of URL: %m");
- local = empty_or_dash_to_null(local);
-
- if (local) {
- r = raw_strip_suffixes(local, &ll);
+ r = raw_strip_suffixes(l, &ll);
if (r < 0)
return log_oom();
local = ll;
+ }
- if (!hostname_is_valid(local, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Local image name '%s' is not valid.",
- local);
-
- if (!FLAGS_SET(arg_pull_flags, PULL_FORCE)) {
- r = image_find(IMAGE_MACHINE, local, NULL, NULL);
- if (r < 0) {
- if (r != -ENOENT)
- return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
- } else {
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "Image '%s' already exists.",
- local);
- }
- }
-
- log_info("Pulling '%s', saving as '%s'.", url, local);
- } else
- log_info("Pulling '%s'.", url);
-
- r = sd_event_default(&event);
+ r = normalize_local(local, url, &normalized);
if (r < 0)
- return log_error_errno(r, "Failed to allocate event loop: %m");
+ return r;
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
- (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
- (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+ r = import_allocate_event_with_signals(&event);
+ if (r < 0)
+ return r;
- r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
+ if (!FLAGS_SET(arg_pull_flags, PULL_SYNC))
+ log_info("File system synchronization on completion is off.");
+ r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate puller: %m");
- r = raw_pull_start(pull, url, local, arg_pull_flags & PULL_FLAGS_MASK_RAW, arg_verify);
+ r = raw_pull_start(
+ pull,
+ url,
+ normalized,
+ arg_offset,
+ arg_size_max,
+ arg_pull_flags & PULL_FLAGS_MASK_RAW,
+ arg_verify,
+ arg_checksum);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
static int help(int argc, char *argv[], void *userdata) {
- printf("%s [OPTIONS...] {COMMAND} ...\n\n"
- "Download container or virtual machine images.\n\n"
+ printf("%1$s [OPTIONS...] {COMMAND} ...\n"
+ "\n%4$sDownload container or virtual machine images.%5$s\n"
+ "\n%2$sCommands:%3$s\n"
+ " tar URL [NAME] Download a TAR image\n"
+ " raw URL [NAME] Download a RAW image\n"
+ "\n%2$sOptions:%3$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --force Force creation of image\n"
" --verify=MODE Verify downloaded image, one of: 'no',\n"
- " 'checksum', 'signature'\n"
+ " 'checksum', 'signature' or literal SHA256 hash\n"
" --settings=BOOL Download settings file with image\n"
" --roothash=BOOL Download root hash file with image\n"
" --roothash-signature=BOOL\n"
" Download root hash signature file with image\n"
" --verity=BOOL Download verity file with image\n"
" --image-root=PATH Image root directory\n\n"
- "Commands:\n"
- " tar URL [NAME] Download a TAR image\n"
- " raw URL [NAME] Download a RAW image\n",
- program_invocation_short_name);
+ " --read-only Create a read-only image\n"
+ " --direct Download directly to specified file\n"
+ " --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
+ " instead of a directory\n"
+ " --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
+ " subvolume\n"
+ " --convert-qcow2=BOOL Controls whether to convert QCOW2 images to\n"
+ " regular disk images\n"
+ " --sync=BOOL Controls whether to sync() before completing\n"
+ " --offset=BYTES Offset to seek to in destination\n"
+ " --size-max=BYTES Maximum number of bytes to write to destination\n",
+ program_invocation_short_name,
+ ansi_underline(),
+ ansi_normal(),
+ ansi_highlight(),
+ ansi_normal());
return 0;
}
ARG_ROOTHASH,
ARG_ROOTHASH_SIGNATURE,
ARG_VERITY,
+ ARG_READ_ONLY,
+ ARG_DIRECT,
+ ARG_BTRFS_SUBVOL,
+ ARG_BTRFS_QUOTA,
+ ARG_CONVERT_QCOW2,
+ ARG_SYNC,
+ ARG_OFFSET,
+ ARG_SIZE_MAX,
};
static const struct option options[] = {
{ "roothash", required_argument, NULL, ARG_ROOTHASH },
{ "roothash-signature", required_argument, NULL, ARG_ROOTHASH_SIGNATURE },
{ "verity", required_argument, NULL, ARG_VERITY },
+ { "read-only", no_argument, NULL, ARG_READ_ONLY },
+ { "direct", no_argument, NULL, ARG_DIRECT },
+ { "btrfs-subvol", required_argument, NULL, ARG_BTRFS_SUBVOL },
+ { "btrfs-quota", required_argument, NULL, ARG_BTRFS_QUOTA },
+ { "convert-qcow2", required_argument, NULL, ARG_CONVERT_QCOW2 },
+ { "sync", required_argument, NULL, ARG_SYNC },
+ { "offset", required_argument, NULL, ARG_OFFSET },
+ { "size-max", required_argument, NULL, ARG_SIZE_MAX },
{}
};
arg_image_root = optarg;
break;
- case ARG_VERIFY:
- arg_verify = import_verify_from_string(optarg);
- if (arg_verify < 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Invalid verification setting '%s'", optarg);
+ case ARG_VERIFY: {
+ ImportVerify v;
+
+ v = import_verify_from_string(optarg);
+ if (v < 0) {
+ _cleanup_free_ void *h = NULL;
+ char *hh;
+ size_t n;
+
+ /* If this is not a valid verification mode, maybe it's a literally specified
+ * SHA256 hash? We can handle that too... */
+
+ r = unhexmem(optarg, (size_t) -1, &h, &n);
+ if (r < 0 || n == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid verification setting: %s", optarg);
+ if (n != 32)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "64 hex character SHA256 hash required when specifying explicit checksum, %zu specified", n * 2);
+
+ hh = hexmem(h, n); /* bring into canonical (lowercase) form */
+ if (!hh)
+ return log_oom();
+
+ free_and_replace(arg_checksum, hh);
+ arg_pull_flags &= ~(PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY);
+ arg_verify = _IMPORT_VERIFY_INVALID;
+ } else
+ arg_verify = v;
break;
+ }
case ARG_SETTINGS:
- r = parse_boolean(optarg);
+ r = parse_boolean_argument("--settings=", optarg, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to parse --settings= parameter '%s': %m", optarg);
+ return r;
SET_FLAG(arg_pull_flags, PULL_SETTINGS, r);
break;
case ARG_ROOTHASH:
- r = parse_boolean(optarg);
+ r = parse_boolean_argument("--roothash=", optarg, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to parse --roothash= parameter '%s': %m", optarg);
+ return r;
SET_FLAG(arg_pull_flags, PULL_ROOTHASH, r);
break;
case ARG_ROOTHASH_SIGNATURE:
- r = parse_boolean(optarg);
+ r = parse_boolean_argument("--roothash-signature=", optarg, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to parse --roothash-signature= parameter '%s': %m", optarg);
+ return r;
SET_FLAG(arg_pull_flags, PULL_ROOTHASH_SIGNATURE, r);
break;
case ARG_VERITY:
- r = parse_boolean(optarg);
+ r = parse_boolean_argument("--verity=", optarg, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to parse --verity= parameter '%s': %m", optarg);
+ return r;
SET_FLAG(arg_pull_flags, PULL_VERITY, r);
break;
+ case ARG_READ_ONLY:
+ arg_pull_flags |= PULL_READ_ONLY;
+ break;
+
+ case ARG_DIRECT:
+ arg_pull_flags |= PULL_DIRECT;
+ arg_pull_flags &= ~(PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY);
+ break;
+
+ case ARG_BTRFS_SUBVOL:
+ r = parse_boolean_argument("--btrfs-subvol=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_pull_flags, PULL_BTRFS_SUBVOL, r);
+ break;
+
+ case ARG_BTRFS_QUOTA:
+ r = parse_boolean_argument("--btrfs-quota=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_pull_flags, PULL_BTRFS_QUOTA, r);
+ break;
+
+ case ARG_CONVERT_QCOW2:
+ r = parse_boolean_argument("--convert-qcow2=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_pull_flags, PULL_CONVERT_QCOW2, r);
+ break;
+
+ case ARG_SYNC:
+ r = parse_boolean_argument("--sync=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_pull_flags, PULL_SYNC, r);
+ break;
+
+ case ARG_OFFSET: {
+ uint64_t u;
+
+ r = safe_atou64(optarg, &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --offset= argument: %s", optarg);
+ if (!FILE_SIZE_VALID(u))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --offset= switch too large: %s", optarg);
+
+ arg_offset = u;
+ break;
+ }
+
+ case ARG_SIZE_MAX: {
+ uint64_t u;
+
+ r = parse_size(optarg, 1024, &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --size-max= argument: %s", optarg);
+ if (!FILE_SIZE_VALID(u))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --size-max= switch too large: %s", optarg);
+
+ arg_size_max = u;
+ break;
+ }
+
case '?':
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
+ /* Make sure offset+size is still in the valid range if both set */
+ if (arg_offset != UINT64_MAX && arg_size_max != UINT64_MAX &&
+ ((arg_size_max > (UINT64_MAX - arg_offset)) ||
+ !FILE_SIZE_VALID(arg_offset + arg_size_max)))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset und maximum size out of range.");
+
+ if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_pull_flags, PULL_DIRECT))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
+
+ if (arg_checksum && (arg_pull_flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Literal checksum verification only supported if no associated files are downloaded.");
+
return 1;
}
+static void parse_env(void) {
+ int r;
+
+ /* Let's make these relatively low-level settings also controllable via env vars. User can then set
+ * them for systemd-importd.service if they like to tweak behaviour */
+
+ r = getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
+ if (r >= 0)
+ SET_FLAG(arg_pull_flags, PULL_BTRFS_SUBVOL, r);
+ else if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
+
+ r = getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
+ if (r >= 0)
+ SET_FLAG(arg_pull_flags, PULL_BTRFS_QUOTA, r);
+ else if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
+
+ r = getenv_bool("SYSTEMD_IMPORT_SYNC");
+ if (r >= 0)
+ SET_FLAG(arg_pull_flags, PULL_SYNC, r);
+ else if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_SYNC: %m");
+}
+
static int pull_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
log_parse_environment();
log_open();
+ parse_env();
+
r = parse_argv(argc, argv);
if (r <= 0)
return r;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind < argc)
return -EINVAL;
default:
- assert_not_reached("Unknown option code.");
+ assert_not_reached();
}
if (optind < argc)
}
default:
- assert_not_reached("what?");
+ assert_not_reached();
}
r = journal_file_open_reliably(filename,
break;
default:
- assert_not_reached("what split mode?");
+ assert_not_reached();
}
w = hashmap_get(s->writers, key);
else if (split_mode == JOURNAL_WRITE_SPLIT_HOST)
s->output = REMOTE_JOURNAL_PATH;
else
- assert_not_reached("bad split mode");
+ assert_not_reached();
r = sd_event_default(&s->events);
if (r < 0)
_fallthrough_;
case ENTRY_BOOT_ID: {
sd_id128_t boot_id;
- char sid[SD_ID128_STRING_MAX];
r = sd_journal_get_monotonic_usec(u->journal, NULL, &boot_id);
if (r < 0)
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
r = snprintf(buf + pos, size - pos,
- "_BOOT_ID=%s\n", sd_id128_to_string(boot_id, sid));
+ "_BOOT_ID=%s\n", SD_ID128_TO_STRING(boot_id));
assert(r >= 0);
if ((size_t) r > size - pos)
/* not enough space */
return pos;
default:
- assert_not_reached("WTF?");
+ assert_not_reached();
}
}
- assert_not_reached("WTF?");
+ assert_not_reached();
}
static void check_update_watchdog(Uploader *u) {
argv[optind - 1]);
default:
- assert_not_reached("Unhandled option code.");
+ assert_not_reached();
}
if (!arg_url)
if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_MICROHTTPD') == 1
install_data('browse.html',
- install_dir : join_paths(pkgdatadir, 'gatewayd'))
+ install_dir : pkgdatadir / 'gatewayd')
if get_option('create-log-dirs')
meson.add_install_script('sh', '-c',
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_follow && !arg_no_tail && !arg_since && arg_lines == ARG_LINES_DEFAULT)
BootId **ret) {
_cleanup_free_ BootId *next_boot = NULL;
- char match[9+32+1] = "_BOOT_ID=";
+ char match[STRLEN("_BOOT_ID=") + SD_ID128_STRING_MAX] = "_BOOT_ID=";
sd_id128_t boot_id;
int r;
* If no reference is given, the journal head/tail will do,
* they're "virtual" boots after all. */
if (boot_id && !sd_id128_is_null(*boot_id)) {
- char match[9+32+1] = "_BOOT_ID=";
+ char match[STRLEN("_BOOT_ID=") + SD_ID128_STRING_MAX] = "_BOOT_ID=";
sd_journal_flush_matches(j);
}
static int add_boot(sd_journal *j) {
- char match[9+32+1] = "_BOOT_ID=";
+ char match[STRLEN("_BOOT_ID=") + SD_ID128_STRING_MAX] = "_BOOT_ID=";
sd_id128_t boot_id;
int r;
break;
default:
- assert_not_reached("Unknown action");
+ assert_not_reached();
}
if (arg_directory)
case ACTION_FLUSH:
case ACTION_SYNC:
case ACTION_ROTATE:
- assert_not_reached("Unexpected action.");
+ assert_not_reached();
case ACTION_PRINT_HEADER:
journal_print_header(j);
break;
default:
- assert_not_reached("Unknown action");
+ assert_not_reached();
}
if (arg_boot_offset != 0 &&
if (s->runtime_journal)
return s->runtime_journal;
+ /* If we are not in persistent mode, then we need return NULL immediately rather than opening a
+ * persistent journal of any sort.
+ *
+ * Fixes https://github.com/systemd/systemd/issues/20390 */
+ if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
+ return NULL;
+
if (uid_for_system_journal(uid))
return s->system_journal;
return stdout_stream_log(s, orig, line_break);
}
- assert_not_reached("Unknown stream state");
+ assert_not_reached();
}
static int stdout_stream_found(
if install_sysconfdir
meson.add_install_script('sh', '-c',
- mkdir_p.format(join_paths(sysconfdir, 'kernel/install.d')))
+ mkdir_p.format(sysconfdir / 'kernel/install.d'))
endif
endif
} DHCPClientId;
typedef struct DHCPLease {
+ sd_dhcp_server *server;
+
DHCPClientId client_id;
be32_t address;
bool emit_router;
- Hashmap *leases_by_client_id;
+ Hashmap *bound_leases_by_client_id;
+ Hashmap *bound_leases_by_address;
Hashmap *static_leases_by_client_id;
- DHCPLease **bound_leases;
- DHCPLease invalid_lease;
+ Hashmap *static_leases_by_address;
uint32_t max_lease_time, default_lease_time;
const uint8_t *agent_info_option;
} DHCPRequest;
+extern const struct hash_ops dhcp_lease_hash_ops;
+
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length);
int dhcp_server_send_packet(sd_dhcp_server *server,
static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
uint8_t *client_id;
DHCPLease *lease;
- int pool_offset;
if (size < sizeof(DHCPMessage))
return 0;
lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1));
lease->expiration = UINT64_MAX;
memcpy(lease->chaddr, chaddr, 16);
- pool_offset = get_pool_offset(server, lease->address);
- server->bound_leases[pool_offset] = lease;
- assert_se(hashmap_put(server->leases_by_client_id, &lease->client_id, lease) >= 0);
+ 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);
/* namelen == 0 only happens when running the test-suite over a socketpair */
- assert(!(msg.msg_flags & MSG_CTRUNC));
assert(!(msg.msg_flags & MSG_TRUNC));
CMSG_FOREACH(cmsg, &msg) {
goto error;
default:
- assert_not_reached("Unhandled choice");
+ assert_not_reached();
}
r = event_reset_time(client->event, &client->timeout_resend,
r = -EINVAL;
goto error;
default:
- assert_not_reached("invalid state");
+ assert_not_reached();
}
error:
if (!lease)
return NULL;
+ if (lease->server) {
+ DHCPLease *e;
+
+ e = hashmap_get(lease->server->bound_leases_by_client_id, &lease->client_id);
+ if (e == lease) {
+ hashmap_remove(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address));
+ hashmap_remove(lease->server->bound_leases_by_client_id, &lease->client_id);
+ }
+
+ e = hashmap_get(lease->server->static_leases_by_client_id, &lease->client_id);
+ if (e == lease) {
+ hashmap_remove(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address));
+ hashmap_remove(lease->server->static_leases_by_client_id, &lease->client_id);
+ }
+ }
+
free(lease->client_id.data);
return mfree(lease);
}
if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
- free(server->bound_leases);
- server->bound_leases = new0(DHCPLease*, size);
- if (!server->bound_leases)
- return -ENOMEM;
-
server->pool_offset = offset;
server->pool_size = size;
server->netmask = netmask;
server->subnet = address->s_addr & netmask;
- if (server_off >= offset && server_off - offset < size)
- server->bound_leases[server_off - offset] = &server->invalid_lease;
-
/* Drop any leases associated with the old address range */
- hashmap_clear(server->leases_by_client_id);
+ hashmap_clear(server->bound_leases_by_address);
+ hashmap_clear(server->bound_leases_by_client_id);
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
return memcmp(a->data, b->data, a->length);
}
-DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(dhcp_lease_hash_ops, DHCPClientId, client_id_hash_func, client_id_compare_func,
- DHCPLease, dhcp_lease_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ dhcp_lease_hash_ops,
+ DHCPClientId,
+ client_id_hash_func,
+ client_id_compare_func,
+ DHCPLease,
+ dhcp_lease_free);
static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
assert(server);
for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
free(server->servers[i].addr);
- hashmap_free(server->leases_by_client_id);
- hashmap_free(server->static_leases_by_client_id);
+ server->bound_leases_by_address = hashmap_free(server->bound_leases_by_address);
+ server->bound_leases_by_client_id = hashmap_free(server->bound_leases_by_client_id);
+ server->static_leases_by_address = hashmap_free(server->static_leases_by_address);
+ server->static_leases_by_client_id = hashmap_free(server->static_leases_by_client_id);
ordered_set_free(server->extra_options);
ordered_set_free(server->vendor_options);
free(server->agent_circuit_id);
free(server->agent_remote_id);
- free(server->bound_leases);
-
free(server->ifname);
return mfree(server);
}
.max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC),
};
- server->leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
- if (!server->leases_by_client_id)
- return -ENOMEM;
- server->static_leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
- if (!server->static_leases_by_client_id)
- return -ENOMEM;
-
*ret = TAKE_PTR(server);
return 0;
return 0;
}
-static bool static_leases_have_address(sd_dhcp_server *server, be32_t address) {
- DHCPLease *s;
-
- assert(server);
-
- HASHMAP_FOREACH(s, server->static_leases_by_client_id)
- if (s->address == address)
- return true;
-
- return false;
-}
-
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) {
/* this only fails on critical errors */
return r;
- existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
+ existing_lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
switch(type) {
case DHCP_DISCOVER: {
be32_t address = INADDR_ANY;
- unsigned i;
log_dhcp_server(server, "DISCOVER (0x%x)", be32toh(req->message->xid));
else {
struct siphash state;
uint64_t hash;
- uint32_t next_offer;
/* even with no persistence of leases, we try to offer the same client
the same IP address. we do this by using the hash of the client id
siphash24_init(&state, HASH_KEY.bytes);
client_id_hash_func(&req->client_id, &state);
hash = htole64(siphash24_finalize(&state));
- next_offer = hash % server->pool_size;
-
- for (i = 0; i < server->pool_size; i++) {
- if (!server->bound_leases[next_offer]) {
- be32_t tmp = server->subnet | htobe32(server->pool_offset + next_offer);
- if (!static_leases_have_address(server, tmp)) {
- address = tmp;
- break;
- }
- }
- next_offer = (next_offer + 1) % server->pool_size;
+ for (unsigned i = 0; i < server->pool_size; i++) {
+ be32_t tmp_address;
+
+ tmp_address = server->subnet | htobe32(server->pool_offset + (hash + i) % server->pool_size);
+ if (!hashmap_contains(server->bound_leases_by_address, &tmp_address) &&
+ !hashmap_contains(server->static_leases_by_address, &tmp_address)) {
+ address = tmp_address;
+ break;
+ }
}
}
return 1;
case DHCP_REQUEST: {
+ DHCPLease *existing_lease_by_address;
be32_t address;
bool init_reboot = false;
int pool_offset;
}
pool_offset = get_pool_offset(server, address);
+ existing_lease_by_address = hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(address));
/* verify that the requested address is from the pool, and either
owned by the current client or free */
- if (pool_offset >= 0 && static_lease) {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old_lease = NULL;
+ if (static_lease && static_lease->address == address) {
+ _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
usec_t time_now, expiration;
r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
- server->bound_leases[pool_offset] = lease;
+ dhcp_lease_free(hashmap_remove(server->bound_leases_by_client_id, &lease->client_id));
+ r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
+ if (r < 0)
+ return log_dhcp_server_errno(server, r, "Could not save lease: %m");
- old_lease = hashmap_remove(server->leases_by_client_id, &lease->client_id);
- r = hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
+ r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
+
+ lease->server = server;
TAKE_PTR(lease);
if (server->callback)
return DHCP_ACK;
- } else if (pool_offset >= 0 && server->bound_leases[pool_offset] == existing_lease) {
+ } else if (pool_offset >= 0 && existing_lease_by_address == existing_lease) {
_cleanup_(dhcp_lease_freep) DHCPLease *new_lease = NULL;
usec_t time_now, expiration;
DHCPLease *lease;
+ /* Note that in the above condition we accept the case that both leases are NULL. */
+
r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
return r;
log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
- server->bound_leases[pool_offset] = lease;
- r = hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
+ r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m");
+ r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+ if (r < 0)
+ return log_dhcp_server_errno(server, r, "Could not save lease: %m");
+
+ lease->server = server;
TAKE_PTR(new_lease);
if (server->callback)
}
case DHCP_RELEASE: {
- int pool_offset;
-
log_dhcp_server(server, "RELEASE (0x%x)",
be32toh(req->message->xid));
if (existing_lease->address != req->message->ciaddr)
return 0;
- pool_offset = get_pool_offset(server, req->message->ciaddr);
- if (pool_offset < 0)
- return 0;
+ dhcp_lease_free(existing_lease);
- if (server->bound_leases[pool_offset] == existing_lease) {
- server->bound_leases[pool_offset] = NULL;
- hashmap_remove(server->leases_by_client_id, existing_lease);
- dhcp_lease_free(existing_lease);
-
- if (server->callback)
- server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
- }
+ if (server->callback)
+ server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
return 0;
}}
}
int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
- int r = 0;
+ DHCPLease *lease;
+ int k, r = 0;
assert_return(server, -EINVAL);
- assert(server->bound_leases);
-
- for (uint32_t i = 0; i < server->pool_size; i++) {
- DHCPLease *lease = server->bound_leases[i];
- if (!lease || lease == &server->invalid_lease)
- continue;
+ log_dhcp_server(server, "FORCERENEW");
- r = server_send_forcerenew(server, lease->address,
- lease->gateway,
- lease->chaddr);
- if (r < 0)
- return r;
-
- 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);
+ if (k < 0)
+ r = k;
}
return r;
uint8_t *client_id,
size_t client_id_size) {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old = NULL;
- DHCPClientId c;
+ _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
int r;
assert_return(server, -EINVAL);
* 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)
.data = data,
};
- old = hashmap_remove(server->static_leases_by_client_id, &c);
+ dhcp_lease_free(hashmap_remove(server->static_leases_by_client_id, &c));
return 0;
}
- if (static_leases_have_address(server, address->s_addr))
+ if (hashmap_contains(server->static_leases_by_address, UINT32_TO_PTR(address->s_addr)))
return -EEXIST;
lease = new(DHCPLease, 1);
return -ENOMEM;
r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
+ if (r < 0)
+ return r;
+ r = hashmap_ensure_put(&server->static_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
if (r < 0)
return r;
+ lease->server = server;
TAKE_PTR(lease);
return 0;
}
switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID:
- if (clientid) {
- log_dhcp6_client(client, "%s contains multiple clientids",
- dhcp6_message_type_to_string(message->type));
- return -EINVAL;
- }
+ if (clientid)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple clientids",
+ dhcp6_message_type_to_string(message->type));
if (optlen != client->duid_len ||
- memcmp(&client->duid, optval, optlen) != 0) {
- log_dhcp6_client(client, "%s DUID does not match",
- dhcp6_message_type_to_string(message->type));
+ memcmp(&client->duid, optval, optlen) != 0)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s DUID does not match",
+ dhcp6_message_type_to_string(message->type));
- return -EINVAL;
- }
clientid = true;
break;
case SD_DHCP6_OPTION_SERVERID:
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
- if (r >= 0) {
- log_dhcp6_client(client, "%s contains multiple serverids",
- dhcp6_message_type_to_string(message->type));
- return -EINVAL;
- }
+ if (r >= 0)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple serverids",
+ dhcp6_message_type_to_string(message->type));
r = dhcp6_lease_set_serverid(lease, optval, optlen);
if (r < 0)
if (status < 0)
return status;
- if (status > 0) {
- log_dhcp6_client(client, "%s Status %s",
- dhcp6_message_type_to_string(message->type),
- dhcp6_message_status_to_string(status));
-
- return -EINVAL;
- }
+ if (status > 0)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s Status %s",
+ dhcp6_message_type_to_string(message->type),
+ dhcp6_message_status_to_string(status));
break;
case SD_DHCP6_OPTION_IA_NA:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
- log_dhcp6_client(client, "Information request ignoring IA NA option");
-
+ log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
break;
}
if (r < 0)
return r;
- if (client->ia_na.ia_na.id != iaid_lease) {
- log_dhcp6_client(client, "%s has wrong IAID for IA NA",
- dhcp6_message_type_to_string(message->type));
- return -EINVAL;
- }
+ if (client->ia_na.ia_na.id != iaid_lease)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has wrong IAID for IA NA",
+ dhcp6_message_type_to_string(message->type));
if (lease->ia.addresses) {
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
- lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
+ lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2));
}
break;
case SD_DHCP6_OPTION_IA_PD:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
- log_dhcp6_client(client, "Information request ignoring IA PD option");
-
+ log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
break;
}
if (r < 0)
return r;
- if (client->ia_pd.ia_pd.id != iaid_lease) {
- log_dhcp6_client(client, "%s has wrong IAID for IA PD",
- dhcp6_message_type_to_string(message->type));
- return -EINVAL;
- }
+ if (client->ia_pd.ia_pd.id != iaid_lease)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has wrong IAID for IA PD",
+ dhcp6_message_type_to_string(message->type));
if (lease->pd.addresses) {
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
pos += offsetof(DHCP6Option, data) + optlen;
}
- if (ia_na_status > 0 && ia_pd_status > 0) {
- log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
- return -EINVAL;
- }
+ if (ia_na_status > 0 && ia_pd_status > 0)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
- if (!clientid) {
- log_dhcp6_client(client, "%s has incomplete options",
- dhcp6_message_type_to_string(message->type));
- return -EINVAL;
- }
+ if (!clientid)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options",
+ dhcp6_message_type_to_string(message->type));
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
- if (r < 0) {
- log_dhcp6_client(client, "%s has no server id",
- dhcp6_message_type_to_string(message->type));
- return -EINVAL;
- }
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "%s has no server id",
+ dhcp6_message_type_to_string(message->type));
+ }
- } else {
- if (lease->ia.addresses) {
- lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
- lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
- }
+ if (lease->ia.addresses) {
+ lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
+ lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
+ }
- if (lease->pd.addresses) {
- lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
- lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
- }
+ if (lease->pd.addresses) {
+ lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
+ lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
}
client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
return 0;
}
- if (client->transaction_id != (message->transaction_id &
- htobe32(0x00ffffff)))
+ if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
return 0;
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
r = client_receive_reply(client, message, len);
- if (r < 0)
+ if (r < 0) {
+ log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
return 0;
+ }
client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
case DHCP6_STATE_SOLICITATION:
r = client_receive_advertise(client, message, len);
+ if (r < 0) {
+ log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m");
+ return 0;
+ }
if (r == DHCP6_STATE_REQUEST) {
client_start(client, r);
-
break;
}
case DHCP6_STATE_REBIND:
r = client_receive_reply(client, message, len);
- if (r < 0)
+ if (r < 0) {
+ log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
return 0;
+ }
if (r == DHCP6_STATE_BOUND) {
-
r = client_start(client, DHCP6_STATE_BOUND);
if (r < 0) {
client_stop(client, r);
break;
default:
- assert_not_reached("Invalid state.");
+ assert_not_reached();
}
return 0;
break;
default:
- assert_not_reached("Invalid state.");
+ assert_not_reached();
}
return 0;
break;
default:
- assert_not_reached("Invalid IPv4ACD event.");
+ assert_not_reached();
}
return;
log_error("the client was stopped");
break;
default:
- assert_not_reached("invalid ACD event");
+ assert_not_reached();
}
}
static sd_event_source *test_hangcheck;
static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
- assert_not_reached("Test case should have completed in 2 seconds");
+ assert_not_reached();
return 0;
}
return 0;
}
-static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
- assert_not_reached("Test case should have completed in 2 seconds");
-
- return 0;
+static int test_check_completed_in_2_seconds(sd_event_source *s, uint64_t usec, void *userdata) {
+ assert_not_reached();
}
static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
break;
case SD_DHCP6_OPTION_IA_NA:
- assert_not_reached("IA TA option must not be present");
-
- break;
-
case SD_DHCP6_OPTION_SERVERID:
- assert_not_reached("Server ID option must not be present");
-
- break;
+ assert_not_reached();
case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
assert_se(sd_event_add_time_relative(e, &hangcheck, clock_boottime_or_monotonic(),
2 * USEC_PER_SEC, 0,
- test_hangcheck, NULL) >= 0);
+ test_check_completed_in_2_seconds, NULL) >= 0);
assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
log_error("the client was stopped with address %s", strna(address));
break;
default:
- assert_not_reached("invalid LL event");
+ assert_not_reached();
}
}
c_args : libsystemd_c_args)
libsystemd_sym = files('libsystemd.sym')
-libsystemd_sym_path = join_paths(meson.current_source_dir(), 'libsystemd.sym')
+libsystemd_sym_path = meson.current_source_dir() / 'libsystemd.sym'
static_libsystemd = get_option('static-libsystemd')
static_libsystemd_pic = static_libsystemd == 'true' or static_libsystemd == 'pic'
r = wait_for_terminate_and_check("(sd-buscntrns)", child, 0);
if (r < 0)
return r;
- if (r != EXIT_SUCCESS)
- return -EPROTO;
+ bool nonzero_exit_status = r != EXIT_SUCCESS;
n = read(pair[0], &error_buf, sizeof(error_buf));
if (n < 0)
return 1;
if (error_buf > 0)
- return log_debug_errno(error_buf, "Got error from (sd-buscntr): %m");
+ return log_debug_errno(error_buf, "(sd-buscntr) failed to connect to D-Bus socket: %m");
}
+ if (nonzero_exit_status)
+ return -EPROTO;
+
return bus_socket_start_auth(b);
}
break;
default:
- assert_not_reached("Unknown basic type.");
+ assert_not_reached();
}
}
return -EINVAL;
default:
- assert_not_reached("Unknown signature type");
+ assert_not_reached();
}
p += n;
}
default:
- assert_not_reached("Unknown signature type");
+ assert_not_reached();
}
if (a < 0)
}
default:
- assert_not_reached("Unknown signature type");
+ assert_not_reached();
}
p += n;
else if (sz == 8)
return le64toh(x.u64);
- assert_not_reached("unknown word width");
+ assert_not_reached();
}
void bus_gvariant_write_word_le(void *p, size_t sz, size_t value) {
else if (sz == 8)
x.u64 = htole64((uint64_t) value);
else
- assert_not_reached("unknown word width");
+ assert_not_reached();
memcpy(p, &x, sz);
}
return false;
default:
- assert_not_reached("Invalid node type");
+ assert_not_reached();
}
}
return streq(node->value.str, value_str);
default:
- assert_not_reached("Invalid node type");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Unknown match type.");
+ assert_not_reached();
}
if (BUS_MATCH_CAN_HASH(node->type)) {
return "path_namespace";
case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
- snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
- return buf;
+ return snprintf_ok(buf, l, "arg%i", t - BUS_MATCH_ARG);
case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
- snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
- return buf;
+ return snprintf_ok(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
- snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
- return buf;
+ return snprintf_ok(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
- snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
- return buf;
+ return snprintf_ok(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
default:
return NULL;
else if (IN_SET(c->enclosing, SD_BUS_TYPE_STRUCT, SD_BUS_TYPE_DICT_ENTRY))
r = bus_message_close_struct(m, c, true);
else
- assert_not_reached("Unknown container type");
+ assert_not_reached();
free(c->signature);
free(c->offsets);
} else if (c->enclosing == SD_BUS_TYPE_VARIANT)
goto end;
else
- assert_not_reached("Unknown container type");
+ assert_not_reached();
return 0;
}
default:
- assert_not_reached("unexpected type");
+ assert_not_reached();
}
}
}
default:
- assert_not_reached("Unknown basic type...");
+ assert_not_reached();
}
}
}
break;
default:
- assert_not_reached("Wut? Unknown slot type?");
+ assert_not_reached();
}
bus = slot->bus;
if (!d)
return 0;
- e = memmem(d + 2, b->rbuffer_size - (d - (char*) b->rbuffer) - 2, "\r\n", 2);
+ e = memmem_safe(d + 2, b->rbuffer_size - (d - (char*) b->rbuffer) - 2, "\r\n", 2);
if (!e)
return 0;
if (b->accept_fd) {
- f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
+ f = memmem_safe(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
if (!f)
return 0;
for (;;) {
/* Check if line is complete */
line = (char*) b->rbuffer + b->auth_rbegin;
- e = memmem(line, b->rbuffer_size - b->auth_rbegin, "\r\n", 2);
+ e = memmem_safe(line, b->rbuffer_size - b->auth_rbegin, "\r\n", 2);
if (!e)
return processed;
break;
default:
- assert_not_reached("Unknown state");
+ assert_not_reached();
}
return flags;
return 0;
default:
- assert_not_reached("Unknown or unexpected stat");
+ assert_not_reached();
}
}
r = sd_bus_message_new_method_return(m, &reply);
else if (streq_ptr(m->member, "GetMachineId")) {
sd_id128_t id;
- char sid[SD_ID128_STRING_MAX];
r = sd_id128_get_machine(&id);
if (r < 0)
if (r < 0)
return r;
- r = sd_bus_message_append(reply, "s", sd_id128_to_string(id, sid));
+ r = sd_bus_message_append(reply, "s", SD_ID128_TO_STRING(id));
} else {
r = sd_bus_message_new_method_errorf(
m, &reply,
else
exit(EXIT_FAILURE);
- assert_not_reached("exit() didn't exit?");
+ assert_not_reached();
}
static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c) {
return process_closing(bus, ret);
default:
- assert_not_reached("Unknown state");
+ assert_not_reached();
}
if (ERRNO_IS_DISCONNECT(r)) {
return;
} else if (!sd_bus_message_is_signal(m, NULL, NULL))
- assert_not_reached("Unknown method");
+ assert_not_reached();
}
}
OBJECT_PATH_FOREACH_PREFIX(prefix, "/") {
log_info("<%s>", prefix);
- assert_not_reached("???");
+ assert_not_reached();
}
r = 0;
break;
default:
- assert_not_reached("Invalid state when parsing uevent file");
+ assert_not_reached();
}
if (major) {
sd_device_action_t action,
sd_id128_t *ret_uuid) {
- char buf[ID128_UUID_STRING_MAX];
const char *s, *j;
sd_id128_t u;
int r;
if (r < 0)
return r;
- id128_to_uuid_string(u, buf);
- j = strjoina(s, " ", buf);
+ j = strjoina(s, " ", ID128_TO_UUID_STRING(u));
r = sd_device_set_sysattr_value(device, "uevent", j);
if (r < 0)
}
default:
- assert_not_reached("Wut? I shouldn't exist.");
+ assert_not_reached();
}
if (s->pending)
break;
default:
- assert_not_reached("Wut? I shouldn't exist.");
+ assert_not_reached();
}
/* Always reshuffle time prioq, as the ratelimited flag may be changed. */
break;
default:
- assert_not_reached("Wut? I shouldn't exist.");
+ assert_not_reached();
}
s->enabled = enabled;
case SOURCE_WATCHDOG:
case _SOURCE_EVENT_SOURCE_TYPE_MAX:
case _SOURCE_EVENT_SOURCE_TYPE_INVALID:
- assert_not_reached("Wut? I shouldn't exist.");
+ assert_not_reached();
}
s->dispatching = false;
break;
default:
- assert_not_reached("Unexpected event source type");
+ assert_not_reached();
}
break;
break;
default:
- assert_not_reached("Invalid wake-up pointer");
+ assert_not_reached();
}
}
if (r < 0)
else
assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
} else
- assert_not_reached("Yuck!");
+ assert_not_reached();
return 1;
}
got_c = true;
}
} else
- assert_not_reached("Huh?");
+ assert_not_reached();
return 2;
}
log_info("inotify-handler <%s>: delete of %s", description, ev->name);
assert_se(streq(ev->name, "sub"));
} else
- assert_not_reached("unexpected inotify event");
+ assert_not_reached();
maybe_exit(s, c);
return 1;
} else if (ev->mask & IN_IGNORED) {
log_info("delete-self-handler: ignore");
} else
- assert_not_reached("unexpected inotify event (delete-self)");
+ assert_not_reached();
maybe_exit(s, c);
return 1;
char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]);
+#define ID128_TO_UUID_STRING(id) id128_to_uuid_string((id), (char[ID128_UUID_STRING_MAX]) {})
+
bool id128_is_valid(const char *s) _pure_;
typedef enum Id128Format {
#define FORMAT_TIMESTAMP_SAFE(t) (FORMAT_TIMESTAMP(t) ?: " --- ")
void journal_file_print_header(JournalFile *f) {
- char a[SD_ID128_STRING_MAX], b[SD_ID128_STRING_MAX], c[SD_ID128_STRING_MAX], d[SD_ID128_STRING_MAX];
struct stat st;
assert(f);
"Objects: %"PRIu64"\n"
"Entry objects: %"PRIu64"\n",
f->path,
- sd_id128_to_string(f->header->file_id, a),
- sd_id128_to_string(f->header->machine_id, b),
- sd_id128_to_string(f->header->boot_id, c),
- sd_id128_to_string(f->header->seqnum_id, d),
+ SD_ID128_TO_STRING(f->header->file_id),
+ SD_ID128_TO_STRING(f->header->machine_id),
+ SD_ID128_TO_STRING(f->header->boot_id),
+ SD_ID128_TO_STRING(f->header->seqnum_id),
f->header->state == STATE_OFFLINE ? "OFFLINE" :
f->header->state == STATE_ONLINE ? "ONLINE" :
f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
return true;
}
- assert_not_reached("\"=\" not found");
+ assert_not_reached();
}
static Match *match_new(Match *p, MatchType t) {
_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
Object *o;
int r;
- char bid[SD_ID128_STRING_MAX], sid[SD_ID128_STRING_MAX];
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
if (r < 0)
return r;
- sd_id128_to_string(j->current_file->header->seqnum_id, sid);
- sd_id128_to_string(o->entry.boot_id, bid);
-
if (asprintf(cursor,
"s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
- sid, le64toh(o->entry.seqnum),
- bid, le64toh(o->entry.monotonic),
+ SD_ID128_TO_STRING(j->current_file->header->seqnum_id), le64toh(o->entry.seqnum),
+ SD_ID128_TO_STRING(o->entry.boot_id), le64toh(o->entry.monotonic),
le64toh(o->entry.realtime),
le64toh(o->entry.xor_hash)) < 0)
return -ENOMEM;
random_bytes(buf + 8*step, step);
memzero(buf + 9*step, step);
} else
- assert_not_reached("here");
+ assert_not_reached();
return buf;
}
_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
if (m)
- close_nointr(MONITOR_TO_FD(m));
+ (void) close_nointr(MONITOR_TO_FD(m));
return NULL;
}
break;
}
default:
- assert_not_reached("sd-netlink: invalid type system union type");
+ assert_not_reached();
}
} else
return -EINVAL;
break;
default:
- assert_not_reached("Wut? Unknown slot type?");
+ assert_not_reached();
}
slot->type = _NETLINK_SLOT_INVALID;
assert_return(IN_SET(nh_family, AF_UNSPEC, AF_INET, AF_INET6), -EINVAL);
break;
default:
- assert_not_reached("Invalid message type.");
+ assert_not_reached();
}
assert_return(ret, -EINVAL);
if (r < 0)
return r;
- if (nlmsg_type == RTM_NEWNEIGH)
- (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+ if (nlmsg_type == RTM_NEWNEIGH) {
+ if (ndm_family == AF_BRIDGE)
+ (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
+ else
+ (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+ }
ndm = NLMSG_DATA((*ret)->hdr);
_public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
if (m)
- close_nointr(MONITOR_TO_FD(m));
+ (void) close_nointr(MONITOR_TO_FD(m));
return NULL;
}
return -ECONNRESET;
default:
- assert_not_reached("Unknown request");
+ assert_not_reached();
}
return 0;
break;
default:
- assert_not_reached("Cannot complete unknown query type");
+ assert_not_reached();
}
resolve->current = NULL;
}
if (r < 0) {
log_error_errno(r, "sd_resolve_wait(): %m");
- assert_not_reached("sd_resolve_wait() failed");
+ assert_not_reached();
}
}
libudev_includes = [includes, include_directories('.')]
libudev_sym = files('libudev.sym')
-libudev_sym_path = join_paths(meson.current_source_dir(), 'libudev.sym')
+libudev_sym_path = meson.current_source_dir() / 'libudev.sym'
install_headers('libudev.h')
-libudev_h_path = join_paths(meson.current_source_dir(), 'libudev.h')
+libudev_h_path = meson.current_source_dir() / 'libudev.h'
libudev_basic = static_library(
'udev-basic',
return -EINVAL;
default:
- assert_not_reached("Unhandled option code.");
+ assert_not_reached();
}
return 1;
}
}
- assert_not_reached("should not be here");
+ assert_not_reached();
}
int x11_convert_to_vconsole(Context *c) {
r = copy_access(fileno(fr), fileno(fw));
if (r < 0)
return r;
- r = copy_xattr(fileno(fr), fileno(fw));
+ r = copy_xattr(fileno(fr), fileno(fw), COPY_ALL_XATTRS);
if (r < 0)
return r;
}
else if (streq(argv[0], "list-x11-keymap-options"))
look_for = OPTIONS;
else
- assert_not_reached("Wrong parameter");
+ assert_not_reached();
for (;;) {
_cleanup_free_ char *line = NULL;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
# If you know a way that allows the same variables to be used
# in sources list and concatenated to a string for test_env,
# let me know.
-kbd_model_map = join_paths(meson.current_source_dir(), 'kbd-model-map')
-language_fallback_map = join_paths(meson.current_source_dir(), 'language-fallback-map')
+kbd_model_map = meson.current_source_dir() / 'kbd-model-map'
+language_fallback_map = meson.current_source_dir() / 'language-fallback-map'
if conf.get('ENABLE_LOCALED') == 1
install_data('kbd-model-map',
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_action == ACTION_INHIBIT && optind == argc)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
[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);
[HANDLE_HIBERNATE] = "Hibernating...",
[HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...",
[HANDLE_SUSPEND_THEN_HIBERNATE] = "Suspending, then hibernating...",
+ [HANDLE_FACTORY_RESET] = "Performing factory reset...",
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
[HANDLE_HIBERNATE] = "hibernate",
[HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
[HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate",
+ [HANDLE_FACTORY_RESET] = "factory-reset",
[HANDLE_LOCK] = "lock",
};
HANDLE_HYBRID_SLEEP,
HANDLE_SUSPEND_THEN_HIBERNATE,
HANDLE_LOCK,
+ HANDLE_FACTORY_RESET,
_HANDLE_ACTION_MAX,
_HANDLE_ACTION_INVALID = -EINVAL,
} HandleAction;
}
} else if (ev.type == EV_KEY && ev.value == 0) {
- if (ev.code == KEY_RESTART) {
- if (b->manager->reboot_key_long_press_event_source) {
+
+ switch (ev.code) {
+
+ case KEY_POWER:
+ case KEY_POWER2:
+ if (b->manager->power_key_long_press_event_source) {
/* Long press event timer is still pending and key release
event happened. This means that key press duration was
insufficient to trigger a long press event
*/
+ log_struct(LOG_INFO,
+ LOG_MESSAGE("Power key pressed short."),
+ "MESSAGE_ID=" SD_MESSAGE_POWER_KEY_STR);
+
+ b->manager->power_key_long_press_event_source = sd_event_source_unref(b->manager->power_key_long_press_event_source);
+
+ manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
+ }
+ break;
+
+ case KEY_RESTART:
+ if (b->manager->reboot_key_long_press_event_source) {
log_struct(LOG_INFO,
LOG_MESSAGE("Reboot key pressed short."),
"MESSAGE_ID=" SD_MESSAGE_REBOOT_KEY_STR);
manager_handle_action(b->manager, INHIBIT_HANDLE_REBOOT_KEY, b->manager->handle_reboot_key, b->manager->reboot_key_ignore_inhibited, true);
}
+ break;
+
+ case KEY_SLEEP:
+ if (b->manager->suspend_key_long_press_event_source) {
+ log_struct(LOG_INFO,
+ LOG_MESSAGE("Suspend key pressed short."),
+ "MESSAGE_ID=" SD_MESSAGE_SUSPEND_KEY_STR);
+
+ b->manager->suspend_key_long_press_event_source = sd_event_source_unref(b->manager->suspend_key_long_press_event_source);
+
+ manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
+ }
+ break;
+ case KEY_SUSPEND:
+ if (b->manager->hibernate_key_long_press_event_source) {
+ log_struct(LOG_INFO,
+ LOG_MESSAGE("Hibernate key pressed short."),
+ "MESSAGE_ID=" SD_MESSAGE_HIBERNATE_KEY_STR);
+
+ b->manager->hibernate_key_long_press_event_source = sd_event_source_unref(b->manager->hibernate_key_long_press_event_source);
+
+ manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
+ }
+ break;
}
} else if (ev.type == EV_SW && ev.value > 0) {
return false;
}
-static int bus_manager_log_shutdown(
- Manager *m,
- const char *unit_name) {
-
- const char *p, *q;
-
+_printf_(2, 0)
+static int log_with_wall_message(Manager *m, const char *d, const char *p, const char *q) {
assert(m);
- assert(unit_name);
-
- if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) {
- p = "MESSAGE=System is powering down";
- q = "SHUTDOWN=power-off";
- } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) {
- p = "MESSAGE=System is rebooting";
- q = "SHUTDOWN=reboot";
- } else if (streq(unit_name, SPECIAL_HALT_TARGET)) {
- p = "MESSAGE=System is halting";
- q = "SHUTDOWN=halt";
- } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) {
- p = "MESSAGE=System is rebooting with kexec";
- q = "SHUTDOWN=kexec";
- } else {
- p = "MESSAGE=System is shutting down";
- q = NULL;
- }
if (isempty(m->wall_message))
p = strjoina(p, ".");
else
p = strjoina(p, " (", m->wall_message, ").");
- return log_struct(LOG_NOTICE,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- p,
- q);
+ return log_struct(LOG_NOTICE, d, p, q);
+}
+
+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);
}
static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) {
else if (streq(m->scheduled_shutdown_type, "halt"))
target = SPECIAL_HALT_TARGET;
else
- assert_not_reached("unexpected shutdown type");
+ assert_not_reached();
/* Don't allow multiple jobs being executed at the same time */
if (m->action_what > 0) {
* that so fail at all times and let caller retry in inactive state. */
r = sd_drmsetmaster(fd);
if (r < 0) {
- close_nointr(fd);
+ (void) close_nointr(fd);
return r;
}
} else
* "systemd-user" we simply set XDG_RUNTIME_DIR and
* leave. */
- (void) pam_get_item(handle, PAM_SERVICE, (const void**) &service);
+ r = pam_get_item(handle, PAM_SERVICE, (const void**) &service);
+ if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM service: %s", pam_strerror(handle, r));
+ return r;
+ }
if (streq_ptr(service, "systemd-user")) {
char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
/* Otherwise, we ask logind to create a session for us */
- (void) pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
- (void) pam_get_item(handle, PAM_TTY, (const void**) &tty);
- (void) pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
- (void) pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
+ r = pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
+ if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM XDISPLAY: %s", pam_strerror(handle, r));
+ return r;
+ }
+ r = pam_get_item(handle, PAM_TTY, (const void**) &tty);
+ if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM TTY: %s", pam_strerror(handle, r));
+ return r;
+ }
+ r = pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
+ if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM RUSER: %s", pam_strerror(handle, r));
+ return r;
+ }
+ r = pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
+ if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM RHOST: %s", pam_strerror(handle, r));
+ return r;
+ }
seat = getenv_harder(handle, "XDG_SEAT", NULL);
cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
remote = !isempty(remote_host) && !is_localhost(remote_host);
- (void) pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max);
- (void) pam_get_data(handle, "systemd.tasks_max", (const void **)&tasks_max);
- (void) pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight);
- (void) pam_get_data(handle, "systemd.io_weight", (const void **)&io_weight);
- (void) pam_get_data(handle, "systemd.runtime_max_sec", (const void **)&runtime_max_sec);
+ r = pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max);
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.memory_max data: %s", pam_strerror(handle, r));
+ return r;
+ }
+ r = pam_get_data(handle, "systemd.tasks_max", (const void **)&tasks_max);
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.tasks_max data: %s", pam_strerror(handle, r));
+ return r;
+ }
+ r = pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight);
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.cpu_weight data: %s", pam_strerror(handle, r));
+ return r;
+ }
+ r = pam_get_data(handle, "systemd.io_weight", (const void **)&io_weight);
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.io_weight data: %s", pam_strerror(handle, r));
+ return r;
+ }
+ r = pam_get_data(handle, "systemd.runtime_max_sec", (const void **)&runtime_max_sec);
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.runtime_max_sec data: %s", pam_strerror(handle, r));
+ return r;
+ }
/* Talk to logind over the message bus */
/* Only release session if it wasn't pre-existing when we
* tried to create it */
- (void) pam_get_data(handle, "systemd.existing", &existing);
+ r = pam_get_data(handle, "systemd.existing", &existing);
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.existing data: %s", pam_strerror(handle, r));
+ return r;
+ }
id = pam_getenv(handle, "XDG_SESSION_ID");
if (id && !existing) {
return do_mount(argv[2]);
if (streq(argv[1], "stop"))
return do_umount(argv[2]);
- assert_not_reached("Unknown verb!");
+ assert_not_reached();
}
DEFINE_MAIN_FUNCTION(run);
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind < argc)
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
_cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
- char buf[SD_ID128_STRING_MAX];
sd_id128_t id;
int r;
}
if (arg_print)
- puts(sd_id128_to_string(id, buf));
+ puts(SD_ID128_TO_STRING(id));
return 0;
}
assert(bus);
remote = argv[1];
- if (!http_url_is_valid(remote))
+ if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"URL '%s' is not valid.", remote);
assert(bus);
remote = argv[1];
- if (!http_url_is_valid(remote))
+ if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"URL '%s' is not valid.", remote);
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
" --uid=USER Specify user ID to invoke shell as\n"
- " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
+ " -E --setenv=VAR[=VALUE] Add an environment variable for shell\n"
" --read-only Create read-only bind mount\n"
" --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n"
break;
case 'E':
- if (!env_assignment_is_valid(optarg))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Environment assignment invalid: %s", optarg);
-
- r = strv_extend(&arg_setenv, optarg);
+ r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
if (r < 0)
- return log_oom();
+ return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
break;
case ARG_MAX_ADDRESSES:
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
desc = mfree(desc);
*ret_gid = converted_gid;
- *ret_description = desc;
+ *ret_description = TAKE_PTR(desc);
return 0;
}
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL)
break;
default:
- assert_not_reached("Unexpected action.");
+ assert_not_reached();
}
return r;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
log_netdev_info(netdev, "BareUDP netdev exists, using existing without changing its parameters.");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "BareUDP netdev could not be created: %m");
- netdev_drop(netdev);
+ netdev_enter_failed(netdev);
return 1;
}
encap_type = FOU_ENCAP_GUE;
break;
default:
- assert_not_reached("invalid encap type");
+ assert_not_reached();
}
r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, encap_type);
log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
- netdev_drop(netdev);
+ netdev_enter_failed(netdev);
return 1;
}
filename);
break;
default:
- assert_not_reached("Invalid fou encap type");
+ assert_not_reached();
}
if (t->peer_family == AF_UNSPEC && t->peer_port > 0)
log_netdev_info(netdev, "Geneve netdev exists, using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "Geneve netdev could not be created: %m");
- netdev_drop(netdev);
+ netdev_enter_failed(netdev);
return 1;
}
return r;
}
-int config_parse_geneve_vni(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) {
+int config_parse_geneve_vni(
+ 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) {
+
Geneve *v = userdata;
uint32_t f;
int r;
return 0;
}
-int config_parse_geneve_address(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) {
+int config_parse_geneve_address(
+ 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) {
+
Geneve *v = userdata;
union in_addr_union *addr = data, buffer;
int r, f;
return 0;
}
-int config_parse_geneve_flow_label(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) {
+int config_parse_geneve_flow_label(
+ 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) {
+
Geneve *v = userdata;
uint32_t f;
int r;
return 0;
}
-int config_parse_geneve_ttl(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) {
+int config_parse_geneve_ttl(
+ 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) {
+
Geneve *v = userdata;
unsigned f;
int r;
log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
- netdev_drop(netdev);
+ netdev_enter_failed(netdev);
return 1;
}
else if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to add receive secure association: %m");
- netdev_drop(netdev);
+ netdev_enter_failed(netdev);
return 1;
}
else if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to add receive secure channel: %m");
- netdev_drop(netdev);
+ netdev_enter_failed(netdev);
return 1;
}
if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to configure receive security association: %m");
- netdev_drop(netdev);
+ netdev_enter_failed(netdev);
return 1;
}
}
else if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to add transmit secure association: %m");
- netdev_drop(netdev);
+ netdev_enter_failed(netdev);
return 1;
}
#include "conf-parser.h"
#include "macvlan.h"
#include "macvlan-util.h"
+#include "networkd-network.h"
#include "parse-util.h"
DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
assert(netdev);
assert(link);
assert(netdev->ifname);
+ assert(link->network);
if (netdev->kind == NETDEV_KIND_MACVLAN)
m = MACVLAN(netdev);
return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MODE attribute: %m");
}
+ /* set the nopromisc flag if Promiscuous= of the link is explicitly set to false */
+ if (m->mode == NETDEV_MACVLAN_MODE_PASSTHRU && link->network->promiscuous == 0) {
+ r = sd_netlink_message_append_u16(req, IFLA_MACVLAN_FLAGS, MACVLAN_FLAG_NOPROMISC);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_FLAGS attribute: %m");
+ }
+
if (m->bc_queue_length != UINT32_MAX) {
r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_BC_QUEUE_LEN, m->bc_queue_length);
if (r < 0)
return hashmap_get(netdev->manager->netdevs, netdev->ifname) == netdev;
}
+static bool netdev_is_stacked_and_independent(NetDev *netdev) {
+ assert(netdev);
+
+ if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
+ return false;
+
+ switch (netdev->kind) {
+ case NETDEV_KIND_ERSPAN:
+ return ERSPAN(netdev)->independent;
+ case NETDEV_KIND_GRE:
+ return GRE(netdev)->independent;
+ case NETDEV_KIND_GRETAP:
+ return GRETAP(netdev)->independent;
+ case NETDEV_KIND_IP6GRE:
+ return IP6GRE(netdev)->independent;
+ case NETDEV_KIND_IP6GRETAP:
+ return IP6GRETAP(netdev)->independent;
+ case NETDEV_KIND_IP6TNL:
+ return IP6TNL(netdev)->independent;
+ case NETDEV_KIND_IPIP:
+ return IPIP(netdev)->independent;
+ case NETDEV_KIND_SIT:
+ return SIT(netdev)->independent;
+ case NETDEV_KIND_VTI:
+ return VTI(netdev)->independent;
+ case NETDEV_KIND_VTI6:
+ return VTI6(netdev)->independent;
+ case NETDEV_KIND_VXLAN:
+ return VXLAN(netdev)->independent;
+ case NETDEV_KIND_XFRM:
+ return XFRM(netdev)->independent;
+ default:
+ return false;
+ }
+}
+
+static bool netdev_is_stacked(NetDev *netdev) {
+ assert(netdev);
+
+ if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
+ return false;
+
+ if (netdev_is_stacked_and_independent(netdev))
+ return false;
+
+ return true;
+}
+
static void netdev_detach_from_manager(NetDev *netdev) {
if (netdev->ifname && netdev->manager)
hashmap_remove(netdev->manager->netdevs, netdev->ifname);
DEFINE_TRIVIAL_REF_UNREF_FUNC(NetDev, netdev, netdev_free);
void netdev_drop(NetDev *netdev) {
- if (!netdev || netdev->state == NETDEV_STATE_LINGER)
+ if (!netdev)
+ return;
+
+ if (netdev_is_stacked(netdev)) {
+ /* The netdev may be removed due to the underlying device removal, and the device may
+ * be re-added later. */
+ netdev->state = NETDEV_STATE_LOADING;
+ netdev->ifindex = 0;
+
+ log_netdev_debug(netdev, "netdev removed");
return;
+ }
netdev->state = NETDEV_STATE_LINGER;
return 0;
}
-static int netdev_enter_failed(NetDev *netdev) {
+void netdev_enter_failed(NetDev *netdev) {
netdev->state = NETDEV_STATE_FAILED;
- return 0;
}
static int netdev_enter_ready(NetDev *netdev) {
log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
- netdev_drop(netdev);
+ netdev_enter_failed(netdev);
return 1;
}
return r;
break;
default:
- assert_not_reached("Cannot join independent netdev");
+ assert_not_reached();
}
return 0;
return true;
}
-int request_process_create_stacked_netdev(Request *req) {
+int request_process_stacked_netdev(Request *req) {
int r;
assert(req);
assert(req->link);
- assert(req->type == REQUEST_TYPE_CREATE_STACKED_NETDEV);
+ assert(req->type == REQUEST_TYPE_STACKED_NETDEV);
assert(req->netdev);
assert(req->netlink_handler);
return 0;
}
-int link_request_to_crate_stacked_netdev(Link *link, NetDev *netdev) {
- NetDevCreateType create_type;
+int link_request_stacked_netdev(Link *link, NetDev *netdev) {
int r;
assert(link);
assert(netdev);
- create_type = netdev_get_create_type(netdev);
- if (!IN_SET(create_type, NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
+ if (!netdev_is_stacked(netdev))
return -EINVAL;
- if (netdev->state != NETDEV_STATE_LOADING || netdev->ifindex > 0)
- /* Already created (or removed?) */
- return 0;
+ if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0)
+ return 0; /* Already created. */
- if (create_type == NETDEV_CREATE_STACKED) {
+ if (netdev_get_create_type(netdev) == NETDEV_CREATE_STACKED) {
link->stacked_netdevs_created = false;
- r = link_queue_request(link, REQUEST_TYPE_CREATE_STACKED_NETDEV, netdev, false,
+ r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
&link->create_stacked_netdev_messages,
link_create_stacked_netdev_handler,
NULL);
} else {
link->stacked_netdevs_after_configured_created = false;
- r = link_queue_request(link, REQUEST_TYPE_CREATE_STACKED_NETDEV, netdev, false,
+ r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
&link->create_stacked_netdev_after_configured_messages,
link_create_stacked_netdev_after_configured_handler,
NULL);
}
if (r < 0)
- return log_link_error_errno(link, r, "Failed to request to create stacked netdev '%s': %m",
+ return log_link_error_errno(link, r, "Failed to request stacked netdev '%s': %m",
netdev->ifname);
- log_link_debug(link, "Requested to create stacked netdev '%s'", netdev->ifname);
+ log_link_debug(link, "Requested stacked netdev '%s'", netdev->ifname);
return 0;
}
int netdev_load_one(Manager *manager, const char *filename) {
_cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL;
const char *dropin_dirname;
- bool independent = false;
int r;
assert(manager);
return r;
}
- switch (netdev->kind) {
- case NETDEV_KIND_IPIP:
- independent = IPIP(netdev)->independent;
- break;
- case NETDEV_KIND_GRE:
- independent = GRE(netdev)->independent;
- break;
- case NETDEV_KIND_GRETAP:
- independent = GRETAP(netdev)->independent;
- break;
- case NETDEV_KIND_IP6GRE:
- independent = IP6GRE(netdev)->independent;
- break;
- case NETDEV_KIND_IP6GRETAP:
- independent = IP6GRETAP(netdev)->independent;
- break;
- case NETDEV_KIND_SIT:
- independent = SIT(netdev)->independent;
- break;
- case NETDEV_KIND_VTI:
- independent = VTI(netdev)->independent;
- break;
- case NETDEV_KIND_VTI6:
- independent = VTI6(netdev)->independent;
- break;
- case NETDEV_KIND_IP6TNL:
- independent = IP6TNL(netdev)->independent;
- break;
- case NETDEV_KIND_ERSPAN:
- independent = ERSPAN(netdev)->independent;
- break;
- case NETDEV_KIND_XFRM:
- independent = XFRM(netdev)->independent;
- break;
- case NETDEV_KIND_VXLAN:
- independent = VXLAN(netdev)->independent;
- break;
- default:
- break;
- }
-
- if (independent) {
+ if (netdev_is_stacked_and_independent(netdev)) {
r = netdev_create(netdev, NULL, NULL);
if (r < 0)
return r;
int netdev_load(Manager *manager, bool reload);
int netdev_load_one(Manager *manager, const char *filename);
void netdev_drop(NetDev *netdev);
+void netdev_enter_failed(NetDev *netdev);
NetDev *netdev_unref(NetDev *netdev);
NetDev *netdev_ref(NetDev *netdev);
int netdev_get_mac(const char *ifname, struct ether_addr **ret);
int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb);
-int request_process_create_stacked_netdev(Request *req);
-int link_request_to_crate_stacked_netdev(Link *link, NetDev *netdev);
+int request_process_stacked_netdev(Request *req);
+int link_request_stacked_netdev(Link *link, NetDev *netdev);
const char *netdev_kind_to_string(NetDevKind d) _const_;
NetDevKind netdev_kind_from_string(const char *d) _pure_;
t = GRETAP(netdev);
break;
default:
- assert_not_reached("invalid netdev kind");
+ assert_not_reached();
}
assert(t);
t = ERSPAN(netdev);
break;
default:
- assert_not_reached("Invalid tunnel kind");
+ assert_not_reached();
}
assert(t);
return 0;
}
-int config_parse_tunnel_address(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) {
+int config_parse_tunnel_address(
+ 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) {
+
Tunnel *t = userdata;
union in_addr_union *addr = data, buffer;
int r, f;
return 0;
}
-int config_parse_tunnel_key(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) {
+int config_parse_tunnel_key(
+ 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) {
+
union in_addr_union buffer;
Tunnel *t = userdata;
uint32_t k;
return 0;
}
-int config_parse_ipv6_flowlabel(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) {
+int config_parse_ipv6_flowlabel(
+ 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) {
+
IPv6FlowLabel *ipv6_flowlabel = data;
Tunnel *t = userdata;
int k = 0;
return 0;
}
-int config_parse_encap_limit(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) {
+int config_parse_encap_limit(
+ 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) {
+
Tunnel *t = userdata;
int k = 0;
int r;
return 0;
}
-int config_parse_6rd_prefix(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) {
+int config_parse_6rd_prefix(
+ 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) {
+
Tunnel *t = userdata;
+ union in_addr_union p;
+ uint8_t l;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- union in_addr_union p;
- uint8_t l;
- int r;
-
r = in_addr_prefix_from_string(rvalue, AF_INET6, &p, &l);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 6rd prefix \"%s\", ignoring: %m", rvalue);
t = SIT(n);
break;
default:
- assert_not_reached("invalid netdev kind");
+ assert_not_reached();
}
assert(t);
t = GRETAP(n);
break;
default:
- assert_not_reached("invalid netdev kind");
+ assert_not_reached();
}
assert(t);
return r;
}
-int config_parse_vxlan_address(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) {
+int config_parse_vxlan_address(
+ 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) {
+
VxLan *v = userdata;
union in_addr_union *addr = data, buffer;
int r, f;
return 0;
}
-int config_parse_port_range(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) {
+int config_parse_port_range(
+ 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) {
+
VxLan *v = userdata;
uint16_t low, high;
int r;
return 0;
}
-int config_parse_flow_label(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) {
+int config_parse_flow_label(
+ 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) {
+
VxLan *v = userdata;
unsigned f;
int r;
return 0;
}
-int config_parse_vxlan_ttl(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) {
+int config_parse_vxlan_ttl(
+ 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) {
+
VxLan *v = userdata;
unsigned f;
int r;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
set_remove(address->link->dhcp6_pd_addresses, address);
set_remove(address->link->dhcp6_pd_addresses_old, address);
SET_FOREACH(n, address->link->ndisc_addresses)
- if (n->address == address)
+ if (address_equal(n->address, address))
free(set_remove(address->link->ndisc_addresses, n));
if (address->family == AF_INET6 &&
break;
default:
- assert_not_reached("Received unsupported address family");
+ assert_not_reached();
}
r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &tmp->cinfo);
break;
default:
- assert_not_reached("Received invalid RTNL message type");
+ assert_not_reached();
}
return 1;
break;
default:
- assert_not_reached("Invalid address family");
+ assert_not_reached();
}
/* create new RTM message */
#include "parse-util.h"
#include "string-util.h"
-#define CAN_TERMINATION_OHM_VALUE 120
-
-int config_parse_can_bitrate(
- 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 *br = data;
- uint64_t sz;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = parse_size(rvalue, 1000, &sz);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse can bitrate '%s', ignoring: %m", rvalue);
- return 0;
- }
-
- /* Linux uses __u32 for bitrates, so the value should not exceed that. */
- if (sz <= 0 || sz > UINT32_MAX) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Bit rate out of permitted range 1...4294967295");
- return 0;
- }
-
- *br = (uint32_t) sz;
-
- return 0;
-}
+#define CAN_TERMINATION_DEFAULT_OHM_VALUE 120
int can_set_netlink_message(Link *link, sd_netlink_message *m) {
- struct can_ctrlmode cm = {};
int r;
assert(link);
if (r < 0)
return log_link_debug_errno(link, r, "Could not open IFLA_INFO_DATA container: %m");
- if (link->network->can_bitrate > 0 || link->network->can_sample_point > 0) {
+ if (link->network->can_bitrate > 0) {
struct can_bittiming bt = {
.bitrate = link->network->can_bitrate,
.sample_point = link->network->can_sample_point,
+ .sjw = link->network->can_sync_jump_width,
};
log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate);
r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
if (r < 0)
return log_link_debug_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m");
+ } else if (link->network->can_time_quanta_ns > 0) {
+ struct can_bittiming bt = {
+ .tq = link->network->can_time_quanta_ns,
+ .prop_seg = link->network->can_propagation_segment,
+ .phase_seg1 = link->network->can_phase_buffer_segment_1,
+ .phase_seg2 = link->network->can_phase_buffer_segment_2,
+ .sjw = link->network->can_sync_jump_width,
+ };
+
+ log_link_debug(link, "Setting time quanta = %"PRIu32" nsec", bt.tq);
+ r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m");
}
- if (link->network->can_data_bitrate > 0 || link->network->can_data_sample_point > 0) {
+ if (link->network->can_data_bitrate > 0) {
struct can_bittiming bt = {
.bitrate = link->network->can_data_bitrate,
.sample_point = link->network->can_data_sample_point,
+ .sjw = link->network->can_data_sync_jump_width,
};
log_link_debug(link, "Setting data bitrate = %d bit/s", bt.bitrate);
r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt));
if (r < 0)
return log_link_debug_errno(link, r, "Could not append IFLA_CAN_DATA_BITTIMING attribute: %m");
+ } else if (link->network->can_data_time_quanta_ns > 0) {
+ struct can_bittiming bt = {
+ .tq = link->network->can_data_time_quanta_ns,
+ .prop_seg = link->network->can_data_propagation_segment,
+ .phase_seg1 = link->network->can_data_phase_buffer_segment_1,
+ .phase_seg2 = link->network->can_data_phase_buffer_segment_2,
+ .sjw = link->network->can_data_sync_jump_width,
+ };
+
+ log_link_debug(link, "Setting data time quanta = %"PRIu32" nsec", bt.tq);
+ r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt));
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not append IFLA_CAN_DATA_BITTIMING attribute: %m");
}
if (link->network->can_restart_us > 0) {
else
restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC);
- if (restart_ms > UINT32_MAX)
- return log_link_debug_errno(link, SYNTHETIC_ERRNO(ERANGE), "restart timeout (%s) too big.",
- FORMAT_TIMESPAN(restart_ms * 1000, MSEC_PER_SEC));
-
log_link_debug(link, "Setting restart = %s", FORMAT_TIMESPAN(restart_ms * 1000, MSEC_PER_SEC));
-
r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms);
if (r < 0)
return log_link_debug_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m");
}
- if (link->network->can_fd_mode >= 0) {
- cm.mask |= CAN_CTRLMODE_FD;
- SET_FLAG(cm.flags, CAN_CTRLMODE_FD, link->network->can_fd_mode);
- log_link_debug(link, "Setting FD mode to '%s'.", yes_no(link->network->can_fd_mode));
- }
-
- if (link->network->can_non_iso >= 0) {
- cm.mask |= CAN_CTRLMODE_FD_NON_ISO;
- SET_FLAG(cm.flags, CAN_CTRLMODE_FD_NON_ISO, link->network->can_non_iso);
- log_link_debug(link, "Setting FD non-ISO mode to '%s'.", yes_no(link->network->can_non_iso));
- }
-
- if (link->network->can_triple_sampling >= 0) {
- cm.mask |= CAN_CTRLMODE_3_SAMPLES;
- SET_FLAG(cm.flags, CAN_CTRLMODE_3_SAMPLES, link->network->can_triple_sampling);
- log_link_debug(link, "Setting triple-sampling to '%s'.", yes_no(link->network->can_triple_sampling));
- }
-
- if (link->network->can_berr_reporting >= 0) {
- cm.mask |= CAN_CTRLMODE_BERR_REPORTING;
- SET_FLAG(cm.flags, CAN_CTRLMODE_BERR_REPORTING, link->network->can_berr_reporting);
- log_link_debug(link, "Setting bus error reporting to '%s'.", yes_no(link->network->can_berr_reporting));
- }
-
- if (link->network->can_listen_only >= 0) {
- cm.mask |= CAN_CTRLMODE_LISTENONLY;
- SET_FLAG(cm.flags, CAN_CTRLMODE_LISTENONLY, link->network->can_listen_only);
- log_link_debug(link, "Setting listen-only mode to '%s'.", yes_no(link->network->can_listen_only));
- }
+ if (link->network->can_control_mode_mask != 0) {
+ struct can_ctrlmode cm = {
+ .mask = link->network->can_control_mode_mask,
+ .flags = link->network->can_control_mode_flags,
+ };
- if (cm.mask != 0) {
r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
if (r < 0)
return log_link_debug_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m");
}
- if (link->network->can_termination >= 0) {
- log_link_debug(link, "Setting can-termination to '%s'.", yes_no(link->network->can_termination));
+ if (link->network->can_termination_set) {
+ log_link_debug(link, "Setting can-termination to '%u'.", link->network->can_termination);
- r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION,
- link->network->can_termination ? CAN_TERMINATION_OHM_VALUE : 0);
+ r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION, link->network->can_termination);
if (r < 0)
return log_link_debug_errno(link, r, "Could not append IFLA_CAN_TERMINATION attribute: %m");
}
return 0;
}
+
+int config_parse_can_bitrate(
+ 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 *br = data;
+ uint64_t sz;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = parse_size(rvalue, 1000, &sz);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse can bitrate '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ /* Linux uses __u32 for bitrates, so the value should not exceed that. */
+ if (sz <= 0 || sz > UINT32_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Bit rate out of permitted range 1...4294967295");
+ return 0;
+ }
+
+ *br = (uint32_t) sz;
+
+ return 0;
+}
+
+int config_parse_can_time_quanta(
+ 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) {
+
+ nsec_t val, *tq = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = parse_nsec(rvalue, &val);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse can time quanta '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ /* Linux uses __u32 for bitrates, so the value should not exceed that. */
+ if (val <= 0 || val > UINT32_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Time quanta out of permitted range 1...4294967295");
+ return 0;
+ }
+
+ *tq = val;
+ return 0;
+}
+
+int config_parse_can_restart_usec(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t usec, *restart_usec = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse CAN restart sec '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ if (usec != USEC_INFINITY &&
+ DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "CAN RestartSec= must be in the range 0...%"PRIu32"ms, ignoring: %s", UINT32_MAX, rvalue);
+ return 0;
+ }
+
+ *restart_usec = usec;
+ return 0;
+}
+
+int config_parse_can_control_mode(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ uint32_t mask = ltype;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(userdata);
+ assert(mask != 0);
+
+ if (isempty(rvalue)) {
+ network->can_control_mode_mask &= ~mask;
+ network->can_control_mode_flags &= ~mask;
+ return 0;
+ }
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse CAN control mode '%s', ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ network->can_control_mode_mask |= mask;
+ SET_FLAG(network->can_control_mode_flags, mask, r);
+ return 0;
+}
+
+int config_parse_can_termination(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ network->can_termination_set = false;
+ return 0;
+ }
+
+ /* Note that 0 termination ohm value means no termination resistor, and there is no conflict
+ * between parse_boolean() and safe_atou16() when Termination=0. However, Termination=1 must be
+ * treated as 1 ohm, instead of true (and then the default ohm value). So, we need to parse the
+ * string with safe_atou16() at first. */
+
+ r = safe_atou16(rvalue, &network->can_termination);
+ if (r < 0) {
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse CAN termination value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ network->can_termination = r ? CAN_TERMINATION_DEFAULT_OHM_VALUE : 0;
+ }
+
+ network->can_termination_set = true;
+ return 0;
+}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <linux/can/netlink.h>
+
#include "sd-netlink.h"
#include "conf-parser.h"
int can_set_netlink_message(Link *link, sd_netlink_message *m);
CONFIG_PARSER_PROTOTYPE(config_parse_can_bitrate);
+CONFIG_PARSER_PROTOTYPE(config_parse_can_time_quanta);
+CONFIG_PARSER_PROTOTYPE(config_parse_can_restart_usec);
+CONFIG_PARSER_PROTOTYPE(config_parse_can_control_mode);
+CONFIG_PARSER_PROTOTYPE(config_parse_can_termination);
return duid;
}
-static int link_configure_and_start_dhcp_delayed(Link *link) {
- int r;
-
- assert(link);
-
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
- return 0;
-
- if (!link->dhcp_client) {
- r = dhcp4_configure(link);
- if (r < 0)
- return r;
- }
-
- if (!link->dhcp6_client) {
- r = dhcp6_configure(link);
- if (r < 0)
- return r;
- }
-
- if (link->set_flags_messages > 0)
- return 0;
-
- if (!link_has_carrier(link))
- return 0;
-
- r = dhcp4_start(link);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to start DHCPv4 client: %m");
-
- r = ndisc_start(link);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
-
- r = dhcp6_start(link);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
-
- return 0;
-}
-
static int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
Manager *manager = userdata;
const sd_bus_error *e;
const void *a;
size_t sz;
- Link *link;
int r;
assert(m);
assert(manager);
+ /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
+ * even if the method fails. */
+ manager->has_product_uuid = true;
+
e = sd_bus_message_get_error(m);
if (e) {
r = sd_bus_error_get_errno(e);
log_warning_errno(r, "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
bus_error_message(e, r));
- goto configure;
+ return 0;
}
r = sd_bus_message_read_array(m, 'y', &a, &sz);
if (r < 0) {
log_warning_errno(r, "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
- goto configure;
+ return 0;
}
if (sz != sizeof(sd_id128_t)) {
log_warning("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
- goto configure;
+ return 0;
}
+ log_debug("Successfully obtained product UUID");
+
memcpy(&manager->duid_product_uuid.raw_data, a, sz);
manager->duid_product_uuid.raw_data_len = sz;
-configure:
- /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
- * even if the method fails. */
- manager->has_product_uuid = true;
-
- while ((link = set_steal_first(manager->links_requesting_uuid))) {
- r = link_configure_and_start_dhcp_delayed(link);
- if (r < 0)
- link_enter_failed(link);
-
- link_unref(link);
- }
-
- manager->links_requesting_uuid = set_free(manager->links_requesting_uuid);
-
- return 1;
+ return 0;
}
int manager_request_product_uuid(Manager *m) {
+ static bool bus_method_is_called = false;
int r;
assert(m);
- if (m->product_uuid_requested)
+ if (bus_method_is_called)
return 0;
- log_debug("Requesting product UUID");
-
- if (sd_bus_is_ready(m->bus) <= 0) {
+ if (sd_bus_is_ready(m->bus) <= 0 && !m->product_uuid_requested) {
log_debug("Not connected to system bus, requesting product UUID later.");
+ m->product_uuid_requested = true;
return 0;
}
+ m->product_uuid_requested = false;
+
r = sd_bus_call_method_async(
m->bus,
NULL,
if (r < 0)
return log_warning_errno(r, "Failed to get product UUID: %m");
- m->product_uuid_requested = true;
+ log_debug("Requesting product UUID.");
+
+ bus_method_is_called = true;
return 0;
}
if (r < 0) {
log_link_warning_errno(link, r,
"Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
+
+ m->has_product_uuid = true; /* Do not request UUID again on failure. */
return 1;
}
- r = set_ensure_put(&m->links_requesting_uuid, NULL, link);
- if (r < 0)
- return log_oom();
- if (r > 0)
- link_ref(link);
-
return 0;
}
return 0;
}
-int config_parse_iaid(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) {
+int config_parse_iaid(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Network *network = userdata;
uint32_t iaid;
network->dhcp_iaid = iaid;
network->dhcp_iaid_set = true;
if (!network->dhcp6_iaid_set_explicitly) {
- /* Backward compatibility. Previously, IAID is shared by DHCP4 and DHCP6.
- * If DHCP6 IAID is not specified explicitly, then use DHCP4 IAID for DHCP6. */
+ /* Backward compatibility. Previously, IAID is shared by DHCPv4 and DHCPv6.
+ * If DHCPv6 IAID is not specified explicitly, then use DHCPv4 IAID for DHCPv6. */
network->dhcp6_iaid = iaid;
network->dhcp6_iaid_set = true;
}
assert(manager);
- /* For backward compatibility. Setting both DHCP4 and DHCP6 DUID if they are not specified explicitly. */
+ /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
if (r < 0)
if (r < 0)
return r;
- /* For backward compatibility, also set DHCP6 DUID if not specified explicitly. */
+ /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
}
assert(manager);
- /* For backward compatibility. Setting both DHCP4 and DHCP6 DUID if they are not specified explicitly. */
+ /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
if (r < 0)
if (r < 0)
return r;
- /* For backward compatibility, also set DHCP6 DUID if not specified explicitly. */
+ /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
}
if (r < 0)
return r;
- HASHMAP_FOREACH(lease, s->leases_by_client_id) {
+ HASHMAP_FOREACH(lease, s->bound_leases_by_client_id) {
r = sd_bus_message_open_container(reply, 'r', "uayayayayt");
if (r < 0)
return r;
if (link->network->dhcp_server_uplink_index > 0)
return link_get_by_index(link->manager, link->network->dhcp_server_uplink_index, ret);
- if (link->network->dhcp_server_uplink_index == 0) {
+ if (link->network->dhcp_server_uplink_index == UPLINK_INDEX_AUTO) {
/* It is not necessary to propagate error in automatic selection. */
if (manager_find_uplink(link->manager, AF_INET, link, ret) < 0)
*ret = NULL;
break;
default:
- assert_not_reached("Unexpected server type");
+ assert_not_reached();
}
if (use_dhcp_lease_data && link->dhcp_lease) {
network->dhcp_server_address_prefixlen = prefixlen;
return 0;
}
-
-int config_parse_dhcp_server_uplink(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *network = userdata;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- if (isempty(rvalue) || streq(rvalue, ":auto")) {
- network->dhcp_server_uplink_index = 0; /* uplink will be selected automatically */
- network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name);
- return 0;
- }
-
- if (streq(rvalue, ":none")) {
- network->dhcp_server_uplink_index = -1; /* uplink will not be selected automatically */
- network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name);
- return 0;
- }
-
- r = parse_ifindex(rvalue);
- if (r > 0) {
- network->dhcp_server_uplink_index = r;
- network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name);
- return 0;
- }
-
- if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue);
- return 0;
- }
-
- r = free_and_strdup_warn(&network->dhcp_server_uplink_name, rvalue);
- if (r < 0)
- return r;
-
- network->dhcp_server_uplink_index = 0;
- return 0;
-}
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_address);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_uplink);
addr->route_metric = link->network->dhcp_route_metric;
addr->duplicate_address_detection = link->network->dhcp_send_decline ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_NO;
+ if (link->network->dhcp_label) {
+ addr->label = strdup(link->network->dhcp_label);
+ if (!addr->label)
+ return log_oom();
+ }
+
if (address_get(link, addr, NULL) < 0)
link->dhcp4_configured = false;
else {
r = gethostname_strict(&hostname);
if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to get hostname: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to get hostname: %m");
hn = hostname;
}
r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
if (r == -EINVAL && hostname)
/* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
- log_link_debug_errno(link, r, "DHCP4 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
+ log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
else if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set hostname: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set hostname: %m");
return 0;
}
duid->raw_data_len > 0 ? duid->raw_data : NULL,
duid->raw_data_len);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set IAID+DUID: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set IAID+DUID: %m");
break;
}
case DHCP_CLIENT_ID_DUID_ONLY: {
duid->raw_data_len > 0 ? duid->raw_data : NULL,
duid->raw_data_len);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set DUID: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set DUID: %m");
break;
}
case DHCP_CLIENT_ID_MAC: {
hw_addr,
hw_addr_len);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set client ID: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set client ID: %m");
break;
}
default:
- assert_not_reached("Unknown client identifier type.");
+ assert_not_reached();
}
return 0;
}
-static int dhcp4_configure_duid(Link *link) {
- assert(link);
-
- if (!IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
- return 1;
-
- return dhcp_configure_duid(link, link_get_dhcp4_duid(link));
-}
-
static int dhcp4_set_request_address(Link *link) {
Address *a;
if (!a)
return 0;
- log_link_debug(link, "DHCP4 CLIENT: requesting " IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
+ log_link_debug(link, "DHCPv4 CLIENT: requesting " IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
return sd_dhcp_client_set_request_address(link->dhcp_client, &a->in_addr.in);
}
if (r < 0 && link->sd_device && sd_device_get_property_value(link->sd_device, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
r = parse_boolean(val);
if (r < 0)
- log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
+ log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
else
- log_link_debug(link, "DHCP4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
+ log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
}
return r == true;
}
-int dhcp4_configure(Link *link) {
+static int dhcp4_configure(Link *link) {
sd_dhcp_option *send_option;
void *request_options;
int r;
assert(link);
assert(link->network);
- if (!link_dhcp4_enabled(link))
- return 0;
-
if (link->dhcp_client)
- return -EBUSY; /* Already configured. */
-
- r = dhcp4_configure_duid(link);
- if (r <= 0)
- return r;
+ return log_link_debug_errno(link, SYNTHETIC_ERRNO(EBUSY), "DHCPv4 client is already configured.");
r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to allocate DHCP4 client: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to allocate DHCPv4 client: %m");
r = sd_dhcp_client_attach_event(link->dhcp_client, link->manager->event, 0);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to attach event to DHCP4 client: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to attach event to DHCPv4 client: %m");
r = sd_dhcp_client_set_mac(link->dhcp_client,
link->hw_addr.bytes,
link->bcast_addr.length > 0 ? link->bcast_addr.bytes : NULL,
link->hw_addr.length, link->iftype);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set MAC address: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set MAC address: %m");
r = sd_dhcp_client_set_ifindex(link->dhcp_client, link->ifindex);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set ifindex: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set ifindex: %m");
r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set callback: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set callback: %m");
r = sd_dhcp_client_set_request_broadcast(link->dhcp_client, link_needs_dhcp_broadcast(link));
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for broadcast: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for broadcast: %m");
if (link->mtu > 0) {
r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set MTU: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set MTU: %m");
}
if (!link->network->dhcp_anonymize) {
if (link->network->dhcp_use_mtu) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_INTERFACE_MTU);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for MTU: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for MTU: %m");
}
if (link->network->dhcp_use_routes) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_STATIC_ROUTE);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for static route: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for static route: %m");
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for classless static route: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for classless static route: %m");
}
if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for domain search list: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for domain search list: %m");
}
if (link->network->dhcp_use_ntp) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for NTP server: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for NTP server: %m");
}
if (link->network->dhcp_use_sip) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_SIP_SERVER);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for SIP server: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for SIP server: %m");
}
if (link->network->dhcp_use_timezone) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for timezone: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for timezone: %m");
}
SET_FOREACH(request_options, link->network->dhcp_request_options) {
r = sd_dhcp_client_set_request_option(link->dhcp_client, option);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for '%u': %m", option);
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for '%u': %m", option);
}
ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_options) {
if (r == -EEXIST)
continue;
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set send option: %m");
}
ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_vendor_options) {
if (r == -EEXIST)
continue;
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set send option: %m");
}
r = dhcp4_set_hostname(link);
r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
link->network->dhcp_vendor_class_identifier);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set vendor class identifier: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set vendor class identifier: %m");
}
if (link->network->dhcp_mudurl) {
r = sd_dhcp_client_set_mud_url(link->dhcp_client, link->network->dhcp_mudurl);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set MUD URL: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set MUD URL: %m");
}
if (link->network->dhcp_user_class) {
r = sd_dhcp_client_set_user_class(link->dhcp_client, link->network->dhcp_user_class);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set user class: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set user class: %m");
}
}
if (link->network->dhcp_client_port > 0) {
r = sd_dhcp_client_set_client_port(link->dhcp_client, link->network->dhcp_client_port);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set listen port: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set listen port: %m");
}
if (link->network->dhcp_max_attempts > 0) {
r = sd_dhcp_client_set_max_attempts(link->dhcp_client, link->network->dhcp_max_attempts);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set max attempts: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set max attempts: %m");
}
if (link->network->dhcp_ip_service_type > 0) {
r = sd_dhcp_client_set_service_type(link->dhcp_client, link->network->dhcp_ip_service_type);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set IP service type: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set IP service type: %m");
}
if (link->network->dhcp_fallback_lease_lifetime > 0) {
r = sd_dhcp_client_set_fallback_lease_lifetime(link->dhcp_client, link->network->dhcp_fallback_lease_lifetime);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed set to lease lifetime: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed set to lease lifetime: %m");
}
r = dhcp4_set_request_address(link);
if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set initial DHCPv4 address: %m");
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set initial DHCPv4 address: %m");
return dhcp4_set_client_identifier(link);
}
}
int dhcp4_start(Link *link) {
+ int r;
+
assert(link);
if (!link->dhcp_client)
return 0;
+ if (!link_has_carrier(link))
+ return 0;
+
if (sd_dhcp_client_is_running(link->dhcp_client) > 0)
return 0;
- log_link_debug(link, "Acquiring DHCPv4 lease");
+ r = sd_dhcp_client_start(link->dhcp_client);
+ if (r < 0)
+ return r;
- return sd_dhcp_client_start(link->dhcp_client);
+ return 1;
+}
+
+static int dhcp4_configure_duid(Link *link) {
+ assert(link);
+
+ if (!IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
+ return 1;
+
+ return dhcp_configure_duid(link, link_get_dhcp4_duid(link));
+}
+
+int request_process_dhcp4_client(Request *req) {
+ Link *link;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_DHCP4_CLIENT);
+
+ link = req->link;
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ r = dhcp4_configure_duid(link);
+ if (r <= 0)
+ return r;
+
+ r = dhcp4_configure(req->link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure DHCPv4 client: %m");
+
+ r = dhcp4_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start DHCPv4 client: %m");
+
+ log_link_debug(link, "DHCPv4 client is configured%s.",
+ r > 0 ? ", acquiring DHCPv4 lease" : "");
+
+ return 1;
+}
+
+int link_request_dhcp4_client(Link *link) {
+ int r;
+
+ assert(link);
+
+ if (!link_dhcp4_enabled(link))
+ return 0;
+
+ if (link->dhcp_client)
+ return 0;
+
+ r = link_queue_request(link, REQUEST_TYPE_DHCP4_CLIENT, NULL, false, NULL, NULL, NULL);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to request configuring of the DHCPv4 client: %m");
+
+ log_link_debug(link, "Requested configuring of the DHCPv4 client.");
+ return 0;
}
int config_parse_dhcp_max_attempts(
return 0;
}
-int config_parse_dhcp_fallback_lease_lifetime(const char *unit,
+int config_parse_dhcp_fallback_lease_lifetime(
+ const char *unit,
const char *filename,
unsigned line,
const char *section,
const char *rvalue,
void *data,
void *userdata) {
+
Network *network = userdata;
uint32_t k;
return 0;
}
+int config_parse_dhcp_label(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char **label = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ *label = mfree(*label);
+ return 0;
+ }
+
+ if (!address_label_valid(rvalue)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Address label is too long or invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ return free_and_strdup_warn(label, rvalue);
+}
+
static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
[DHCP_CLIENT_ID_MAC] = "mac",
[DHCP_CLIENT_ID_DUID] = "duid",
#include "conf-parser.h"
typedef struct Link Link;
+typedef struct Network Network;
+typedef struct Request Request;
typedef enum DHCPClientIdentifier {
DHCP_CLIENT_ID_MAC,
} DHCPClientIdentifier;
void network_adjust_dhcp4(Network *network);
-int dhcp4_configure(Link *link);
int dhcp4_update_mac(Link *link);
int dhcp4_start(Link *link);
int dhcp4_lease_lost(Link *link);
+int request_process_dhcp4_client(Request *req);
+int link_request_dhcp4_client(Link *link);
+
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_acl_ip_address);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_ip_service_type);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_mud_url);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_fallback_lease_lifetime);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_label);
log_link_full(link,
set_contains(link->dhcp6_pd_prefixes, p) ? LOG_DEBUG :
prefixlen > 64 || prefixlen < 48 ? LOG_WARNING : LOG_INFO,
- "DHCP6: received PD Prefix %s%s",
+ "DHCPv6: received PD Prefix %s%s",
strna(buf),
prefixlen > 64 ? " with prefix length > 64, ignoring." :
prefixlen < 48 ? " with prefix length < 48, looks unusual.": "");
/* Store PD prefix even if prefixlen > 64, not to make logged at warning level so frequently. */
r = set_ensure_put(&link->dhcp6_pd_prefixes, &in_addr_prefix_hash_ops_free, p);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to store DHCP6 PD prefix %s: %m", strna(buf));
+ return log_link_error_errno(link, r, "Failed to store DHCPv6 PD prefix %s: %m", strna(buf));
if (r > 0)
TAKE_PTR(p);
}
int dhcp6_start(Link *link) {
+ int r;
+
assert(link);
if (!link->dhcp6_client)
if (!link_dhcp6_enabled(link))
return 0;
+ if (!link_has_carrier(link))
+ return 0;
+
if (link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_NO)
return 0;
if (sd_dhcp6_client_is_running(link->dhcp6_client) > 0)
return 0;
- log_link_debug(link, "Acquiring DHCPv6 lease");
+ r = dhcp6_request_information(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
+ if (r < 0)
+ return r;
- return dhcp6_request_information(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
+ return 1;
}
int dhcp6_request_prefix_delegation(Link *link) {
else {
r = gethostname_strict(&hostname);
if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
- return r;
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to get hostname: %m");
hn = hostname;
}
r = sd_dhcp6_client_set_fqdn(client, hn);
if (r == -EINVAL && hostname)
/* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
- log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
+ log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
else if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set hostname: %m");
return 0;
}
return 0;
}
-int dhcp6_configure(Link *link) {
+static int dhcp6_configure(Link *link) {
_cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
sd_dhcp6_option *vendor_option;
sd_dhcp6_option *send_option;
assert(link);
assert(link->network);
- if (!link_dhcp6_enabled(link) && !link_ipv6_accept_ra_enabled(link))
- return 0;
-
if (link->dhcp6_client)
- return -EBUSY;
-
- r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
- if (r <= 0)
- return r;
+ return log_link_debug_errno(link, SYNTHETIC_ERRNO(EBUSY), "DHCPv6 client is already configured.");
r = sd_dhcp6_client_new(&client);
if (r == -ENOMEM)
- return log_oom();
+ return log_oom_debug();
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to create DHCPv6 client: %m");
r = sd_dhcp6_client_attach_event(client, link->manager->event, 0);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to attach event: %m");
r = dhcp6_set_identifier(link, client);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set identifier: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set identifier: %m");
ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options) {
r = sd_dhcp6_client_add_option(client, send_option);
if (r == -EEXIST)
continue;
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set option: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set option: %m");
}
r = dhcp6_set_hostname(client, link);
r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set ifindex: %m");
if (link->network->dhcp6_rapid_commit) {
r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set request flag for rapid commit: %m");
}
if (link->network->dhcp6_mudurl) {
r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MUD URL: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set MUD URL: %m");
}
SET_FOREACH(request_options, link->network->dhcp6_request_options) {
r = sd_dhcp6_client_set_request_option(client, option);
if (r == -EEXIST) {
- log_link_debug(link, "DHCP6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
+ log_link_debug(link, "DHCPv6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
continue;
}
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for '%u': %m", option);
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set request flag for '%u': %m", option);
}
if (link->network->dhcp6_user_class) {
r = sd_dhcp6_client_set_request_user_class(client, link->network->dhcp6_user_class);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set user class: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set user class: %m");
}
if (link->network->dhcp6_vendor_class) {
r = sd_dhcp6_client_set_request_vendor_class(client, link->network->dhcp6_vendor_class);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor class: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set vendor class: %m");
}
ORDERED_HASHMAP_FOREACH(vendor_option, link->network->dhcp6_client_send_vendor_options) {
if (r == -EEXIST)
continue;
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor option: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set vendor option: %m");
}
r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set callback: %m");
if (dhcp6_enable_prefix_delegation(link)) {
r = sd_dhcp6_client_set_prefix_delegation(client, true);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix delegation: %m");
}
if (link->network->dhcp6_pd_length > 0) {
r = sd_dhcp6_client_set_prefix_delegation_hint(client, link->network->dhcp6_pd_length, &link->network->dhcp6_pd_address);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix hint: %m");
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix hint: %m");
}
link->dhcp6_client = TAKE_PTR(client);
return 0;
}
+int request_process_dhcp6_client(Request *req) {
+ Link *link;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_DHCP6_CLIENT);
+
+ link = req->link;
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
+ if (r <= 0)
+ return r;
+
+ r = dhcp6_configure(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure DHCPv6 client: %m");
+
+ r = ndisc_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
+
+ r = dhcp6_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
+
+ log_link_debug(link, "DHCPv6 client is configured%s.",
+ r > 0 ? ", acquiring DHCPv6 lease" : "");
+
+ return 1;
+}
+
+int link_request_dhcp6_client(Link *link) {
+ int r;
+
+ assert(link);
+
+ if (!link_dhcp6_enabled(link) && !link_ipv6_accept_ra_enabled(link))
+ return 0;
+
+ if (link->dhcp6_client)
+ return 0;
+
+ r = link_queue_request(link, REQUEST_TYPE_DHCP6_CLIENT, NULL, false, NULL, NULL, NULL);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to request configuring of the DHCPv6 client: %m");
+
+ log_link_debug(link, "Requested configuring of the DHCPv6 client.");
+ return 0;
+}
+
int link_serialize_dhcp6_client(Link *link, FILE *f) {
_cleanup_free_ char *duid = NULL;
uint32_t iaid;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include "sd-dhcp6-client.h"
-
#include "conf-parser.h"
+#include "in-addr-util.h"
#include "macro.h"
typedef enum DHCP6ClientStartMode {
} DHCP6ClientStartMode;
typedef struct Link Link;
-typedef struct Manager Manager;
+typedef struct Request Request;
typedef struct DHCP6DelegatedPrefix {
struct in6_addr prefix; /* Prefix assigned to the link */
bool link_dhcp6_with_address_enabled(Link *link);
bool link_dhcp6_pd_is_enabled(Link *link);
int dhcp6_pd_remove(Link *link);
-int dhcp6_configure(Link *link);
int dhcp6_update_mac(Link *link);
int dhcp6_start(Link *link);
int dhcp6_request_information(Link *link, int ir);
int dhcp6_request_prefix_delegation(Link *link);
+int request_process_dhcp6_client(Request *req);
+int link_request_dhcp6_client(Link *link);
+
int link_serialize_dhcp6_client(Link *link, FILE *f);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
break;
default:
- assert_not_reached("Invalid IPv4ACD event.");
+ assert_not_reached();
}
}
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_link.h>
+#include <linux/netdevice.h>
#include <sys/socket.h>
#include <unistd.h>
!link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address &&
!link->ipv4ll_address_configured)
/* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */
- return (void) log_link_debug(link, "%s(): DHCP4, DHCP6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__);
+ return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__);
/* Ignore NDisc when ConfigureWithoutCarrier= is enabled, as IPv6AcceptRA= is enabled by default. */
if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) ||
/* When DHCP[46], NDisc, or IPv4LL is enabled, at least one protocol must be finished. */
return (void) log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__);
- log_link_debug(link, "%s(): dhcp4:%s ipv4ll:%s dhcp6_addresses:%s dhcp6_routes:%s "
- "dhcp6_pd_addresses:%s dhcp6_pd_routes:%s ndisc_addresses:%s ndisc_routes:%s",
+ log_link_debug(link, "%s(): DHCPv4:%s IPv4LL:%s DHCPv6_addresses:%s DHCPv6_routes:%s "
+ "DHCPv6PD_addresses:%s DHCPv6PD_routes:%s NDisc_addresses:%s NDisc_routes:%s",
__func__,
yes_no(link->dhcp4_configured),
yes_no(link->ipv4ll_address_configured),
link->stacked_netdevs_after_configured_created = false;
HASHMAP_FOREACH(netdev, link->network->stacked_netdevs) {
- r = link_request_to_crate_stacked_netdev(link, netdev);
+ r = link_request_stacked_netdev(link, netdev);
if (r < 0)
return r;
}
log_link_debug(link, "Starting IPv6 Router Advertisements");
- r = radv_emit_dns(link);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to configure DNS or Domains in IPv6 Router Advertisement: %m");
-
r = sd_radv_start(link->radv);
- if (r < 0 && r != -EBUSY)
+ if (r < 0)
return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
}
if (r < 0)
return log_link_warning_errno(link, r, "Failed to start DHCPv4 client: %m");
- } else if (link->ipv4ll) {
- log_link_debug(link, "Acquiring IPv4 link-local address");
+ log_link_debug(link, "Acquiring DHCPv4 lease.");
+ } else if (link->ipv4ll) {
r = sd_ipv4ll_start(link->ipv4ll);
if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
+
+ log_link_debug(link, "Acquiring IPv4 link-local address.");
}
if (link->dhcp_server) {
if (r < 0)
return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m");
+ if (link->lldp) {
+ r = sd_lldp_start(link->lldp);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start LLDP client: %m");
+ }
+
return 0;
}
link_drop_from_master(link);
- link_unref(set_remove(link->manager->links_requesting_uuid, link));
-
(void) unlink(link->state_file);
link_clean(link);
assert(link);
assert(link->manager);
+ /* Drop foreign config, but ignore unmanaged, loopback, or critical interfaces. We do not want
+ * to remove loopback address or addresses used for root NFS. */
+
+ if (IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED))
+ return 0;
+ if (FLAGS_SET(link->flags, IFF_LOOPBACK))
+ return 0;
+ if (link->network->keep_configuration == KEEP_CONFIGURATION_YES)
+ return 0;
+
r = link_drop_foreign_routes(link);
k = link_drop_foreign_nexthops(link);
if (r < 0)
return r;
- r = dhcp4_configure(link);
+ r = link_request_dhcp4_client(link);
if (r < 0)
return r;
- r = dhcp6_configure(link);
+ r = link_request_dhcp6_client(link);
if (r < 0)
return r;
if (r < 0)
return r;
- r = radv_configure(link);
+ r = link_request_radv(link);
if (r < 0)
return r;
if (r < 0)
return r;
- /* Drop foreign config, but ignore loopback or critical devices.
- * We do not want to remove loopback address or addresses used for root NFS. */
- if (!(link->flags & IFF_LOOPBACK) &&
- link->network->keep_configuration != KEEP_CONFIGURATION_YES) {
- r = link_drop_foreign_config(link);
- if (r < 0)
- return r;
- }
+ r = link_drop_foreign_config(link);
+ if (r < 0)
+ return r;
r = link_request_static_configs(link);
if (r < 0)
}
static int link_reconfigure_impl(Link *link, bool force) {
- Network *network;
+ Network *network = NULL;
int r;
assert(link);
r = link_get_network(link, &network);
- if (r == -ENOENT) {
- link_set_state(link, LINK_STATE_UNMANAGED);
- return 0;
- }
- if (r < 0)
+ if (r < 0 && r != -ENOENT)
return r;
if (link->network == network && !force)
return 0;
- log_link_info(link, "Re-configuring with %s", network->filename);
+ if (network)
+ log_link_info(link, "Reconfiguring with %s.", network->filename);
+ else
+ log_link_info(link, "Unmanaging interface.");
/* Dropping old .network file */
r = link_stop_engines(link, false);
if (r < 0)
return r;
- if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) {
- log_link_debug(link, "State is %s, dropping foreign config", link_state_to_string(link->state));
- r = link_drop_foreign_config(link);
- if (r < 0)
- return r;
- }
-
link_free_carrier_maps(link);
link_free_engines(link);
link->network = network_unref(link->network);
- link_unref(set_remove(link->manager->links_requesting_uuid, link));
+
+ if (!network) {
+ link_set_state(link, LINK_STATE_UNMANAGED);
+ return 0;
+ }
/* Then, apply new .network file */
link->network = network_ref(network);
return 1;
}
-static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force) {
+static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force, bool update_wifi) {
+ bool link_was_lower_up;
int r;
+ assert(link);
+
+ link_was_lower_up = link->flags & IFF_LOWER_UP;
+
r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state");
if (r <= 0)
return r;
+ if (update_wifi && link_was_lower_up && link->flags & IFF_LOWER_UP) {
+ /* If the interface's L1 was not up, then wifi_get_info() is already called in
+ * link_update_flags(). So, it is not necessary to re-call here. */
+ r = wifi_get_info(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return 0;
+ }
+ }
+
r = link_reconfigure_impl(link, force);
- if (r < 0)
+ if (r < 0) {
link_enter_failed(link);
+ return 0;
+ }
- return 0;
+ return r;
}
static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return link_reconfigure_handler_internal(rtnl, m, link, false);
+ return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ false);
}
static int link_force_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return link_reconfigure_handler_internal(rtnl, m, link, true);
+ return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true, /* update_wifi = */ false);
}
-int link_reconfigure(Link *link, bool force) {
+static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(link);
+
+ r = link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ true);
+ if (r != 0)
+ return r;
+
+ /* r == 0 means an error occurs, the link is unmanaged, or the matching network file is unchanged. */
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ /* re-request static configs, and restart engines. */
+ r = link_stop_engines(link, false);
+ if (r < 0) {
+ link_enter_failed(link);
+ return 0;
+ }
+
+ r = link_acquire_dynamic_conf(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return 0;
+ }
+
+ r = link_request_static_configs(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int link_reconfigure_internal(Link *link, link_netlink_message_handler_t callback) {
int r;
+ assert(link);
+ assert(callback);
+
/* When link in pending or initialized state, then link_configure() will be called. To prevent
* the function from being called multiple times simultaneously, refuse to reconfigure the
* interface in these cases. */
if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_LINGER))
return 0; /* 0 means no-op. */
- r = link_call_getlink(link, force ? link_force_reconfigure_handler : link_reconfigure_handler);
+ r = link_call_getlink(link, callback);
if (r < 0)
return r;
return 1; /* 1 means the interface will be reconfigured. */
}
+int link_reconfigure(Link *link, bool force) {
+ return link_reconfigure_internal(link, force ? link_force_reconfigure_handler : link_reconfigure_handler);
+}
+
+int link_reconfigure_after_sleep(Link *link) {
+ return link_reconfigure_internal(link, link_reconfigure_after_sleep_handler);
+}
+
static int link_initialized_and_synced(Link *link) {
Network *network;
int r;
if (r < 0)
return r;
- if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) {
- log_link_debug(link, "State is %s, dropping foreign config", link_state_to_string(link->state));
- r = link_drop_foreign_config(link);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int link_carrier_reset(Link *link) {
- int r;
-
- assert(link);
-
- if (!link_has_carrier(link))
- return 0;
-
- r = link_carrier_lost(link);
- if (r < 0)
- return r;
-
- r = link_carrier_gained(link);
- if (r < 0)
- return r;
-
- log_link_info(link, "Reset carrier");
- return 0;
+ return link_drop_foreign_config(link);
}
static int link_admin_state_up(Link *link) {
return r;
}
- r = link_update_lldp(link);
- if (r < 0)
- return r;
-
if (!had_carrier && link_has_carrier(link)) {
log_link_info(link, "Gained carrier");
break;
default:
- assert_not_reached("Received link message with invalid RTNL message type.");
+ assert_not_reached();
}
return 1;
void link_update_operstate(Link *link, bool also_update_bond_master);
-int link_carrier_reset(Link *link);
bool link_has_carrier(Link *link);
bool link_ipv6_enabled(Link *link);
LinkState link_state_from_string(const char *s) _pure_;
int link_reconfigure(Link *link, bool force);
+int link_reconfigure_after_sleep(Link *link);
int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata);
int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
if (r < 0)
return r;
- r = link_update_lldp(link);
- if (r < 0)
- return r;
-
return 0;
}
-int link_update_lldp(Link *link) {
- int r;
-
- assert(link);
-
- if (!link->lldp)
- return 0;
-
- if (link->flags & IFF_UP) {
- r = sd_lldp_start(link->lldp);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to start LLDP: %m");
- if (r > 0)
- log_link_debug(link, "Started LLDP.");
- } else {
- r = sd_lldp_stop(link->lldp);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to stop LLDP: %m");
- if (r > 0)
- log_link_debug(link, "Stopped LLDP.");
- }
-
- return r;
-}
-
int link_lldp_save(Link *link) {
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
} LLDPMode;
int link_lldp_rx_configure(Link *link);
-int link_update_lldp(Link *link);
int link_lldp_save(Link *link);
const char* lldp_mode_to_string(LLDPMode m) _const_;
}
static int link_send_lldp(Link *link) {
- char machine_id_string[SD_ID128_STRING_MAX];
_cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL;
_cleanup_free_ void *packet = NULL;
size_t packet_size = 0;
r = lldp_make_packet(link->network->lldp_emit,
&link->hw_addr.ether,
- sd_id128_to_string(machine_id, machine_id_string),
+ SD_ID128_TO_STRING(machine_id),
link->ifname,
(uint16_t) ttl,
link->network ? link->network->description : NULL,
assert(m);
HASHMAP_FOREACH(link, m->links_by_index) {
- r = link_carrier_reset(link);
+ r = link_reconfigure_after_sleep(link);
if (r < 0) {
- log_link_warning_errno(link, r, "Could not reset carrier: %m");
+ log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
link_enter_failed(link);
}
}
(void) manager_set_hostname(m, m->dynamic_hostname);
if (m->dynamic_timezone)
(void) manager_set_timezone(m, m->dynamic_timezone);
- if (!set_isempty(m->links_requesting_uuid))
+ if (m->product_uuid_requested)
(void) manager_request_product_uuid(m);
return 0;
m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free);
m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
- m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref);
m->links_by_name = hashmap_free(m->links_by_name);
m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr);
m->links_by_index = hashmap_free_with_destructor(m->links_by_index, link_unref);
DUID duid_product_uuid;
bool has_product_uuid;
bool product_uuid_requested;
- Set *links_requesting_uuid;
char* dynamic_hostname;
char* dynamic_timezone;
#define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e)
+typedef enum IPv6TokenAddressGeneration {
+ IPV6_TOKEN_ADDRESS_GENERATION_NONE,
+ IPV6_TOKEN_ADDRESS_GENERATION_STATIC,
+ IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE,
+ _IPV6_TOKEN_ADDRESS_GENERATION_MAX,
+ _IPV6_TOKEN_ADDRESS_GENERATION_INVALID = -EINVAL,
+} IPv6TokenAddressGeneration;
+
+typedef struct IPv6Token {
+ IPv6TokenAddressGeneration address_generation_type;
+
+ uint8_t dad_counter;
+ struct in6_addr prefix;
+} IPv6Token;
+
bool link_ipv6_accept_ra_enabled(Link *link) {
assert(link);
static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
struct in6_addr gateway;
- uint16_t lifetime;
+ uint32_t table, mtu = 0;
unsigned preference;
- uint32_t table, mtu;
+ uint16_t lifetime;
usec_t time_now;
int r;
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
- r = sd_ndisc_router_get_mtu(rt, &mtu);
- if (r == -ENODATA)
- mtu = 0;
- else if (r < 0)
- return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m");
+ if (link->network->ipv6_accept_ra_use_mtu) {
+ r = sd_ndisc_router_get_mtu(rt, &mtu);
+ if (r < 0 && r != -ENODATA)
+ return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m");
+ }
table = link_get_ipv6_accept_ra_route_table(link);
return 0;
}
-static bool stableprivate_address_is_valid(const struct in6_addr *addr) {
+static bool stable_private_address_is_valid(const struct in6_addr *addr) {
assert(addr);
/* According to rfc4291, generated address should not be in the following ranges. */
return true;
}
-static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr **ret) {
+static int make_stable_private_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr **ret) {
_cleanup_free_ struct in6_addr *addr = NULL;
sd_id128_t secret_key;
struct siphash state;
r = sd_id128_get_machine_app_specific(NDISC_APP_ID, &secret_key);
if (r < 0)
- return log_error_errno(r, "Failed to generate key: %m");
+ return log_link_warning_errno(link, r, "Failed to generate key for IPv6 stable private address: %m");
siphash24_init(&state, secret_key.bytes);
memcpy(addr->s6_addr, prefix->s6_addr, l);
memcpy(addr->s6_addr + l, &rid, 16 - l);
- if (!stableprivate_address_is_valid(addr)) {
+ if (!stable_private_address_is_valid(addr)) {
*ret = NULL;
return 0;
}
* only when the address generation algorithm produces an invalid address, and the loop
* may exit with an address which ends up being unusable due to duplication on the link. */
for (; j->dad_counter < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; j->dad_counter++) {
- r = make_stableprivate_address(link, address, prefixlen, j->dad_counter, &new_address);
+ r = make_stable_private_address(link, address, prefixlen, j->dad_counter, &new_address);
if (r < 0)
return r;
if (r > 0)
assert(link);
assert(rt);
- r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ /* Do not use clock_boottime_or_monotonic() here, as the kernel internally manages cstamp and
+ * tstamp with jiffies, and it is not increased while the system is suspended. */
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &time_now);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
if (rdnss) {
rdnss->marked = false;
rdnss->router = router;
- rdnss->valid_until = time_now + lifetime * USEC_PER_SEC;
+ rdnss->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC);
continue;
}
*x = (NDiscRDNSS) {
.address = a[j],
.router = router,
- .valid_until = time_now + lifetime * USEC_PER_SEC,
+ .valid_until = usec_add(time_now, lifetime * USEC_PER_SEC),
};
r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x));
if (dnssl) {
dnssl->marked = false;
dnssl->router = router;
- dnssl->valid_until = time_now + lifetime * USEC_PER_SEC;
+ dnssl->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC);
continue;
}
s->router = router;
- s->valid_until = time_now + lifetime * USEC_PER_SEC;
+ s->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC);
r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s));
if (r < 0)
}
break;
default:
- assert_not_reached("Unknown NDisc event");
+ assert_not_reached();
}
}
if (!link->ndisc || !link->dhcp6_client)
return 0;
+ if (!link_has_carrier(link))
+ return 0;
+
log_link_debug(link, "Discovering IPv6 routers");
return sd_ndisc_start(link->ndisc);
link->ndisc_dnssl = set_free(link->ndisc_dnssl);
}
-int ipv6token_new(IPv6Token **ret) {
+static int ipv6token_new(IPv6Token **ret) {
IPv6Token *p;
p = new(IPv6Token, 1);
#include "networkd-route.h"
#include "time-util.h"
-typedef struct IPv6Token IPv6Token;
-
-typedef enum IPv6TokenAddressGeneration {
- IPV6_TOKEN_ADDRESS_GENERATION_NONE,
- IPV6_TOKEN_ADDRESS_GENERATION_STATIC,
- IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE,
- _IPV6_TOKEN_ADDRESS_GENERATION_MAX,
- _IPV6_TOKEN_ADDRESS_GENERATION_INVALID = -EINVAL,
-} IPv6TokenAddressGeneration;
-
typedef enum IPv6AcceptRAStartDHCP6Client {
IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO,
IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS,
/* The domain name follows immediately. */
} NDiscDNSSL;
-struct IPv6Token {
- IPv6TokenAddressGeneration address_generation_type;
-
- uint8_t dad_counter;
- struct in6_addr prefix;
-};
-
-int ipv6token_new(IPv6Token **ret);
-
static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
}
break;
default:
- assert_not_reached("Received invalid RTNL message type");
+ assert_not_reached();
}
return 1;
DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
DHCPv4.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
DHCPv4.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
+DHCPv4.Label, config_parse_dhcp_label, 0, offsetof(Network, dhcp_label)
DHCPv4.RequestBroadcast, config_parse_tristate, 0, offsetof(Network, dhcp_broadcast)
DHCPv4.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
DHCPv4.MUDURL, config_parse_mud_url, 0, offsetof(Network, dhcp_mudurl)
IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_ipv6_accept_ra_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
+IPv6AcceptRA.UseMTU, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_mtu)
IPv6AcceptRA.DHCPv6Client, config_parse_ipv6_accept_ra_start_dhcp6_client, 0, offsetof(Network, ipv6_accept_ra_start_dhcp6_client)
IPv6AcceptRA.RouteTable, config_parse_section_route_table, 0, 0
IPv6AcceptRA.RouteMetric, config_parse_dhcp_route_metric, 0, 0
IPv6AcceptRA.RouteAllowList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_allow_listed_route_prefix)
IPv6AcceptRA.RouteDenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_route_prefix)
DHCPServer.ServerAddress, config_parse_dhcp_server_address, 0, 0
-DHCPServer.UplinkInterface, config_parse_dhcp_server_uplink, 0, 0
+DHCPServer.UplinkInterface, config_parse_uplink, 0, 0
DHCPServer.RelayTarget, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_relay_target)
DHCPServer.RelayAgentCircuitId, config_parse_dhcp_server_relay_agent_suboption, 0, offsetof(Network, dhcp_server_relay_agent_circuit_id)
DHCPServer.RelayAgentRemoteId, config_parse_dhcp_server_relay_agent_suboption, 0, offsetof(Network, dhcp_server_relay_agent_remote_id)
IPv6SendRA.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains)
IPv6SendRA.Domains, config_parse_radv_search_domains, 0, 0
IPv6SendRA.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec)
+IPv6SendRA.UplinkInterface, config_parse_uplink, 0, 0
IPv6Prefix.Prefix, config_parse_prefix, 0, 0
IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0
IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0
LLDP.MUDURL, config_parse_mud_url, 0, offsetof(Network, lldp_mud)
CAN.BitRate, config_parse_can_bitrate, 0, offsetof(Network, can_bitrate)
CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point)
+CAN.TimeQuantaNSec, config_parse_can_time_quanta, 0, offsetof(Network, can_time_quanta_ns)
+CAN.PropagationSegment, config_parse_uint32, 0, offsetof(Network, can_propagation_segment)
+CAN.PhaseBufferSegment1, config_parse_uint32, 0, offsetof(Network, can_phase_buffer_segment_1)
+CAN.PhaseBufferSegment2, config_parse_uint32, 0, offsetof(Network, can_phase_buffer_segment_2)
+CAN.SyncJumpWidth, config_parse_uint32, 0, offsetof(Network, can_sync_jump_width)
CAN.DataBitRate, config_parse_can_bitrate, 0, offsetof(Network, can_data_bitrate)
CAN.DataSamplePoint, config_parse_permille, 0, offsetof(Network, can_data_sample_point)
-CAN.FDMode, config_parse_tristate, 0, offsetof(Network, can_fd_mode)
-CAN.FDNonISO, config_parse_tristate, 0, offsetof(Network, can_non_iso)
-CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us)
-CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling)
-CAN.BusErrorReporting, config_parse_tristate, 0, offsetof(Network, can_berr_reporting)
-CAN.Termination, config_parse_tristate, 0, offsetof(Network, can_termination)
-CAN.ListenOnly, config_parse_tristate, 0, offsetof(Network, can_listen_only)
+CAN.DataTimeQuantaNSec, config_parse_can_time_quanta, 0, offsetof(Network, can_data_time_quanta_ns)
+CAN.DataPropagationSegment, config_parse_uint32, 0, offsetof(Network, can_data_propagation_segment)
+CAN.DataPhaseBufferSegment1, config_parse_uint32, 0, offsetof(Network, can_data_phase_buffer_segment_1)
+CAN.DataPhaseBufferSegment2, config_parse_uint32, 0, offsetof(Network, can_data_phase_buffer_segment_2)
+CAN.DataSyncJumpWidth, config_parse_uint32, 0, offsetof(Network, can_data_sync_jump_width)
+CAN.RestartSec, config_parse_can_restart_usec, 0, offsetof(Network, can_restart_us)
+CAN.Loopback, config_parse_can_control_mode, CAN_CTRLMODE_LOOPBACK, 0
+CAN.ListenOnly, config_parse_can_control_mode, CAN_CTRLMODE_LISTENONLY, 0
+CAN.TripleSampling, config_parse_can_control_mode, CAN_CTRLMODE_3_SAMPLES, 0
+CAN.OneShot, config_parse_can_control_mode, CAN_CTRLMODE_ONE_SHOT, 0
+CAN.BusErrorReporting, config_parse_can_control_mode, CAN_CTRLMODE_BERR_REPORTING, 0
+CAN.FDMode, config_parse_can_control_mode, CAN_CTRLMODE_FD, 0
+CAN.PresumeACK, config_parse_can_control_mode, CAN_CTRLMODE_PRESUME_ACK, 0
+CAN.FDNonISO, config_parse_can_control_mode, CAN_CTRLMODE_FD_NON_ISO, 0
+CAN.ClassicDataLengthCode, config_parse_can_control_mode, CAN_CTRLMODE_CC_LEN8_DLC, 0
+CAN.Termination, config_parse_can_termination, 0, 0
QDisc.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0
QDisc.Handle, config_parse_qdisc_handle, _QDISC_KIND_INVALID, 0
BFIFO.Parent, config_parse_qdisc_parent, QDISC_KIND_BFIFO, 0
.ipv4_accept_local = -1,
.ipv4_route_localnet = -1,
.ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
- .ipv6_accept_ra = -1,
.ipv6_dad_transmits = -1,
.ipv6_hop_limit = -1,
.ipv6_proxy_ndp = -1,
.proxy_arp = -1,
+ .ipv6_accept_ra = -1,
.ipv6_accept_ra_use_dns = true,
.ipv6_accept_ra_use_autonomous_prefix = true,
.ipv6_accept_ra_use_onlink_prefix = true,
+ .ipv6_accept_ra_use_mtu = true,
.ipv6_accept_ra_route_table = RT_TABLE_MAIN,
.ipv6_accept_ra_route_metric = DHCP_ROUTE_METRIC,
.ipv6_accept_ra_start_dhcp6_client = IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES,
- .can_triple_sampling = -1,
- .can_berr_reporting = -1,
.can_termination = -1,
- .can_listen_only = -1,
- .can_fd_mode = -1,
- .can_non_iso = -1,
};
r = config_parse_many(
free(network->dhcp_mudurl);
strv_free(network->dhcp_user_class);
free(network->dhcp_hostname);
+ free(network->dhcp_label);
set_free(network->dhcp_deny_listed_ip);
set_free(network->dhcp_allow_listed_ip);
set_free(network->dhcp_request_options);
free(network->dhcp_server_timezone);
free(network->dhcp_server_uplink_name);
+ free(network->router_uplink_name);
for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
free(network->dhcp_server_emit[t].addresses);
return false;
}
-int config_parse_stacked_netdev(const char *unit,
+int config_parse_stacked_netdev(
+ const char *unit,
const char *filename,
unsigned line,
const char *section,
const char *rvalue,
void *data,
void *userdata) {
+
_cleanup_free_ char *name = NULL;
NetDevKind kind = ltype;
Hashmap **h = data;
void *data,
void *userdata) {
- _cleanup_free_ char *hn = NULL;
char **hostname = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(hostname);
+ assert(data);
- r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
- if (r < 0)
- return r;
+ if (isempty(rvalue)) {
+ *hostname = mfree(*hostname);
+ return 0;
+ }
- if (!hostname_is_valid(hn, 0)) {
+ if (!hostname_is_valid(rvalue, 0)) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Hostname is not valid, ignoring assignment: %s", rvalue);
return 0;
}
- r = dns_name_is_valid(hn);
+ r = dns_name_is_valid(rvalue);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
return 0;
}
- return free_and_replace(*hostname, hn);
+ return free_and_strdup_warn(hostname, rvalue);
}
int config_parse_timezone(
void *data,
void *userdata) {
- _cleanup_free_ char *tz = NULL;
- char **datap = data;
+ char **tz = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(datap);
+ assert(data);
- r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
- if (r < 0)
- return r;
+ if (isempty(rvalue)) {
+ *tz = mfree(*tz);
+ return 0;
+ }
- if (!timezone_is_valid(tz, LOG_WARNING)) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
+ r = verify_timezone(rvalue, LOG_WARNING);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
"Timezone is not valid, ignoring assignment: %s", rvalue);
return 0;
}
- return free_and_replace(*datap, tz);
+ return free_and_strdup_warn(tz, rvalue);
}
int config_parse_dns(
return 0;
}
+
+int config_parse_uplink(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ int *index, r;
+ char **name;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (streq(section, "DHCPServer")) {
+ index = &network->dhcp_server_uplink_index;
+ name = &network->dhcp_server_uplink_name;
+ } else if (streq(section, "IPv6SendRA")) {
+ index = &network->router_uplink_index;
+ name = &network->router_uplink_name;
+ } else
+ assert_not_reached();
+
+ if (isempty(rvalue) || streq(rvalue, ":auto")) {
+ *index = UPLINK_INDEX_AUTO;
+ *name = mfree(*name);
+ return 0;
+ }
+
+ if (streq(rvalue, ":none")) {
+ *index = UPLINK_INDEX_NONE;
+ *name = mfree(*name);
+ return 0;
+ }
+
+ r = parse_ifindex(rvalue);
+ if (r > 0) {
+ *index = r;
+ *name = mfree(*name);
+ return 0;
+ }
+
+ if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ /* The interface name will be resolved later. */
+ r = free_and_strdup_warn(name, rvalue);
+ if (r < 0)
+ return r;
+
+ /* Note, if uplink_name is set, then uplink_index will be ignored. So, the below does not mean
+ * an uplink interface will be selected automatically. */
+ *index = UPLINK_INDEX_AUTO;
+ return 0;
+}
+
DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
"Failed to parse RequiredFamilyForOnline= setting");
#include "resolve-util.h"
#include "socket-netlink.h"
+/* Special values for *_uplink_index. */
+#define UPLINK_INDEX_AUTO 0 /* uplink will be selected automatically */
+#define UPLINK_INDEX_NONE -1 /* uplink will not be selected automatically */
+
typedef enum KeepConfiguration {
KEEP_CONFIGURATION_NO = 0,
KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0,
char *dhcp_mudurl;
char **dhcp_user_class;
char *dhcp_hostname;
+ char *dhcp_label;
uint64_t dhcp_max_attempts;
uint32_t dhcp_route_metric;
bool dhcp_route_metric_set;
struct in6_addr *router_dns;
unsigned n_router_dns;
OrderedSet *router_search_domains;
+ int router_uplink_index;
+ char *router_uplink_name;
/* DHCPv6 Prefix Delegation support */
int dhcp6_pd;
/* CAN support */
uint32_t can_bitrate;
unsigned can_sample_point;
+ nsec_t can_time_quanta_ns;
+ uint32_t can_propagation_segment;
+ uint32_t can_phase_buffer_segment_1;
+ uint32_t can_phase_buffer_segment_2;
+ uint32_t can_sync_jump_width;
uint32_t can_data_bitrate;
unsigned can_data_sample_point;
+ nsec_t can_data_time_quanta_ns;
+ uint32_t can_data_propagation_segment;
+ uint32_t can_data_phase_buffer_segment_1;
+ uint32_t can_data_phase_buffer_segment_2;
+ uint32_t can_data_sync_jump_width;
usec_t can_restart_us;
- int can_triple_sampling;
- int can_berr_reporting;
- int can_termination;
- int can_listen_only;
- int can_fd_mode;
- int can_non_iso;
+ uint32_t can_control_mode_mask;
+ uint32_t can_control_mode_flags;
+ uint16_t can_termination;
+ bool can_termination_set;
/* sysctl settings */
AddressFamily ip_forward;
bool ipv6_accept_ra_use_dns;
bool ipv6_accept_ra_use_autonomous_prefix;
bool ipv6_accept_ra_use_onlink_prefix;
+ bool ipv6_accept_ra_use_mtu;
bool active_slave;
bool primary_slave;
DHCPUseDomains ipv6_accept_ra_use_domains;
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_link_group);
+CONFIG_PARSER_PROTOTYPE(config_parse_uplink);
const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
break;
default:
- assert_not_reached("Received invalid RTNL message type");
+ assert_not_reached();
}
return 1;
n->family = AF_INET6;
break;
default:
- assert_not_reached("Invalid family.");
+ assert_not_reached();
}
TAKE_PTR(n);
#include "networkd-bridge-fdb.h"
#include "networkd-bridge-mdb.h"
#include "networkd-dhcp-server.h"
+#include "networkd-dhcp4.h"
+#include "networkd-dhcp6.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-manager.h"
#include "networkd-neighbor.h"
case REQUEST_TYPE_BRIDGE_MDB:
bridge_mdb_free(object);
break;
- case REQUEST_TYPE_CREATE_STACKED_NETDEV:
- break;
case REQUEST_TYPE_DHCP_SERVER:
+ case REQUEST_TYPE_DHCP4_CLIENT:
+ case REQUEST_TYPE_DHCP6_CLIENT:
break;
case REQUEST_TYPE_IPV6_PROXY_NDP:
free(object);
case REQUEST_TYPE_NEXTHOP:
nexthop_free(object);
break;
+ case REQUEST_TYPE_RADV:
+ break;
case REQUEST_TYPE_ROUTE:
route_free(object);
break;
routing_policy_rule_free(object);
break;
case REQUEST_TYPE_SET_LINK:
+ case REQUEST_TYPE_STACKED_NETDEV:
case REQUEST_TYPE_UP_DOWN:
break;
default:
- assert_not_reached("invalid request type.");
+ assert_not_reached();
}
}
case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB:
- case REQUEST_TYPE_CREATE_STACKED_NETDEV:
+ case REQUEST_TYPE_STACKED_NETDEV:
/* TODO: Currently, these types do not have any specific hash and compare functions.
* Fortunately, all these objects are 'static', thus we can use the trivial functions. */
trivial_hash_func(req->object, state);
break;
case REQUEST_TYPE_DHCP_SERVER:
- /* This type does not have object. */
+ case REQUEST_TYPE_DHCP4_CLIENT:
+ case REQUEST_TYPE_DHCP6_CLIENT:
+ /* These types do not have an object. */
break;
case REQUEST_TYPE_IPV6_PROXY_NDP:
in6_addr_hash_func(req->ipv6_proxy_ndp, state);
case REQUEST_TYPE_NEXTHOP:
nexthop_hash_func(req->nexthop, state);
break;
+ case REQUEST_TYPE_RADV:
+ /* This type does not have an object. */
+ break;
case REQUEST_TYPE_ROUTE:
route_hash_func(req->route, state);
break;
case REQUEST_TYPE_UP_DOWN:
break;
default:
- assert_not_reached("invalid request type.");
+ assert_not_reached();
}
}
case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB:
- case REQUEST_TYPE_CREATE_STACKED_NETDEV:
+ case REQUEST_TYPE_STACKED_NETDEV:
return trivial_compare_func(a->object, b->object);
case REQUEST_TYPE_DHCP_SERVER:
+ case REQUEST_TYPE_DHCP4_CLIENT:
+ case REQUEST_TYPE_DHCP6_CLIENT:
return 0;
case REQUEST_TYPE_IPV6_PROXY_NDP:
return in6_addr_compare_func(a->ipv6_proxy_ndp, b->ipv6_proxy_ndp);
return nexthop_compare_func(a->nexthop, b->nexthop);
case REQUEST_TYPE_ROUTE:
return route_compare_func(a->route, b->route);
+ case REQUEST_TYPE_RADV:
+ return 0;
case REQUEST_TYPE_ROUTING_POLICY_RULE:
return routing_policy_rule_compare_func(a->rule, b->rule);
case REQUEST_TYPE_SET_LINK:
case REQUEST_TYPE_UP_DOWN:
return 0;
default:
- assert_not_reached("invalid request type.");
+ assert_not_reached();
}
}
assert(IN_SET(type,
REQUEST_TYPE_ACTIVATE_LINK,
REQUEST_TYPE_DHCP_SERVER,
+ REQUEST_TYPE_DHCP4_CLIENT,
+ REQUEST_TYPE_DHCP6_CLIENT,
+ REQUEST_TYPE_RADV,
REQUEST_TYPE_SET_LINK,
REQUEST_TYPE_UP_DOWN) ||
object);
- assert(type == REQUEST_TYPE_DHCP_SERVER || netlink_handler);
+ assert(IN_SET(type,
+ REQUEST_TYPE_DHCP_SERVER,
+ REQUEST_TYPE_DHCP4_CLIENT,
+ REQUEST_TYPE_DHCP6_CLIENT,
+ REQUEST_TYPE_RADV) ||
+ netlink_handler);
req = new(Request, 1);
if (!req) {
case REQUEST_TYPE_BRIDGE_MDB:
r = request_process_bridge_mdb(req);
break;
- case REQUEST_TYPE_CREATE_STACKED_NETDEV:
- r = request_process_create_stacked_netdev(req);
- break;
case REQUEST_TYPE_DHCP_SERVER:
r = request_process_dhcp_server(req);
break;
+ case REQUEST_TYPE_DHCP4_CLIENT:
+ r = request_process_dhcp4_client(req);
+ break;
+ case REQUEST_TYPE_DHCP6_CLIENT:
+ r = request_process_dhcp6_client(req);
+ break;
case REQUEST_TYPE_IPV6_PROXY_NDP:
r = request_process_ipv6_proxy_ndp_address(req);
break;
case REQUEST_TYPE_NEXTHOP:
r = request_process_nexthop(req);
break;
+ case REQUEST_TYPE_RADV:
+ r = request_process_radv(req);
+ break;
case REQUEST_TYPE_ROUTE:
r = request_process_route(req);
break;
case REQUEST_TYPE_SET_LINK:
r = request_process_set_link(req);
break;
+ case REQUEST_TYPE_STACKED_NETDEV:
+ r = request_process_stacked_netdev(req);
+ break;
case REQUEST_TYPE_UP_DOWN:
r = request_process_link_up_or_down(req);
break;
REQUEST_TYPE_ADDRESS_LABEL,
REQUEST_TYPE_BRIDGE_FDB,
REQUEST_TYPE_BRIDGE_MDB,
- REQUEST_TYPE_CREATE_STACKED_NETDEV,
REQUEST_TYPE_DHCP_SERVER,
+ REQUEST_TYPE_DHCP4_CLIENT,
+ REQUEST_TYPE_DHCP6_CLIENT,
REQUEST_TYPE_IPV6_PROXY_NDP,
REQUEST_TYPE_NEIGHBOR,
REQUEST_TYPE_NEXTHOP,
+ REQUEST_TYPE_RADV,
REQUEST_TYPE_ROUTE,
REQUEST_TYPE_ROUTING_POLICY_RULE,
REQUEST_TYPE_SET_LINK,
+ REQUEST_TYPE_STACKED_NETDEV,
REQUEST_TYPE_UP_DOWN,
_REQUEST_TYPE_MAX,
_REQUEST_TYPE_INVALID = -EINVAL,
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
+#include "networkd-queue.h"
#include "networkd-radv.h"
#include "parse-util.h"
#include "string-util.h"
goto set_dns;
if (uplink) {
- if (!uplink->network) {
- log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us");
- return 0;
- }
+ assert(uplink->network);
r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
if (r > 0)
return 0;
- set_dns:
+set_dns:
return sd_radv_set_rdnss(link->radv,
DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
dns, n_dns);
goto set_domains;
if (uplink) {
- if (!uplink->network) {
- log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us");
- return 0;
- }
+ assert(uplink->network);
search_domains = uplink->network->search_domains;
if (search_domains)
return 0;
- set_domains:
+set_domains:
s = ordered_set_get_strv(search_domains);
if (!s)
return log_oom();
}
-int radv_emit_dns(Link *link) {
- Link *uplink = NULL;
- int r;
+static int radv_find_uplink(Link *link, Link **ret) {
+ assert(link);
- (void) manager_find_uplink(link->manager, AF_INET6, link, &uplink);
+ if (link->network->router_uplink_name)
+ return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
- r = radv_set_dns(link, uplink);
- if (r < 0)
- log_link_warning_errno(link, r, "Could not set RA DNS: %m");
+ if (link->network->router_uplink_index > 0)
+ return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
- r = radv_set_domains(link, uplink);
- if (r < 0)
- log_link_warning_errno(link, r, "Could not set RA Domains: %m");
+ if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
+ /* It is not necessary to propagate error in automatic selection. */
+ if (manager_find_uplink(link->manager, AF_INET6, link, ret) < 0)
+ *ret = NULL;
+ return 0;
+ }
+ *ret = NULL;
return 0;
}
return link->network->router_prefix_delegation;
}
-int radv_configure(Link *link) {
+static int radv_configure(Link *link) {
uint16_t router_lifetime;
+ Link *uplink = NULL;
RoutePrefix *q;
Prefix *p;
int r;
assert(link);
assert(link->network);
- if (!link_radv_enabled(link))
- return 0;
-
if (link->radv)
return -EBUSY;
return r;
}
+ (void) radv_find_uplink(link, &uplink);
+
+ r = radv_set_dns(link, uplink);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not set RA DNS: %m");
+
+ r = radv_set_domains(link, uplink);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
+
return 0;
}
return 0;
}
+static int radv_is_ready_to_configure(Link *link) {
+ bool needs_uplink = false;
+ int r;
+
+ assert(link);
+ assert(link->network);
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return false;
+
+ if (in6_addr_is_null(&link->ipv6ll_address))
+ return false;
+
+ if (link->network->router_emit_dns && !link->network->router_dns) {
+ _cleanup_free_ struct in6_addr *dns = NULL;
+ size_t n_dns;
+
+ r = network_get_ipv6_dns(link->network, &dns, &n_dns);
+ if (r < 0)
+ return r;
+
+ needs_uplink = r == 0;
+ }
+
+ if (link->network->router_emit_domains &&
+ !link->network->router_search_domains &&
+ !link->network->search_domains)
+ needs_uplink = true;
+
+ if (needs_uplink) {
+ Link *uplink = NULL;
+
+ if (radv_find_uplink(link, &uplink) < 0)
+ return false;
+
+ if (uplink && !uplink->network)
+ return false;
+ }
+
+ return true;
+}
+
+int request_process_radv(Request *req) {
+ Link *link;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_RADV);
+
+ link = req->link;
+
+ r = radv_is_ready_to_configure(link);
+ if (r <= 0)
+ return r;
+
+ r = radv_configure(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
+
+ if (link_has_carrier(link)) {
+ r = sd_radv_start(link->radv);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
+ }
+
+ log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
+ link_has_carrier(link) ? " and started." : "");
+ return 1;
+}
+
+int link_request_radv(Link *link) {
+ int r;
+
+ assert(link);
+
+ if (!link_radv_enabled(link))
+ return 0;
+
+ if (link->radv)
+ return 0;
+
+ r = link_queue_request(link, REQUEST_TYPE_RADV, NULL, false, NULL, NULL, NULL);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
+
+ log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
+ return 0;
+}
+
int radv_add_prefix(
Link *link,
const struct in6_addr *prefix,
#include "conf-parser.h"
#include "networkd-util.h"
-typedef struct Network Network;
typedef struct Link Link;
+typedef struct Network Network;
+typedef struct Request Request;
typedef enum RADVPrefixDelegation {
RADV_PREFIX_DELEGATION_NONE = 0,
void network_drop_invalid_route_prefixes(Network *network);
void network_adjust_radv(Network *network);
-int radv_emit_dns(Link *link);
-int radv_configure(Link *link);
int radv_update_mac(Link *link);
int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
uint32_t lifetime_preferred, uint32_t lifetime_valid);
+int request_process_radv(Request *req);
+int link_request_radv(Link *link);
+
const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_;
RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
set_remove(route->link->dhcp6_pd_routes, route);
set_remove(route->link->dhcp6_pd_routes_old, route);
SET_FOREACH(n, route->link->ndisc_routes)
- if (n->route == route)
+ if (route_equal(n->route, route))
free(set_remove(route->link->ndisc_routes, n));
}
route_compare_func,
route_free);
-static bool route_equal(const Route *r1, const Route *r2) {
+bool route_equal(const Route *r1, const Route *r2) {
if (r1 == r2)
return true;
break;
default:
- assert_not_reached("Received route message with invalid RTNL message type");
+ assert_not_reached();
}
return 1;
buffer = &n->src;
prefixlen = &n->src_prefixlen;
} else
- assert_not_reached(lvalue);
+ assert_not_reached();
if (n->family == AF_UNSPEC)
r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
else if (streq(lvalue, "TTLPropagate"))
n->ttl_propagate = r;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
TAKE_PTR(n);
return 0;
else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
n->initrwnd = k;
else
- assert_not_reached("Invalid TCP window type.");
+ assert_not_reached();
TAKE_PTR(n);
return 0;
void route_hash_func(const Route *route, struct siphash *state);
int route_compare_func(const Route *a, const Route *b);
+bool route_equal(const Route *r1, const Route *r2);
extern const struct hash_ops route_hash_ops;
int route_new(Route **ret);
siphash24_compress(&rule->type, sizeof(rule->type), state);
siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state);
siphash24_compress(&rule->fwmask, sizeof(rule->fwmask), state);
- siphash24_compress(&rule->priority, sizeof(rule->priority), state);
+ siphash24_compress_boolean(rule->priority_set, state);
+ if (rule->priority_set)
+ siphash24_compress(&rule->priority, sizeof(rule->priority), state);
siphash24_compress(&rule->table, sizeof(rule->table), state);
siphash24_compress(&rule->suppress_prefixlen, sizeof(rule->suppress_prefixlen), state);
if (r != 0)
return r;
- r = CMP(a->priority, b->priority);
+ r = CMP(a->priority_set, b->priority_set);
if (r != 0)
return r;
+ if (a->priority_set) {
+ r = CMP(a->priority, b->priority);
+ if (r != 0)
+ return r;
+ }
+
r = CMP(a->table, b->table);
if (r != 0)
return r;
routing_policy_rule_compare_func,
routing_policy_rule_free);
-static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
+static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *rule, bool require_priority, RoutingPolicyRule **ret) {
RoutingPolicyRule *existing;
+ int r;
assert(m);
return 0;
}
+ if (!require_priority && rule->priority_set) {
+ _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
+
+ r = routing_policy_rule_dup(rule, &tmp);
+ if (r < 0)
+ return r;
+
+ tmp->priority_set = false;
+
+ existing = set_get(m->rules, tmp);
+ if (existing) {
+ if (ret)
+ *ret = existing;
+ return 1;
+ }
+ }
+
return -ENOENT;
}
if (r < 0)
return r;
- r = routing_policy_rule_get(m, rule, &existing);
+ r = routing_policy_rule_get(m, rule, true, &existing);
if (r == -ENOENT) {
/* Rule does not exist, use a new one. */
r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, rule);
return 1;
}
+static int routing_policy_rule_update_priority(RoutingPolicyRule *rule, uint32_t priority) {
+ int r;
+
+ assert(rule);
+ assert(rule->manager);
+
+ if (rule->priority_set)
+ return 0;
+
+ if (!set_remove(rule->manager->rules, rule))
+ return -ENOENT;
+
+ rule->priority = priority;
+ rule->priority_set = true;
+
+ r = set_put(rule->manager->rules, rule);
+ if (r <= 0) {
+ /* Undo */
+ rule->priority_set = false;
+ assert_se(set_put(rule->manager->rules, rule) > 0);
+ return r == 0 ? -EEXIST : r;
+ }
+
+ return 1;
+}
+
static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, const char *str, const Link *link, const Manager *m) {
_cleanup_free_ char *from = NULL, *to = NULL, *table = NULL;
return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
}
- r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append FRA_PRIORITY attribute: %m");
+ if (rule->priority_set) {
+ r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append FRA_PRIORITY attribute: %m");
+ }
if (rule->tos > 0) {
r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos);
continue;
}
+ if (!foreign) {
+ _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
+
+ /* The rule may be configured without priority. Try to find without priority. */
+
+ k = routing_policy_rule_dup(rule, &tmp);
+ if (k < 0) {
+ if (r >= 0)
+ r = k;
+ continue;
+ }
+
+ tmp->priority_set = false;
+
+ k = links_have_routing_policy_rule(m, tmp, except);
+ if (k != 0) {
+ if (k < 0 && r >= 0)
+ r = k;
+ continue;
+ }
+ }
+
k = routing_policy_rule_remove(rule, m);
if (k < 0 && r >= 0)
r = k;
}
static const RoutingPolicyRule kernel_rules[] = {
- { .family = AF_INET, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
- { .family = AF_INET, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
- { .family = AF_INET, .priority = 32767, .table = RT_TABLE_DEFAULT, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
- { .family = AF_INET6, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
- { .family = AF_INET6, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+ { .family = AF_INET, .priority_set = true, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+ { .family = AF_INET, .priority_set = true, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+ { .family = AF_INET, .priority_set = true, .priority = 32767, .table = RT_TABLE_DEFAULT, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+ { .family = AF_INET6, .priority_set = true, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+ { .family = AF_INET6, .priority_set = true, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
};
static bool routing_policy_rule_is_created_by_kernel(const RoutingPolicyRule *rule) {
log_warning_errno(r, "rtnl: could not get FRA_PRIORITY attribute, ignoring: %m");
return 0;
}
+ /* The kernel does not send priority if priority is zero. So, the flag below must be always set
+ * even if the message does not contain FRA_PRIORITY. */
+ tmp->priority_set = true;
r = sd_netlink_message_read_u32(message, FRA_TABLE, &tmp->table);
if (r < 0 && r != -ENODATA) {
* protocol of the received rule is RTPROT_KERNEL or RTPROT_STATIC. */
tmp->protocol = routing_policy_rule_is_created_by_kernel(tmp) ? RTPROT_KERNEL : RTPROT_STATIC;
- (void) routing_policy_rule_get(m, tmp, &rule);
+ (void) routing_policy_rule_get(m, tmp, false, &rule);
switch (type) {
case RTM_NEWRULE:
- if (rule)
+ if (rule) {
log_routing_policy_rule_debug(tmp, "Received remembered", NULL, m);
- else if (!m->manage_foreign_routes)
+ r = routing_policy_rule_update_priority(rule, tmp->priority);
+ if (r < 0)
+ log_warning_errno(r, "Failed to update priority of remembered routing policy rule, ignoring: %m");
+ } else if (!m->manage_foreign_routes)
log_routing_policy_rule_debug(tmp, "Ignoring received foreign", NULL, m);
else {
log_routing_policy_rule_debug(tmp, "Remembering foreign", NULL, m);
break;
default:
- assert_not_reached("Received invalid RTNL message type");
+ assert_not_reached();
}
return 1;
if (r < 0)
return log_oom();
+ if (isempty(rvalue)) {
+ n->priority = 0;
+ n->priority_set = false;
+ TAKE_PTR(n);
+ return 0;
+ }
+
r = safe_atou32(rvalue, &n->priority);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue);
return 0;
}
+ n->priority_set = true;
TAKE_PTR(n);
return 0;
* update routing_policy_rule_is_created_by_kernel() when a new setting which sets the flag is
* added in the future. */
if (rule->l3mdev > 0)
- assert_not_reached("FRA_L3MDEV flag should not be configured.");
+ assert_not_reached();
return 0;
}
NetworkConfigSection *section;
bool invert_rule;
+ bool priority_set;
uint8_t tos;
uint8_t type;
return 1;
on_error:
- if (op == SET_LINK_FLAGS) {
+ switch (op) {
+ case SET_LINK_FLAGS:
assert(link->set_flags_messages > 0);
link->set_flags_messages--;
+ break;
+ case SET_LINK_MASTER:
+ link->master_set = true;
+ break;
+ default:
+ break;
}
return 0;
static int link_set_addrgen_mode_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
- r = set_link_handler_internal(rtnl, m, link, SET_LINK_ADDRESS_GENERATION_MODE, true, NULL);
+ r = set_link_handler_internal(rtnl, m, link, SET_LINK_ADDRESS_GENERATION_MODE, /* ignore = */ true, NULL);
if (r <= 0)
return r;
}
static int link_set_bond_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return set_link_handler_internal(rtnl, m, link, SET_LINK_BOND, false, NULL);
+ return set_link_handler_internal(rtnl, m, link, SET_LINK_BOND, /* ignore = */ false, NULL);
}
static int link_set_bridge_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE, false, NULL);
+ return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE, /* ignore = */ true, NULL);
}
static int link_set_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE_VLAN, false, NULL);
+ return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE_VLAN, /* ignore = */ false, NULL);
}
static int link_set_can_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return set_link_handler_internal(rtnl, m, link, SET_LINK_CAN, false, NULL);
+ return set_link_handler_internal(rtnl, m, link, SET_LINK_CAN, /* ignore = */ false, NULL);
}
static int link_set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return set_link_handler_internal(rtnl, m, link, SET_LINK_FLAGS, false, get_link_update_flag_handler);
+ return set_link_handler_internal(rtnl, m, link, SET_LINK_FLAGS, /* ignore = */ false, get_link_update_flag_handler);
}
static int link_set_group_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return set_link_handler_internal(rtnl, m, link, SET_LINK_GROUP, false, NULL);
+ return set_link_handler_internal(rtnl, m, link, SET_LINK_GROUP, /* ignore = */ false, NULL);
}
static int link_set_mac_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return set_link_handler_internal(rtnl, m, link, SET_LINK_MAC, true, get_link_default_handler);
+ return set_link_handler_internal(rtnl, m, link, SET_LINK_MAC, /* ignore = */ true, get_link_default_handler);
}
static int link_set_mac_allow_retry_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
}
static int link_set_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- return set_link_handler_internal(rtnl, m, link, SET_LINK_MASTER, false, get_link_master_handler);
+ return set_link_handler_internal(rtnl, m, link, SET_LINK_MASTER, /* ignore = */ false, get_link_master_handler);
+}
+
+static int link_unset_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ /* Some devices do not support setting master ifindex. Let's ignore error on unsetting master ifindex. */
+ return set_link_handler_internal(rtnl, m, link, SET_LINK_MASTER, /* ignore = */ true, get_link_master_handler);
}
static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
- r = set_link_handler_internal(rtnl, m, link, SET_LINK_MTU, true, get_link_default_handler);
+ r = set_link_handler_internal(rtnl, m, link, SET_LINK_MTU, /* ignore = */ true, get_link_default_handler);
if (r <= 0)
return r;
log_link_debug(link, "Setting %s", set_link_operation_to_string(op));
- if (IN_SET(op, SET_LINK_BOND, SET_LINK_CAN)) {
+ if (op == SET_LINK_BOND) {
r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_NEWLINK, link->master_ifindex);
if (r < 0)
return log_link_debug_errno(link, r, "Could not allocate RTM_NEWLINK message: %m");
+ } else if (op == SET_LINK_CAN) {
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_NEWLINK, link->ifindex);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "Could not allocate RTM_NEWLINK message: %m");
} else {
r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
if (r < 0)
return log_link_debug_errno(link, r, "Could not append IFLA_MTU attribute: %m");
break;
default:
- assert_not_reached("Invalid set link operation");
+ assert_not_reached();
}
r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
int link_request_to_set_master(Link *link) {
assert(link);
+ assert(link->network);
link->master_set = false;
- return link_request_set_link(link, SET_LINK_MASTER, link_set_master_handler, NULL);
+ if (link->network->batadv || link->network->bond || link->network->bridge || link->network->vrf)
+ return link_request_set_link(link, SET_LINK_MASTER, link_set_master_handler, NULL);
+ else
+ return link_request_set_link(link, SET_LINK_MASTER, link_unset_master_handler, NULL);
}
int link_request_to_set_mtu(Link *link, uint32_t mtu) {
up = false;
break;
default:
- assert_not_reached("invalid activation policy");
+ assert_not_reached();
}
link->activated = false;
else if (streq(lvalue, "QualityOfService"))
sr_iov->qos = 0;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
TAKE_PTR(sr_iov);
return 0;
} else if (streq(lvalue, "QualityOfService"))
sr_iov->qos = k;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
TAKE_PTR(sr_iov);
return 0;
else if (streq(lvalue, "Trust"))
sr_iov->trust = -1;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
TAKE_PTR(sr_iov);
return 0;
else if (streq(lvalue, "Trust"))
sr_iov->trust = r;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
TAKE_PTR(sr_iov);
return 0;
else if (streq(lvalue, "CEThresholdSec"))
p = &cd->ce_threshold_usec;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
if (isempty(rvalue)) {
if (streq(lvalue, "CEThresholdSec"))
else if (streq(lvalue, "StrictBands"))
p = &ets->n_strict;
else
- assert_not_reached("Invalid lvalue.");
+ assert_not_reached();
if (isempty(rvalue)) {
*p = 0;
fifo = PFIFO_HEAD_DROP(qdisc);
break;
default:
- assert_not_reached("Invalid QDisc kind.");
+ assert_not_reached();
}
opt.limit = fifo->limit;
fifo = PFIFO_HEAD_DROP(qdisc);
break;
default:
- assert_not_reached("Invalid QDisc kind.");
+ assert_not_reached();
}
if (isempty(rvalue)) {
else if (streq(lvalue, "Flows"))
p = &fqcd->flows;
else
- assert_not_reached("Invalid lvalue.");
+ assert_not_reached();
if (isempty(rvalue)) {
*p = 0;
else if (streq(lvalue, "CEThresholdSec"))
p = &fqcd->ce_threshold_usec;
else
- assert_not_reached("Invalid lvalue.");
+ assert_not_reached();
if (isempty(rvalue)) {
if (streq(lvalue, "CEThresholdSec"))
else if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum"))
p = &fqcd->quantum;
else
- assert_not_reached("Invalid lvalue.");
+ assert_not_reached();
if (isempty(rvalue)) {
if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit"))
else if (streq(lvalue, "OrphanMask"))
p = &fq->orphan_mask;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
if (isempty(rvalue)) {
*p = 0;
else if (STR_IN_SET(lvalue, "InitialQuantumBytes", "InitialQuantum"))
p = &fq->initial_quantum;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
if (isempty(rvalue)) {
*p = 0;
else if (streq(lvalue, "DefaultVirtualQueue"))
p = &gred->default_virtual_queue;
else
- assert_not_reached("Invalid lvalue.");
+ assert_not_reached();
if (isempty(rvalue)) {
*p = 0;
else if (streq(lvalue, "CeilBufferBytes"))
htb->ceil_buffer = 0;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
tclass = NULL;
return 0;
else if (streq(lvalue, "CeilBufferBytes"))
htb->ceil_buffer = v;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
tclass = NULL;
else if (streq(lvalue, "CeilRate"))
v = &htb->ceil_rate;
else
- assert_not_reached("Invalid lvalue");
+ assert_not_reached();
if (isempty(rvalue)) {
*v = 0;
else if (streq(lvalue, "MPUBytes"))
tbf->mpu = 0;
else
- assert_not_reached("unknown lvalue");
+ assert_not_reached();
TAKE_PTR(qdisc);
return 0;
else if (streq(lvalue, "MTUBytes"))
tbf->mtu = k;
else
- assert_not_reached("unknown lvalue");
+ assert_not_reached();
TAKE_PTR(qdisc);
else if (streq(lvalue, "PeakRate"))
p = &tbf->peak_rate;
else
- assert_not_reached("unknown lvalue");
+ assert_not_reached();
if (isempty(rvalue)) {
*p = 0;
tclass_free(TC_TO_TCLASS(tc));
break;
default:
- assert_not_reached("Invalid traffic control type");
+ assert_not_reached();
}
}
case TC_KIND_TCLASS:
return tclass_configure(link, TC_TO_TCLASS(tc));
default:
- assert_not_reached("Invalid traffic control type");
+ assert_not_reached();
}
}
case TC_KIND_TCLASS:
return tclass_section_verify(TC_TO_TCLASS(tc));
default:
- assert_not_reached("Invalid traffic control type");
+ assert_not_reached();
}
}
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
our_env[i++] = NULL;
- final_env = strv_env_merge(2, our_env, argv + optind);
+ final_env = strv_env_merge(our_env, argv + optind);
if (!final_env)
return log_oom();
break;
default:
- assert_not_reached("Unknown custom mount type");
+ assert_not_reached();
}
if (r < 0)
#if HAVE_ACL
static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
acl_t acl;
assert(fd >= 0);
if (child_fd < 0)
return -errno;
- xsprintf(procfs_path, "/proc/self/fd/%i", child_fd);
- acl = acl_get_file(procfs_path, type);
+ acl = acl_get_file(FORMAT_PROC_FD_PATH(child_fd), type);
} else if (type == ACL_TYPE_ACCESS)
acl = acl_get_fd(fd);
- else {
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- acl = acl_get_file(procfs_path, type);
- }
+ else
+ acl = acl_get_file(FORMAT_PROC_FD_PATH(fd), type);
if (!acl)
return -errno;
}
static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
int r;
assert(fd >= 0);
if (child_fd < 0)
return -errno;
- xsprintf(procfs_path, "/proc/self/fd/%i", child_fd);
- r = acl_set_file(procfs_path, type, acl);
+ r = acl_set_file(FORMAT_PROC_FD_PATH(child_fd), type, acl);
} else if (type == ACL_TYPE_ACCESS)
r = acl_set_fd(fd, acl);
- else {
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- r = acl_set_file(procfs_path, type, acl);
- }
+ else
+ r = acl_set_file(FORMAT_PROC_FD_PATH(fd), type, acl);
if (r < 0)
return -errno;
state = STATE_REBOOT;
else
- assert_not_reached("Got unexpected signal");
+ assert_not_reached();
r = kill_and_sigcont(pid, SIGTERM);
" -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n"
" -b --boot Boot up full system (i.e. invoke init)\n"
" --chdir=PATH Set working directory in the container\n"
- " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n"
+ " -E --setenv=NAME[=VALUE] Pass an environment variable to PID 1\n"
" -u --user=USER Run the command under specified user or UID\n"
" --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n"
" --notify-ready=BOOLEAN Receive notifications from the child init process\n\n"
arg_settings_mask |= SETTING_CUSTOM_MOUNTS;
break;
- case 'E': {
- if (!env_assignment_is_valid(optarg))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Environment variable assignment '%s' is not valid.", optarg);
- r = strv_env_replace_strdup(&arg_setenv, optarg);
+ case 'E':
+ r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
if (r < 0)
- return r;
+ return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
arg_settings_mask |= SETTING_ENVIRONMENT;
break;
- }
case 'q':
arg_quiet = true;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (argc > optind) {
break;
default:
- assert_not_reached("unexpected mode");
+ assert_not_reached();
}
/* Fix permissions of the symlink or file copy we just created */
static int setup_journal(const char *directory) {
_cleanup_free_ char *d = NULL;
- char id[SD_ID128_STRING_MAX];
const char *dirname, *p, *q;
sd_id128_t this_id;
bool try;
if (sd_id128_equal(arg_uuid, this_id)) {
log_full(try ? LOG_WARNING : LOG_ERR,
- "Host and machine ids are equal (%s): refusing to link journals", sd_id128_to_string(arg_uuid, id));
+ "Host and machine ids are equal (%s): refusing to link journals", SD_ID128_TO_STRING(arg_uuid));
if (try)
return 0;
return -EEXIST;
}
}
- (void) sd_id128_to_string(arg_uuid, id);
-
- p = strjoina("/var/log/journal/", id);
+ p = strjoina("/var/log/journal/", SD_ID128_TO_STRING(arg_uuid));
q = prefix_roota(directory, p);
if (path_is_mount_point(p, NULL, 0) > 0) {
char **os_release_pairs) {
_cleanup_free_ char *home = NULL;
- char as_uuid[ID128_UUID_STRING_MAX];
size_t n_env = 1;
- const char *envp[] = {
- "PATH=" DEFAULT_PATH_COMPAT,
+ char *envp[] = {
+ (char*) "PATH=" DEFAULT_PATH_COMPAT,
NULL, /* container */
NULL, /* TERM */
NULL, /* HOME */
n_env++;
if (home || !uid_is_valid(arg_uid) || arg_uid == 0)
- if (asprintf((char**)(envp + n_env++), "HOME=%s", home ?: "/root") < 0)
+ if (asprintf(envp + n_env++, "HOME=%s", home ?: "/root") < 0)
return log_oom();
if (arg_user || !uid_is_valid(arg_uid) || arg_uid == 0)
- if (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ?: "root") < 0 ||
- asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)
+ if (asprintf(envp + n_env++, "USER=%s", arg_user ?: "root") < 0 ||
+ asprintf(envp + n_env++, "LOGNAME=%s", arg_user ? arg_user : "root") < 0)
return log_oom();
assert(!sd_id128_is_null(arg_uuid));
- if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0)
+ if (asprintf(envp + n_env++, "container_uuid=%s", ID128_TO_UUID_STRING(arg_uuid)) < 0)
return log_oom();
if (fdset_size(fds) > 0) {
if (r < 0)
return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
- if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", fdset_size(fds)) < 0) ||
- (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0))
+ if ((asprintf(envp + n_env++, "LISTEN_FDS=%u", fdset_size(fds)) < 0) ||
+ (asprintf(envp + n_env++, "LISTEN_PID=1") < 0))
return log_oom();
}
- if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
+ if (asprintf(envp + n_env++, "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
return log_oom();
if (arg_n_credentials > 0) {
n_env++;
}
- env_use = strv_env_merge(3, envp, os_release_pairs, arg_setenv);
+ env_use = strv_env_merge(envp, os_release_pairs, arg_setenv);
if (!env_use)
return log_oom();
return -EINVAL;
default:
- assert_not_reached("Invalid option passed.");
+ assert_not_reached();
}
return 1;
return -EINVAL;
default:
- assert_not_reached("Unknown option code.");
+ assert_not_reached();
}
if (optind < argc)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind + 1 != argc)
* /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
if (*backing_fd < 0)
r = fdisk_assign_device(c, node, arg_dry_run);
- else {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
- xsprintf(procfs_path, "/proc/self/fd/%i", *backing_fd);
-
- r = fdisk_assign_device(c, procfs_path, arg_dry_run);
- }
+ else
+ r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(*backing_fd), arg_dry_run);
if (r == -EINVAL && arg_size_auto) {
struct stat st;
static int partition_hint(const Partition *p, const char *node, char **ret) {
_cleanup_free_ char *buf = NULL;
- char ids[ID128_UUID_STRING_MAX];
const char *label;
sd_id128_t id;
else
id = p->type_uuid;
- buf = strdup(id128_to_uuid_string(id, ids));
+ buf = strdup(ID128_TO_UUID_STRING(id));
done:
if (!buf)
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(erase_and_freep) void *volume_key = NULL;
_cleanup_free_ char *dm_name = NULL, *vol = NULL;
- char suuid[ID128_UUID_STRING_MAX];
size_t volume_key_size = 256 / 8;
sd_id128_t uuid;
int r;
CRYPT_LUKS2,
"aes",
"xts-plain64",
- id128_to_uuid_string(uuid, suuid),
+ ID128_TO_UUID_STRING(uuid),
volume_key,
volume_key_size,
&(struct crypt_params_luks2) {
_cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_free_ void *blob = NULL, *hash = NULL;
size_t secret_size, blob_size, hash_size;
+ uint16_t pcr_bank;
int keyslot;
- r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size);
+ r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank);
if (r < 0)
return log_error_errno(r, "Failed to seal to TPM2: %m");
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
- r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, blob, blob_size, hash, hash_size, &v);
+ r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v);
if (r < 0)
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
sfd, ".",
pfd, fn,
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
+ COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
+ COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
} else {
if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
- (void) copy_xattr(sfd, tfd);
+ (void) copy_xattr(sfd, tfd, COPY_ALL_XATTRS);
(void) copy_access(sfd, tfd);
(void) copy_times(sfd, tfd, 0);
}
}
if (!sd_id128_equal(p->new_uuid, p->current_uuid)) {
- char buf[ID128_UUID_STRING_MAX];
-
assert(!sd_id128_is_null(p->new_uuid));
- r = fdisk_partition_set_uuid(p->current_partition, id128_to_uuid_string(p->new_uuid, buf));
+ r = fdisk_partition_set_uuid(p->current_partition, ID128_TO_UUID_STRING(p->new_uuid));
if (r < 0)
return log_error_errno(r, "Failed to set partition UUID: %m");
} else {
_cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
_cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
- char ids[ID128_UUID_STRING_MAX];
assert(!p->new_partition);
assert(p->offset % 512 == 0);
if (!t)
return log_oom();
- r = fdisk_parttype_set_typestr(t, id128_to_uuid_string(p->type_uuid, ids));
+ r = fdisk_parttype_set_typestr(t, ID128_TO_UUID_STRING(p->type_uuid));
if (r < 0)
return log_error_errno(r, "Failed to initialize partition type: %m");
if (r < 0)
return log_error_errno(r, "Failed to set partition number: %m");
- r = fdisk_partition_set_uuid(q, id128_to_uuid_string(p->new_uuid, ids));
+ r = fdisk_partition_set_uuid(q, ID128_TO_UUID_STRING(p->new_uuid));
if (r < 0)
return log_error_errno(r, "Failed to set partition UUID: %m");
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (argc - optind > 1)
}
static int resize_pt(int fd) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
int r;
if (!c)
return log_oom();
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- r = fdisk_assign_device(c, procfs_path, 0);
+ r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(fd), 0);
if (r < 0)
- return log_error_errno(r, "Failed to open device '%s': %m", procfs_path);
+ return log_error_errno(r, "Failed to open device '%s': %m", FORMAT_PROC_FD_PATH(fd));
r = fdisk_has_label(c);
if (r < 0)
- return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", procfs_path);
+ return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", FORMAT_PROC_FD_PATH(fd));
if (r == 0) {
log_debug("Not resizing partition table, as there currently is none.");
return 0;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
install_data('org.freedesktop.portable1.policy',
install_dir : polkitpolicydir)
- install_data('profile/default/service.conf', install_dir : join_paths(profiledir, 'default'))
- install_data('profile/nonetwork/service.conf', install_dir : join_paths(profiledir, 'nonetwork'))
- install_data('profile/strict/service.conf', install_dir : join_paths(profiledir, 'strict'))
- install_data('profile/trusted/service.conf', install_dir : join_paths(profiledir, 'trusted'))
+ install_data('profile/default/service.conf', install_dir : profiledir / 'default')
+ install_data('profile/nonetwork/service.conf', install_dir : profiledir / 'nonetwork')
+ install_data('profile/strict/service.conf', install_dir : profiledir / 'strict')
+ install_data('profile/trusted/service.conf', install_dir : profiledir / 'trusted')
endif
assert(!os_release);
os_release = TAKE_PTR(add);
} else
- assert_not_reached("Unexpected metadata item from child.");
+ assert_not_reached();
}
r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_mode == _MODE_INVALID)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_type == 0 && arg_class != 0)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_type == 0 && arg_class != 0)
return translate("revert", arg_ifname, 0, NULL, bus);
case _MODE_INVALID:
- assert_not_reached("invalid mode");
+ assert_not_reached();
}
return 0;
case DNS_TRANSACTION_VALIDATING:
case DNS_TRANSACTION_SUCCESS:
default:
- assert_not_reached("Impossible state");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Unknown type of Txt config");
+ assert_not_reached();
}
LIST_INSERT_AFTER(items, txt_data->txt, last, i);
continue;
default:
- assert_not_reached("Unexpected DNSSEC validation result");
+ assert_not_reached();
}
}
}
case AF_INET6:
return UDP6_PACKET_HEADER_SIZE;
default:
- assert_not_reached("Unexpected address family");
+ assert_not_reached();
}
}
if (cname->key->type == DNS_TYPE_CNAME)
return dns_resource_key_new(key->class, key->type, cname->cname.name);
else {
+ _cleanup_free_ char *destination = NULL;
DnsResourceKey *k;
- char *destination = NULL;
r = dns_name_change_suffix(dns_resource_key_name(key), dns_resource_key_name(cname->key), cname->dname.name, &destination);
if (r < 0)
k = dns_resource_key_new_consume(key->class, key->type, destination);
if (!k)
- return mfree(destination);
+ return NULL;
+ TAKE_PTR(destination);
return k;
}
}
c = dns_class_to_string(key->class);
t = dns_type_to_string(key->type);
- snprintf(buf, buf_size, "%s %s%s%.0u %s%s%.0u",
- dns_resource_key_name(key),
- strempty(c), c ? "" : "CLASS", c ? 0 : key->class,
- strempty(t), t ? "" : "TYPE", t ? 0 : key->type);
+ (void) snprintf(buf, buf_size, "%s %s%s%.0u %s%s%.0u",
+ dns_resource_key_name(key),
+ strempty(c), c ? "" : "CLASS", c ? 0 : key->class,
+ strempty(t), t ? "" : "TYPE", t ? 0 : key->type);
return ans;
}
}
default:
- assert_not_reached("Unknown scope protocol");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Unknown search domain type");
+ assert_not_reached();
}
d->linked = true;
break;
default:
- assert_not_reached("Unknown search domain type");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Unknown server type");
+ assert_not_reached();
}
s->linked = true;
s->manager->n_dns_servers--;
break;
default:
- assert_not_reached("Unknown server type");
+ assert_not_reached();
}
s->linked = false;
break;
default:
- assert_not_reached("Unknown server type");
+ assert_not_reached();
}
}
if (s->possible_feature_level > level) {
s->possible_feature_level = level;
dns_server_reset_counters(s);
+ log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
}
-
- log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
}
void dns_server_packet_invalid(DnsServer *s, DnsServerFeatureLevel level) {
DNS_PACKET_RD(q->request_packet),
!!q->request_packet->opt,
edns0_do,
- DNS_PACKET_AD(q->request_packet) && dns_query_fully_authenticated(q),
+ (DNS_PACKET_AD(q->request_packet) || DNS_PACKET_DO(q->request_packet)) && dns_query_fully_authenticated(q),
DNS_PACKET_CD(q->request_packet),
q->stub_listener_extra ? ADVERTISE_EXTRA_DATAGRAM_SIZE_MAX : ADVERTISE_DATAGRAM_SIZE_MAX,
dns_packet_has_nsid_request(q->request_packet) > 0 && !q->stub_listener_extra);
DNS_PACKET_RD(p),
!!p->opt,
DNS_PACKET_DO(p),
- DNS_PACKET_AD(p) && authenticated,
+ (DNS_PACKET_AD(p) || DNS_PACKET_DO(p)) && authenticated,
DNS_PACKET_CD(p),
l ? ADVERTISE_EXTRA_DATAGRAM_SIZE_MAX : ADVERTISE_DATAGRAM_SIZE_MAX,
dns_packet_has_nsid_request(p) > 0 && !l);
case DNS_TRANSACTION_PENDING:
case DNS_TRANSACTION_VALIDATING:
default:
- assert_not_reached("Impossible state");
+ assert_not_reached();
}
dns_query_free(q);
break;
default:
- assert_not_reached("Invalid DNS protocol.");
+ assert_not_reached();
}
if (t->received != p) {
break;
}
- /* Reduce this feature level by one and try again. */
- switch (t->current_feature_level) {
- case DNS_SERVER_FEATURE_LEVEL_TLS_DO:
- t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN;
- break;
- case DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN + 1:
- /* Skip plain TLS when TLS is not supported */
- t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN - 1;
- break;
- default:
- t->clamp_feature_level_servfail = t->current_feature_level - 1;
- }
+ /* SERVFAIL can happen for many reasons and may be transient.
+ * To avoid unnecessary downgrades retry once with the initial level.
+ * Check for clamp_feature_level_servfail having an invalid value as a sign that this is the
+ * first attempt to downgrade. If so, clamp to the current value so that the transaction
+ * is retried without actually downgrading. If the next try also fails we will downgrade by
+ * hitting the else branch below. */
+ if (DNS_PACKET_RCODE(p) == DNS_RCODE_SERVFAIL &&
+ t->clamp_feature_level_servfail < 0) {
+ t->clamp_feature_level_servfail = t->current_feature_level;
+ log_debug("Server returned error %s, retrying transaction.",
+ dns_rcode_to_string(DNS_PACKET_RCODE(p)));
+ } else {
+ /* Reduce this feature level by one and try again. */
+ switch (t->current_feature_level) {
+ case DNS_SERVER_FEATURE_LEVEL_TLS_DO:
+ t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN;
+ break;
+ case DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN + 1:
+ /* Skip plain TLS when TLS is not supported */
+ t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN - 1;
+ break;
+ default:
+ t->clamp_feature_level_servfail = t->current_feature_level - 1;
+ }
- log_debug("Server returned error %s, retrying transaction with reduced feature level %s.",
- dns_rcode_to_string(DNS_PACKET_RCODE(p)),
- dns_server_feature_level_to_string(t->clamp_feature_level_servfail));
+ log_debug("Server returned error %s, retrying transaction with reduced feature level %s.",
+ dns_rcode_to_string(DNS_PACKET_RCODE(p)),
+ dns_server_feature_level_to_string(t->clamp_feature_level_servfail));
+ }
dns_transaction_retry(t, false /* use the same server */);
return;
break;
default:
- assert_not_reached("Invalid DNS protocol.");
+ assert_not_reached();
}
if (DNS_PACKET_TC(p)) {
break;
default:
- assert_not_reached("Invalid DNS protocol.");
+ assert_not_reached();
}
log_debug("Timeout reached on transaction %" PRIu16 ".", t->id);
return t->scope->resend_timeout;
default:
- assert_not_reached("Invalid DNS protocol.");
+ assert_not_reached();
}
}
accuracy = MDNS_JITTER_RANGE_USEC;
break;
default:
- assert_not_reached("bad protocol");
+ assert_not_reached();
}
assert(!t->timeout_event_source);
break;
default:
- assert_not_reached("Unexpected NSEC result.");
+ assert_not_reached();
}
}
case DNS_TRANSACTION_VALIDATING:
case DNS_TRANSACTION_SUCCESS:
default:
- assert_not_reached("Impossible state");
+ assert_not_reached();
}
}
" --nice=NICE Nice level\n"
" --working-directory=PATH Set working directory\n"
" -d --same-dir Inherit working directory from caller\n"
- " -E --setenv=NAME=VALUE Set environment\n"
+ " -E --setenv=NAME[=VALUE] Set environment variable\n"
" -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n"
" STDERR\n"
" -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n"
break;
case 'E':
- if (strv_extend(&arg_environment, optarg) < 0)
- return log_oom();
+ r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
break;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
with_trigger = !!arg_path_property || !!arg_socket_property || arg_with_timer;
if (!pty_path)
return log_oom();
} else
- assert_not_reached("Can't allocate tty via ssh");
+ assert_not_reached();
}
/* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin
return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
}
- env = strv_env_merge(3, environ, user_env, arg_environment);
+ env = strv_env_merge(environ, user_env, arg_environment);
if (!env)
return log_oom();
else if (streq(suffix, ".timer"))
r = transient_timer_set_properties(m);
else
- assert_not_reached("Invalid suffix");
+ assert_not_reached();
if (r < 0)
return r;
return *gid_a == *gid_b;
}
default:
- assert_not_reached("Unknown acl tag type");
+ assert_not_reached();
}
}
return -EISDIR;
r = btrfs_subvol_make(new_path);
- if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
+ if (ERRNO_IS_NOT_SUPPORTED(r) && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
/* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
if (mkdir(new_path, 0755) < 0)
return -errno;
r = copy_directory_fd_full(
old_fd, new_path,
- COPY_MERGE|COPY_REFLINK|COPY_SAME_MOUNT|COPY_HARDLINKS|(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0),
- progress_path, progress_bytes, userdata);
+ COPY_MERGE_EMPTY|
+ COPY_REFLINK|
+ COPY_SAME_MOUNT|
+ COPY_HARDLINKS|
+ COPY_ALL_XATTRS|
+ (FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0)|
+ (FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGTERM) ? COPY_SIGTERM : 0),
+ progress_path,
+ progress_bytes,
+ userdata);
if (r < 0)
goto fallback_fail;
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 1 << 4, /* If the destination doesn't support subvolumes, reflink/copy instead */
BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 1 << 5, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */
BTRFS_SNAPSHOT_SIGINT = 1 << 6, /* Check for SIGINT regularly, and return EINTR if seen */
+ BTRFS_SNAPSHOT_SIGTERM = 1 << 7, /* Ditto, but for SIGTERM */
} BtrfsSnapshotFlags;
typedef enum BtrfsRemoveFlags {
if (STR_IN_SET(field, "RestrictAddressFamilies",
"SystemCallFilter",
- "SystemCallLog")) {
+ "SystemCallLog",
+ "RestrictNetworkInterfaces")) {
int allow_list = 1;
const char *p = eq;
break;
default:
- assert_not_reached("Hmm, unknown transport type.");
+ assert_not_reached();
}
if (r < 0)
return r;
break;
default:
- assert_not_reached("Hmm, unknown transport type.");
+ assert_not_reached();
}
return r;
gid_t gid,
mode_t mask) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
const char *n;
int r;
/* We change ACLs through the /proc/self/fd/%i path, so that we have a stable reference that works
* with O_PATH. */
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
/* Drop any ACL if there is one */
FOREACH_STRING(n, "system.posix_acl_access", "system.posix_acl_default")
- if (removexattr(procfs_path, n) < 0)
+ if (removexattr(FORMAT_PROC_FD_PATH(fd), n) < 0)
if (!IN_SET(errno, ENODATA, EOPNOTSUPP, ENOSYS, ENOTTY))
return -errno;
#define EPOCH_FILE "/usr/lib/clock-epoch"
-int clock_apply_epoch(void) {
+int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
struct stat st;
struct timespec ts;
- usec_t epoch_usec;
+ usec_t epoch_usec, now_usec;
+
+ /* NB: we update *ret_attempted_change in *all* cases, both
+ * on success and failure, to indicate what we intended to do! */
+
+ assert(ret_attempted_change);
if (stat(EPOCH_FILE, &st) < 0) {
if (errno != ENOENT)
} else
epoch_usec = timespec_load(&st.st_mtim);
- if (now(CLOCK_REALTIME) >= epoch_usec)
+ now_usec = now(CLOCK_REALTIME);
+ if (now_usec < epoch_usec)
+ *ret_attempted_change = CLOCK_CHANGE_FORWARD;
+ else if (now_usec > usec_add(epoch_usec, CLOCK_VALID_RANGE_USEC_MAX))
+ *ret_attempted_change = CLOCK_CHANGE_BACKWARD;
+ else {
+ *ret_attempted_change = CLOCK_CHANGE_NOOP;
return 0;
+ }
if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, epoch_usec)) < 0)
return -errno;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <errno.h>
#include <time.h>
+typedef enum ClockChangeDirection {
+ CLOCK_CHANGE_NOOP,
+ CLOCK_CHANGE_FORWARD,
+ CLOCK_CHANGE_BACKWARD,
+ _CLOCK_CHANGE_MAX,
+ _CLOCK_CHANGE_INVALID = -EINVAL,
+} ClockChangeDirection;
+
int clock_is_localtime(const char* adjtime_path);
int clock_set_timezone(int *ret_minutesdelta);
int clock_reset_timewarp(void);
int clock_get_hwclock(struct tm *tm);
int clock_set_hwclock(const struct tm *tm);
-int clock_apply_epoch(void);
+int clock_apply_epoch(ClockChangeDirection *ret_attempted_change);
return k > 0;
default:
- assert_not_reached("unknown order");
+ assert_not_reached();
}
}
return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE;
}
+static int look_for_signals(CopyFlags copy_flags) {
+ int r;
+
+ if ((copy_flags & (COPY_SIGINT|COPY_SIGTERM)) == 0)
+ return 0;
+
+ r = pop_pending_signal(copy_flags & COPY_SIGINT ? SIGINT : 0,
+ copy_flags & COPY_SIGTERM ? SIGTERM : 0);
+ if (r < 0)
+ return r;
+ if (r != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINTR),
+ "Got %s, cancelling copy operation.", signal_to_string(r));
+
+ return 0;
+}
+
int copy_bytes_full(
int fdf, int fdt,
uint64_t max_bytes,
if (max_bytes <= 0)
return 1; /* return > 0 if we hit the max_bytes limit */
- if (FLAGS_SET(copy_flags, COPY_SIGINT)) {
- r = pop_pending_signal(SIGINT);
- if (r < 0)
- return r;
- if (r > 0)
- return -EINTR;
- }
+ r = look_for_signals(copy_flags);
+ if (r < 0)
+ return r;
if (max_bytes != UINT64_MAX && m > max_bytes)
m = max_bytes;
return -errno;
r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress, userdata);
- if (r < 0) {
- (void) unlinkat(dt, to, 0);
- return r;
- }
+ if (r < 0)
+ goto fail;
if (fchown(fdt,
uid_is_valid(override_uid) ? override_uid : st->st_uid,
r = -errno;
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
- (void) copy_xattr(fdf, fdt);
+ (void) copy_xattr(fdf, fdt, copy_flags);
- q = close(fdt);
- fdt = -1;
+ if (copy_flags & COPY_FSYNC) {
+ if (fsync(fdt) < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+ q = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
if (q < 0) {
- r = -errno;
- (void) unlinkat(dt, to, 0);
+ r = q;
+ goto fail;
}
(void) memorize_hardlink(hardlink_context, st, dt, to);
return r;
+
+fail:
+ (void) unlinkat(dt, to, 0);
+ return r;
}
static int fd_copy_fifo(
if (dot_or_dot_dot(de->d_name))
continue;
- if (FLAGS_SET(copy_flags, COPY_SIGINT)) {
- r = pop_pending_signal(SIGINT);
- if (r < 0)
- return r;
- if (r > 0)
- return -EINTR;
- }
+ r = look_for_signals(copy_flags);
+ if (r < 0)
+ return r;
if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
r = -errno;
else
q = -EOPNOTSUPP;
- if (q == -EINTR) /* Propagate SIGINT up instantly */
+ if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */
return q;
if (q == -EEXIST && (copy_flags & COPY_MERGE))
q = 0;
if (fchmod(fdt, st->st_mode & 07777) < 0)
r = -errno;
- (void) copy_xattr(dirfd(d), fdt);
+ (void) copy_xattr(dirfd(d), fdt, copy_flags);
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
}
+ if (copy_flags & COPY_FSYNC_FULL) {
+ if (fsync(fdt) < 0)
+ return -errno;
+ }
+
return r;
}
void *userdata) {
struct stat st;
+ int r;
assert(from);
assert(to);
return -errno;
if (S_ISREG(st.st_mode))
- return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL, progress_bytes, userdata);
+ r = fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL, progress_bytes, userdata);
else if (S_ISDIR(st.st_mode))
- return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+ r = fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
else if (S_ISLNK(st.st_mode))
- return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
+ r = fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(st.st_mode))
- return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
+ r = fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
- return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
+ r = fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
else
return -EOPNOTSUPP;
+ if (r < 0)
+ return r;
+
+ if (S_ISDIR(st.st_mode) && (copy_flags & COPY_SYNCFS)) {
+ /* If the top-level inode is a directory run syncfs() now. */
+ r = syncfs_path(fdt, to);
+ if (r < 0)
+ return r;
+ } else if ((copy_flags & (COPY_FSYNC_FULL|COPY_SYNCFS)) != 0) {
+ /* fsync() the parent dir of what we just copied if COPY_FSYNC_FULL is set. Also do this in
+ * case COPY_SYNCFS is set but the top-level inode wasn't actually a directory. We do this so that
+ * COPY_SYNCFS provides reasonable synchronization semantics on any kind of inode: when the
+ * copy operation is done the whole inode — regardless of its type — and all its children
+ * will be synchronized to disk. */
+ r = fsync_parent_at(fdt, to);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int sync_dir_by_flags(const char *path, CopyFlags copy_flags) {
+
+ if (copy_flags & COPY_SYNCFS)
+ return syncfs_path(AT_FDCWD, path);
+ if (copy_flags & COPY_FSYNC_FULL)
+ return fsync_parent_at(AT_FDCWD, path);
+
+ return 0;
}
int copy_directory_fd_full(
if (r < 0)
return r;
- return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+ r = fd_copy_directory(
+ dirfd, NULL,
+ &st,
+ AT_FDCWD, to,
+ st.st_dev,
+ COPY_DEPTH_MAX,
+ UID_INVALID, GID_INVALID,
+ copy_flags,
+ NULL, NULL,
+ progress_path,
+ progress_bytes,
+ userdata);
+ if (r < 0)
+ return r;
+
+ r = sync_dir_by_flags(to, copy_flags);
+ if (r < 0)
+ return r;
+
+ return 0;
}
int copy_directory_full(
if (r < 0)
return r;
- return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+ r = fd_copy_directory(
+ AT_FDCWD, from,
+ &st,
+ AT_FDCWD, to,
+ st.st_dev,
+ COPY_DEPTH_MAX,
+ UID_INVALID, GID_INVALID,
+ copy_flags,
+ NULL, NULL,
+ progress_path,
+ progress_bytes,
+ userdata);
+ if (r < 0)
+ return r;
+
+ r = sync_dir_by_flags(to, copy_flags);
+ if (r < 0)
+ return r;
+
+ return 0;
}
int copy_file_fd_full(
void *userdata) {
_cleanup_close_ int fdf = -1;
+ struct stat st;
int r;
assert(from);
if (fdf < 0)
return -errno;
+ r = fd_verify_regular(fdf);
+ if (r < 0)
+ return r;
+
+ if (fstat(fdt, &st) < 0)
+ return -errno;
+
r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata);
+ if (r < 0)
+ return r;
- (void) copy_times(fdf, fdt, copy_flags);
- (void) copy_xattr(fdf, fdt);
+ if (S_ISREG(fdt)) {
+ (void) copy_times(fdf, fdt, copy_flags);
+ (void) copy_xattr(fdf, fdt, copy_flags);
+ }
- return r;
+ if (copy_flags & COPY_FSYNC_FULL) {
+ r = fsync_full(fdt);
+ if (r < 0)
+ return r;
+ } else if (copy_flags & COPY_FSYNC) {
+ if (fsync(fdt) < 0)
+ return -errno;
+ }
+
+ return 0;
}
int copy_file_full(
copy_progress_bytes_t progress_bytes,
void *userdata) {
- _cleanup_close_ int fdf = -1;
+ _cleanup_close_ int fdf = -1, fdt = -1;
struct stat st;
- int r, fdt = -1; /* avoid false maybe-uninitialized warning */
+ int r;
assert(from);
assert(to);
if (fdf < 0)
return -errno;
- if (mode == MODE_INVALID)
- if (fstat(fdf, &st) < 0)
- return -errno;
+ if (fstat(fdf, &st) < 0)
+ return -errno;
+
+ r = stat_verify_regular(&st);
+ if (r < 0)
+ return r;
RUN_WITH_UMASK(0000) {
if (copy_flags & COPY_MAC_CREATE) {
return -errno;
}
+ if (!FLAGS_SET(flags, O_EXCL)) { /* if O_EXCL was used we created the thing as regular file, no need to check again */
+ r = fd_verify_regular(fdt);
+ if (r < 0)
+ goto fail;
+ }
+
if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata);
- if (r < 0) {
- close(fdt);
- (void) unlink(to);
- return r;
- }
+ if (r < 0)
+ goto fail;
(void) copy_times(fdf, fdt, copy_flags);
- (void) copy_xattr(fdf, fdt);
+ (void) copy_xattr(fdf, fdt, copy_flags);
if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
- if (close(fdt) < 0) {
- unlink_noerrno(to);
- return -errno;
+ if (copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL)) {
+ if (fsync(fdt) < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+
+ r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
+ if (r < 0)
+ goto fail;
+
+ if (copy_flags & COPY_FSYNC_FULL) {
+ r = fsync_parent_at(AT_FDCWD, to);
+ if (r < 0)
+ goto fail;
}
return 0;
+
+fail:
+ /* Only unlink if we definitely are the ones who created the file */
+ if (FLAGS_SET(flags, O_EXCL))
+ (void) unlink(to);
+
+ return r;
}
int copy_file_atomic_full(
if (fchmod(fdt, mode) < 0)
return -errno;
+ if ((copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL))) {
+ /* Sync the file */
+ if (fsync(fdt) < 0)
+ return -errno;
+ }
+
if (copy_flags & COPY_REPLACE) {
if (renameat(AT_FDCWD, t, AT_FDCWD, to) < 0)
return -errno;
return r;
}
+ t = mfree(t);
+
if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
- t = mfree(t);
+ r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
+ if (r < 0)
+ goto fail;
+
+ if (copy_flags & COPY_FSYNC_FULL) {
+ /* Sync the parent directory */
+ r = fsync_parent_at(AT_FDCWD, to);
+ if (r < 0)
+ goto fail;
+ }
+
return 0;
+
+fail:
+ (void) unlink(to);
+ return r;
}
int copy_times(int fdf, int fdt, CopyFlags flags) {
return fchmod_and_chown_with_fallback(fdt, patht, st.st_mode & 07777, st.st_uid, st.st_gid);
}
-int copy_xattr(int fdf, int fdt) {
+int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) {
_cleanup_free_ char *names = NULL;
int ret = 0, r;
const char *p;
NULSTR_FOREACH(p, names) {
_cleanup_free_ char *value = NULL;
- if (!startswith(p, "user."))
+ if (!(copy_flags & COPY_ALL_XATTRS) && !startswith(p, "user."))
continue;
r = fgetxattr_malloc(fdf, p, &value);
#include <sys/types.h>
typedef enum CopyFlags {
- COPY_REFLINK = 1 << 0, /* Try to reflink */
- COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
- COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
- COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
- COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
- COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
- COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
- COPY_MAC_CREATE = 1 << 7, /* Create files with the correct MAC label (currently SELinux only) */
- COPY_HARDLINKS = 1 << 8, /* Try to reproduce hard links */
+ COPY_REFLINK = 1 << 0, /* Try to reflink */
+ COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
+ COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
+ COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
+ COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
+ COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
+ COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
+ COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */
+ COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */
+ COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */
+ COPY_FSYNC = 1 << 10, /* fsync() after we are done */
+ COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */
+ COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */
+ COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
} CopyFlags;
typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
static inline int copy_rights(int fdf, int fdt) {
return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */
}
-int copy_xattr(int fdf, int fdt);
+int copy_xattr(int fdf, int fdt, CopyFlags copy_flags);
if (ret) {
void *copy;
+ assert(sz <= sizeof(f->data)); /* Ensure we don't read past f->data bounds */
+
copy = memdup(f->data, sz);
if (!copy)
return -ENOMEM;
};
struct _packed_ tpm2_credential_header {
- le64_t pcr_mask;
+ le64_t pcr_mask; /* Note that the spec for PC Clients only mandates 24 PCRs, and that's what systems
+ * generally have. But keep the door open for more. */
+ le16_t pcr_bank; /* For now, either TPM2_ALG_SHA256 or TPM2_ALG_SHA1 */
+ le16_t _zero; /* Filler to maintain 32bit alignment */
le32_t blob_size;
le32_t policy_hash_size;
uint8_t policy_hash_and_blob[];
struct encrypted_credential_header *h;
int ksz, bsz, ivsz, tsz, added, r;
uint8_t md[SHA256_DIGEST_LENGTH];
+ uint16_t tpm2_pcr_bank = 0;
const EVP_CIPHER *cc;
#if HAVE_TPM2
bool try_tpm2 = false;
&tpm2_blob,
&tpm2_blob_size,
&tpm2_policy_hash,
- &tpm2_policy_hash_size);
+ &tpm2_policy_hash_size,
+ &tpm2_pcr_bank);
if (r < 0) {
if (!sd_id128_is_null(with_key))
return r;
t = (struct tpm2_credential_header*) ((uint8_t*) output + p);
t->pcr_mask = htole64(tpm2_pcr_mask);
+ t->pcr_bank = htole16(tpm2_pcr_bank);
t->blob_size = htole32(tpm2_blob_size);
t->policy_hash_size = htole32(tpm2_policy_hash_size);
memcpy(t->policy_hash_and_blob, tpm2_blob, tpm2_blob_size);
p += tsz;
assert(p <= output_size);
- if (DEBUG_LOGGING) {
+ if (DEBUG_LOGGING && input_size > 0) {
size_t base64_size;
base64_size = DIV_ROUND_UP(p * 4, 3); /* Include base64 size increase in debug output */
-
+ assert(base64_size >= input_size);
log_debug("Input of %zu bytes grew to output of %zu bytes (+%2zu%%).", input_size, base64_size, base64_size * 100 / input_size - 100);
}
if (le64toh(t->pcr_mask) >= (UINT64_C(1) << TPM2_PCRS_MAX))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR mask out of range.");
+ if (!tpm2_pcr_bank_supported(le16toh(t->pcr_bank)))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR bank invalid or not supported");
+ if (le16toh(t->_zero) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 padding space not zero.");
if (le32toh(t->blob_size) > CREDENTIAL_FIELD_SIZE_MAX)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected TPM2 blob size.");
if (le32toh(t->policy_hash_size) > CREDENTIAL_FIELD_SIZE_MAX)
r = tpm2_unseal(tpm2_device,
le64toh(t->pcr_mask),
+ le16toh(t->pcr_bank),
t->policy_hash_and_blob,
le32toh(t->blob_size),
t->policy_hash_and_blob + le32toh(t->blob_size),
}
/* Get directory creation time (not available everywhere, but that's OK */
- (void) fd_getcrtime(dfd, &crtime);
+ (void) fd_getcrtime(fd, &crtime);
/* If the IMMUTABLE bit is set, we consider the directory read-only. Since the ioctl is not
* supported everywhere we ignore failures. */
_META_MAX,
};
- static const char *paths[_META_MAX] = {
+ static const char *const paths[_META_MAX] = {
[META_HOSTNAME] = "/etc/hostname\0",
[META_MACHINE_ID] = "/etc/machine-id\0",
[META_MACHINE_INFO] = "/etc/machine-info\0",
- [META_OS_RELEASE] = "/etc/os-release\0"
- "/usr/lib/os-release\0",
- [META_EXTENSION_RELEASE] = NULL,
+ [META_OS_RELEASE] = ("/etc/os-release\0"
+ "/usr/lib/os-release\0"),
+ [META_EXTENSION_RELEASE] = "extension-release\0", /* Used only for logging. */
};
_cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **extension_release = NULL;
assert(m);
- /* As per the os-release spec, if the image is an extension it will have a file
- * named after the image name in extension-release.d/ */
- if (m->image_name) {
- char *ext;
-
- ext = strjoina("/usr/lib/extension-release.d/extension-release.", m->image_name, "0");
- ext[strlen(ext) - 1] = '\0'; /* Extra \0 for NULSTR_FOREACH using placeholder from above */
- paths[META_EXTENSION_RELEASE] = ext;
- } else
- log_debug("No image name available, will skip extension-release metadata");
-
for (; n_meta_initialized < _META_MAX; n_meta_initialized ++) {
if (!paths[n_meta_initialized]) {
fds[2*n_meta_initialized] = fds[2*n_meta_initialized+1] = -1;
fds[2*k] = safe_close(fds[2*k]);
- NULSTR_FOREACH(p, paths[k]) {
- fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
- if (fd >= 0)
- break;
- }
+ if (k == META_EXTENSION_RELEASE) {
+ /* As per the os-release spec, if the image is an extension it will have a file
+ * named after the image name in extension-release.d/ - we use the image name
+ * and try to resolve it with the extension-release helpers, as sometimes
+ * the image names are mangled on deployment and do not match anymore.
+ * Unlike other paths this is not fixed, and the image name
+ * can be mangled on deployment, so by calling into the helper
+ * we allow a fallback that matches on the first extension-release
+ * file found in the directory, if one named after the image cannot
+ * be found first. */
+ r = open_extension_release(t, m->image_name, NULL, &fd);
+ if (r < 0)
+ fd = r; /* Propagate the error. */
+ } else
+ NULSTR_FOREACH(p, paths[k]) {
+ fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
+ if (fd >= 0)
+ break;
+ }
if (fd < 0) {
log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
fds[2*k+1] = safe_close(fds[2*k+1]);
#include "memory-util.h"
#include "socket-util.h"
#include "string-table.h"
+#include "strv.h"
#include "strxcpyx.h"
static const char* const duplex_table[_DUP_MAX] = {
DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
- [NET_DEV_FEAT_RX] = "rx-checksum",
- [NET_DEV_FEAT_TX] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
- [NET_DEV_FEAT_GSO] = "tx-generic-segmentation",
- [NET_DEV_FEAT_GRO] = "rx-gro",
- [NET_DEV_FEAT_LRO] = "rx-lro",
- [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation",
- [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation",
+ [NET_DEV_FEAT_RX] = "rx-checksum",
+ [NET_DEV_FEAT_TX] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
+ [NET_DEV_FEAT_GSO] = "tx-generic-segmentation",
+ [NET_DEV_FEAT_GRO] = "rx-gro",
+ [NET_DEV_FEAT_GRO_HW] = "rx-gro-hw",
+ [NET_DEV_FEAT_LRO] = "rx-lro",
+ [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation",
+ [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation",
};
static const char* const ethtool_link_mode_bit_table[] = {
dest = _v; \
} while(false)
+#define UPDATE_WITH_MAX(dest, max, val, updated) \
+ do { \
+ typeof(dest) _v = (val); \
+ typeof(dest) _max = (max); \
+ if (_v == 0 || _v > _max) \
+ _v = _max; \
+ if (dest != _v) \
+ updated = true; \
+ dest = _v; \
+ } while(false)
+
int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
struct ethtool_wolinfo ecmd = {
.cmd = ETHTOOL_GWOL,
assert(ifname);
assert(ring);
- if (!ring->rx_pending_set &&
- !ring->rx_mini_pending_set &&
- !ring->rx_jumbo_pending_set &&
- !ring->tx_pending_set)
+ if (!ring->rx.set &&
+ !ring->rx_mini.set &&
+ !ring->rx_jumbo.set &&
+ !ring->tx.set)
return 0;
r = ethtool_connect(ethtool_fd);
if (r < 0)
return -errno;
- if (ring->rx_pending_set)
- UPDATE(ecmd.rx_pending, ring->rx_pending, need_update);
+ if (ring->rx.set)
+ UPDATE_WITH_MAX(ecmd.rx_pending, ecmd.rx_max_pending, ring->rx.value, need_update);
- if (ring->rx_mini_pending_set)
- UPDATE(ecmd.rx_mini_pending, ring->rx_mini_pending, need_update);
+ if (ring->rx_mini.set)
+ UPDATE_WITH_MAX(ecmd.rx_mini_pending, ecmd.rx_mini_max_pending, ring->rx_mini.value, need_update);
- if (ring->rx_jumbo_pending_set)
- UPDATE(ecmd.rx_jumbo_pending, ring->rx_jumbo_pending, need_update);
+ if (ring->rx_jumbo.set)
+ UPDATE_WITH_MAX(ecmd.rx_jumbo_pending, ecmd.rx_jumbo_max_pending, ring->rx_jumbo.value, need_update);
- if (ring->tx_pending_set)
- UPDATE(ecmd.tx_pending, ring->tx_pending, need_update);
+ if (ring->tx.set)
+ UPDATE_WITH_MAX(ecmd.tx_pending, ecmd.tx_max_pending, ring->tx.value, need_update);
if (!need_update)
return 0;
return found ? 0 : -ENODATA;
}
-int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features) {
+int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) {
_cleanup_free_ struct ethtool_gstrings *strings = NULL;
struct ethtool_sfeatures *sfeatures;
struct ifreq ifr = {};
- int i, r;
+ bool have = false;
+ int r;
assert(ethtool_fd);
assert(ifname);
assert(features);
+ for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
+ if (features[i] >= 0) {
+ have = true;
+ break;
+ }
+
+ if (!have)
+ return 0;
+
r = ethtool_connect(ethtool_fd);
if (r < 0)
return r;
sfeatures->cmd = ETHTOOL_SFEATURES;
sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
- for (i = 0; i < _NET_DEV_FEAT_MAX; i++)
- if (features[i] != -1) {
+ for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
+ if (features[i] >= 0) {
r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
if (r < 0) {
log_debug_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
assert(ifname);
assert(channels);
- if (!channels->rx_count_set &&
- !channels->tx_count_set &&
- !channels->other_count_set &&
- !channels->combined_count_set)
+ if (!channels->rx.set &&
+ !channels->tx.set &&
+ !channels->other.set &&
+ !channels->combined.set)
return 0;
r = ethtool_connect(fd);
if (r < 0)
return -errno;
- if (channels->rx_count_set)
- UPDATE(ecmd.rx_count, channels->rx_count, need_update);
+ if (channels->rx.set)
+ UPDATE_WITH_MAX(ecmd.rx_count, ecmd.max_rx, channels->rx.value, need_update);
- if (channels->tx_count_set)
- UPDATE(ecmd.tx_count, channels->tx_count, need_update);
+ if (channels->tx.set)
+ UPDATE_WITH_MAX(ecmd.tx_count, ecmd.max_tx, channels->tx.value, need_update);
- if (channels->other_count_set)
- UPDATE(ecmd.other_count, channels->other_count, need_update);
+ if (channels->other.set)
+ UPDATE_WITH_MAX(ecmd.other_count, ecmd.max_other, channels->other.value, need_update);
- if (channels->combined_count_set)
- UPDATE(ecmd.combined_count, channels->combined_count, need_update);
+ if (channels->combined.set)
+ UPDATE_WITH_MAX(ecmd.combined_count, ecmd.max_combined, channels->combined.value, need_update);
if (!need_update)
return 0;
return 0;
}
-int config_parse_channel(
- 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) {
-
- netdev_channels *channels = data;
- uint32_t k;
- int r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = safe_atou32(rvalue, &k);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse channel value for %s=, ignoring: %s", lvalue, rvalue);
- return 0;
- }
- if (k < 1) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Invalid %s= value, ignoring: %s", lvalue, rvalue);
- return 0;
- }
-
- if (streq(lvalue, "RxChannels")) {
- channels->rx_count = k;
- channels->rx_count_set = true;
- } else if (streq(lvalue, "TxChannels")) {
- channels->tx_count = k;
- channels->tx_count_set = true;
- } else if (streq(lvalue, "OtherChannels")) {
- channels->other_count = k;
- channels->other_count_set = true;
- } else if (streq(lvalue, "CombinedChannels")) {
- channels->combined_count = k;
- channels->combined_count_set = true;
- }
-
- return 0;
-}
-
int config_parse_advertise(
const char *unit,
const char *filename,
}
}
-int config_parse_nic_buffer_size(
+int config_parse_ring_buffer_or_channel(
const char *unit,
const char *filename,
unsigned line,
void *data,
void *userdata) {
- netdev_ring_param *ring = data;
+ u32_opt *dst = data;
uint32_t k;
int r;
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ dst->value = 0;
+ dst->set = false;
+ return 0;
+ }
+
+ if (streq(rvalue, "max")) {
+ dst->value = 0;
+ dst->set = true;
+ return 0;
+ }
+
r = safe_atou32(rvalue, &k);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse interface buffer value, ignoring: %s", rvalue);
+ "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
return 0;
}
if (k < 1) {
return 0;
}
- if (streq(lvalue, "RxBufferSize")) {
- ring->rx_pending = k;
- ring->rx_pending_set = true;
- } else if (streq(lvalue, "RxMiniBufferSize")) {
- ring->rx_mini_pending = k;
- ring->rx_mini_pending_set = true;
- } else if (streq(lvalue, "RxJumboBufferSize")) {
- ring->rx_jumbo_pending = k;
- ring->rx_jumbo_pending_set = true;
- } else if (streq(lvalue, "TxBufferSize")) {
- ring->tx_pending = k;
- ring->tx_pending_set = true;
- }
-
+ dst->value = k;
+ dst->set = true;
return 0;
}
return 0;
}
+
+int config_parse_coalesce_u32(
+ 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) {
+ u32_opt *dst = data;
+ uint32_t k;
+ int r;
+
+ if (isempty(rvalue)) {
+ dst->value = 0;
+ dst->set = false;
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ dst->value = k;
+ dst->set = true;
+ return 0;
+}
+
+int config_parse_coalesce_sec(
+ 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) {
+ u32_opt *dst = data;
+ usec_t usec;
+ int r;
+
+ if (isempty(rvalue)) {
+ dst->value = 0;
+ dst->set = false;
+ return 0;
+ }
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse coalesce setting value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (usec > UINT32_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Too large %s= value, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid %s= value, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ dst->value = (uint32_t) usec;
+ dst->set = true;
+
+ return 0;
+}
+
+int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce) {
+ struct ethtool_coalesce ecmd = {
+ .cmd = ETHTOOL_GCOALESCE,
+ };
+ struct ifreq ifr = {
+ .ifr_data = (void*) &ecmd,
+ };
+ bool need_update = false;
+ int r;
+
+ assert(ethtool_fd);
+ assert(ifname);
+ assert(coalesce);
+
+ if (coalesce->use_adaptive_rx_coalesce < 0 &&
+ coalesce->use_adaptive_tx_coalesce < 0 &&
+ !coalesce->rx_coalesce_usecs.set &&
+ !coalesce->rx_max_coalesced_frames.set &&
+ !coalesce->rx_coalesce_usecs_irq.set &&
+ !coalesce->rx_max_coalesced_frames_irq.set &&
+ !coalesce->tx_coalesce_usecs.set &&
+ !coalesce->tx_max_coalesced_frames.set &&
+ !coalesce->tx_coalesce_usecs_irq.set &&
+ !coalesce->tx_max_coalesced_frames_irq.set &&
+ !coalesce->stats_block_coalesce_usecs.set &&
+ !coalesce->pkt_rate_low.set &&
+ !coalesce->rx_coalesce_usecs_low.set &&
+ !coalesce->rx_max_coalesced_frames_low.set &&
+ !coalesce->tx_coalesce_usecs_low.set &&
+ !coalesce->tx_max_coalesced_frames_low.set &&
+ !coalesce->pkt_rate_high.set &&
+ !coalesce->rx_coalesce_usecs_high.set &&
+ !coalesce->rx_max_coalesced_frames_high.set &&
+ !coalesce->tx_coalesce_usecs_high.set &&
+ !coalesce->tx_max_coalesced_frames_high.set &&
+ !coalesce->rate_sample_interval.set)
+ return 0;
+
+ r = ethtool_connect(ethtool_fd);
+ if (r < 0)
+ return r;
+
+ strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+
+ r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
+ if (r < 0)
+ return -errno;
+
+ if (coalesce->use_adaptive_rx_coalesce >= 0)
+ UPDATE(ecmd.use_adaptive_rx_coalesce, (uint32_t) coalesce->use_adaptive_rx_coalesce, need_update);
+
+ if (coalesce->use_adaptive_tx_coalesce >= 0)
+ UPDATE(ecmd.use_adaptive_tx_coalesce, (uint32_t) coalesce->use_adaptive_tx_coalesce, need_update);
+
+ if (coalesce->rx_coalesce_usecs.set)
+ UPDATE(ecmd.rx_coalesce_usecs, coalesce->rx_coalesce_usecs.value, need_update);
+
+ if (coalesce->rx_max_coalesced_frames.set)
+ UPDATE(ecmd.rx_max_coalesced_frames, coalesce->rx_max_coalesced_frames.value, need_update);
+
+ if (coalesce->rx_coalesce_usecs_irq.set)
+ UPDATE(ecmd.rx_coalesce_usecs_irq, coalesce->rx_coalesce_usecs_irq.value, need_update);
+
+ if (coalesce->rx_max_coalesced_frames_irq.set)
+ UPDATE(ecmd.rx_max_coalesced_frames_irq, coalesce->rx_max_coalesced_frames_irq.value, need_update);
+
+ if (coalesce->tx_coalesce_usecs.set)
+ UPDATE(ecmd.tx_coalesce_usecs, coalesce->tx_coalesce_usecs.value, need_update);
+
+ if (coalesce->tx_max_coalesced_frames.set)
+ UPDATE(ecmd.tx_max_coalesced_frames, coalesce->tx_max_coalesced_frames.value, need_update);
+
+ if (coalesce->tx_coalesce_usecs_irq.set)
+ UPDATE(ecmd.tx_coalesce_usecs_irq, coalesce->tx_coalesce_usecs_irq.value, need_update);
+
+ if (coalesce->tx_max_coalesced_frames_irq.set)
+ UPDATE(ecmd.tx_max_coalesced_frames_irq, coalesce->tx_max_coalesced_frames_irq.value, need_update);
+
+ if (coalesce->stats_block_coalesce_usecs.set)
+ UPDATE(ecmd.stats_block_coalesce_usecs, coalesce->stats_block_coalesce_usecs.value, need_update);
+
+ if (coalesce->pkt_rate_low.set)
+ UPDATE(ecmd.pkt_rate_low, coalesce->pkt_rate_low.value, need_update);
+
+ if (coalesce->rx_coalesce_usecs_low.set)
+ UPDATE(ecmd.rx_coalesce_usecs_low, coalesce->rx_coalesce_usecs_low.value, need_update);
+
+ if (coalesce->rx_max_coalesced_frames_low.set)
+ UPDATE(ecmd.rx_max_coalesced_frames_low, coalesce->rx_max_coalesced_frames_low.value, need_update);
+
+ if (coalesce->tx_coalesce_usecs_low.set)
+ UPDATE(ecmd.tx_coalesce_usecs_low, coalesce->tx_coalesce_usecs_low.value, need_update);
+
+ if (coalesce->tx_max_coalesced_frames_low.set)
+ UPDATE(ecmd.tx_max_coalesced_frames_low, coalesce->tx_max_coalesced_frames_low.value, need_update);
+
+ if (coalesce->pkt_rate_high.set)
+ UPDATE(ecmd.pkt_rate_high, coalesce->pkt_rate_high.value, need_update);
+
+ if (coalesce->rx_coalesce_usecs_high.set)
+ UPDATE(ecmd.rx_coalesce_usecs_high, coalesce->rx_coalesce_usecs_high.value, need_update);
+
+ if (coalesce->rx_max_coalesced_frames_high.set)
+ UPDATE(ecmd.rx_max_coalesced_frames_high, coalesce->rx_max_coalesced_frames_high.value, need_update);
+
+ if (coalesce->tx_coalesce_usecs_high.set)
+ UPDATE(ecmd.tx_coalesce_usecs_high, coalesce->tx_coalesce_usecs_high.value, need_update);
+
+ if (coalesce->tx_max_coalesced_frames_high.set)
+ UPDATE(ecmd.tx_max_coalesced_frames_high, coalesce->tx_max_coalesced_frames_high.value, need_update);
+
+ if (coalesce->rate_sample_interval.set)
+ UPDATE(ecmd.rate_sample_interval, DIV_ROUND_UP(coalesce->rate_sample_interval.value, USEC_PER_SEC), need_update);
+
+ if (!need_update)
+ return 0;
+
+ ecmd.cmd = ETHTOOL_SCOALESCE;
+ r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
NET_DEV_FEAT_TX,
NET_DEV_FEAT_GSO,
NET_DEV_FEAT_GRO,
+ NET_DEV_FEAT_GRO_HW,
NET_DEV_FEAT_LRO,
NET_DEV_FEAT_TSO,
NET_DEV_FEAT_TSO6,
} link_modes;
};
+typedef struct u32_opt {
+ uint32_t value; /* a value of 0 indicates the hardware advertised maximum should be used.*/
+ bool set;
+} u32_opt;
+
typedef struct netdev_channels {
- uint32_t rx_count;
- uint32_t tx_count;
- uint32_t other_count;
- uint32_t combined_count;
-
- bool rx_count_set;
- bool tx_count_set;
- bool other_count_set;
- bool combined_count_set;
+ u32_opt rx;
+ u32_opt tx;
+ u32_opt other;
+ u32_opt combined;
} netdev_channels;
typedef struct netdev_ring_param {
- uint32_t rx_pending;
- uint32_t rx_mini_pending;
- uint32_t rx_jumbo_pending;
- uint32_t tx_pending;
-
- bool rx_pending_set;
- bool rx_mini_pending_set;
- bool rx_jumbo_pending_set;
- bool tx_pending_set;
+ u32_opt rx;
+ u32_opt rx_mini;
+ u32_opt rx_jumbo;
+ u32_opt tx;
} netdev_ring_param;
+typedef struct netdev_coalesce_param {
+ u32_opt rx_coalesce_usecs;
+ u32_opt rx_max_coalesced_frames;
+ u32_opt rx_coalesce_usecs_irq;
+ u32_opt rx_max_coalesced_frames_irq;
+ u32_opt tx_coalesce_usecs;
+ u32_opt tx_max_coalesced_frames;
+ u32_opt tx_coalesce_usecs_irq;
+ u32_opt tx_max_coalesced_frames_irq;
+ u32_opt stats_block_coalesce_usecs;
+ int use_adaptive_rx_coalesce;
+ int use_adaptive_tx_coalesce;
+ u32_opt pkt_rate_low;
+ u32_opt rx_coalesce_usecs_low;
+ u32_opt rx_max_coalesced_frames_low;
+ u32_opt tx_coalesce_usecs_low;
+ u32_opt tx_max_coalesced_frames_low;
+ u32_opt pkt_rate_high;
+ u32_opt rx_coalesce_usecs_high;
+ u32_opt rx_max_coalesced_frames_high;
+ u32_opt tx_coalesce_usecs_high;
+ u32_opt tx_max_coalesced_frames_high;
+ u32_opt rate_sample_interval;
+} netdev_coalesce_param;
+
int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret);
int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
int *ret_autonegotiation, uint64_t *ret_speed,
int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret);
int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts);
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring);
-int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features);
+int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]);
int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
int autonegotiation, const uint32_t advertise[static N_ADVERTISE],
uint64_t speed, Duplex duplex, NetDevPort port);
int ethtool_set_channels(int *ethtool_fd, const char *ifname, const netdev_channels *channels);
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg);
+int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce);
const char *duplex_to_string(Duplex d) _const_;
Duplex duplex_from_string(const char *d) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_duplex);
CONFIG_PARSER_PROTOTYPE(config_parse_wol);
CONFIG_PARSER_PROTOTYPE(config_parse_port);
-CONFIG_PARSER_PROTOTYPE(config_parse_channel);
CONFIG_PARSER_PROTOTYPE(config_parse_advertise);
-CONFIG_PARSER_PROTOTYPE(config_parse_nic_buffer_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_ring_buffer_or_channel);
+CONFIG_PARSER_PROTOTYPE(config_parse_coalesce_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_coalesce_sec);
+CONFIG_PARSER_PROTOTYPE(config_parse_nic_coalesce_setting);
return sizeof(mode_t);
default:
- assert_not_reached("Uh? Unexpected cell type");
+ assert_not_reached();
}
}
return 0;
default:
- assert_not_reached("Uh? Unexpected data type.");
+ assert_not_reached();
}
r = table_add_cell(t, &last_cell, type, data);
}
default:
- assert_not_reached("Unexpected type?");
+ assert_not_reached();
}
return d->formatted;
case TABLE_IN6_ADDR:
return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET6));
- case TABLE_ID128: {
- char buf[SD_ID128_STRING_MAX];
- return json_variant_new_string(ret, sd_id128_to_string(d->id128, buf));
- }
+ case TABLE_ID128:
+ return json_variant_new_string(ret, SD_ID128_TO_STRING(d->id128));
- case TABLE_UUID: {
- char buf[ID128_UUID_STRING_MAX];
- return json_variant_new_string(ret, id128_to_uuid_string(d->id128, buf));
- }
+ case TABLE_UUID:
+ return json_variant_new_string(ret, ID128_TO_UUID_STRING(d->id128));
case TABLE_UID:
if (!uid_is_valid(d->uid))
{},
};
- char smid[SD_ID128_STRING_MAX];
JsonVariant *m;
sd_id128_t mid;
int r;
if (r < 0)
return json_log(variant, flags, r, "Failed to determine machine ID: %m");
- m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+ m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
if (!m)
return 0;
{},
};
- char smid[SD_ID128_STRING_MAX];
JsonVariant *m;
sd_id128_t mid;
int r;
if (r < 0)
return json_log(variant, flags, r, "Failed to determine machine ID: %m");
- m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+ m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
if (!m)
return 0;
#include "util.h"
static int sethostname_idempotent_full(const char *s, bool really) {
- char buf[HOST_NAME_MAX + 1] = {};
+ _cleanup_free_ char *buf = NULL;
+ int r;
assert(s);
- if (gethostname(buf, sizeof(buf) - 1) < 0)
- return -errno;
+ r = gethostname_full(GET_HOSTNAME_ALLOW_NONE | GET_HOSTNAME_ALLOW_LOCALHOST, &buf);
+ if (r < 0)
+ return r;
if (streq(buf, s))
return 0;
return sethostname_idempotent_full(s, true);
}
-bool get_hostname_filtered(char ret[static HOST_NAME_MAX + 1]) {
- char buf[HOST_NAME_MAX + 1] = {};
-
- /* Returns true if we got a good hostname, false otherwise. */
-
- if (gethostname(buf, sizeof(buf) - 1) < 0)
- return false; /* This can realistically only fail with ENAMETOOLONG.
- * Let's treat that case the same as an invalid hostname. */
-
- if (isempty(buf))
- return false;
-
- /* This is the built-in kernel default hostname */
- if (streq(buf, "(none)"))
- return false;
-
- memcpy(ret, buf, sizeof buf);
- return true;
-}
-
int shorten_overlong(const char *s, char **ret) {
char *h, *p;
}
if (!hn) {
+ _cleanup_free_ char *buf = NULL;
+
/* Don't override the hostname if it is already set and not explicitly configured */
- char buf[HOST_NAME_MAX + 1] = {};
- if (get_hostname_filtered(buf)) {
+ r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &buf);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r >= 0) {
log_debug("No hostname configured, leaving existing hostname <%s> in place.", buf);
return 0;
}
int read_etc_hostname_stream(FILE *f, char **ret);
int read_etc_hostname(const char *path, char **ret);
-bool get_hostname_filtered(char ret[static HOST_NAME_MAX + 1]);
void hostname_update_source_hint(const char *hostname, HostnameSource source);
int hostname_setup(bool really);
#include "string-table.h"
#include "string-util.h"
-int import_url_last_component(const char *url, char **ret) {
- const char *e, *p;
- char *s;
+static const char *skip_protocol_and_hostname(const char *url) {
+ const char *d;
+ size_t n;
+
+ /* A very very lenient implementation of RFC3986 Section 3.2 */
+
+ /* Find colon separating protocol and hostname */
+ d = strchr(url, ':');
+ if (!d || url == d)
+ return NULL;
+ d++;
+
+ /* Skip slashes after colon */
+ d += strspn(d, "/");
+
+ /* Skip everything till next slash or end */
+ n = strcspn(d, "/?#");
+ if (n == 0)
+ return NULL;
+
+ return d + n;
+}
- e = strchrnul(url, '?');
+int import_url_last_component(
+ const char *url,
+ char **ret) {
- while (e > url && e[-1] == '/')
+ const char *e, *p, *h;
+
+ /* This extracts the last path component of the specified URI, i.e. the last non-empty substrings
+ * between two "/" characters. This ignores "Query" and "Fragment" suffixes (as per RFC3986). */
+
+ h = skip_protocol_and_hostname(url);
+ if (!h)
+ return -EINVAL;
+
+ e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
+
+ while (e > h && e[-1] == '/') /* Eat trailing slashes */
e--;
p = e;
- while (p > url && p[-1] != '/')
+ while (p > h && p[-1] != '/') /* Find component before that */
p--;
- if (e <= p)
- return -EINVAL;
+ if (e <= p) /* Empty component? */
+ return -EADDRNOTAVAIL;
- s = strndup(p, e - p);
- if (!s)
- return -ENOMEM;
+ if (ret) {
+ char *s;
+
+ s = strndup(p, e - p);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ }
- *ret = s;
return 0;
}
-int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
- const char *e;
+int import_url_change_suffix(
+ const char *url,
+ size_t n_drop_components,
+ const char *suffix,
+ char **ret) {
+
+ const char *e, *h;
char *s;
assert(url);
assert(ret);
- e = strchrnul(url, '?');
+ /* This drops the specified number of path components of the specified URI, i.e. the specified number
+ * of non-empty substring between two "/" characters from the end of the string, and then append the
+ * specified suffix instead. Before doing all this it chops off the "Query" and "Fragment" suffixes
+ * (they are *not* readded to the final URL). Note that n_drop_components may be 0 (in which case the
+ * component are simply added to the end). The suffix may be specified as NULL or empty string in
+ * which case nothing is appended, only the specified number of components chopped off. Note that the
+ * function may be called with n_drop_components == 0 and suffix == NULL, in which case the "Query"
+ * and "Fragment" is chopped off, and ensured the URL ends in a single "/", and that's it. */
+
+ h = skip_protocol_and_hostname(url);
+ if (!h)
+ return -EINVAL;
- while (e > url && e[-1] == '/')
- e--;
+ e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
- while (e > url && e[-1] != '/')
+ while (e > h && e[-1] == '/') /* Eat trailing slashes */
e--;
- if (e <= url)
- return -EINVAL;
+ /* Drop the specified number of components from the end. Note that this is pretty lenient: if there
+ * are less component we silently drop those and then append the suffix to the top. */
+ while (n_drop_components > 0) {
+ while (e > h && e[-1] != '/') /* Eat last word (we don't mind if empty) */
+ e--;
+
+ while (e > h && e[-1] == '/') /* Eat slashes before the last word */
+ e--;
+
+ n_drop_components--;
+ }
- s = new(char, (e - url) + strlen(suffix) + 1);
+ s = new(char, (e - url) + 1 + strlen_ptr(suffix) + 1);
if (!s)
return -ENOMEM;
- strcpy(mempcpy(s, url, e - url), suffix);
+ strcpy(stpcpy(mempcpy(s, url, e - url), "/"), strempty(suffix));
*ret = s;
return 0;
}
} ImportVerify;
int import_url_last_component(const char *url, char **ret);
-int import_url_change_last_component(const char *url, const char *suffix, char **ret);
+
+int import_url_change_suffix(const char *url, size_t n_drop_components, const char *suffix, char **ret);
+
+static inline int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
+ return import_url_change_suffix(url, 1, suffix, ret);
+}
+
+static inline int import_url_append_component(const char *url, const char *suffix, char **ret) {
+ return import_url_change_suffix(url, 0, suffix, ret);
+}
const char* import_verify_to_string(ImportVerify v) _const_;
ImportVerify import_verify_from_string(const char *s) _pure_;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/ioctl.h>
+
+#include "btrfs-util.h"
+#include "chattr-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "install-file.h"
+#include "missing_syscall.h"
+#include "rm-rf.h"
+
+int fs_make_very_read_only(int fd) {
+ struct stat st;
+ int r;
+
+ assert(fd >= 0);
+
+ /* Tries to make the specified fd "comprehensively" read-only. Primary usecase for this is OS images,
+ * i.e. either loopback files or larger directory hierarchies. Depending on the inode type and
+ * backing file system this means something different:
+ *
+ * 1. If the fd refers to a btrfs subvolume we'll mark it read-only as a whole
+ * 2. If the fd refers to any other directory we'll set the FS_IMMUTABLE_FL flag on it
+ * 3. If the fd refers to a regular file we'll drop the w bits.
+ * 4. If the fd refers to a block device, use BLKROSET to set read-only state
+ *
+ * You might wonder why not drop the x bits for directories. That's because we want to guarantee that
+ * everything "inside" the image remains largely the way it is, in case you mount it. And since the
+ * mode of the root dir of the image is pretty visible we don't want to modify it. btrfs subvol flags
+ * and the FS_IMMUTABLE_FL otoh are much less visible. Changing the mode of regular files should be
+ * OK though, since after all this is supposed to be used for disk images, i.e. the fs in the disk
+ * image doesn't make the mode of the loopback file it is stored in visible. */
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ switch (st.st_mode & S_IFMT) {
+
+ case S_IFDIR:
+ if (btrfs_might_be_subvol(&st)) {
+ r = btrfs_subvol_set_read_only_fd(fd, true);
+ if (r >= 0)
+ return 0;
+
+ if (!ERRNO_IS_NOT_SUPPORTED(r) && r != -EINVAL)
+ return r;
+ }
+
+ r = chattr_fd(fd, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case S_IFREG:
+ if ((st.st_mode & 0222) != 0)
+ if (fchmod(fd, st.st_mode & 07555) < 0)
+ return -errno;
+
+ break;
+
+ case S_IFBLK: {
+ int ro = 1;
+
+ if (ioctl(fd, BLKROSET, &ro) < 0)
+ return -errno;
+
+ break;
+ }
+
+ default:
+ return -EBADFD;
+ }
+
+ return 0;
+}
+
+static int unlinkat_maybe_dir(int dirfd, const char *pathname) {
+
+ /* Invokes unlinkat() for regular files first, and if this fails with EISDIR tries again with
+ * AT_REMOVEDIR */
+
+ if (unlinkat(dirfd, pathname, 0) < 0) {
+ if (errno != EISDIR)
+ return -errno;
+
+ if (unlinkat(dirfd, pathname, AT_REMOVEDIR) < 0)
+ return -errno;
+ }
+
+ return 0;
+}
+
+int install_file(int source_atfd, const char *source_name,
+ int target_atfd, const char *target_name,
+ InstallFileFlags flags) {
+
+ _cleanup_close_ int rofd = -1;
+ int r;
+
+ /* Moves a file or directory tree into place, with some bells and whistles:
+ *
+ * 1. Optionally syncs before/after to ensure file installation can be used as barrier
+ * 2. Optionally marks the file/directory read-only using fs_make_very_read_only()
+ * 3. Optionally operates in replacing or in non-replacing mode.
+ * 4. If it replaces will remove the old tree if needed.
+ */
+
+ assert(source_atfd >= 0 || source_atfd == AT_FDCWD);
+ assert(source_name);
+ assert(target_atfd >= 0 || target_atfd == AT_FDCWD);
+
+ /* If target_name is specified as NULL no renaming takes place. Instead it is assumed the file is
+ * already in place, and only the syncing/read-only marking shall be applied. Note that with
+ * target_name=NULL and flags=0 this call is a NOP */
+
+ if ((flags & (INSTALL_FSYNC|INSTALL_FSYNC_FULL|INSTALL_SYNCFS|INSTALL_READ_ONLY)) != 0) {
+ _cleanup_close_ int pfd = -1;
+ struct stat st;
+
+ /* Open an O_PATH fd for the source if we need to sync things or mark things read only. */
+
+ pfd = openat(source_atfd, source_name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (pfd < 0)
+ return -errno;
+
+ if (fstat(pfd, &st) < 0)
+ return -errno;
+
+ switch (st.st_mode & S_IFMT) {
+
+ case S_IFREG: {
+ _cleanup_close_ int regfd = -1;
+
+ regfd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
+ if (regfd < 0)
+ return regfd;
+
+ if ((flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0) {
+ /* If this is just a regular file (as oppose to a fully populated directory)
+ * let's downgrade INSTALL_SYNCFS to INSTALL_FSYNC_FULL, after all this is
+ * going to be a single inode we install */
+ r = fsync_full(regfd);
+ if (r < 0)
+ return r;
+ } else if (flags & INSTALL_FSYNC) {
+ if (fsync(regfd) < 0)
+ return -errno;
+ }
+
+ if (flags & INSTALL_READ_ONLY)
+ rofd = TAKE_FD(regfd);
+
+ break;
+ }
+
+ case S_IFDIR: {
+ _cleanup_close_ int dfd = -1;
+
+ dfd = fd_reopen(pfd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+ if (dfd < 0)
+ return dfd;
+
+ if (flags & INSTALL_SYNCFS) {
+ if (syncfs(dfd) < 0)
+ return -errno;
+ } else if (flags & INSTALL_FSYNC_FULL) {
+ r = fsync_full(dfd);
+ if (r < 0)
+ return r;
+ } else if (flags & INSTALL_FSYNC) {
+ if (fsync(dfd) < 0)
+ return -errno;
+ }
+
+ if (flags & INSTALL_READ_ONLY)
+ rofd = TAKE_FD(dfd);
+
+ break;
+ }
+
+ default:
+ /* Other inodes: char/block device inodes, fifos, symlinks, sockets don't need
+ * syncing themselves, as they only exist in the directory, and have no contents on
+ * disk */
+
+ if (target_name && (flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0) {
+ r = fsync_directory_of_file(pfd);
+ if (r < 0)
+ return r;
+ }
+
+ break;
+ }
+ }
+
+ if (target_name) {
+ /* Rename the file */
+
+ if (flags & INSTALL_REPLACE) {
+ /* First, try a simple renamat(), maybe that's enough */
+ if (renameat(source_atfd, source_name, target_atfd, target_name) < 0) {
+ _cleanup_close_ int dfd = -1;
+
+ if (!IN_SET(errno, EEXIST, ENOTDIR, ENOTEMPTY, EISDIR, EBUSY))
+ return -errno;
+
+ /* Hmm, the target apparently existed already. Let's try to use
+ * RENAME_EXCHANGE. But let's first open the inode if it's a directory, so
+ * that we can later remove its contents if it's a directory. Why do this
+ * before the rename()? Mostly because if we have trouble opening the thing
+ * we want to know before we start actually modifying the file system. */
+
+ dfd = openat(target_atfd, target_name, O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
+ if (dfd < 0 && errno != ENOTDIR)
+ return -errno;
+
+ if (renameat2(source_atfd, source_name, target_atfd, target_name, RENAME_EXCHANGE) < 0) {
+
+ if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL)
+ return -errno;
+
+ /* The exchange didn't work, let's remove the target first, and try again */
+
+ if (dfd >= 0)
+ (void) rm_rf_children(TAKE_FD(dfd), REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD, NULL);
+
+ r = unlinkat_maybe_dir(target_atfd, target_name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to remove target directory: %m");
+
+ if (renameat(source_atfd, source_name, target_atfd, target_name) < 0)
+ return -errno;
+ } else {
+ /* The exchange worked, hence let's remove the source (i.e. the old target) */
+ if (dfd >= 0)
+ (void) rm_rf_children(TAKE_FD(dfd), REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD, NULL);
+
+ r = unlinkat_maybe_dir(source_atfd, source_name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to remove replaced target directory: %m");
+ }
+ }
+ } else {
+ r = rename_noreplace(source_atfd, source_name, target_atfd, target_name);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (rofd >= 0) {
+ r = fs_make_very_read_only(rofd);
+ if (r < 0)
+ return r;
+ }
+
+ if ((flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0) {
+ if (target_name)
+ r = fsync_parent_at(target_atfd, target_name);
+ else
+ r = fsync_parent_at(source_atfd, source_name);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int fs_make_very_read_only(int fd);
+
+typedef enum InstallFileFlags {
+ INSTALL_REPLACE = 1 << 0, /* Replace an existing inode */
+ INSTALL_READ_ONLY = 1 << 1, /* Call fs_make_very_read_only() to make the inode comprehensively read-only */
+ INSTALL_FSYNC = 1 << 2, /* fsync() file contents before moving file in */
+ INSTALL_FSYNC_FULL = 1 << 3, /* like INSTALL_FSYNC, but also fsync() parent dir before+after moving file in */
+ INSTALL_SYNCFS = 1 << 4, /* syncfs() before moving file in, fsync() parent dir after moving file in */
+} InstallFileFlags;
+
+int install_file(int source_atfd, const char *source_name, int target_atfd, const char *target_name, InstallFileFlags flags);
break;
default:
- assert_not_reached("Unexpected unit file type.");
+ assert_not_reached();
}
*ret = state;
else if (IN_SET(scope, UNIT_FILE_GLOBAL, UNIT_FILE_USER))
dirs = user_dirs;
else
- assert_not_reached("Invalid unit file scope");
+ assert_not_reached();
return conf_files_list_strv(files, ".preset", root_dir, 0, dirs);
}
log_debug("Preset files say disable %s.", name);
return 0;
default:
- assert_not_reached("invalid preset action");
+ assert_not_reached();
}
}
return 0; /* continue */
default:
- assert_not_reached("wtf?");
+ assert_not_reached();
}
}
}
int json_variant_new_id128(JsonVariant **ret, sd_id128_t id) {
- char s[SD_ID128_STRING_MAX];
-
- return json_variant_new_string(ret, sd_id128_to_string(id, s));
+ return json_variant_new_string(ret, SD_ID128_TO_STRING(id));
}
static void json_variant_set(JsonVariant *a, JsonVariant *b) {
break;
default:
- assert_not_reached("Unexpected variant type");
+ assert_not_reached();
}
}
return offsetof(JsonVariant, value);
default:
- assert_not_reached("unexpected type");
+ assert_not_reached();
}
}
}
default:
- assert_not_reached("Unknown variant type.");
+ assert_not_reached();
}
}
}
default:
- assert_not_reached("Unexpected variant type.");
+ assert_not_reached();
}
return 0;
return -EINVAL;
default:
- assert_not_reached("Unexpected tokenizer state");
+ assert_not_reached();
}
null_return:
break;
default:
- assert_not_reached("Unexpected token");
+ assert_not_reached();
}
if (add) {
break;
default:
- assert_not_reached("Unexpected protocol");
+ assert_not_reached();
}
/* So ideally we'd just use IP_UNICAST_IF here to pass the ifindex info to the kernel before
break;
default:
- assert_not_reached("Unexpected protocol");
+ assert_not_reached();
}
}
break;
default:
- assert_not_reached("Unknown time format");
+ assert_not_reached();
}
}
const Set *output_fields,
const size_t highlight[2]) {
- sd_id128_t boot_id;
- char sid[SD_ID128_STRING_MAX];
- int r;
- usec_t realtime, monotonic;
_cleanup_free_ char *cursor = NULL;
+ usec_t realtime, monotonic;
+ sd_id128_t boot_id;
const void *data;
size_t length;
+ int r;
assert(j);
cursor,
realtime,
monotonic,
- sd_id128_to_string(boot_id, sid));
+ SD_ID128_TO_STRING(boot_id));
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
size_t fieldlen;
import-util.c
import-util.h
initreq.h
+ install-file.c
+ install-file.h
install-printf.c
install-printf.h
install.c
if (r < 0)
return r;
if (r == 0) {
- char suuid[ID128_UUID_STRING_MAX];
-
/* Child */
- id128_to_uuid_string(uuid, suuid);
-
if (streq(fstype, "ext4"))
(void) execlp(mkfs, mkfs,
- "-L", label,
- "-U", suuid,
- "-I", "256",
- "-O", "has_journal",
- "-m", "0",
- "-E", discard ? "lazy_itable_init=1,discard" : "lazy_itable_init=1,nodiscard",
- node, NULL);
+ "-L", label,
+ "-U", ID128_TO_UUID_STRING(uuid),
+ "-I", "256",
+ "-O", "has_journal",
+ "-m", "0",
+ "-E", discard ? "lazy_itable_init=1,discard" : "lazy_itable_init=1,nodiscard",
+ node, NULL);
else if (streq(fstype, "btrfs")) {
if (discard)
- (void) execlp(mkfs, mkfs, "-L", label, "-U", suuid, node, NULL);
+ (void) execlp(mkfs, mkfs, "-L", label, "-U", ID128_TO_UUID_STRING(uuid), node, NULL);
else
- (void) execlp(mkfs, mkfs, "-L", label, "-U", suuid, "--nodiscard", node, NULL);
+ (void) execlp(mkfs, mkfs, "-L", label, "-U", ID128_TO_UUID_STRING(uuid), "--nodiscard", node, NULL);
} else if (streq(fstype, "xfs")) {
const char *j;
- j = strjoina("uuid=", suuid);
+ j = strjoina("uuid=", ID128_TO_UUID_STRING(uuid));
if (discard)
(void) execlp(mkfs, mkfs, "-L", label, "-m", j, "-m", "reflink=1", node, NULL);
else
} else if (streq(fstype, "swap")) {
(void) execlp(mkfs, mkfs,
- "-L", label,
- "-U", suuid,
- node, NULL);
+ "-L", label,
+ "-U", ID128_TO_UUID_STRING(uuid),
+ node, NULL);
} else
/* Generic fallback for all other file systems */
unsigned long mountflags,
const void *data) {
- char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
- xsprintf(path, "/proc/self/fd/%i", target_fd);
- if (mount(source, path, filesystemtype, mountflags, data) < 0) {
+ if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
if (errno != ENOENT)
return -errno;
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
_cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1, pidns_fd = -1, chased_src_fd = -1;
- char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p,
- chased_src[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+ char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
bool mount_slave_created = false, mount_slave_mounted = false,
mount_tmp_created = false, mount_tmp_mounted = false,
mount_outside_created = false, mount_outside_mounted = false;
if (st.st_ino == self_mntns_st.st_ino && st.st_dev == self_mntns_st.st_dev)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to activate bind mount in target, not running in a mount namespace");
- /* One day, when bind mounting /proc/self/fd/n works across
- * namespace boundaries we should rework this logic to make
- * use of it... */
+ /* One day, when bind mounting /proc/self/fd/n works across namespace boundaries we should rework
+ * this logic to make use of it... */
p = strjoina(propagate_path, "/");
r = laccess(p, F_OK);
r = chase_symlinks(src, NULL, CHASE_TRAIL_SLASH, NULL, &chased_src_fd);
if (r < 0)
return log_debug_errno(r, "Failed to resolve source path of %s: %m", src);
- xsprintf(chased_src, "/proc/self/fd/%i", chased_src_fd);
if (fstat(chased_src_fd, &st) < 0)
return log_debug_errno(errno, "Failed to stat() resolved source path %s: %m", src);
mount_tmp_created = true;
if (is_image)
- r = verity_dissect_and_mount(chased_src, mount_tmp, options, NULL, NULL, NULL);
+ r = verity_dissect_and_mount(FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, options, NULL, NULL, NULL);
else
- r = mount_follow_verbose(LOG_DEBUG, chased_src, mount_tmp, NULL, MS_BIND, NULL);
+ r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, NULL, MS_BIND, NULL);
if (r < 0)
goto finish;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <sys/poll.h>
+#include <poll.h>
#include "fd-util.h"
#include "io-util.h"
#include <fcntl.h>
#include "ask-password-api.h"
+#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
#include "format-table.h"
return t;
}
+int pkcs11_token_login_by_pin(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ const CK_TOKEN_INFO *token_info,
+ const char *token_label,
+ const void *pin,
+ size_t pin_size) {
+
+ CK_RV rv;
+
+ assert(m);
+ assert(token_info);
+
+ if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
+ rv = m->C_Login(session, CKU_USER, NULL, 0);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+
+ log_info("Successfully logged into security token '%s' via protected authentication path.", token_label);
+ return 0;
+ }
+
+ if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
+ log_info("No login into security token '%s' required.", token_label);
+ return 0;
+ }
+
+ if (!pin)
+ return -ENOANO;
+
+ rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) pin, pin_size);
+ if (rv == CKR_OK) {
+ log_info("Successfully logged into security token '%s'.", token_label);
+ return 0;
+ }
+
+ if (rv == CKR_PIN_LOCKED)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "PIN has been locked, please reset PIN of security token '%s'.", token_label);
+ if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+
+ log_notice("PIN for token '%s' is incorrect, please try again.", token_label);
+
+ return -ENOLCK;
+}
+
int pkcs11_token_login(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session,
if (uri_result != P11_KIT_URI_OK)
return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
- if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
- rv = m->C_Login(session, CKU_USER, NULL, 0);
- if (rv != CKR_OK)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+ r = pkcs11_token_login_by_pin(m, session, token_info, token_label, /* pin= */ NULL, 0);
+ if (r == 0 && ret_used_pin)
+ *ret_used_pin = NULL;
- log_info("Successfully logged into security token '%s' via protected authentication path.", token_label);
- if (ret_used_pin)
- *ret_used_pin = NULL;
- return 0;
- }
-
- if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
- log_info("No login into security token '%s' required.", token_label);
- if (ret_used_pin)
- *ret_used_pin = NULL;
- return 0;
- }
+ if (r != -ENOANO) /* pin required */
+ return r;
token_uri_escaped = cescape(token_uri_string);
if (!token_uri_escaped)
if (!passwords)
return log_oom();
- string_erase(e);
- if (unsetenv("PIN") < 0)
- return log_error_errno(errno, "Failed to unset $PIN: %m");
+ assert_se(unsetenv_erase("PIN") >= 0);
} else if (headless)
return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the 'PIN' environment variable.");
else {
}
STRV_FOREACH(i, passwords) {
- rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i));
- if (rv == CKR_OK) {
-
- if (ret_used_pin) {
- char *c;
-
- c = strdup(*i);
- if (!c)
- return log_oom();
+ r = pkcs11_token_login_by_pin(m, session, token_info, token_label, *i, strlen(*i));
+ if (r == 0 && ret_used_pin) {
+ char *c;
- *ret_used_pin = c;
- }
+ c = strdup(*i);
+ if (!c)
+ return log_oom();
- log_info("Successfully logged into security token '%s'.", token_label);
- return 0;
+ *ret_used_pin = c;
}
- if (rv == CKR_PIN_LOCKED)
- return log_error_errno(SYNTHETIC_ERRNO(EPERM),
- "PIN has been locked, please reset PIN of security token '%s'.", token_label);
- if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+
+ if (r != -ENOLCK)
+ return r;
/* Referesh the token info, so that we can prompt knowing the new flags if they changed. */
rv = m->C_GetTokenInfo(slotid, &updated_token_info);
slotid, p11_kit_strerror(rv));
token_info = &updated_token_info;
- log_notice("PIN for token '%s' is incorrect, please try again.", token_label);
}
}
"PKCS#11 tokens not supported on this build.");
#endif
}
+
+#if HAVE_P11KIT
+void pkcs11_crypt_device_callback_data_release(pkcs11_crypt_device_callback_data *data) {
+ erase_and_free(data->decrypted_key);
+
+ if (data->free_encrypted_key)
+ free(data->encrypted_key);
+}
+
+int pkcs11_crypt_device_callback(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_SLOT_ID slot_id,
+ const CK_SLOT_INFO *slot_info,
+ const CK_TOKEN_INFO *token_info,
+ P11KitUri *uri,
+ void *userdata) {
+
+ pkcs11_crypt_device_callback_data *data = userdata;
+ CK_OBJECT_HANDLE object;
+ int r;
+
+ assert(m);
+ assert(slot_info);
+ assert(token_info);
+ assert(uri);
+ assert(data);
+
+ /* Called for every token matching our URI */
+
+ r = pkcs11_token_login(
+ m,
+ session,
+ slot_id,
+ token_info,
+ data->friendly_name,
+ "drive-harddisk",
+ "pkcs11-pin",
+ "cryptsetup.pkcs11-pin",
+ data->until,
+ data->headless,
+ NULL);
+ if (r < 0)
+ return r;
+
+ /* We are likely called during early boot, where entropy is scarce. Mix some data from the PKCS#11
+ * token, if it supports that. It should be cheap, given that we already are talking to it anyway and
+ * shouldn't hurt. */
+ (void) pkcs11_token_acquire_rng(m, session);
+
+ r = pkcs11_token_find_private_key(m, session, uri, &object);
+ if (r < 0)
+ return r;
+
+ r = pkcs11_token_decrypt_data(
+ m,
+ session,
+ object,
+ data->encrypted_key,
+ data->encrypted_key_size,
+ &data->decrypted_key,
+ &data->decrypted_key_size);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+#endif
char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info);
char *pkcs11_token_model(const CK_TOKEN_INFO *token_info);
+int pkcs11_token_login_by_pin(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, const CK_TOKEN_INFO *token_info, const char *token_label, const void *pin, size_t pin_size);
int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, bool headless, char **ret_used_pin);
int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
int pkcs11_acquire_certificate(const char *uri, const char *askpw_friendly_name, const char *askpw_icon_name, X509 **ret_cert, char **ret_pin_used);
#endif
+typedef struct {
+ const char *friendly_name;
+ usec_t until;
+ void *encrypted_key;
+ size_t encrypted_key_size;
+ void *decrypted_key;
+ size_t decrypted_key_size;
+ bool free_encrypted_key;
+ bool headless;
+} pkcs11_crypt_device_callback_data;
+
+void pkcs11_crypt_device_callback_data_release(pkcs11_crypt_device_callback_data *data);
+
+int pkcs11_crypt_device_callback(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_SLOT_ID slot_id,
+ const CK_SLOT_INFO *slot_info,
+ const CK_TOKEN_INFO *token_info,
+ P11KitUri *uri,
+ void *userdata);
+
#endif
+typedef struct {
+ const char *friendly_name;
+ usec_t until;
+ bool headless;
+} systemd_pkcs11_plugin_params;
+
int pkcs11_list_tokens(void);
int pkcs11_find_token_auto(char **ret);
#include "stat-util.h"
#include "string-util.h"
+/* We treat tmpfs/ramfs + cgroupfs as non-physical file systems. cgroupfs is similar to tmpfs in a way
+ * after all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it
+ * to remove those again. */
static bool is_physical_fs(const struct statfs *sfs) {
return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
}
return 0;
}
-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
- int ret = 0, r;
- struct statfs sfs;
+static int rm_rf_children_inner(
+ int fd,
+ const char *fname,
+ int is_dir,
+ RemoveFlags flags,
+ const struct stat *root_dev) {
- assert(fd >= 0);
+ struct stat st;
+ int r;
- /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
- * fd, in all cases, including on failure.. */
+ assert(fd >= 0);
+ assert(fname);
- if (!(flags & REMOVE_PHYSICAL)) {
+ if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
- r = fstatfs(fd, &sfs);
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
+ r = fstatat_harder(fd, fname, &st, AT_SYMLINK_NOFOLLOW, flags);
+ if (r < 0)
+ return r;
- if (is_physical_fs(&sfs)) {
- /* We refuse to clean physical file systems with this call,
- * unless explicitly requested. This is extra paranoia just
- * to be sure we never ever remove non-state data. */
- _cleanup_free_ char *path = NULL;
+ is_dir = S_ISDIR(st.st_mode);
+ }
- (void) fd_get_path(fd, &path);
- log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.",
- strna(path));
+ if (is_dir) {
+ _cleanup_close_ int subdir_fd = -1;
+ int q;
- safe_close(fd);
- return -EPERM;
- }
- }
+ /* if root_dev is set, remove subdirectories only if device is same */
+ if (root_dev && st.st_dev != root_dev->st_dev)
+ return 0;
- d = fdopendir(fd);
- if (!d) {
- safe_close(fd);
- return errno == ENOENT ? 0 : -errno;
- }
+ /* Stop at mount points */
+ r = fd_is_mount_point(fd, fname, 0);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
- FOREACH_DIRENT_ALL(de, d, return -errno) {
- bool is_dir;
- struct stat st;
+ if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
- if (dot_or_dot_dot(de->d_name))
- continue;
+ /* This could be a subvolume, try to remove it */
- if (de->d_type == DT_UNKNOWN ||
- (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
- r = fstatat_harder(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW, flags);
+ r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (r < 0) {
- if (ret == 0 && r != -ENOENT)
- ret = r;
- continue;
- }
+ if (!IN_SET(r, -ENOTTY, -EINVAL))
+ return r;
- is_dir = S_ISDIR(st.st_mode);
- } else
- is_dir = de->d_type == DT_DIR;
+ /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */
+ } else
+ /* It was a subvolume, done. */
+ return 1;
+ }
- if (is_dir) {
- _cleanup_close_ int subdir_fd = -1;
+ subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ if (subdir_fd < 0)
+ return -errno;
- /* if root_dev is set, remove subdirectories only if device is same */
- if (root_dev && st.st_dev != root_dev->st_dev)
- continue;
+ /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type
+ * again for each directory */
+ q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
- subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (subdir_fd < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
+ r = unlinkat_harder(fd, fname, AT_REMOVEDIR, flags);
+ if (r < 0)
+ return r;
+ if (q < 0)
+ return q;
- /* Stop at mount points */
- r = fd_is_mount_point(fd, de->d_name, 0);
- if (r < 0) {
- if (ret == 0 && r != -ENOENT)
- ret = r;
+ return 1;
- continue;
- }
- if (r > 0)
- continue;
+ } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
+ r = unlinkat_harder(fd, fname, 0, flags);
+ if (r < 0)
+ return r;
- if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
+ return 1;
+ }
- /* This could be a subvolume, try to remove it */
+ return 0;
+}
- r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
- if (r < 0) {
- if (!IN_SET(r, -ENOTTY, -EINVAL)) {
- if (ret == 0)
- ret = r;
+int rm_rf_children(
+ int fd,
+ RemoveFlags flags,
+ const struct stat *root_dev) {
- continue;
- }
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int ret = 0, r;
- /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */
- } else
- /* It was a subvolume, continue. */
- continue;
- }
+ assert(fd >= 0);
+
+ /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
+ * fd, in all cases, including on failure. */
+
+ d = fdopendir(fd);
+ if (!d) {
+ safe_close(fd);
+ return -errno;
+ }
- /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file
- * system type again for each directory */
- r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
- if (r < 0 && ret == 0)
- ret = r;
+ if (!(flags & REMOVE_PHYSICAL)) {
+ struct statfs sfs;
- r = unlinkat_harder(fd, de->d_name, AT_REMOVEDIR, flags);
- if (r < 0 && r != -ENOENT && ret == 0)
- ret = r;
+ if (fstatfs(dirfd(d), &sfs) < 0)
+ return -errno;
+
+ if (is_physical_fs(&sfs)) {
+ /* We refuse to clean physical file systems with this call, unless explicitly
+ * requested. This is extra paranoia just to be sure we never ever remove non-state
+ * data. */
- } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
+ _cleanup_free_ char *path = NULL;
- r = unlinkat_harder(fd, de->d_name, 0, flags);
- if (r < 0 && r != -ENOENT && ret == 0)
- ret = r;
+ (void) fd_get_path(fd, &path);
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Attempted to remove disk file system under \"%s\", and we can't allow that.",
+ strna(path));
}
}
+
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
+ int is_dir;
+
+ if (dot_or_dot_dot(de->d_name))
+ continue;
+
+ is_dir =
+ de->d_type == DT_UNKNOWN ? -1 :
+ de->d_type == DT_DIR;
+
+ r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev);
+ if (r < 0 && r != -ENOENT && ret == 0)
+ ret = r;
+ }
+
return ret;
}
int rm_rf(const char *path, RemoveFlags flags) {
int fd, r;
- struct statfs s;
assert(path);
if (FLAGS_SET(flags, REMOVE_ROOT)) {
if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
+ struct statfs s;
+
if (statfs(path, &s) < 0)
return -errno;
-
if (is_physical_fs(&s))
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"Attempted to remove files from a disk file system under \"%s\", refusing.",
return r;
}
+
+int rm_rf_child(int fd, const char *name, RemoveFlags flags) {
+
+ /* Removes one specific child of the specified directory */
+
+ if (fd < 0)
+ return -EBADF;
+
+ if (!filename_is_valid(name))
+ return -EINVAL;
+
+ if ((flags & (REMOVE_ROOT|REMOVE_MISSING_OK)) != 0) /* Doesn't really make sense here, we are not supposed to remove 'fd' anyway */
+ return -EINVAL;
+
+ if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
+ return -EINVAL;
+
+ return rm_rf_children_inner(fd, name, -1, flags, NULL);
+}
int fstatat_flags,
RemoveFlags remove_flags);
-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
+int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev);
+int rm_rf_child(int fd, const char *name, RemoveFlags flags);
int rm_rf(const char *path, RemoveFlags flags);
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
"restart_syscall\0"
"rseq\0"
"rt_sigreturn\0"
+ "sched_getaffinity\0"
"sched_yield\0"
"set_robust_list\0"
"set_thread_area\0"
"get_mempolicy\0"
"getcpu\0"
"getpriority\0"
- "getrandom\0"
"ioctl\0"
"ioprio_get\0"
"kcmp\0"
"remap_file_pages\0"
"sched_get_priority_max\0"
"sched_get_priority_min\0"
- "sched_getaffinity\0"
"sched_getattr\0"
"sched_getparam\0"
"sched_getscheduler\0"
assert(inside_path);
#if HAVE_SELINUX
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_freecon_ char* fcon = NULL;
struct stat st;
int r;
goto fail;
}
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- if (setfilecon_raw(procfs_path, fcon) < 0) {
+ if (setfilecon_raw(FORMAT_PROC_FD_PATH(fd), fcon) < 0) {
_cleanup_freecon_ char *oldcon = NULL;
/* If the FS doesn't support labels, then exit without warning */
r = -errno;
/* If the old label is identical to the new one, suppress any kind of error */
- if (getfilecon_raw(procfs_path, &oldcon) >= 0 && streq(fcon, oldcon))
+ if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq(fcon, oldcon))
return 0;
goto fail;
return -EINVAL;
default:
- assert_not_reached("Unknown option code.");
+ assert_not_reached();
}
if (optind < argc)
return r;
}
-static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+static int smack_fix_fd(int fd, const char *abspath, LabelFixFlags flags) {
const char *label;
struct stat st;
int r;
else
return 0;
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- if (setxattr(procfs_path, "security.SMACK64", label, strlen(label), 0) < 0) {
+ if (setxattr(FORMAT_PROC_FD_PATH(fd), "security.SMACK64", label, strlen(label), 0) < 0) {
_cleanup_free_ char *old_label = NULL;
r = -errno;
return 0;
/* If the old label is identical to the new one, suppress any kind of error */
- if (getxattr_malloc(procfs_path, "security.SMACK64", &old_label, false) >= 0 &&
+ if (getxattr_malloc(FORMAT_PROC_FD_PATH(fd), "security.SMACK64", &old_label, false) >= 0 &&
streq(old_label, label))
return 0;
.size = sizeof(struct sockaddr_in6),
};
else
- assert_not_reached("Family quarrel");
+ assert_not_reached();
}
}
if (si.si_status == EXIT_FAILURE)
return false;
- assert_not_reached("unexpected exit code");
+ assert_not_reached();
}
bool can_memlock(void) {
void (*sym_Esys_Finalize)(ESYS_CONTEXT **context) = NULL;
TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle) = NULL;
void (*sym_Esys_Free)(void *ptr) = NULL;
+TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData);
TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes) = NULL;
TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL;
TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle) = NULL;
DLSYM_ARG(Esys_Finalize),
DLSYM_ARG(Esys_FlushContext),
DLSYM_ARG(Esys_Free),
+ DLSYM_ARG(Esys_GetCapability),
DLSYM_ARG(Esys_GetRandom),
DLSYM_ARG(Esys_Initialize),
DLSYM_ARG(Esys_Load),
return 0;
}
+static int tpm2_get_best_pcr_bank(
+ ESYS_CONTEXT *c,
+ TPMI_ALG_HASH *ret) {
+
+ _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
+ TPMI_ALG_HASH hash = TPM2_ALG_SHA1;
+ bool found = false;
+ TPMI_YES_NO more;
+ TSS2_RC rc;
+
+ rc = sym_Esys_GetCapability(
+ c,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ TPM2_CAP_PCRS,
+ 0,
+ 1,
+ &more,
+ &pcap);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to determine TPM2 PCR bank capabilities: %s", sym_Tss2_RC_Decode(rc));
+
+ assert(pcap->capability == TPM2_CAP_PCRS);
+
+ for (size_t i = 0; i < pcap->data.assignedPCR.count; i++) {
+ bool valid = true;
+
+ /* As per
+ * https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a
+ * TPM2 on a Client PC must have at least 24 PCRs. If this TPM has less, just skip over
+ * it. */
+ if (pcap->data.assignedPCR.pcrSelections[i].sizeofSelect < TPM2_PCRS_MAX/8) {
+ log_debug("Skipping TPM2 PCR bank %s with fewer than 24 PCRs.",
+ strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
+ continue;
+ }
+
+ assert_cc(TPM2_PCRS_MAX % 8 == 0);
+
+ /* It's not enough to check how many PCRs there are, we also need to check that the 24 are
+ * enabled for this bank. Otherwise this TPM doesn't qualify. */
+ for (size_t j = 0; j < TPM2_PCRS_MAX/8; j++)
+ if (pcap->data.assignedPCR.pcrSelections[i].pcrSelect[j] != 0xFF) {
+ valid = false;
+ break;
+ }
+
+ if (!valid) {
+ log_debug("TPM2 PCR bank %s has fewer than 24 PCR bits enabled, ignoring.",
+ strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
+ continue;
+ }
+
+ if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA256) {
+ hash = TPM2_ALG_SHA256;
+ found = true;
+ break;
+ }
+
+ if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA1)
+ found = true;
+ }
+
+ if (!found)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "TPM2 module supports neither SHA1 nor SHA256 PCR banks, cannot operate.");
+
+ if (hash == TPM2_ALG_SHA256)
+ log_debug("TPM2 device supports SHA256 PCR banks, yay!");
+ else {
+ assert(hash == TPM2_ALG_SHA1);
+ log_debug("TPM2 device lacks support for SHA256 PCR banks, falling back to SHA1 banks.");
+ }
+
+ *ret = hash;
+ return 0;
+}
+
static int tpm2_make_pcr_session(
ESYS_CONTEXT *c,
uint32_t pcr_mask,
+ uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
ESYS_TR *ret_session,
- TPM2B_DIGEST **ret_policy_digest) {
+ TPM2B_DIGEST **ret_policy_digest,
+ TPMI_ALG_HASH *ret_pcr_bank) {
static const TPMT_SYM_DEF symmetric = {
.algorithm = TPM2_ALG_AES,
};
TPML_PCR_SELECTION pcr_selection = {
.count = 1,
- .pcrSelections[0].hash = TPM2_ALG_SHA256,
+ .pcrSelections[0].hash = TPM2_ALG_SHA256, /* overridden below, depending on TPM2 capabilities */
.pcrSelections[0].sizeofSelect = 3,
.pcrSelections[0].pcrSelect[0] = pcr_mask & 0xFF,
.pcrSelections[0].pcrSelect[1] = (pcr_mask >> 8) & 0xFF,
log_debug("Starting authentication session.");
+ if (pcr_bank != UINT16_MAX)
+ pcr_selection.pcrSelections[0].hash = pcr_bank;
+ else {
+ /* No bank configured, pick automatically. Some TPM2 devices only can do SHA1. If we detect
+ * that use that, but preferably use SHA256 */
+ r = tpm2_get_best_pcr_bank(c, &pcr_selection.pcrSelections[0].hash);
+ if (r < 0)
+ return r;
+ }
+
rc = sym_Esys_StartAuthSession(
c,
ESYS_TR_NONE,
if (ret_policy_digest)
*ret_policy_digest = TAKE_PTR(policy_digest);
+ if (ret_pcr_bank)
+ *ret_pcr_bank = pcr_selection.pcrSelections[0].hash;
+
r = 0;
finish:
void **ret_blob,
size_t *ret_blob_size,
void **ret_pcr_hash,
- size_t *ret_pcr_hash_size) {
+ size_t *ret_pcr_hash_size,
+ uint16_t *ret_pcr_bank) {
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
TPM2B_SENSITIVE_CREATE hmac_sensitive;
ESYS_TR primary = ESYS_TR_NONE;
TPM2B_PUBLIC hmac_template;
+ TPMI_ALG_HASH pcr_bank;
size_t k, blob_size;
usec_t start;
TSS2_RC rc;
assert(ret_blob_size);
assert(ret_pcr_hash);
assert(ret_pcr_hash_size);
+ assert(ret_pcr_bank);
assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
if (r < 0)
return r;
- r = tpm2_make_pcr_session(c.esys_context, pcr_mask, NULL, &policy_digest);
+ r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, NULL, &policy_digest, &pcr_bank);
if (r < 0)
goto finish;
*ret_blob_size = blob_size;
*ret_pcr_hash = TAKE_PTR(hash);
*ret_pcr_hash_size = policy_digest->size;
+ *ret_pcr_bank = pcr_bank;
r = 0;
int tpm2_unseal(
const char *device,
uint32_t pcr_mask,
+ uint16_t pcr_bank,
const void *blob,
size_t blob_size,
const void *known_policy_hash,
if (r < 0)
return r;
- r = tpm2_make_pcr_session(c.esys_context, pcr_mask, &session, &policy_digest);
+ r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, &session, &policy_digest, NULL);
if (r < 0)
goto finish;
int tpm2_make_luks2_json(
int keyslot,
uint32_t pcr_mask,
+ uint16_t pcr_bank,
const void *blob,
size_t blob_size,
const void *policy_hash,
JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)),
JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(a)),
+ JSON_BUILD_PAIR_CONDITION(!!tpm2_pcr_bank_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_pcr_bank_to_string(pcr_bank))),
JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size))));
if (r < 0)
return r;
return keyslot;
}
+
+/* We want the helpers below to work also if TPM2 libs are not available, hence define these two defines if
+ * they are missing. */
+#ifndef TPM2_ALG_SHA256
+#define TPM2_ALG_SHA256 0xB
+#endif
+
+#ifndef TPM2_ALG_SHA1
+#define TPM2_ALG_SHA1 0x4
+#endif
+
+int tpm2_pcr_bank_supported(uint16_t bank) {
+ /* For now, let's officially only support these two. We can extend this later on, should the need
+ * arise. */
+ return IN_SET(bank, TPM2_ALG_SHA256, TPM2_ALG_SHA1);
+}
+
+const char *tpm2_pcr_bank_to_string(uint16_t bank) {
+ /* Similar here, only support the two for now, we can always extend this later. */
+ if (bank == TPM2_ALG_SHA256)
+ return "sha256";
+ if (bank == TPM2_ALG_SHA1)
+ return "sha1";
+ return NULL;
+}
+
+int tpm2_pcr_bank_from_string(const char *bank) {
+ if (streq_ptr(bank, "sha256"))
+ return TPM2_ALG_SHA256;
+ if (streq_ptr(bank, "sha1"))
+ return TPM2_ALG_SHA1;
+ return -EINVAL;
+}
extern void (*sym_Esys_Finalize)(ESYS_CONTEXT **context);
extern TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle);
extern void (*sym_Esys_Free)(void *ptr);
+extern TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData);
extern TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes);
extern TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion);
extern TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle);
int dlopen_tpm2(void);
-int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size);
-int tpm2_unseal(const char *device, uint32_t pcr_mask, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size);
+int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank);
+int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size);
#endif
int tpm2_parse_pcrs(const char *s, uint32_t *ret);
-int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret);
+int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret);
#define TPM2_PCRS_MAX 24
/* Default to PCR 7 only */
#define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7)
+int tpm2_pcr_bank_supported(uint16_t bank);
+const char *tpm2_pcr_bank_to_string(uint16_t bank);
+int tpm2_pcr_bank_from_string(const char *bank);
+
typedef struct {
uint32_t search_pcr_mask;
const char *device;
{},
};
- char smid[SD_ID128_STRING_MAX];
JsonVariant *m;
sd_id128_t mid;
int r;
if (r < 0)
return json_log(variant, flags, r, "Failed to determine machine ID: %m");
- m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+ m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
if (!m)
return 0;
{},
};
- char smid[SD_ID128_STRING_MAX];
JsonVariant *m;
sd_id128_t mid;
int r;
if (r < 0)
return json_log(variant, flags, r, "Failed to determine machine ID: %m");
- m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+ m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
if (!m)
return 0;
break;
default:
- assert_not_reached("Unexpected state?");
+ assert_not_reached();
}
sd_event_unref(iterator->event);
}
default:
- assert_not_reached("unexpected lookup");
+ assert_not_reached();
}
finish:
}
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
+ _cleanup_(utxent_cleanup) bool utmpx = false;
struct utmpx lookup = {
.ut_type = INIT_PROCESS /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
}, store, store_wtmp, *found;
assert(id);
- setutxent();
+ utmpx = utxent_start();
/* Copy the whole string if it fits, or just the suffix without the terminating NUL. */
copy_suffix(store.ut_id, sizeof(store.ut_id), id);
bool (*match_tty)(const char *tty, void *userdata),
void *userdata) {
+ _cleanup_(utxent_cleanup) bool utmpx = false;
_cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL;
struct utmpx *u;
int r;
message) < 0)
return -ENOMEM;
- setutxent();
+ utmpx = utxent_start();
r = 0;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <malloc.h>
-#include <sys/poll.h>
+#include <poll.h>
#include "alloc-util.h"
#include "errno-util.h"
break;
default:
- assert_not_reached("Unexpected state");
+ assert_not_reached();
}
return varlink_log_errno(v, SYNTHETIC_ERRNO(ETIME), "Connection timed out.");
default:
- assert_not_reached("Unexpected state after method call.");
+ assert_not_reached();
}
}
return ascii_is_valid(p);
}
+bool file_url_is_valid(const char *url) {
+ const char *p;
+
+ if (isempty(url))
+ return false;
+
+ p = startswith(url, "file:/");
+ if (isempty(p))
+ return false;
+
+ return ascii_is_valid(p);
+}
+
bool documentation_url_is_valid(const char *url) {
const char *p;
if (isempty(url))
return false;
- if (http_url_is_valid(url))
+ if (http_url_is_valid(url) || file_url_is_valid(url))
return true;
- p = STARTSWITH_SET(url, "file:/", "info:", "man:");
+ p = STARTSWITH_SET(url, "info:", "man:");
if (isempty(p))
return false;
#include "macro.h"
bool http_url_is_valid(const char *url) _pure_;
+bool file_url_is_valid(const char *url) _pure_;
bool documentation_url_is_valid(const char *url) _pure_;
}
- assert_not_reached("Bad state");
+ assert_not_reached();
}
return -EINVAL;
default:
- assert_not_reached("Unhandled option code.");
+ assert_not_reached();
}
if (!arg_verb)
break;
default:
- assert_not_reached("Unknown magic");
+ assert_not_reached();
}
(void) reboot(cmd);
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (argc - optind != 1)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind >= argc)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_cat_config && argc > optind)
break;
}
default:
- assert_not_reached("Unsupported image type");
+ assert_not_reached();
}
r = validate_version(
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
else
log_info(r == 0 ? "Done!" : "Action!");
- if (orig_stdout_fd >= 0) {
- char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
- xsprintf(path, "/proc/self/fd/%d", orig_stdout_fd);
- assert_se(freopen(path, "w", stdout));
- }
+ if (orig_stdout_fd >= 0)
+ assert_se(freopen(FORMAT_PROC_FD_PATH(orig_stdout_fd), "w", stdout));
release_busses(); /* We open the bus for communication with logind.
* It needs to be closed to avoid apparent leaks. */
else if (streq(verb, "add-requires"))
dep = UNIT_REQUIRES;
else
- assert_not_reached("Unknown verb");
+ assert_not_reached();
if (install_client_side()) {
r = unit_file_add_dependency(arg_scope, unit_file_flags_from_args(), arg_root, names, target, dep, &changes, &n_changes);
else if (streq(argv[0], "thaw"))
method = "ThawUnit";
else
- assert_not_reached("Unhandled method");
+ assert_not_reached();
STRV_FOREACH(name, names) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind < argc)
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) {
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind >= argc)
break;
default:
- assert_not_reached("Unexpected action");
+ assert_not_reached();
}
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
else if (streq(verb, "revert"))
r = unit_file_revert(arg_scope, arg_root, names, &changes, &n_changes);
else
- assert_not_reached("Unknown verb");
+ assert_not_reached();
unit_file_dump_changes(r, verb, changes, n_changes, arg_quiet);
if (r < 0)
method = "RevertUnitFiles";
send_runtime = send_force = false;
} else
- assert_not_reached("Unknown verb");
+ assert_not_reached();
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
if (r < 0)
#include "systemctl-util.h"
#include "systemctl.h"
-int set_property(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+static int set_property_one(sd_bus *bus, const char *name, char **properties) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *n = NULL;
- UnitType t;
- sd_bus *bus;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
- r = acquire_bus(BUS_MANAGER, &bus);
- if (r < 0)
- return r;
-
- polkit_agent_open_maybe();
-
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "SetUnitProperties");
if (r < 0)
return bus_log_create_error(r);
- r = unit_name_mangle(argv[1], arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, &n);
- if (r < 0)
- return log_error_errno(r, "Failed to mangle unit name: %m");
-
- t = unit_name_to_type(n);
+ UnitType t = unit_name_to_type(name);
if (t < 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid unit type: %s", n);
+ return log_error_errno(t, "Invalid unit type: %s", name);
- r = sd_bus_message_append(m, "sb", n, arg_runtime);
+ r = sd_bus_message_append(m, "sb", name, arg_runtime);
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
- r = bus_append_unit_property_assignment_many(m, t, strv_skip(argv, 2));
+ r = bus_append_unit_property_assignment_many(m, t, properties);
if (r < 0)
return r;
r = sd_bus_call(bus, m, 0, &error, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to set unit properties on %s: %s", n, bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to set unit properties on %s: %s",
+ name, bus_error_message(&error, r));
return 0;
}
+
+int set_property(int argc, char *argv[], void *userdata) {
+ sd_bus *bus;
+ _cleanup_strv_free_ char **names = NULL;
+ char **name;
+ int r, k;
+
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ polkit_agent_open_maybe();
+
+ r = expand_unit_names(bus, STRV_MAKE(argv[1]), NULL, &names, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to expand '%s' into names: %m", argv[1]);
+
+ r = 0;
+ STRV_FOREACH(name, names) {
+ k = set_property_one(bus, *name, strv_skip(argv, 2));
+ if (k < 0 && r >= 0)
+ r = k;
+ }
+ return r;
+}
return 1;
- } else if (STR_IN_SET(name, "SystemCallFilter", "SystemCallLog", "RestrictAddressFamilies")) {
+ } else if (STR_IN_SET(name, "SystemCallFilter", "SystemCallLog", "RestrictAddressFamilies", "RestrictNetworkInterfaces")) {
_cleanup_strv_free_ char **l = NULL;
int allow_list;
(arg_dry_run ? REBOOT_DRY_RUN : 0));
default:
- assert_not_reached("Unknown action.");
+ assert_not_reached();
}
}
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_transport == BUS_TRANSPORT_REMOTE && arg_scope != UNIT_FILE_SYSTEM)
case _ACTION_INVALID:
default:
- assert_not_reached("Unknown action");
+ assert_not_reached();
}
finish:
check_compilation_sh,
args : cc.cmd_array() + ['-c', '-x'] + opt +
['-Werror', '-include',
- join_paths(meson.current_source_dir(), header)])
+ meson.current_source_dir() / header])
endif
endforeach
endforeach
const unsigned *vtable_format_reference;
} start;
struct {
- size_t reserved;
+ /* This field exists only to make sure we have something to initialize in
+ * SD_BUS_VTABLE_END in a way that is both compatible with pedantic versions of C and
+ * C++. It's unused otherwise. */
+ size_t _reserved;
} end;
struct {
const char *member;
.type = _SD_BUS_VTABLE_START, \
.flags = _flags, \
.x = { \
- .start = { \
- .element_size = sizeof(sd_bus_vtable), \
- .features = _SD_BUS_VTABLE_PARAM_NAMES, \
- .vtable_format_reference = &sd_bus_object_vtable_format, \
- }, \
+ .start = { \
+ .element_size = sizeof(sd_bus_vtable), \
+ .features = _SD_BUS_VTABLE_PARAM_NAMES, \
+ .vtable_format_reference = &sd_bus_object_vtable_format, \
+ }, \
}, \
}
.type = _SD_BUS_VTABLE_METHOD, \
.flags = _flags, \
.x = { \
- .method = { \
- .member = _member, \
- .signature = _signature, \
- .result = _result, \
- .handler = _handler, \
- .offset = _offset, \
- .names = _in_names _out_names, \
- }, \
+ .method = { \
+ .member = _member, \
+ .signature = _signature, \
+ .result = _result, \
+ .handler = _handler, \
+ .offset = _offset, \
+ .names = _in_names _out_names, \
+ }, \
}, \
}
#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \
.type = _SD_BUS_VTABLE_SIGNAL, \
.flags = _flags, \
.x = { \
- .signal = { \
- .member = _member, \
- .signature = _signature, \
- .names = _out_names, \
- }, \
+ .signal = { \
+ .member = _member, \
+ .signature = _signature, \
+ .names = _out_names, \
+ }, \
}, \
- }
-#define SD_BUS_SIGNAL(_member, _signature, _flags) \
+ }
+#define SD_BUS_SIGNAL(_member, _signature, _flags) \
SD_BUS_SIGNAL_WITH_NAMES(_member, _signature, "", _flags)
#define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \
.type = _SD_BUS_VTABLE_PROPERTY, \
.flags = _flags, \
.x = { \
- .property = { \
- .member = _member, \
- .signature = _signature, \
- .get = _get, \
- .set = NULL, \
- .offset = _offset, \
- }, \
+ .property = { \
+ .member = _member, \
+ .signature = _signature, \
+ .get = _get, \
+ .set = NULL, \
+ .offset = _offset, \
+ }, \
}, \
}
.type = _SD_BUS_VTABLE_WRITABLE_PROPERTY, \
.flags = _flags, \
.x = { \
- .property = { \
- .member = _member, \
- .signature = _signature, \
- .get = _get, \
- .set = _set, \
- .offset = _offset, \
- }, \
+ .property = { \
+ .member = _member, \
+ .signature = _signature, \
+ .get = _get, \
+ .set = _set, \
+ .offset = _offset, \
+ }, \
}, \
}
.type = _SD_BUS_VTABLE_END, \
.flags = 0, \
.x = { \
- .end = { \
- .reserved = 0, \
- }, \
+ .end = { \
+ ._reserved = 0, \
+ }, \
}, \
}
uint64_t qwords[2];
};
-#define SD_ID128_STRING_MAX 33
+#define SD_ID128_STRING_MAX 33U
char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
int sd_id128_from_string(const char *s, sd_id128_t *ret);
+#define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {})
+
int sd_id128_randomize(sd_id128_t *ret);
int sd_id128_get_machine(sd_id128_t *ret);
#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
#define SD_MESSAGE_SHUTDOWN_STR SD_ID128_MAKE_STR(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
+#define SD_MESSAGE_FACTORY_RESET SD_ID128_MAKE(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c)
+#define SD_MESSAGE_FACTORY_RESET_STR SD_ID128_MAKE_STR(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c)
+
/* The messages below are actually about jobs, not really about units, the macros are misleadingly named. Moreover
* SD_MESSAGE_UNIT_FAILED is not actually about a failing unit but about a failed start job. A job either finishes with
* SD_MESSAGE_UNIT_STARTED or with SD_MESSAGE_UNIT_FAILED hence. */
return add_group(i);
default:
- assert_not_reached("Unknown item type");
+ assert_not_reached();
}
}
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_replace && arg_cat_config)
test_env = environment()
test_env.set('SYSTEMD_KBD_MODEL_MAP', kbd_model_map)
test_env.set('SYSTEMD_LANGUAGE_FALLBACK_MAP', language_fallback_map)
-test_env.set('PATH', '@0@:@1@'.format(meson.build_root(), path))
+test_env.set('PATH', project_build_root + ':' + path)
############################################################
[['src/test/test-util.c']],
+ [['src/test/test-macro.c']],
+
[['src/test/test-json.c']],
[['src/test/test-modhex.c']],
[['src/test/test-fs-util.c']],
+ [['src/test/test-install-file.c']],
+
[['src/test/test-umask-util.c']],
[['src/test/test-proc-cmdline.c']],
[['src/test/test-sysctl-util.c']],
+ [['src/test/test-import-util.c']],
+
[['src/test/test-user-record.c']],
[['src/test/test-user-util.c']],
case NSS_STATUS_RETURN:
return "NSS_STATUS_RETURN";
default:
- snprintf(buf, buf_len, "%i", status);
+ (void) snprintf(buf, buf_len, "%i", status);
return buf;
}
};
bitmap_unset(b, 32);
BITMAP_FOREACH(n, NULL)
- assert_not_reached("NULL bitmap");
+ assert_not_reached();
assert_se(bitmap_set(b, 0) == 0);
assert_se(bitmap_set(b, 1) == 0);
/* The simple tests succeeded. Now let's try full unit-based use-case. */
assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
assert_se(u = unit_new(m, sizeof(Service)));
assert_se(unit_add_name(u, "foo.service") == 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
assert_se(test_bpf_cgroup_programs(m,
"single_prog.service", single_prog, ELEMENTSOF(single_prog)) >= 0);
m->default_tasks_accounting = false;
m->default_tasks_max = TASKS_MAX_UNSET;
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
/* Load units and verify hierarchy. */
assert_se(manager_load_startable_unit_or_warn(m, "parent.slice", NULL, &parent) >= 0);
static void test_cg_mask_to_string(void) {
test_cg_mask_to_string_one(0, NULL);
- test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign bpf-socket-bind");
+ test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign bpf-socket-bind bpf-restrict-network-interfaces");
test_cg_mask_to_string_one(CGROUP_MASK_CPU, "cpu");
test_cg_mask_to_string_one(CGROUP_MASK_CPUACCT, "cpuacct");
test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset");
}
assert_se(r >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
/* dml.slice has DefaultMemoryLow=50. Beyond that, individual subhierarchies look like this:
*
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind == argc)
static void test_condition_test_host(void) {
_cleanup_free_ char *hostname = NULL;
- char sid[SD_ID128_STRING_MAX];
Condition *condition;
sd_id128_t id;
- int r;
- r = sd_id128_get_machine(&id);
- assert_se(r >= 0);
- assert_se(sd_id128_to_string(id, sid));
+ assert_se(sd_id128_get_machine(&id) >= 0);
- condition = condition_new(CONDITION_HOST, sid, false, false);
+ condition = condition_new(CONDITION_HOST, SD_ID128_TO_STRING(id), false, false);
assert_se(condition);
assert_se(condition_test(condition, environ) > 0);
condition_free(condition);
assert_se(condition_test(condition, environ) == 0);
condition_free(condition);
- condition = condition_new(CONDITION_HOST, sid, false, true);
+ condition = condition_new(CONDITION_HOST, SD_ID128_TO_STRING(id), false, true);
assert_se(condition);
assert_se(condition_test(condition, environ) == 0);
condition_free(condition);
if (manager_errno_skip_test(r))
return log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
printf("Load1:\n");
assert_se(manager_load_startable_unit_or_warn(m, "a.service", NULL, &a) >= 0);
static void test_strv_env_merge(void) {
log_info("/* %s */", __func__);
- _cleanup_strv_free_ char **a = NULL, **b = NULL, **r = NULL;
+ char **a = STRV_MAKE("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", "EQ===");
+ char **b = STRV_MAKE("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES");
- a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF");
- assert_se(a);
-
- b = strv_new("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES");
- assert_se(b);
-
- r = strv_env_merge(2, a, b);
+ _cleanup_strv_free_ char **r = strv_env_merge(NULL, a, NULL, b, NULL, a, b, b, NULL);
assert_se(r);
assert_se(streq(r[0], "FOO="));
assert_se(streq(r[1], "WALDO="));
assert_se(streq(r[2], "PIEP"));
assert_se(streq(r[3], "SCHLUMPF=SMURFF"));
- assert_se(streq(r[4], "PIEP="));
- assert_se(streq(r[5], "NANANANA=YES"));
- assert_se(strv_length(r) == 6);
+ assert_se(streq(r[4], "EQ==="));
+ assert_se(streq(r[5], "PIEP="));
+ assert_se(streq(r[6], "NANANANA=YES"));
+ assert_se(strv_length(r) == 7);
assert_se(strv_env_clean(r) == r);
assert_se(streq(r[0], "FOO="));
assert_se(streq(r[1], "WALDO="));
assert_se(streq(r[2], "SCHLUMPF=SMURFF"));
- assert_se(streq(r[3], "PIEP="));
- assert_se(streq(r[4], "NANANANA=YES"));
- assert_se(strv_length(r) == 5);
+ assert_se(streq(r[3], "EQ==="));
+ assert_se(streq(r[4], "PIEP="));
+ assert_se(streq(r[5], "NANANANA=YES"));
+ assert_se(strv_length(r) == 6);
}
static void test_strv_env_replace_strdup(void) {
assert_se(strv_env_replace_strdup(&a, "a=a") == 1);
assert_se(strv_env_replace_strdup(&a, "b=b") == 1);
assert_se(strv_env_replace_strdup(&a, "a=A") == 0);
+ assert_se(strv_env_replace_strdup(&a, "c") == -EINVAL);
assert_se(strv_length(a) == 2);
strv_sort(a);
assert_se(streq(a[1], "b=b"));
}
+static void test_strv_env_replace_strdup_passthrough(void) {
+ log_info("/* %s */", __func__);
+
+ _cleanup_strv_free_ char **a = NULL;
+
+ assert_se(putenv((char*) "a=a") == 0);
+ assert_se(putenv((char*) "b=") == 0);
+ assert_se(unsetenv("c") == 0);
+
+ assert_se(strv_env_replace_strdup_passthrough(&a, "a") == 1);
+ assert_se(strv_env_replace_strdup_passthrough(&a, "b") == 1);
+ assert_se(strv_env_replace_strdup_passthrough(&a, "c") == 1);
+ assert_se(strv_env_replace_strdup_passthrough(&a, "a") == 0);
+ assert_se(strv_env_replace_strdup_passthrough(&a, "$a") == -EINVAL);
+
+ assert_se(strv_length(a) == 3);
+ assert_se(streq(a[0], "a=a"));
+ assert_se(streq(a[1], "b="));
+ assert_se(streq(a[2], "c="));
+}
+
static void test_strv_env_assign(void) {
log_info("/* %s */", __func__);
assert_se(set_unset_env("SYSTEMD_EXEC_PID", saved, 1) >= 0);
}
+static void test_unsetenv_erase(void) {
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ r = safe_fork("(sd-unsetenverase)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ if (r == 0) {
+ _cleanup_strv_free_ char **l = NULL;
+ char **e;
+
+ /* child */
+
+ assert_se(unsetenv_erase("thisenvvardefinitelywontexist") == 0);
+
+ l = strv_new("FOO=BAR", "QUUX=PIFF", "ONE=TWO", "A=B");
+ assert_se(strv_length(l) == 4);
+
+ environ = l;
+
+ STRV_FOREACH(e, environ) {
+ _cleanup_free_ char *n = NULL;
+ char *eq;
+
+ eq = strchr(*e, '=');
+ if (!eq)
+ continue;
+
+ n = strndup(*e, eq - *e);
+ assert_se(n);
+
+ assert_se(streq_ptr(getenv(n), eq + 1));
+ assert_se(getenv(n) == eq + 1);
+ assert_se(unsetenv_erase(n) > 0);
+ assert_se(isempty(eq + 1));
+ assert_se(!getenv(n));
+ }
+
+ environ = NULL;
+ l = strv_free(l);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ assert_se(r > 0);
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_strv_env_unset();
test_strv_env_merge();
test_strv_env_replace_strdup();
+ test_strv_env_replace_strdup_passthrough();
test_strv_env_assign();
test_env_strv_get_n();
test_replace_env(false);
test_env_assignment_is_valid();
test_putenv_dup();
test_setenv_systemd_exec_pid();
+ test_unsetenv_erase();
return 0;
}
if (manager_errno_skip_test(r))
return log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
for (const test_entry *test = tests; test->f; test++)
if (strv_fnmatch_or_empty(patterns, test->name, FNM_NOESCAPE))
log_open();
}
+static void test_format_proc_fd_path(void) {
+ assert_se(streq_ptr(FORMAT_PROC_FD_PATH(0), "/proc/self/fd/0"));
+ assert_se(streq_ptr(FORMAT_PROC_FD_PATH(1), "/proc/self/fd/1"));
+ assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2), "/proc/self/fd/2"));
+ assert_se(streq_ptr(FORMAT_PROC_FD_PATH(3), "/proc/self/fd/3"));
+ assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2147483647), "/proc/self/fd/2147483647"));
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_rearrange_stdio();
test_read_nr_open();
test_close_all_fds();
+ test_format_proc_fd_path();
return 0;
}
/* Do some basic checks on STRLEN() and DECIMAL_STR_MAX() */
assert_cc(STRLEN("xxx") == 3);
assert_cc(STRLEN("") == 0);
+assert_cc(STRLEN(L"xxx") == 3 * sizeof(wchar_t));
+assert_cc(STRLEN(L"") == 0);
assert_cc(DECIMAL_STR_MAX(uint8_t) == 5);
assert_cc(DECIMAL_STR_MAX(int8_t) == 5);
assert_cc(DECIMAL_STR_MAX(uint64_t) == 22);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "import-util.h"
+#include "log.h"
+#include "string-util.h"
+#include "tests.h"
+
+static void test_import_url_last_component_one(const char *input, const char *output, int ret) {
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(import_url_last_component(input, &s) == ret);
+ assert_se(streq_ptr(output, s));
+}
+
+static void test_import_url_last_component(void) {
+ test_import_url_last_component_one("https://foobar/waldo/quux", "quux", 0);
+ test_import_url_last_component_one("https://foobar/waldo/quux/", "quux", 0);
+ test_import_url_last_component_one("https://foobar/waldo/", "waldo", 0);
+ test_import_url_last_component_one("https://foobar/", NULL, -EADDRNOTAVAIL);
+ test_import_url_last_component_one("https://foobar", NULL, -EADDRNOTAVAIL);
+ test_import_url_last_component_one("https://foobar/waldo/quux?foo=bar", "quux", 0);
+ test_import_url_last_component_one("https://foobar/waldo/quux/?foo=bar", "quux", 0);
+ test_import_url_last_component_one("https://foobar/waldo/quux/?foo=bar#piep", "quux", 0);
+ test_import_url_last_component_one("https://foobar/waldo/quux/#piep", "quux", 0);
+ test_import_url_last_component_one("https://foobar/waldo/quux#piep", "quux", 0);
+ test_import_url_last_component_one("https://", NULL, -EINVAL);
+ test_import_url_last_component_one("", NULL, -EINVAL);
+ test_import_url_last_component_one(":", NULL, -EINVAL);
+ test_import_url_last_component_one(":/", NULL, -EINVAL);
+ test_import_url_last_component_one("x:/", NULL, -EINVAL);
+ test_import_url_last_component_one("x:y", NULL, -EADDRNOTAVAIL);
+ test_import_url_last_component_one("x:y/z", "z", 0);
+}
+
+static void test_import_url_change_suffix_one(const char *input, size_t n, const char *suffix, const char *output, int ret) {
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(import_url_change_suffix(input, n, suffix, &s) == ret);
+ assert_se(streq_ptr(output, s));
+}
+
+static void test_import_url_change_suffix(void) {
+ test_import_url_change_suffix_one("https://foobar/waldo/quux", 1, "wuff", "https://foobar/waldo/wuff", 0);
+ test_import_url_change_suffix_one("https://foobar/waldo/quux/", 1, "wuff", "https://foobar/waldo/wuff", 0);
+ test_import_url_change_suffix_one("https://foobar/waldo/quux///?mief", 1, "wuff", "https://foobar/waldo/wuff", 0);
+ test_import_url_change_suffix_one("https://foobar/waldo/quux///?mief#opopo", 1, "wuff", "https://foobar/waldo/wuff", 0);
+ test_import_url_change_suffix_one("https://foobar/waldo/quux/quff", 2, "wuff", "https://foobar/waldo/wuff", 0);
+ test_import_url_change_suffix_one("https://foobar/waldo/quux/quff/", 2, "wuff", "https://foobar/waldo/wuff", 0);
+ test_import_url_change_suffix_one("https://foobar/waldo/quux/quff", 0, "wuff", "https://foobar/waldo/quux/quff/wuff", 0);
+ test_import_url_change_suffix_one("https://foobar/waldo/quux/quff?aa?bb##4", 0, "wuff", "https://foobar/waldo/quux/quff/wuff", 0);
+ test_import_url_change_suffix_one("https://", 0, "wuff", NULL, -EINVAL);
+ test_import_url_change_suffix_one("", 0, "wuff", NULL, -EINVAL);
+ test_import_url_change_suffix_one(":", 0, "wuff", NULL, -EINVAL);
+ test_import_url_change_suffix_one(":/", 0, "wuff", NULL, -EINVAL);
+ test_import_url_change_suffix_one("x:/", 0, "wuff", NULL, -EINVAL);
+ test_import_url_change_suffix_one("x:y", 0, "wuff", "x:y/wuff", 0);
+ test_import_url_change_suffix_one("x:y/z", 0, "wuff", "x:y/z/wuff", 0);
+ test_import_url_change_suffix_one("x:y/z/", 0, "wuff", "x:y/z/wuff", 0);
+ test_import_url_change_suffix_one("x:y/z/", 1, "wuff", "x:y/wuff", 0);
+ test_import_url_change_suffix_one("x:y/z/", 2, "wuff", "x:y/wuff", 0);
+}
+
+int main(int argc, char *argv[]) {
+
+ test_setup_logging(LOG_INFO);
+
+ test_import_url_last_component();
+ test_import_url_change_suffix();
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fileio.h"
+#include "install-file.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+#include "umask-util.h"
+
+static void test_install_file(void) {
+ _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
+ _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL;
+ struct stat stat1, stat2;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(mkdtemp_malloc(NULL, &p) >= 0);
+ assert_se(a = path_join(p, "foo"));
+ assert_se(b = path_join(p, "bar"));
+
+ RUN_WITH_UMASK(0077)
+ assert_se(write_string_file(a, "wups", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(lstat(a, &stat1) >= 0);
+ assert_se(S_ISREG(stat1.st_mode));
+
+ assert_se(install_file(AT_FDCWD, a, AT_FDCWD, b, 0) >= 0);
+ assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC) >= 0);
+
+ assert_se(write_string_file(b, "ttss", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(install_file(AT_FDCWD, a, AT_FDCWD, b, INSTALL_FSYNC_FULL) == -EEXIST);
+ assert_se(install_file(AT_FDCWD, a, AT_FDCWD, b, INSTALL_FSYNC_FULL|INSTALL_REPLACE) >= 0);
+
+ assert_se(stat(b, &stat2) >= 0);
+ assert_se(stat1.st_dev == stat2.st_dev);
+ assert_se(stat1.st_ino == stat2.st_ino);
+ assert_se((stat2.st_mode & 0222) != 0); /* writable */
+
+ assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL|INSTALL_REPLACE|INSTALL_READ_ONLY) >= 0);
+
+ assert_se(stat(a, &stat2) >= 0);
+ assert_se(stat1.st_dev == stat2.st_dev);
+ assert_se(stat1.st_ino == stat2.st_ino);
+ assert_se((stat2.st_mode & 0222) == 0); /* read-only */
+
+ assert_se(mkdir(b, 0755) >= 0);
+ assert_se(c = path_join(b, "dir"));
+ assert_se(mkdir(c, 0755) >= 0);
+ free(c);
+ assert_se(c = path_join(b, "reg"));
+ assert_se(mknod(c, S_IFREG|0755, 0) >= 0);
+ free(c);
+ assert_se(c = path_join(b, "fifo"));
+ assert_se(mknod(c, S_IFIFO|0755, 0) >= 0);
+
+ assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL) == -EEXIST);
+ assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL|INSTALL_REPLACE) == 0);
+
+ assert_se(write_string_file(b, "ttss", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL) == -EEXIST);
+ assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL|INSTALL_REPLACE) == 0);
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_INFO);
+
+ test_install_file();
+
+ return 0;
+}
else if (q && streq(changes[i].path, q))
q = NULL;
else
- assert_not_reached("wut?");
+ assert_not_reached();
}
assert(!p && !q);
unit_file_changes_free(changes, n_changes);
else if (q && streq(changes[i].path, q))
q = NULL;
else
- assert_not_reached("wut?");
+ assert_not_reached();
}
assert(!p && !q);
unit_file_changes_free(changes, n_changes);
else if (q && streq(changes[i].path, q))
q = NULL;
else
- assert_not_reached("wut?");
+ assert_not_reached();
}
assert(!p && !q);
unit_file_changes_free(changes, n_changes);
}
assert_se(r >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
assert_se(u = unit_new(m, sizeof(Service)));
}
assert_se(r >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
assert_se(u = unit_new(m, sizeof(Service)));
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
static void dump_special_glyphs(void) {
- assert_cc(SPECIAL_GLYPH_TOUCH + 1 == _SPECIAL_GLYPH_MAX);
+ assert_cc(SPECIAL_GLYPH_SPARKLES + 1 == _SPECIAL_GLYPH_MAX);
log_info("/* %s */", __func__);
dump_glyph(SPECIAL_GLYPH_DEPRESSED_SMILEY);
dump_glyph(SPECIAL_GLYPH_LOCK_AND_KEY);
dump_glyph(SPECIAL_GLYPH_TOUCH);
+ dump_glyph(SPECIAL_GLYPH_RECYCLING);
+ dump_glyph(SPECIAL_GLYPH_DOWNLOAD);
+ dump_glyph(SPECIAL_GLYPH_SPARKLES);
}
int main(int argc, char *argv[]) {
}
int main(int argc, char* argv[]) {
- int target;
-
test_file();
- for (target = 0; target < _LOG_TARGET_MAX; target++) {
+ assert_se(log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), "foo") == -EUCLEAN);
+
+ for (int target = 0; target < _LOG_TARGET_MAX; target++) {
log_set_target(target);
log_open();
test_log_syntax();
}
- assert_se(log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), "foo") == -EUCLEAN);
-
return 0;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stddef.h>
+
+#include "log.h"
+#include "macro.h"
+#include "tests.h"
+
+static void test_align_power2(void) {
+ unsigned long i, p2;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(ALIGN_POWER2(0) == 0);
+ assert_se(ALIGN_POWER2(1) == 1);
+ assert_se(ALIGN_POWER2(2) == 2);
+ assert_se(ALIGN_POWER2(3) == 4);
+ assert_se(ALIGN_POWER2(4) == 4);
+ assert_se(ALIGN_POWER2(5) == 8);
+ assert_se(ALIGN_POWER2(6) == 8);
+ assert_se(ALIGN_POWER2(7) == 8);
+ assert_se(ALIGN_POWER2(9) == 16);
+ assert_se(ALIGN_POWER2(10) == 16);
+ assert_se(ALIGN_POWER2(11) == 16);
+ assert_se(ALIGN_POWER2(12) == 16);
+ assert_se(ALIGN_POWER2(13) == 16);
+ assert_se(ALIGN_POWER2(14) == 16);
+ assert_se(ALIGN_POWER2(15) == 16);
+ assert_se(ALIGN_POWER2(16) == 16);
+ assert_se(ALIGN_POWER2(17) == 32);
+
+ assert_se(ALIGN_POWER2(ULONG_MAX) == 0);
+ assert_se(ALIGN_POWER2(ULONG_MAX - 1) == 0);
+ assert_se(ALIGN_POWER2(ULONG_MAX - 1024) == 0);
+ assert_se(ALIGN_POWER2(ULONG_MAX / 2) == ULONG_MAX / 2 + 1);
+ assert_se(ALIGN_POWER2(ULONG_MAX + 1) == 0);
+
+ for (i = 1; i < 131071; ++i) {
+ for (p2 = 1; p2 < i; p2 <<= 1)
+ /* empty */ ;
+
+ assert_se(ALIGN_POWER2(i) == p2);
+ }
+
+ for (i = ULONG_MAX - 1024; i < ULONG_MAX; ++i) {
+ for (p2 = 1; p2 && p2 < i; p2 <<= 1)
+ /* empty */ ;
+
+ assert_se(ALIGN_POWER2(i) == p2);
+ }
+}
+
+static void test_max(void) {
+ static const struct {
+ int a;
+ int b[CONST_MAX(10, 100)];
+ } val1 = {
+ .a = CONST_MAX(10, 100),
+ };
+ int d = 0;
+ unsigned long x = 12345;
+ unsigned long y = 54321;
+ const char str[] = "a_string_constant";
+ const unsigned long long arr[] = {9999ULL, 10ULL, 0ULL, 3000ULL, 2000ULL, 1000ULL, 100ULL, 9999999ULL};
+ void *p = (void *)str;
+ void *q = (void *)&str[16];
+
+ log_info("/* %s */", __func__);
+
+ assert_cc(sizeof(val1.b) == sizeof(int) * 100);
+
+ /* CONST_MAX returns (void) instead of a value if the passed arguments
+ * are not of the same type or not constant expressions. */
+ assert_cc(__builtin_types_compatible_p(typeof(CONST_MAX(1, 10)), int));
+ assert_cc(__builtin_types_compatible_p(typeof(CONST_MAX(1, 1U)), void));
+
+ assert_se(val1.a == 100);
+ assert_se(MAX(++d, 0) == 1);
+ assert_se(d == 1);
+
+ assert_cc(MAXSIZE(char[3], uint16_t) == 3);
+ assert_cc(MAXSIZE(char[3], uint32_t) == 4);
+ assert_cc(MAXSIZE(char, long) == sizeof(long));
+
+ assert_se(MAX(-5, 5) == 5);
+ assert_se(MAX(5, 5) == 5);
+ assert_se(MAX(MAX(1, MAX(2, MAX(3, 4))), 5) == 5);
+ assert_se(MAX(MAX(1, MAX(2, MAX(3, 2))), 1) == 3);
+ assert_se(MAX(MIN(1, MIN(2, MIN(3, 4))), 5) == 5);
+ assert_se(MAX(MAX(1, MIN(2, MIN(3, 2))), 1) == 2);
+ assert_se(LESS_BY(8, 4) == 4);
+ assert_se(LESS_BY(8, 8) == 0);
+ assert_se(LESS_BY(4, 8) == 0);
+ assert_se(LESS_BY(16, LESS_BY(8, 4)) == 12);
+ assert_se(LESS_BY(4, LESS_BY(8, 4)) == 0);
+ assert_se(CMP(3, 5) == -1);
+ assert_se(CMP(5, 3) == 1);
+ assert_se(CMP(5, 5) == 0);
+ assert_se(CMP(x, y) == -1);
+ assert_se(CMP(y, x) == 1);
+ assert_se(CMP(x, x) == 0);
+ assert_se(CMP(y, y) == 0);
+ assert_se(CMP(UINT64_MAX, (uint64_t) 0) == 1);
+ assert_se(CMP((uint64_t) 0, UINT64_MAX) == -1);
+ assert_se(CMP(UINT64_MAX, UINT64_MAX) == 0);
+ assert_se(CMP(INT64_MIN, INT64_MAX) == -1);
+ assert_se(CMP(INT64_MAX, INT64_MIN) == 1);
+ assert_se(CMP(INT64_MAX, INT64_MAX) == 0);
+ assert_se(CMP(INT64_MIN, INT64_MIN) == 0);
+ assert_se(CMP(INT64_MAX, (int64_t) 0) == 1);
+ assert_se(CMP((int64_t) 0, INT64_MIN) == 1);
+ assert_se(CMP(INT64_MIN, (int64_t) 0) == -1);
+ assert_se(CMP((int64_t) 0, INT64_MAX) == -1);
+ assert_se(CMP(&str[2], &str[7]) == -1);
+ assert_se(CMP(&str[2], &str[2]) == 0);
+ assert_se(CMP(&str[7], (const char *)str) == 1);
+ assert_se(CMP(str[2], str[7]) == 1);
+ assert_se(CMP(str[7], *str) == 1);
+ assert_se(CMP((const unsigned long long *)arr, &arr[3]) == -1);
+ assert_se(CMP(*arr, arr[3]) == 1);
+ assert_se(CMP(p, q) == -1);
+ assert_se(CMP(q, p) == 1);
+ assert_se(CMP(p, p) == 0);
+ assert_se(CMP(q, q) == 0);
+ assert_se(CLAMP(-5, 0, 1) == 0);
+ assert_se(CLAMP(5, 0, 1) == 1);
+ assert_se(CLAMP(5, -10, 1) == 1);
+ assert_se(CLAMP(5, -10, 10) == 5);
+ assert_se(CLAMP(CLAMP(0, -10, 10), CLAMP(-5, 10, 20), CLAMP(100, -5, 20)) == 10);
+}
+
+#pragma GCC diagnostic push
+#ifdef __clang__
+# pragma GCC diagnostic ignored "-Waddress-of-packed-member"
+#endif
+
+static void test_container_of(void) {
+ struct mytype {
+ uint8_t pad1[3];
+ uint64_t v1;
+ uint8_t pad2[2];
+ uint32_t v2;
+ } myval = { };
+
+ log_info("/* %s */", __func__);
+
+ assert_cc(sizeof(myval) >= 17);
+ assert_se(container_of(&myval.v1, struct mytype, v1) == &myval);
+ assert_se(container_of(&myval.v2, struct mytype, v2) == &myval);
+ assert_se(container_of(&container_of(&myval.v2,
+ struct mytype,
+ v2)->v1,
+ struct mytype,
+ v1) == &myval);
+}
+
+#pragma GCC diagnostic pop
+
+static void test_div_round_up(void) {
+ int div;
+
+ log_info("/* %s */", __func__);
+
+ /* basic tests */
+ assert_se(DIV_ROUND_UP(0, 8) == 0);
+ assert_se(DIV_ROUND_UP(1, 8) == 1);
+ assert_se(DIV_ROUND_UP(8, 8) == 1);
+ assert_se(DIV_ROUND_UP(12, 8) == 2);
+ assert_se(DIV_ROUND_UP(16, 8) == 2);
+
+ /* test multiple evaluation */
+ div = 0;
+ assert_se(DIV_ROUND_UP(div++, 8) == 0 && div == 1);
+ assert_se(DIV_ROUND_UP(++div, 8) == 1 && div == 2);
+ assert_se(DIV_ROUND_UP(8, div++) == 4 && div == 3);
+ assert_se(DIV_ROUND_UP(8, ++div) == 2 && div == 4);
+
+ /* overflow test with exact division */
+ assert_se(sizeof(0U) == 4);
+ assert_se(0xfffffffaU % 10U == 0U);
+ assert_se(0xfffffffaU / 10U == 429496729U);
+ assert_se(DIV_ROUND_UP(0xfffffffaU, 10U) == 429496729U);
+ assert_se((0xfffffffaU + 10U - 1U) / 10U == 0U);
+ assert_se(0xfffffffaU / 10U + !!(0xfffffffaU % 10U) == 429496729U);
+
+ /* overflow test with rounded division */
+ assert_se(0xfffffffdU % 10U == 3U);
+ assert_se(0xfffffffdU / 10U == 429496729U);
+ assert_se(DIV_ROUND_UP(0xfffffffdU, 10U) == 429496730U);
+ assert_se((0xfffffffdU + 10U - 1U) / 10U == 0U);
+ assert_se(0xfffffffdU / 10U + !!(0xfffffffdU % 10U) == 429496730U);
+}
+
+static void test_ptr_to_int(void) {
+ log_info("/* %s */", __func__);
+
+ /* Primary reason to have this test is to validate that pointers are large enough to hold entire int range */
+ assert_se(PTR_TO_INT(INT_TO_PTR(0)) == 0);
+ assert_se(PTR_TO_INT(INT_TO_PTR(1)) == 1);
+ assert_se(PTR_TO_INT(INT_TO_PTR(-1)) == -1);
+ assert_se(PTR_TO_INT(INT_TO_PTR(INT_MAX)) == INT_MAX);
+ assert_se(PTR_TO_INT(INT_TO_PTR(INT_MIN)) == INT_MIN);
+}
+
+static void test_in_set(void) {
+ log_info("/* %s */", __func__);
+
+ assert_se(IN_SET(1, 1));
+ assert_se(IN_SET(1, 1, 2, 3, 4));
+ assert_se(IN_SET(2, 1, 2, 3, 4));
+ assert_se(IN_SET(3, 1, 2, 3, 4));
+ assert_se(IN_SET(4, 1, 2, 3, 4));
+ assert_se(!IN_SET(0, 1));
+ assert_se(!IN_SET(0, 1, 2, 3, 4));
+}
+
+static void test_foreach_pointer(void) {
+ int a, b, c, *i;
+ size_t k = 0;
+
+ log_info("/* %s */", __func__);
+
+ FOREACH_POINTER(i, &a, &b, &c) {
+ switch (k) {
+
+ case 0:
+ assert_se(i == &a);
+ break;
+
+ case 1:
+ assert_se(i == &b);
+ break;
+
+ case 2:
+ assert_se(i == &c);
+ break;
+
+ default:
+ assert_not_reached();
+ break;
+ }
+
+ k++;
+ }
+
+ assert(k == 3);
+
+ FOREACH_POINTER(i, &b) {
+ assert(k == 3);
+ assert(i == &b);
+ k = 4;
+ }
+
+ assert(k == 4);
+
+ FOREACH_POINTER(i, NULL, &c, NULL, &b, NULL, &a, NULL) {
+ switch (k) {
+
+ case 4:
+ assert_se(i == NULL);
+ break;
+
+ case 5:
+ assert_se(i == &c);
+ break;
+
+ case 6:
+ assert_se(i == NULL);
+ break;
+
+ case 7:
+ assert_se(i == &b);
+ break;
+
+ case 8:
+ assert_se(i == NULL);
+ break;
+
+ case 9:
+ assert_se(i == &a);
+ break;
+
+ case 10:
+ assert_se(i == NULL);
+ break;
+
+ default:
+ assert_not_reached();
+ break;
+ }
+
+ k++;
+ }
+
+ assert(k == 11);
+}
+
+static void test_align_to(void) {
+ log_info("/* %s */", __func__);
+
+ assert_se(ALIGN_TO(0, 1) == 0);
+ assert_se(ALIGN_TO(1, 1) == 1);
+ assert_se(ALIGN_TO(2, 1) == 2);
+ assert_se(ALIGN_TO(3, 1) == 3);
+ assert_se(ALIGN_TO(4, 1) == 4);
+ assert_se(ALIGN_TO(SIZE_MAX-1, 1) == SIZE_MAX-1);
+ assert_se(ALIGN_TO(SIZE_MAX, 1) == SIZE_MAX);
+
+ assert_se(ALIGN_TO(0, 2) == 0);
+ assert_se(ALIGN_TO(1, 2) == 2);
+ assert_se(ALIGN_TO(2, 2) == 2);
+ assert_se(ALIGN_TO(3, 2) == 4);
+ assert_se(ALIGN_TO(4, 2) == 4);
+ assert_se(ALIGN_TO(SIZE_MAX-3, 2) == SIZE_MAX-3);
+ assert_se(ALIGN_TO(SIZE_MAX-2, 2) == SIZE_MAX-1);
+ assert_se(ALIGN_TO(SIZE_MAX-1, 2) == SIZE_MAX-1);
+ assert_se(ALIGN_TO(SIZE_MAX, 2) == SIZE_MAX); /* overflow */
+
+ assert_se(ALIGN_TO(0, 4) == 0);
+ assert_se(ALIGN_TO(1, 4) == 4);
+ assert_se(ALIGN_TO(2, 4) == 4);
+ assert_se(ALIGN_TO(3, 4) == 4);
+ assert_se(ALIGN_TO(4, 4) == 4);
+ assert_se(ALIGN_TO(SIZE_MAX-3, 4) == SIZE_MAX-3);
+ assert_se(ALIGN_TO(SIZE_MAX-2, 4) == SIZE_MAX); /* overflow */
+ assert_se(ALIGN_TO(SIZE_MAX-1, 4) == SIZE_MAX); /* overflow */
+ assert_se(ALIGN_TO(SIZE_MAX, 4) == SIZE_MAX); /* overflow */
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_INFO);
+
+ test_align_power2();
+ test_max();
+ test_container_of();
+ test_div_round_up();
+ test_in_set();
+ test_foreach_pointer();
+ test_ptr_to_int();
+ test_align_to();
+
+ return 0;
+}
}
int main(int argc, char *argv[]) {
- sd_id128_t bid;
- char boot_id[SD_ID128_STRING_MAX];
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *zz = NULL;
+ sd_id128_t bid;
test_setup_logging(LOG_INFO);
}
assert_se(sd_id128_get_boot(&bid) >= 0);
- sd_id128_to_string(bid, boot_id);
- x = strjoin("/tmp/systemd-private-", boot_id, "-abcd.service-");
- y = strjoin("/var/tmp/systemd-private-", boot_id, "-abcd.service-");
+ x = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-");
+ y = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-");
assert_se(x && y);
test_tmpdir("abcd.service", x, y);
- z = strjoin("/tmp/systemd-private-", boot_id, "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
- zz = strjoin("/var/tmp/systemd-private-", boot_id, "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
+ z = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
+ zz = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
assert_se(z && zz);
if (name)
return name;
- snprintf(buf, buf_len, "%i", family);
+ (void) snprintf(buf, buf_len, "%i", family);
return buf;
}
log_info("/* %s */", __func__);
- assert_se(find_executable_full("sh", true, &p, NULL) == 0);
+ assert_se(find_executable_full("sh", NULL, true, &p, NULL) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);
- assert_se(find_executable_full("sh", false, &p, NULL) == 0);
+ assert_se(find_executable_full("sh", NULL, false, &p, NULL) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);
assert_se(unsetenv("PATH") == 0);
- assert_se(find_executable_full("sh", true, &p, NULL) == 0);
+ assert_se(find_executable_full("sh", NULL, true, &p, NULL) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);
- assert_se(find_executable_full("sh", false, &p, NULL) == 0);
+ assert_se(find_executable_full("sh", NULL, false, &p, NULL) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);
pid_t pid;
int r;
- r = find_executable_full(path, false, &t, &fd);
+ r = find_executable_full(path, NULL, false, &t, &fd);
log_info_errno(r, "%s: %s → %s: %d/%m", __func__, path, t ?: "-", fd);
assert_se(values[i] == NULL);
PATH_FOREACH_PREFIX(s, "////")
- assert_not_reached("Wut?");
+ assert_not_reached();
b = false;
PATH_FOREACH_PREFIX_MORE(s, "////") {
assert_se(b);
PATH_FOREACH_PREFIX(s, "")
- assert_not_reached("wut?");
+ assert_not_reached();
b = false;
PATH_FOREACH_PREFIX_MORE(s, "") {
if (manager_errno_skip_test(r))
return log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
- assert_se(manager_startup(tmp, NULL, NULL) >= 0);
+ assert_se(manager_startup(tmp, NULL, NULL, NULL) >= 0);
STRV_FOREACH(test_path, tests_path) {
_cleanup_free_ char *p = NULL;
else if (in_initrd() && !*strip && proc_cmdline_key_streq(key, "rd.zumm"))
assert_se(!value);
else
- assert_not_reached("Bad key!");
+ assert_not_reached();
return 0;
}
if (manager_errno_skip_test(r))
return log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
/* load idle ok */
assert_se(manager_load_startable_unit_or_warn(m, "sched_idle_ok.service", NULL, &idle_ok) >= 0);
assert_se(set_size(m) == 0);
}
+static void test_set_copy(void) {
+ Set *s, *copy;
+ char *key1, *key2, *key3, *key4;
+
+ log_info("/* %s */", __func__);
+
+ key1 = strdup("key1");
+ assert_se(key1);
+ key2 = strdup("key2");
+ assert_se(key2);
+ key3 = strdup("key3");
+ assert_se(key3);
+ key4 = strdup("key4");
+ assert_se(key4);
+
+ s = set_new(&string_hash_ops);
+ assert_se(s);
+
+ assert_se(set_put(s, key1) >= 0);
+ assert_se(set_put(s, key2) >= 0);
+ assert_se(set_put(s, key3) >= 0);
+ assert_se(set_put(s, key4) >= 0);
+
+ copy = set_copy(s);
+ assert_se(copy);
+
+ assert(set_equal(s, copy));
+
+ set_free(s);
+ set_free_free(copy);
+}
+
static void test_set_ensure_put(void) {
_cleanup_set_free_ Set *m = NULL;
test_set_ensure_consume();
test_set_strjoin();
test_set_equal();
+ test_set_copy();
return 0;
}
printf("big_enum2_pos → %zu\n", sizeof(big_enum2_pos));
printf("big_enum2_neg → %zu\n", sizeof(big_enum2_neg));
+ printf("timeval: %zu\n", sizeof(struct timeval));
+ printf("timespec: %zu\n", sizeof(struct timespec));
return 0;
}
assert_se(runtime_dir = setup_fake_runtime_dir());
assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "2000", STRV_MAKE("2000"), STRV_MAKE("any")) >= 0);
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "2000", STRV_MAKE("ipv6:2001-2002"), STRV_MAKE("any")) >= 0);
assert_se(streq_ptr(*check, input_table_multiple[i--]));
STRV_FOREACH_BACKWARDS(check, (char**) NULL)
- assert_not_reached("Let's see that we check empty strv right, too.");
+ assert_not_reached();
STRV_FOREACH_BACKWARDS(check, (char**) { NULL })
- assert_not_reached("Let's see that we check empty strv right, too.");
+ assert_not_reached();
}
static void test_strv_foreach_pair(void) {
#include "tests.h"
#include "util.h"
-static void test_align_power2(void) {
- unsigned long i, p2;
-
- log_info("/* %s */", __func__);
-
- assert_se(ALIGN_POWER2(0) == 0);
- assert_se(ALIGN_POWER2(1) == 1);
- assert_se(ALIGN_POWER2(2) == 2);
- assert_se(ALIGN_POWER2(3) == 4);
- assert_se(ALIGN_POWER2(4) == 4);
- assert_se(ALIGN_POWER2(5) == 8);
- assert_se(ALIGN_POWER2(6) == 8);
- assert_se(ALIGN_POWER2(7) == 8);
- assert_se(ALIGN_POWER2(9) == 16);
- assert_se(ALIGN_POWER2(10) == 16);
- assert_se(ALIGN_POWER2(11) == 16);
- assert_se(ALIGN_POWER2(12) == 16);
- assert_se(ALIGN_POWER2(13) == 16);
- assert_se(ALIGN_POWER2(14) == 16);
- assert_se(ALIGN_POWER2(15) == 16);
- assert_se(ALIGN_POWER2(16) == 16);
- assert_se(ALIGN_POWER2(17) == 32);
-
- assert_se(ALIGN_POWER2(ULONG_MAX) == 0);
- assert_se(ALIGN_POWER2(ULONG_MAX - 1) == 0);
- assert_se(ALIGN_POWER2(ULONG_MAX - 1024) == 0);
- assert_se(ALIGN_POWER2(ULONG_MAX / 2) == ULONG_MAX / 2 + 1);
- assert_se(ALIGN_POWER2(ULONG_MAX + 1) == 0);
-
- for (i = 1; i < 131071; ++i) {
- for (p2 = 1; p2 < i; p2 <<= 1)
- /* empty */ ;
-
- assert_se(ALIGN_POWER2(i) == p2);
- }
-
- for (i = ULONG_MAX - 1024; i < ULONG_MAX; ++i) {
- for (p2 = 1; p2 && p2 < i; p2 <<= 1)
- /* empty */ ;
-
- assert_se(ALIGN_POWER2(i) == p2);
- }
-}
-
-static void test_max(void) {
- static const struct {
- int a;
- int b[CONST_MAX(10, 100)];
- } val1 = {
- .a = CONST_MAX(10, 100),
- };
- int d = 0;
- unsigned long x = 12345;
- unsigned long y = 54321;
- const char str[] = "a_string_constant";
- const unsigned long long arr[] = {9999ULL, 10ULL, 0ULL, 3000ULL, 2000ULL, 1000ULL, 100ULL, 9999999ULL};
- void *p = (void *)str;
- void *q = (void *)&str[16];
-
- log_info("/* %s */", __func__);
-
- assert_cc(sizeof(val1.b) == sizeof(int) * 100);
-
- /* CONST_MAX returns (void) instead of a value if the passed arguments
- * are not of the same type or not constant expressions. */
- assert_cc(__builtin_types_compatible_p(typeof(CONST_MAX(1, 10)), int));
- assert_cc(__builtin_types_compatible_p(typeof(CONST_MAX(1, 1U)), void));
-
- assert_se(val1.a == 100);
- assert_se(MAX(++d, 0) == 1);
- assert_se(d == 1);
-
- assert_cc(MAXSIZE(char[3], uint16_t) == 3);
- assert_cc(MAXSIZE(char[3], uint32_t) == 4);
- assert_cc(MAXSIZE(char, long) == sizeof(long));
-
- assert_se(MAX(-5, 5) == 5);
- assert_se(MAX(5, 5) == 5);
- assert_se(MAX(MAX(1, MAX(2, MAX(3, 4))), 5) == 5);
- assert_se(MAX(MAX(1, MAX(2, MAX(3, 2))), 1) == 3);
- assert_se(MAX(MIN(1, MIN(2, MIN(3, 4))), 5) == 5);
- assert_se(MAX(MAX(1, MIN(2, MIN(3, 2))), 1) == 2);
- assert_se(LESS_BY(8, 4) == 4);
- assert_se(LESS_BY(8, 8) == 0);
- assert_se(LESS_BY(4, 8) == 0);
- assert_se(LESS_BY(16, LESS_BY(8, 4)) == 12);
- assert_se(LESS_BY(4, LESS_BY(8, 4)) == 0);
- assert_se(CMP(3, 5) == -1);
- assert_se(CMP(5, 3) == 1);
- assert_se(CMP(5, 5) == 0);
- assert_se(CMP(x, y) == -1);
- assert_se(CMP(y, x) == 1);
- assert_se(CMP(x, x) == 0);
- assert_se(CMP(y, y) == 0);
- assert_se(CMP(UINT64_MAX, (uint64_t) 0) == 1);
- assert_se(CMP((uint64_t) 0, UINT64_MAX) == -1);
- assert_se(CMP(UINT64_MAX, UINT64_MAX) == 0);
- assert_se(CMP(INT64_MIN, INT64_MAX) == -1);
- assert_se(CMP(INT64_MAX, INT64_MIN) == 1);
- assert_se(CMP(INT64_MAX, INT64_MAX) == 0);
- assert_se(CMP(INT64_MIN, INT64_MIN) == 0);
- assert_se(CMP(INT64_MAX, (int64_t) 0) == 1);
- assert_se(CMP((int64_t) 0, INT64_MIN) == 1);
- assert_se(CMP(INT64_MIN, (int64_t) 0) == -1);
- assert_se(CMP((int64_t) 0, INT64_MAX) == -1);
- assert_se(CMP(&str[2], &str[7]) == -1);
- assert_se(CMP(&str[2], &str[2]) == 0);
- assert_se(CMP(&str[7], (const char *)str) == 1);
- assert_se(CMP(str[2], str[7]) == 1);
- assert_se(CMP(str[7], *str) == 1);
- assert_se(CMP((const unsigned long long *)arr, &arr[3]) == -1);
- assert_se(CMP(*arr, arr[3]) == 1);
- assert_se(CMP(p, q) == -1);
- assert_se(CMP(q, p) == 1);
- assert_se(CMP(p, p) == 0);
- assert_se(CMP(q, q) == 0);
- assert_se(CLAMP(-5, 0, 1) == 0);
- assert_se(CLAMP(5, 0, 1) == 1);
- assert_se(CLAMP(5, -10, 1) == 1);
- assert_se(CLAMP(5, -10, 10) == 5);
- assert_se(CLAMP(CLAMP(0, -10, 10), CLAMP(-5, 10, 20), CLAMP(100, -5, 20)) == 10);
-}
-
-#pragma GCC diagnostic push
-#ifdef __clang__
-# pragma GCC diagnostic ignored "-Waddress-of-packed-member"
-#endif
-
-static void test_container_of(void) {
- struct mytype {
- uint8_t pad1[3];
- uint64_t v1;
- uint8_t pad2[2];
- uint32_t v2;
- } myval = { };
-
- log_info("/* %s */", __func__);
-
- assert_cc(sizeof(myval) >= 17);
- assert_se(container_of(&myval.v1, struct mytype, v1) == &myval);
- assert_se(container_of(&myval.v2, struct mytype, v2) == &myval);
- assert_se(container_of(&container_of(&myval.v2,
- struct mytype,
- v2)->v1,
- struct mytype,
- v1) == &myval);
-}
-
-#pragma GCC diagnostic pop
-
-static void test_div_round_up(void) {
- int div;
-
- log_info("/* %s */", __func__);
-
- /* basic tests */
- assert_se(DIV_ROUND_UP(0, 8) == 0);
- assert_se(DIV_ROUND_UP(1, 8) == 1);
- assert_se(DIV_ROUND_UP(8, 8) == 1);
- assert_se(DIV_ROUND_UP(12, 8) == 2);
- assert_se(DIV_ROUND_UP(16, 8) == 2);
-
- /* test multiple evaluation */
- div = 0;
- assert_se(DIV_ROUND_UP(div++, 8) == 0 && div == 1);
- assert_se(DIV_ROUND_UP(++div, 8) == 1 && div == 2);
- assert_se(DIV_ROUND_UP(8, div++) == 4 && div == 3);
- assert_se(DIV_ROUND_UP(8, ++div) == 2 && div == 4);
-
- /* overflow test with exact division */
- assert_se(sizeof(0U) == 4);
- assert_se(0xfffffffaU % 10U == 0U);
- assert_se(0xfffffffaU / 10U == 429496729U);
- assert_se(DIV_ROUND_UP(0xfffffffaU, 10U) == 429496729U);
- assert_se((0xfffffffaU + 10U - 1U) / 10U == 0U);
- assert_se(0xfffffffaU / 10U + !!(0xfffffffaU % 10U) == 429496729U);
-
- /* overflow test with rounded division */
- assert_se(0xfffffffdU % 10U == 3U);
- assert_se(0xfffffffdU / 10U == 429496729U);
- assert_se(DIV_ROUND_UP(0xfffffffdU, 10U) == 429496730U);
- assert_se((0xfffffffdU + 10U - 1U) / 10U == 0U);
- assert_se(0xfffffffdU / 10U + !!(0xfffffffdU % 10U) == 429496730U);
-}
-
static void test_u64log2(void) {
log_info("/* %s */", __func__);
assert_se(errno == 4711);
}
-static void test_in_set(void) {
- log_info("/* %s */", __func__);
-
- assert_se(IN_SET(1, 1));
- assert_se(IN_SET(1, 1, 2, 3, 4));
- assert_se(IN_SET(2, 1, 2, 3, 4));
- assert_se(IN_SET(3, 1, 2, 3, 4));
- assert_se(IN_SET(4, 1, 2, 3, 4));
- assert_se(!IN_SET(0, 1));
- assert_se(!IN_SET(0, 1, 2, 3, 4));
-}
-
static void test_log2i(void) {
log_info("/* %s */", __func__);
assert_se(system_tasks_max_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX);
}
-static void test_foreach_pointer(void) {
- int a, b, c, *i;
- size_t k = 0;
-
- log_info("/* %s */", __func__);
-
- FOREACH_POINTER(i, &a, &b, &c) {
- switch (k) {
-
- case 0:
- assert_se(i == &a);
- break;
-
- case 1:
- assert_se(i == &b);
- break;
-
- case 2:
- assert_se(i == &c);
- break;
-
- default:
- assert_not_reached("unexpected index");
- break;
- }
-
- k++;
- }
-
- assert(k == 3);
-
- FOREACH_POINTER(i, &b) {
- assert(k == 3);
- assert(i == &b);
- k = 4;
- }
-
- assert(k == 4);
-
- FOREACH_POINTER(i, NULL, &c, NULL, &b, NULL, &a, NULL) {
- switch (k) {
-
- case 4:
- assert_se(i == NULL);
- break;
-
- case 5:
- assert_se(i == &c);
- break;
-
- case 6:
- assert_se(i == NULL);
- break;
-
- case 7:
- assert_se(i == &b);
- break;
-
- case 8:
- assert_se(i == NULL);
- break;
-
- case 9:
- assert_se(i == &a);
- break;
-
- case 10:
- assert_se(i == NULL);
- break;
-
- default:
- assert_not_reached("unexpected index");
- break;
- }
-
- k++;
- }
-
- assert(k == 11);
-}
-
-static void test_ptr_to_int(void) {
- log_info("/* %s */", __func__);
-
- /* Primary reason to have this test is to validate that pointers are large enough to hold entire int range */
- assert_se(PTR_TO_INT(INT_TO_PTR(0)) == 0);
- assert_se(PTR_TO_INT(INT_TO_PTR(1)) == 1);
- assert_se(PTR_TO_INT(INT_TO_PTR(-1)) == -1);
- assert_se(PTR_TO_INT(INT_TO_PTR(INT_MAX)) == INT_MAX);
- assert_se(PTR_TO_INT(INT_TO_PTR(INT_MIN)) == INT_MIN);
-}
-
int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
- test_align_power2();
- test_max();
- test_container_of();
- test_div_round_up();
test_u64log2();
test_protect_errno();
test_unprotect_errno();
- test_in_set();
test_log2i();
test_eqzero();
test_raw_clone();
test_physical_memory_scale();
test_system_tasks_max();
test_system_tasks_max_scale();
- test_foreach_pointer();
- test_ptr_to_int();
return 0;
}
assert_se(runtime_dir = setup_fake_runtime_dir());
assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
- assert_se(manager_startup(m, NULL, NULL) >= 0);
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
assert_se(a = unit_new(m, sizeof(Service)));
assert_se(unit_add_name(a, "a.service") >= 0);
static void test_fgetxattrat_fake(void) {
char t[] = "/var/tmp/xattrtestXXXXXX";
+ _cleanup_free_ char *value = NULL;
_cleanup_close_ int fd = -1;
const char *x;
char v[3];
int r;
size_t size;
+ log_info("/* %s */", __func__);
+
assert_se(mkdtemp(t));
x = strjoina(t, "/test");
assert_se(touch(x) >= 0);
r = fgetxattrat_fake(fd, "usr", "user.idontexist", v, 3, 0, &size);
assert_se(r == -ENODATA || ERRNO_IS_NOT_SUPPORTED(r));
+ safe_close(fd);
+ fd = open(x, O_PATH|O_CLOEXEC);
+ assert_se(fd >= 0);
+ r = fgetxattrat_fake_malloc(fd, NULL, "user.foo", AT_EMPTY_PATH, &value);
+ assert_se(r == 3);
+ assert_se(streq(value, "bar"));
+
cleanup:
assert_se(unlink(x) >= 0);
assert_se(rmdir(t) >= 0);
usec_t usec, k;
int r;
+ log_info("/* %s */", __func__);
+
assert_se(tmp_dir(&vt) >= 0);
fd = open_tmpfile_unlinkable(vt, O_RDWR);
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1;
Time.PollIntervalMinSec, config_parse_sec, 0, offsetof(Manager, poll_interval_min_usec)
Time.PollIntervalMaxSec, config_parse_sec, 0, offsetof(Manager, poll_interval_max_usec)
Time.ConnectionRetrySec, config_parse_sec, 0, offsetof(Manager, connection_retry_usec)
+Time.SaveIntervalSec, config_parse_sec, 0, offsetof(Manager, save_time_interval_usec)
static int manager_clock_watch_setup(Manager *m);
static int manager_listen_setup(Manager *m);
static void manager_listen_stop(Manager *m);
+static int manager_save_time_and_rearm(Manager *m);
static double ntp_ts_short_to_d(const struct ntp_ts_short *ts) {
return be16toh(ts->sec) + (be16toh(ts->frac) / 65536.0);
if (r < 0)
return -errno;
+ r = manager_save_time_and_rearm(m);
+ if (r < 0)
+ return r;
+
/* If touch fails, there isn't much we can do. Maybe it'll work next time. */
- (void) touch("/var/lib/systemd/timesync/clock");
(void) touch("/run/systemd/timesync/synchronized");
m->drift_freq = tmx.freq;
.iov_base = &ntpmsg,
.iov_len = sizeof(ntpmsg),
};
- CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct timeval))) control;
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct timespec))) control;
union sockaddr_union server_addr;
struct msghdr msghdr = {
.msg_iov = &iov,
m->poll_interval_usec / USEC_PER_SEC);
if (!spike) {
- m->sync = true;
r = manager_adjust_clock(m, offset, leap_sec);
if (r < 0)
log_error_errno(r, "Failed to call clock_adjtime(): %m");
sd_event_source_unref(m->network_event_source);
sd_network_monitor_unref(m->network_monitor);
+ sd_event_source_unref(m->event_save_time);
+
sd_resolve_unref(m->resolve);
sd_event_unref(m->event);
m->ratelimit = (RateLimit) { RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST };
+ m->save_time_interval_usec = DEFAULT_SAVE_TIME_INTERVAL_USEC;
+
r = sd_event_default(&m->event);
if (r < 0)
return r;
return 0;
}
+
+static int manager_save_time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+ Manager *m = userdata;
+
+ assert(m);
+
+ (void) manager_save_time_and_rearm(m);
+ return 0;
+}
+
+int manager_setup_save_time_event(Manager *m) {
+ int r;
+
+ assert(m);
+ assert(!m->event_save_time);
+
+ if (m->save_time_interval_usec == USEC_INFINITY)
+ return 0;
+
+ /* NB: we'll accumulate scheduling latencies here, but this doesn't matter */
+ r = sd_event_add_time_relative(
+ m->event, &m->event_save_time,
+ clock_boottime_or_monotonic(),
+ m->save_time_interval_usec,
+ 10 * USEC_PER_SEC,
+ manager_save_time_handler, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add save time event: %m");
+
+ (void) sd_event_source_set_description(m->event_save_time, "save-time");
+
+ return 0;
+}
+
+static int manager_save_time_and_rearm(Manager *m) {
+ int r;
+
+ assert(m);
+
+ r = touch(CLOCK_FILE);
+ if (r < 0)
+ log_debug_errno(r, "Failed to update " CLOCK_FILE ", ignoring: %m");
+
+ m->save_on_exit = true;
+
+ if (m->save_time_interval_usec != USEC_INFINITY) {
+ r = sd_event_source_set_time_relative(m->event_save_time, m->save_time_interval_usec);
+ if (r < 0)
+ return log_error_errno(r, "Failed to rearm save time event: %m");
+
+ r = sd_event_source_set_enabled(m->event_save_time, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable save time event: %m");
+ }
+
+ return 0;
+}
#define NTP_RETRY_INTERVAL_MIN_USEC (15 * USEC_PER_SEC)
#define NTP_RETRY_INTERVAL_MAX_USEC (6 * 60 * USEC_PER_SEC) /* 6 minutes */
-#define DEFAULT_CONNECTION_RETRY_USEC (30*USEC_PER_SEC)
+#define DEFAULT_CONNECTION_RETRY_USEC (30 * USEC_PER_SEC)
+
+#define DEFAULT_SAVE_TIME_INTERVAL_USEC (60 * USEC_PER_SEC)
+
+#define STATE_DIR "/var/lib/systemd/timesync"
+#define CLOCK_FILE STATE_DIR "/clock"
struct Manager {
sd_bus *bus;
/* last change */
bool jumped;
- bool sync;
int64_t drift_freq;
/* watch for time changes */
struct ntp_msg ntpmsg;
struct timespec origin_time, dest_time;
bool spike;
+
+ /* save time event */
+ sd_event_source *event_save_time;
+ usec_t save_time_interval_usec;
+ bool save_on_exit;
};
int manager_new(Manager **ret);
int manager_connect(Manager *m);
void manager_disconnect(Manager *m);
+
+int manager_setup_save_time_event(Manager *m);
LIST_FIND_TAIL(names, m->fallback_servers, tail);
LIST_INSERT_AFTER(names, m->fallback_servers, tail, n);
} else
- assert_not_reached("Unknown server type");
+ assert_not_reached();
n->manager = m;
else if (n->type == SERVER_FALLBACK)
LIST_REMOVE(names, n->manager->fallback_servers, n);
else
- assert_not_reached("Unknown server type");
+ assert_not_reached();
if (n->manager->current_server_name == n)
manager_set_server_name(n->manager, NULL);
#include "timesyncd-manager.h"
#include "user-util.h"
-#define STATE_DIR "/var/lib/systemd/timesync"
-#define CLOCK_FILE STATE_DIR "/clock"
-
static int load_clock_timestamp(uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
usec_t min = TIME_EPOCH * USEC_PER_SEC;
"STATUS=Daemon is running",
NOTIFY_STOPPING);
+ r = manager_setup_save_time_event(m);
+ if (r < 0)
+ return r;
+
if (network_is_online()) {
r = manager_connect(m);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
/* if we got an authoritative time, store it in the file system */
- if (m->sync) {
+ if (m->save_on_exit) {
r = touch(CLOCK_FILE);
if (r < 0)
- log_debug_errno(r, "Failed to touch %s, ignoring: %m", CLOCK_FILE);
+ log_debug_errno(r, "Failed to touch " CLOCK_FILE ", ignoring: %m");
}
return 0;
#RootDistanceMaxSec=5
#PollIntervalMinSec=32
#PollIntervalMaxSec=2048
+#SaveIntervalSec=60
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 0;
}
static int fd_set_xattrs(Item *i, int fd, const char *path, const struct stat *st) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
char **name, **value;
assert(i);
assert(fd >= 0);
assert(path);
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-
STRV_FOREACH_PAIR(name, value, i->xattrs) {
log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
- if (setxattr(procfs_path, *name, *value, strlen(*value), 0) < 0)
+ if (setxattr(FORMAT_PROC_FD_PATH(fd), *name, *value, strlen(*value), 0) < 0)
return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m",
*name, *value, path);
}
static int fd_set_acls(Item *item, int fd, const char *path, const struct stat *st) {
int r = 0;
#if HAVE_ACL
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
struct stat stbuf;
assert(item);
return 0;
}
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-
if (item->acl_access)
- r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->append_or_force);
+ r = path_set_acl(FORMAT_PROC_FD_PATH(fd), path, ACL_TYPE_ACCESS, item->acl_access, item->append_or_force);
/* set only default acls to folders */
if (r == 0 && item->acl_default && S_ISDIR(st->st_mode))
- r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->append_or_force);
+ r = path_set_acl(FORMAT_PROC_FD_PATH(fd), path, ACL_TYPE_DEFAULT, item->acl_default, item->append_or_force);
if (ERRNO_IS_NOT_SUPPORTED(r)) {
log_debug_errno(r, "ACLs not supported by file system at %s", path);
r = action(i, fd, path, &st);
if (S_ISDIR(st.st_mode)) {
- char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
- /* The passed 'fd' was opened with O_PATH. We need to convert
- * it into a 'regular' fd before reading the directory content. */
- xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-
- d = opendir(procfs_path);
+ /* The passed 'fd' was opened with O_PATH. We need to convert it into a 'regular' fd before
+ * reading the directory content. */
+ d = opendir(FORMAT_PROC_FD_PATH(fd));
if (!d) {
- log_error_errno(errno, "Failed to opendir() '%s': %m", procfs_path);
+ log_error_errno(errno, "Failed to opendir() '%s': %m", FORMAT_PROC_FD_PATH(fd));
if (r == 0)
r = -errno;
goto finish;
break;
default:
- assert_not_reached("wut?");
+ assert_not_reached();
}
return 0;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (arg_operation == 0 && !arg_cat_config)
else if (phase == PHASE_CREATE)
op = arg_operation & OPERATION_CREATE;
else
- assert_not_reached("unexpected phase");
+ assert_not_reached();
if (op == 0) /* Nothing requested in this phase */
continue;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
if (optind != argc)
case 'h':
return help();
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
arg_node = argv[optind];
case '?':
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
return 1;
link_with : udev_link_with,
dependencies : [libblkid, libkmod])
-udev_id_progs = [['ata_id/ata_id.c'],
- ['cdrom_id/cdrom_id.c'],
- ['fido_id/fido_id.c',
- 'fido_id/fido_id_desc.c',
- 'fido_id/fido_id_desc.h'],
- ['scsi_id/scsi_id.c',
- 'scsi_id/scsi_id.h',
- 'scsi_id/scsi_serial.c',
- 'scsi_id/scsi.h'],
- ['v4l_id/v4l_id.c'],
- ['mtd_probe/mtd_probe.c',
- 'mtd_probe/mtd_probe.h',
- 'mtd_probe/probe_smartmedia.c']]
+udev_progs = [['ata_id/ata_id.c'],
+ ['cdrom_id/cdrom_id.c'],
+ ['fido_id/fido_id.c',
+ 'fido_id/fido_id_desc.c',
+ 'fido_id/fido_id_desc.h'],
+ ['scsi_id/scsi_id.c',
+ 'scsi_id/scsi_id.h',
+ 'scsi_id/scsi_serial.c',
+ 'scsi_id/scsi.h'],
+ ['v4l_id/v4l_id.c'],
+ ['mtd_probe/mtd_probe.c',
+ 'mtd_probe/mtd_probe.h',
+ 'mtd_probe/probe_smartmedia.c']]
dmi_arches = ['x86', 'x86_64', 'aarch64', 'arm', 'ia64', 'mips']
if dmi_arches.contains(host_machine.cpu_family())
- udev_id_progs += [['dmi_memory_id/dmi_memory_id.c']]
+ udev_progs += [['dmi_memory_id/dmi_memory_id.c']]
endif
-foreach prog : udev_id_progs
+udev_prog_paths = {}
+foreach prog : udev_progs
name = prog[0].split('/')[0]
exe = executable(
name,
prog,
include_directories : includes,
- dependencies : [versiondep],
+ dependencies : versiondep,
link_with : udev_link_with,
install_rpath : udev_rpath,
install : true,
install_dir : udevlibexecdir)
- # TODO: let's use a dictionary instead as soon as we can depend on meson >= 0.47.
- if name == 'dmi_memory_id'
- dmi_memory_id_path = exe.full_path()
- endif
+ udev_prog_paths += {name : exe.full_path()}
endforeach
if install_sysconfdir_samples
install_data('udev.conf',
- install_dir : join_paths(sysconfdir, 'udev'))
+ install_dir : sysconfdir / 'udev')
endif
custom_target(
if install_sysconfdir
meson.add_install_script('sh', '-c',
- mkdir_p.format(join_paths(sysconfdir, 'udev/rules.d')))
+ mkdir_p.format(sysconfdir / 'udev/rules.d'))
endif
fuzzers += [
%struct-type
%includes
%%
-Match.MACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.mac)
-Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.permanent_mac)
-Match.OriginalName, config_parse_match_ifnames, 0, offsetof(LinkConfig, match.ifname)
-Match.Path, config_parse_match_strv, 0, offsetof(LinkConfig, match.path)
-Match.Driver, config_parse_match_strv, 0, offsetof(LinkConfig, match.driver)
-Match.Type, config_parse_match_strv, 0, offsetof(LinkConfig, match.iftype)
-Match.Property, config_parse_match_property, 0, offsetof(LinkConfig, match.property)
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(LinkConfig, conditions)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(LinkConfig, conditions)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions)
-Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(LinkConfig, conditions)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions)
-Link.Description, config_parse_string, 0, offsetof(LinkConfig, description)
-Link.MACAddressPolicy, config_parse_mac_address_policy, 0, offsetof(LinkConfig, mac_address_policy)
-Link.MACAddress, config_parse_hwaddr, 0, offsetof(LinkConfig, mac)
-Link.NamePolicy, config_parse_name_policy, 0, offsetof(LinkConfig, name_policy)
-Link.Name, config_parse_ifname, 0, offsetof(LinkConfig, name)
-Link.AlternativeName, config_parse_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(LinkConfig, alternative_names)
-Link.AlternativeNamesPolicy, config_parse_alternative_names_policy, 0, offsetof(LinkConfig, alternative_names_policy)
-Link.Alias, config_parse_ifalias, 0, offsetof(LinkConfig, alias)
-Link.TransmitQueues, config_parse_rx_tx_queues, 0, offsetof(LinkConfig, txqueues)
-Link.ReceiveQueues, config_parse_rx_tx_queues, 0, offsetof(LinkConfig, rxqueues)
-Link.TransmitQueueLength, config_parse_txqueuelen, 0, offsetof(LinkConfig, txqueuelen)
-Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(LinkConfig, mtu)
-Link.BitsPerSecond, config_parse_si_uint64, 0, offsetof(LinkConfig, speed)
-Link.Duplex, config_parse_duplex, 0, offsetof(LinkConfig, duplex)
-Link.AutoNegotiation, config_parse_tristate, 0, offsetof(LinkConfig, autonegotiation)
-Link.WakeOnLan, config_parse_wol, 0, offsetof(LinkConfig, wol)
-Link.Port, config_parse_port, 0, offsetof(LinkConfig, port)
-Link.ReceiveChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_RX])
-Link.TransmitChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TX])
-Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GSO])
-Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TSO])
-Link.TCP6SegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TSO6])
-Link.UDPSegmentationOffload, config_parse_warn_compat, DISABLED_LEGACY, 0
-Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GRO])
-Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_LRO])
-Link.RxChannels, config_parse_channel, 0, offsetof(LinkConfig, channels)
-Link.TxChannels, config_parse_channel, 0, offsetof(LinkConfig, channels)
-Link.OtherChannels, config_parse_channel, 0, offsetof(LinkConfig, channels)
-Link.CombinedChannels, config_parse_channel, 0, offsetof(LinkConfig, channels)
-Link.Advertise, config_parse_advertise, 0, offsetof(LinkConfig, advertise)
-Link.RxBufferSize, config_parse_nic_buffer_size, 0, offsetof(LinkConfig, ring)
-Link.RxMiniBufferSize, config_parse_nic_buffer_size, 0, offsetof(LinkConfig, ring)
-Link.RxJumboBufferSize, config_parse_nic_buffer_size, 0, offsetof(LinkConfig, ring)
-Link.TxBufferSize, config_parse_nic_buffer_size, 0, offsetof(LinkConfig, ring)
-Link.RxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, rx_flow_control)
-Link.TxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, tx_flow_control)
-Link.AutoNegotiationFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, autoneg_flow_control)
-Link.GenericSegmentOffloadMaxBytes, config_parse_iec_size, 0, offsetof(LinkConfig, gso_max_size)
-Link.GenericSegmentOffloadMaxSegments, config_parse_uint32, 0, offsetof(LinkConfig, gso_max_segments)
+Match.MACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.mac)
+Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.permanent_mac)
+Match.OriginalName, config_parse_match_ifnames, 0, offsetof(LinkConfig, match.ifname)
+Match.Path, config_parse_match_strv, 0, offsetof(LinkConfig, match.path)
+Match.Driver, config_parse_match_strv, 0, offsetof(LinkConfig, match.driver)
+Match.Type, config_parse_match_strv, 0, offsetof(LinkConfig, match.iftype)
+Match.Property, config_parse_match_property, 0, offsetof(LinkConfig, match.property)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(LinkConfig, conditions)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(LinkConfig, conditions)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions)
+Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(LinkConfig, conditions)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions)
+Link.Description, config_parse_string, 0, offsetof(LinkConfig, description)
+Link.MACAddressPolicy, config_parse_mac_address_policy, 0, offsetof(LinkConfig, mac_address_policy)
+Link.MACAddress, config_parse_hwaddr, 0, offsetof(LinkConfig, mac)
+Link.NamePolicy, config_parse_name_policy, 0, offsetof(LinkConfig, name_policy)
+Link.Name, config_parse_ifname, 0, offsetof(LinkConfig, name)
+Link.AlternativeName, config_parse_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(LinkConfig, alternative_names)
+Link.AlternativeNamesPolicy, config_parse_alternative_names_policy, 0, offsetof(LinkConfig, alternative_names_policy)
+Link.Alias, config_parse_ifalias, 0, offsetof(LinkConfig, alias)
+Link.TransmitQueues, config_parse_rx_tx_queues, 0, offsetof(LinkConfig, txqueues)
+Link.ReceiveQueues, config_parse_rx_tx_queues, 0, offsetof(LinkConfig, rxqueues)
+Link.TransmitQueueLength, config_parse_txqueuelen, 0, offsetof(LinkConfig, txqueuelen)
+Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(LinkConfig, mtu)
+Link.BitsPerSecond, config_parse_si_uint64, 0, offsetof(LinkConfig, speed)
+Link.Duplex, config_parse_duplex, 0, offsetof(LinkConfig, duplex)
+Link.AutoNegotiation, config_parse_tristate, 0, offsetof(LinkConfig, autonegotiation)
+Link.WakeOnLan, config_parse_wol, 0, offsetof(LinkConfig, wol)
+Link.Port, config_parse_port, 0, offsetof(LinkConfig, port)
+Link.ReceiveChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_RX])
+Link.TransmitChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TX])
+Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GSO])
+Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TSO])
+Link.TCP6SegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TSO6])
+Link.UDPSegmentationOffload, config_parse_warn_compat, DISABLED_LEGACY, 0
+Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GRO])
+Link.GenericReceiveOffloadHardware, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GRO_HW])
+Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_LRO])
+Link.RxChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.rx)
+Link.TxChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.tx)
+Link.OtherChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.other)
+Link.CombinedChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.combined)
+Link.Advertise, config_parse_advertise, 0, offsetof(LinkConfig, advertise)
+Link.RxBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx)
+Link.RxMiniBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx_mini)
+Link.RxJumboBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx_jumbo)
+Link.TxBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.tx)
+Link.RxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, rx_flow_control)
+Link.TxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, tx_flow_control)
+Link.AutoNegotiationFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, autoneg_flow_control)
+Link.GenericSegmentOffloadMaxBytes, config_parse_iec_size, 0, offsetof(LinkConfig, gso_max_size)
+Link.GenericSegmentOffloadMaxSegments, config_parse_uint32, 0, offsetof(LinkConfig, gso_max_segments)
+Link.RxCoalesceSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs)
+Link.RxMaxCoalescedFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.rx_max_coalesced_frames)
+Link.RxCoalesceIrqSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs_irq)
+Link.RxMaxCoalescedIrqFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.rx_max_coalesced_frames_irq)
+Link.TxCoalesceSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs)
+Link.TxMaxCoalescedFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames)
+Link.TxCoalesceIrqSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_irq)
+Link.TxMaxCoalescedIrqFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_irq)
+Link.StatisticsBlockCoalesceSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.stats_block_coalesce_usecs)
+Link.UseAdaptiveRxCoalesce, config_parse_tristate, 0, offsetof(LinkConfig, coalesce.use_adaptive_rx_coalesce)
+Link.UseAdaptiveTxCoalesce, config_parse_tristate, 0, offsetof(LinkConfig, coalesce.use_adaptive_tx_coalesce)
+Link.CoalescePacketRateLow, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.pkt_rate_low)
+Link.RxCoalesceLowSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs_low)
+Link.RxMaxCoalescedLowFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.rx_max_coalesced_frames_low)
+Link.TxCoalesceLowSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_low)
+Link.TxMaxCoalescedLowFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_low)
+Link.CoalescePacketRateHigh, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.pkt_rate_high)
+Link.RxCoalesceHighSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs_high)
+Link.RxMaxCoalescedHighFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.rx_max_coalesced_frames_high)
+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)
if (r < 0)
log_device_warning_errno(device, r, "Could not set flow control, ignoring: %m");
+ r = ethtool_set_nic_coalesce_settings(ethtool_fd, name, &config->coalesce);
+ if (r < 0)
+ log_device_warning_errno(device, r, "Could not set coalesce settings, ignoring: %m");
+
return 0;
}
(void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &new_name);
break;
default:
- assert_not_reached("invalid policy");
+ assert_not_reached();
}
if (ifname_valid(new_name)) {
log_device_debug(device, "Policy *%s* yields \"%s\".", name_policy_to_string(policy), new_name);
(void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &n);
break;
default:
- assert_not_reached("invalid policy");
+ assert_not_reached();
}
if (!isempty(n)) {
r = strv_extend(&altnames, n);
int rx_flow_control;
int tx_flow_control;
int autoneg_flow_control;
+ netdev_coalesce_param coalesce;
LIST_FIELDS(LinkConfig, links);
};
return -1;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
if (optind < argc && !dev_specified) {
else if (streq(argv[1], "test2"))
test2();
else
- assert_not_reached("unknown command.");
+ assert_not_reached();
return 0;
}
_cleanup_free_ char *root_id = NULL, *root_label = NULL;
bool found_esp = false;
- blkid_partlist pl;
- int i, nvals, r;
+ int r;
assert(pr);
* disk, and add a property indicating its partition UUID. */
errno = 0;
- pl = blkid_probe_get_partitions(pr);
+ blkid_partlist pl = blkid_probe_get_partitions(pr);
if (!pl)
return errno_or_else(ENOMEM);
- nvals = blkid_partlist_numof_partitions(pl);
- for (i = 0; i < nvals; i++) {
+ int nvals = blkid_partlist_numof_partitions(pl);
+ for (int i = 0; i < nvals; i++) {
blkid_partition pp;
const char *stype, *sid, *label;
sd_id128_t type;
bool noraid = false, is_gpt = false;
_cleanup_close_ int fd = -1;
int64_t offset = 0;
- int nvals, i, r;
+ int r;
static const struct option options[] = {
{ "offset", required_argument, NULL, 'o' },
(void) sd_device_get_property_value(dev, "ID_PART_GPT_AUTO_ROOT_UUID", &root_partition);
errno = 0;
- nvals = blkid_probe_numof_values(pr);
+ int nvals = blkid_probe_numof_values(pr);
if (nvals < 0)
return log_device_debug_errno(dev, errno_or_else(ENOMEM), "Failed to get number of probed values: %m");
- for (i = 0; i < nvals; i++) {
+ for (int i = 0; i < nvals; i++) {
if (blkid_probe_get_value(pr, i, &name, &data, NULL) < 0)
continue;
return NULL;
(void) sd_device_get_sysattr_value(dev, "product", &n);
- snprintf(s, size, "usb:v%04Xp%04X:%s", vn, pn, strempty(n));
+ (void) snprintf(s, size, "usb:v%04Xp%04X:%s", vn, pn, strempty(n));
return s;
}
memzero(bitmask, bitmask_size);
i = 0;
- while ((word = strrchr(text, ' ')) != NULL) {
+ while ((word = strrchr(text, ' '))) {
r = safe_atolu_full(word+1, 16, &val);
if (r < 0)
log_device_debug_errno(pdev, r, "Ignoring %s block which failed to parse: %m", attr);
else
log_device_debug(pdev, "Ignoring %s block %lX which is larger than maximum size", attr, val);
*word = '\0';
- ++i;
+ i++;
}
r = safe_atolu_full(text, 16, &val);
if (r < 0)
/* skip over leading zeros */
while (bitmask[val-1] == 0 && val > 0)
--val;
- for (i = 0; i < val; ++i) {
+ for (unsigned long j = 0; j < val; j++) {
DISABLE_WARNING_FORMAT_NONLITERAL;
- log_device_debug(pdev, text, i * BITS_PER_LONG, bitmask[i]);
+ log_device_debug(pdev, text, j * BITS_PER_LONG, bitmask[j]);
REENABLE_WARNING;
}
}
const unsigned long* bitmask_rel,
const unsigned long* bitmask_props,
bool test) {
- int button, axis;
bool has_abs_coordinates = false;
bool has_rel_coordinates = false;
bool has_mt_coordinates = false;
has_stylus = test_bit(BTN_STYLUS, bitmask_key);
has_pen = test_bit(BTN_TOOL_PEN, bitmask_key);
finger_but_no_pen = test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key);
- for (button = BTN_MOUSE; button < BTN_JOYSTICK && !has_mouse_button; button++)
+ for (int button = BTN_MOUSE; button < BTN_JOYSTICK && !has_mouse_button; button++)
has_mouse_button = test_bit(button, bitmask_key);
has_rel_coordinates = test_bit(EV_REL, bitmask_ev) && test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel);
has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs);
* Catz Mad Catz M.M.O.TE). Skip those.
*/
if (!test_bit(BTN_JOYSTICK - 1, bitmask_key)) {
- for (button = BTN_JOYSTICK; button < BTN_DIGI && !has_joystick_axes_or_buttons; button++)
+ for (int button = BTN_JOYSTICK; button < BTN_DIGI && !has_joystick_axes_or_buttons; button++)
has_joystick_axes_or_buttons = test_bit(button, bitmask_key);
- for (button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !has_joystick_axes_or_buttons; button++)
+ for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !has_joystick_axes_or_buttons; button++)
has_joystick_axes_or_buttons = test_bit(button, bitmask_key);
- for (button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !has_joystick_axes_or_buttons; button++)
+ for (int button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !has_joystick_axes_or_buttons; button++)
has_joystick_axes_or_buttons = test_bit(button, bitmask_key);
}
- for (axis = ABS_RX; axis < ABS_PRESSURE && !has_joystick_axes_or_buttons; axis++)
+ for (int axis = ABS_RX; axis < ABS_PRESSURE && !has_joystick_axes_or_buttons; axis++)
has_joystick_axes_or_buttons = test_bit(axis, bitmask_abs);
if (has_abs_coordinates) {
const unsigned long* bitmask_ev,
const unsigned long* bitmask_key,
bool test) {
- unsigned i;
- unsigned long found;
- unsigned long mask;
- bool ret = false;
+
+ bool found = false;
/* do we have any KEY_* capability? */
if (!test_bit(EV_KEY, bitmask_ev)) {
}
/* only consider KEY_* here, not BTN_* */
- found = 0;
- for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) {
- found |= bitmask_key[i];
- log_device_debug(dev, "test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0);
+ for (size_t i = 0; i < BTN_MISC/BITS_PER_LONG && !found; i++) {
+ if (bitmask_key[i])
+ found = true;
+
+ log_device_debug(dev, "test_key: checking bit block %zu for any keys; found=%s",
+ i * BITS_PER_LONG, yes_no(found));
}
/* If there are no keys in the lower block, check the higher blocks */
- if (!found) {
- unsigned block;
- for (block = 0; block < (sizeof(high_key_blocks) / sizeof(struct range)); ++block) {
- for (i = high_key_blocks[block].start; i < high_key_blocks[block].end; ++i) {
- if (test_bit(i, bitmask_key)) {
- log_device_debug(dev, "test_key: Found key %x in high block", i);
- found = 1;
- break;
- }
+ for (size_t block = 0; block < sizeof(high_key_blocks) / sizeof(struct range) && !found; block++)
+ for (unsigned i = high_key_blocks[block].start; i < high_key_blocks[block].end && !found; i++)
+ if (test_bit(i, bitmask_key)) {
+ log_device_debug(dev, "test_key: Found key %x in high block", i);
+ found = true;
}
- }
- }
- if (found > 0) {
+ if (found)
udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
- ret = true;
- }
/* the first 32 bits are ESC, numbers, and Q to D; if we have all of
* those, consider it a full keyboard; do not test KEY_RESERVED, though */
- mask = 0xFFFFFFFE;
- if (FLAGS_SET(bitmask_key[0], mask)) {
+ if (FLAGS_SET(bitmask_key[0], 0xFFFFFFFE)) {
udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1");
- ret = true;
+ return true;
}
- return ret;
+ return found;
}
static int builtin_input_id(sd_device *dev, int argc, char *argv[], bool test) {
}
static int builtin_kmod(sd_device *dev, int argc, char *argv[], bool test) {
- int i;
-
if (!ctx)
return 0;
if (argc < 3 || !streq(argv[1], "load"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: expected: load <module>", argv[0]);
+ "%s: expected: load <module>…", argv[0]);
- for (i = 2; argv[i]; i++)
+ for (int i = 2; argv[i]; i++)
(void) module_load_and_warn(ctx, argv[i], false);
return 0;
/* skip intermediate virtio devices */
static sd_device *skip_virtio(sd_device *dev) {
- sd_device *parent;
-
/* there can only ever be one virtio bus per parent device, so we can
* safely ignore any virtio buses. see
* http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html */
- for (parent = dev; parent; ) {
+ while (dev) {
const char *subsystem;
- if (sd_device_get_subsystem(parent, &subsystem) < 0)
+ if (sd_device_get_subsystem(dev, &subsystem) < 0)
break;
if (!streq(subsystem, "virtio"))
break;
- if (sd_device_get_parent(parent, &parent) < 0)
+ if (sd_device_get_parent(dev, &dev) < 0)
return NULL;
}
- return parent;
+ return dev;
}
static int get_virtfn_info(sd_device *dev, struct netnames *names, struct virtfn_info *ret) {
}
static sd_device *skip_subsystem(sd_device *dev, const char *subsys) {
- sd_device *parent;
-
assert(dev);
assert(subsys);
- for (parent = dev; ; ) {
+ for (;;) {
const char *subsystem;
- if (sd_device_get_subsystem(parent, &subsystem) < 0)
+ if (sd_device_get_subsystem(dev, &subsystem) < 0)
break;
if (!streq(subsystem, subsys))
break;
- dev = parent;
- if (sd_device_get_parent(dev, &parent) < 0)
+ if (sd_device_get_parent(dev, &dev) < 0)
break;
}
const char *guid_str;
_cleanup_free_ char *lun = NULL;
char guid[39];
- size_t i, k;
assert(parent);
assert(path);
if (strlen(guid_str) < guid_str_len || guid_str[0] != '{' || guid_str[guid_str_len-1] != '}')
return NULL;
- for (i = 1, k = 0; i < guid_str_len-1; i++) {
+ size_t k = 0;
+ for (size_t i = 1; i < guid_str_len-1; i++) {
if (guid_str[i] == '-')
continue;
guid[k++] = guid_str[i];
{
char tag[UDEV_NAME_SIZE];
- size_t i;
- const char *p;
+ size_t i = 0;
/* compose valid udev tag name */
- for (p = path, i = 0; *p; p++) {
+ for (const char *p = path; *p; p++) {
if ((*p >= '0' && *p <= '9') ||
(*p >= 'A' && *p <= 'Z') ||
(*p >= 'a' && *p <= 'z') ||
const char *usb_serial;
if (sd_device_get_sysattr_value(dev_usb, "serial", &usb_serial) >= 0) {
- const unsigned char *p;
-
/* http://msdn.microsoft.com/en-us/library/windows/hardware/gg487321.aspx */
- for (p = (unsigned char *) usb_serial; *p != '\0'; p++)
+ for (const unsigned char *p = (unsigned char*) usb_serial; *p != '\0'; p++)
if (*p < 0x20 || *p > 0x7f || *p == ',') {
usb_serial = NULL;
break;
/* wire protocol magic must match */
#define UDEV_CTRL_MAGIC 0xdead1dea
-struct udev_ctrl_msg_wire {
+typedef struct UdevCtrlMessageWire {
char version[16];
unsigned magic;
- enum udev_ctrl_msg_type type;
- union udev_ctrl_msg_value value;
-};
+ UdevCtrlMessageType type;
+ UdevCtrlMessageValue value;
+} UdevCtrlMessageWire;
-struct udev_ctrl {
+struct UdevCtrl {
unsigned n_ref;
int sock;
int sock_connect;
void *userdata;
};
-int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd) {
+int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd) {
_cleanup_close_ int sock = -1;
- struct udev_ctrl *uctrl;
+ UdevCtrl *uctrl;
assert(ret);
return log_error_errno(errno, "Failed to create socket: %m");
}
- uctrl = new(struct udev_ctrl, 1);
+ uctrl = new(UdevCtrl, 1);
if (!uctrl)
return -ENOMEM;
- *uctrl = (struct udev_ctrl) {
+ *uctrl = (UdevCtrl) {
.n_ref = 1,
.sock = fd >= 0 ? fd : TAKE_FD(sock),
.sock_connect = -1,
return 0;
}
-int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
+int udev_ctrl_enable_receiving(UdevCtrl *uctrl) {
int r;
assert(uctrl);
return 0;
}
-static void udev_ctrl_disconnect(struct udev_ctrl *uctrl) {
+static void udev_ctrl_disconnect(UdevCtrl *uctrl) {
if (!uctrl)
return;
uctrl->sock_connect = safe_close(uctrl->sock_connect);
}
-static struct udev_ctrl *udev_ctrl_free(struct udev_ctrl *uctrl) {
+static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) {
assert(uctrl);
udev_ctrl_disconnect(uctrl);
return mfree(uctrl);
}
-DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl, udev_ctrl, udev_ctrl_free);
+DEFINE_TRIVIAL_REF_UNREF_FUNC(UdevCtrl, udev_ctrl, udev_ctrl_free);
-int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
+int udev_ctrl_cleanup(UdevCtrl *uctrl) {
if (!uctrl)
return 0;
if (uctrl->cleanup_socket)
return 0;
}
-int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event) {
+int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event) {
int r;
assert_return(uctrl, -EINVAL);
return 0;
}
-sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl) {
+sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl) {
assert(uctrl);
return uctrl->event_source;
}
-static void udev_ctrl_disconnect_and_listen_again(struct udev_ctrl *uctrl) {
+static void udev_ctrl_disconnect_and_listen_again(UdevCtrl *uctrl) {
udev_ctrl_disconnect(uctrl);
udev_ctrl_unref(uctrl);
(void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_ON);
/* We don't return NULL here because uctrl is not freed */
}
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct udev_ctrl*, udev_ctrl_disconnect_and_listen_again, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UdevCtrl*, udev_ctrl_disconnect_and_listen_again, NULL);
static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(udev_ctrl_disconnect_and_listen_againp) struct udev_ctrl *uctrl = NULL;
- struct udev_ctrl_msg_wire msg_wire;
- struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(struct udev_ctrl_msg_wire));
+ _cleanup_(udev_ctrl_disconnect_and_listen_againp) UdevCtrl *uctrl = NULL;
+ UdevCtrlMessageWire msg_wire;
+ struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(UdevCtrlMessageWire));
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
struct msghdr smsg = {
.msg_iov = &iov,
}
static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- struct udev_ctrl *uctrl = userdata;
+ UdevCtrl *uctrl = userdata;
_cleanup_close_ int sock = -1;
struct ucred ucred;
int r;
return 0;
}
-int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
+int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
int r;
assert(uctrl);
return 0;
}
-int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf) {
- struct udev_ctrl_msg_wire ctrl_msg_wire = {
+int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf) {
+ UdevCtrlMessageWire ctrl_msg_wire = {
.version = "udev-" STRINGIFY(PROJECT_VERSION),
.magic = UDEV_CTRL_MAGIC,
.type = type,
return 0;
}
-int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout) {
+int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) {
_cleanup_(sd_event_source_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL;
int r;
#include "macro.h"
#include "time-util.h"
-struct udev_ctrl;
+typedef struct UdevCtrl UdevCtrl;
-enum udev_ctrl_msg_type {
+typedef enum UdevCtrlMessageType {
_UDEV_CTRL_END_MESSAGES,
UDEV_CTRL_SET_LOG_LEVEL,
UDEV_CTRL_STOP_EXEC_QUEUE,
UDEV_CTRL_SET_CHILDREN_MAX,
UDEV_CTRL_PING,
UDEV_CTRL_EXIT,
-};
+} UdevCtrlMessageType;
-union udev_ctrl_msg_value {
+typedef union UdevCtrlMessageValue {
int intval;
char buf[256];
-};
+} UdevCtrlMessageValue;
-typedef int (*udev_ctrl_handler_t)(struct udev_ctrl *udev_ctrl, enum udev_ctrl_msg_type type,
- const union udev_ctrl_msg_value *value, void *userdata);
+typedef int (*udev_ctrl_handler_t)(UdevCtrl *udev_ctrl, UdevCtrlMessageType type,
+ const UdevCtrlMessageValue *value, void *userdata);
-int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd);
-static inline int udev_ctrl_new(struct udev_ctrl **ret) {
+int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd);
+static inline int udev_ctrl_new(UdevCtrl **ret) {
return udev_ctrl_new_from_fd(ret, -1);
}
-int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl);
-struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl);
-struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl);
-int udev_ctrl_cleanup(struct udev_ctrl *uctrl);
-int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event);
-int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata);
-sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl);
+int udev_ctrl_enable_receiving(UdevCtrl *uctrl);
+UdevCtrl *udev_ctrl_ref(UdevCtrl *uctrl);
+UdevCtrl *udev_ctrl_unref(UdevCtrl *uctrl);
+int udev_ctrl_cleanup(UdevCtrl *uctrl);
+int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event);
+int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata);
+sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl);
-int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout);
+int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout);
-int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf);
-static inline int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority) {
+int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf);
+static inline int udev_ctrl_send_set_log_level(UdevCtrl *uctrl, int priority) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL);
}
-static inline int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_stop_exec_queue(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL);
}
-static inline int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_start_exec_queue(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL);
}
-static inline int udev_ctrl_send_reload(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_reload(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL);
}
-static inline int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key) {
+static inline int udev_ctrl_send_set_env(UdevCtrl *uctrl, const char *key) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key);
}
-static inline int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count) {
+static inline int udev_ctrl_send_set_children_max(UdevCtrl *uctrl, int count) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL);
}
-static inline int udev_ctrl_send_ping(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_ping(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL);
}
-static inline int udev_ctrl_send_exit(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_exit(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL);
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(UdevCtrl*, udev_ctrl_unref);
strpcpy(&s, l, val);
break;
default:
- assert_not_reached("Unknown format substitution type");
+ assert_not_reached();
}
return s - dest;
if (!FLAGS_SET(line->type, LINE_HAS_GOTO))
continue;
- LIST_FOREACH_AFTER(rule_lines, i, line)
+ LIST_FOREACH(rule_lines, i, line->rule_lines_next)
if (streq_ptr(i->label, line->goto_label)) {
line->goto_line = i;
break;
}
break;
default:
- assert_not_reached("Invalid match type");
+ assert_not_reached();
}
return token->op == (match ? OP_MATCH : OP_NOMATCH);
value = vbuf;
break;
default:
- assert_not_reached("Invalid attribute substitution type");
+ assert_not_reached();
}
/* remove trailing whitespace, if not asked to match for it */
else if (streq(k, "virt"))
val = virtualization_to_string(detect_virtualization());
else
- assert_not_reached("Invalid CONST key");
+ assert_not_reached();
return token_match_string(token, val);
}
case TK_M_TAG:
/* do nothing for events. */
break;
default:
- assert_not_reached("Invalid token type");
+ assert_not_reached();
}
return true;
}
int control_main(int argc, char *argv[], void *userdata) {
- _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
+ _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
usec_t timeout = 60 * USEC_PER_SEC;
int c, r;
case '?':
return -EINVAL;
default:
- assert_not_reached("Unknown option.");
+ assert_not_reached();
}
if (optind < argc)
case '?':
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
return 1;
return print_record(device);
}
- assert_not_reached("unknown query type");
+ assert_not_reached();
return 0;
}
case '?':
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
if (action == ACTION_DEVICE_ID_FILE) {
else if (action == ACTION_ATTRIBUTE_WALK)
r = print_device_chain(device);
else
- assert_not_reached("Unknown action");
+ assert_not_reached();
if (r < 0)
return r;
}
case '?':
return -EINVAL;
default:
- assert_not_reached("Unknown option.");
+ assert_not_reached();
}
if (!arg_print_kernel && !arg_print_udev) {
case '?':
return -EINVAL;
default:
- assert_not_reached("Unknown option.");
+ assert_not_reached();
}
}
/* guarantee that the udev daemon isn't pre-processing */
if (getuid() == 0) {
- _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
+ _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
if (udev_ctrl_new(&uctrl) >= 0) {
r = udev_ctrl_send_ping(uctrl);
#include "udevadm.h"
#include "udevadm-util.h"
+static sd_device_action_t arg_action = SD_DEVICE_ADD;
static const char *arg_command = NULL;
static const char *arg_syspath = NULL;
static int help(void) {
printf("%s test-builtin [OPTIONS] COMMAND DEVPATH\n\n"
"Test a built-in command.\n\n"
- " -h --help Print this message\n"
- " -V --version Print version of the program\n\n"
+ " -h --help Print this message\n"
+ " -V --version Print version of the program\n\n"
+ " -a --action=ACTION|help Set action string\n"
"Commands:\n",
program_invocation_short_name);
static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
- { "version", no_argument, NULL, 'V' },
- { "help", no_argument, NULL, 'h' },
+ { "action", required_argument, NULL, 'a' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
{}
};
- int c;
+ int r, c;
- while ((c = getopt_long(argc, argv, "Vh", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "a:Vh", options, NULL)) >= 0)
switch (c) {
+ case 'a':
+ r = parse_device_action(optarg, &arg_action);
+ if (r < 0)
+ return log_error_errno(r, "Invalid action '%s'", optarg);
+ if (r == 0)
+ return 0;
+ break;
case 'V':
return print_version();
case 'h':
case '?':
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
arg_command = argv[optind++];
goto finish;
}
- r = find_device(arg_syspath, "/sys", &dev);
+ r = find_device_with_action(arg_syspath, arg_action, &dev);
if (r < 0) {
log_error_errno(r, "Failed to open device '%s': %m", arg_syspath);
goto finish;
#include "strxcpyx.h"
#include "udev-builtin.h"
#include "udev-event.h"
+#include "udevadm-util.h"
#include "udevadm.h"
-static const char *arg_action = "add";
+static sd_device_action_t arg_action = SD_DEVICE_ADD;
static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
-static char arg_syspath[UDEV_PATH_SIZE] = {};
+static const char *arg_syspath = NULL;
static int help(void) {
{}
};
- int c;
+ int r, c;
while ((c = getopt_long(argc, argv, "a:N:Vh", options, NULL)) >= 0)
switch (c) {
- case 'a': {
- sd_device_action_t a;
-
- if (streq(optarg, "help")) {
- dump_device_action_table();
+ case 'a':
+ r = parse_device_action(optarg, &arg_action);
+ if (r < 0)
+ return log_error_errno(r, "Invalid action '%s'", optarg);
+ if (r == 0)
return 0;
- }
-
- a = device_action_from_string(optarg);
- if (a < 0)
- return log_error_errno(a, "Invalid action '%s'", optarg);
-
- arg_action = device_action_to_string(a);
break;
- }
case 'N':
arg_resolve_name_timing = resolve_name_timing_from_string(optarg);
if (arg_resolve_name_timing < 0)
case '?':
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
- if (!argv[optind])
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "syspath parameter missing.");
-
- /* add /sys if needed */
- if (!path_startswith(argv[optind], "/sys"))
- strscpyl(arg_syspath, sizeof(arg_syspath), "/sys", argv[optind], NULL);
- else
- strscpy(arg_syspath, sizeof(arg_syspath), argv[optind]);
+ arg_syspath = argv[optind];
+ if (!arg_syspath)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "syspath parameter missing.");
return 1;
}
goto out;
}
- r = device_new_from_synthetic_event(&dev, arg_syspath, arg_action);
+ r = find_device_with_action(arg_syspath, arg_action, &dev);
if (r < 0) {
log_error_errno(r, "Failed to open device '%s': %m", arg_syspath);
goto out;
else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg);
break;
- case 'c': {
- if (streq(optarg, "help")) {
- dump_device_action_table();
+ case 'c':
+ r = parse_device_action(optarg, &action);
+ if (r < 0)
+ return log_error_errno(r, "Unknown action '%s'", optarg);
+ if (r == 0)
return 0;
- }
-
- action = device_action_from_string(optarg);
- if (action < 0)
- return log_error_errno(action, "Unknown action '%s'", optarg);
break;
- }
case 's':
r = sd_device_enumerator_add_match_subsystem(e, optarg, true);
if (r < 0)
case '?':
return -EINVAL;
default:
- assert_not_reached("Unknown option");
+ assert_not_reached();
}
}
if (ping) {
- _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
+ _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
r = udev_ctrl_new(&uctrl);
if (r < 0)
return log_error_errno(r, "Failed to scan devices: %m");
break;
default:
- assert_not_reached("Unknown device type");
+ assert_not_reached();
}
r = exec_list(e, action, settle_hashmap);
return find_device_from_path(id, ret);
}
+
+int find_device_with_action(const char *id, sd_device_action_t action, sd_device **ret) {
+ _cleanup_free_ char *path = NULL;
+
+ assert(id);
+ assert(ret);
+ assert(action >= 0 && action < _SD_DEVICE_ACTION_MAX);
+
+ if (!path_startswith(id, "/sys")) {
+ path = path_join("/sys", id);
+ if (!path)
+ return -ENOMEM;
+ id = path;
+ }
+
+ return device_new_from_synthetic_event(ret, id, device_action_to_string(action));
+}
+
+int parse_device_action(const char *str, sd_device_action_t *action) {
+ sd_device_action_t a;
+
+ assert(str);
+ assert(action);
+
+ if (streq(str, "help")) {
+ dump_device_action_table();
+ return 0;
+ }
+
+ a = device_action_from_string(str);
+ if (a < 0)
+ return a;
+
+ *action = a;
+ return 1;
+}
#include "sd-device.h"
int find_device(const char *id, const char *prefix, sd_device **ret);
+int find_device_with_action(const char *id, sd_device_action_t action, sd_device **ret);
+int parse_device_action(const char *str, sd_device_action_t *action);
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
return 1; /* work to do */
static int arg_timeout_signal = SIGKILL;
static bool arg_blockdev_read_only = false;
+typedef struct Event Event;
+typedef struct Worker Worker;
+
typedef struct Manager {
sd_event *event;
Hashmap *workers;
- LIST_HEAD(struct event, events);
+ LIST_HEAD(Event, events);
const char *cgroup;
pid_t pid; /* the process that originally allocated the manager object */
int log_level;
sd_netlink *rtnl;
sd_device_monitor *monitor;
- struct udev_ctrl *ctrl;
+ UdevCtrl *ctrl;
int worker_watch[2];
/* used by udev-watch */
bool exit;
} Manager;
-enum event_state {
+typedef enum EventState {
EVENT_UNDEF,
EVENT_QUEUED,
EVENT_RUNNING,
-};
+} EventState;
-struct event {
+typedef struct Event {
Manager *manager;
- struct worker *worker;
- enum event_state state;
+ Worker *worker;
+ EventState state;
sd_device *dev;
sd_device *dev_kernel; /* clone of originally received device */
uint64_t seqnum;
- uint64_t delaying_seqnum;
+ uint64_t blocker_seqnum;
sd_event_source *timeout_warning_event;
sd_event_source *timeout_event;
- LIST_FIELDS(struct event, event);
-};
-
-static void event_queue_cleanup(Manager *manager, enum event_state type);
+ LIST_FIELDS(Event, event);
+} Event;
-enum worker_state {
+typedef enum WorkerState {
WORKER_UNDEF,
WORKER_RUNNING,
WORKER_IDLE,
WORKER_KILLED,
WORKER_KILLING,
-};
+} WorkerState;
-struct worker {
+typedef struct Worker {
Manager *manager;
pid_t pid;
sd_device_monitor *monitor;
- enum worker_state state;
- struct event *event;
-};
+ WorkerState state;
+ Event *event;
+} Worker;
/* passed from worker to main process */
-struct worker_message {
-};
+typedef struct WorkerMessage {
+} WorkerMessage;
-static void event_free(struct event *event) {
+static Event *event_free(Event *event) {
if (!event)
- return;
+ return NULL;
assert(event->manager);
/* only clean up the queue from the process that created it */
if (LIST_IS_EMPTY(event->manager->events) &&
event->manager->pid == getpid_cached())
- if (unlink("/run/udev/queue") < 0)
- log_warning_errno(errno, "Failed to unlink /run/udev/queue: %m");
+ if (unlink("/run/udev/queue") < 0 && errno != ENOENT)
+ log_warning_errno(errno, "Failed to unlink /run/udev/queue, ignoring: %m");
- free(event);
+ return mfree(event);
}
-static struct worker* worker_free(struct worker *worker) {
+static void event_queue_cleanup(Manager *manager, EventState match_state) {
+ Event *event, *tmp;
+
+ LIST_FOREACH_SAFE(event, event, tmp, manager->events) {
+ if (match_state != EVENT_UNDEF && match_state != event->state)
+ continue;
+
+ event_free(event);
+ }
+}
+
+static Worker *worker_free(Worker *worker) {
if (!worker)
return NULL;
return mfree(worker);
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct worker *, worker_free);
-DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(worker_hash_op, void, trivial_hash_func, trivial_compare_func, struct worker, worker_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Worker*, worker_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(worker_hash_op, void, trivial_hash_func, trivial_compare_func, Worker, worker_free);
-static int worker_new(struct worker **ret, Manager *manager, sd_device_monitor *worker_monitor, pid_t pid) {
- _cleanup_(worker_freep) struct worker *worker = NULL;
+static void manager_clear_for_worker(Manager *manager) {
+ assert(manager);
+
+ manager->inotify_event = sd_event_source_unref(manager->inotify_event);
+ manager->kill_workers_event = sd_event_source_unref(manager->kill_workers_event);
+
+ manager->event = sd_event_unref(manager->event);
+
+ manager->workers = hashmap_free(manager->workers);
+ event_queue_cleanup(manager, EVENT_UNDEF);
+
+ manager->monitor = sd_device_monitor_unref(manager->monitor);
+ manager->ctrl = udev_ctrl_unref(manager->ctrl);
+
+ manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
+}
+
+static Manager* manager_free(Manager *manager) {
+ if (!manager)
+ return NULL;
+
+ udev_builtin_exit();
+
+ if (manager->pid == getpid_cached())
+ udev_ctrl_cleanup(manager->ctrl);
+
+ manager_clear_for_worker(manager);
+
+ sd_netlink_unref(manager->rtnl);
+
+ hashmap_free_free_free(manager->properties);
+ udev_rules_free(manager->rules);
+
+ safe_close(manager->inotify_fd);
+ safe_close_pair(manager->worker_watch);
+
+ return mfree(manager);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+static int worker_new(Worker **ret, Manager *manager, sd_device_monitor *worker_monitor, pid_t pid) {
+ _cleanup_(worker_freep) Worker *worker = NULL;
int r;
assert(ret);
/* close monitor, but keep address around */
device_monitor_disconnect(worker_monitor);
- worker = new(struct worker, 1);
+ worker = new(Worker, 1);
if (!worker)
return -ENOMEM;
- *worker = (struct worker) {
+ *worker = (Worker) {
.manager = manager,
.monitor = sd_device_monitor_ref(worker_monitor),
.pid = pid,
return 0;
}
-static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
- struct event *event = userdata;
-
- assert(event);
- assert(event->worker);
-
- kill_and_sigcont(event->worker->pid, arg_timeout_signal);
- event->worker->state = WORKER_KILLED;
-
- log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum);
-
- return 1;
-}
+static void manager_kill_workers(Manager *manager, bool force) {
+ Worker *worker;
-static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
- struct event *event = userdata;
+ assert(manager);
- assert(event);
- assert(event->worker);
+ HASHMAP_FOREACH(worker, manager->workers) {
+ if (worker->state == WORKER_KILLED)
+ continue;
- log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time", event->worker->pid, event->seqnum);
+ if (worker->state == WORKER_RUNNING && !force) {
+ worker->state = WORKER_KILLING;
+ continue;
+ }
- return 1;
+ worker->state = WORKER_KILLED;
+ (void) kill(worker->pid, SIGTERM);
+ }
}
-static void worker_attach_event(struct worker *worker, struct event *event) {
- sd_event *e;
-
- assert(worker);
- assert(worker->manager);
- assert(event);
- assert(!event->worker);
- assert(!worker->event);
-
- worker->state = WORKER_RUNNING;
- worker->event = event;
- event->state = EVENT_RUNNING;
- event->worker = worker;
-
- e = worker->manager->event;
+static void manager_exit(Manager *manager) {
+ assert(manager);
- (void) sd_event_add_time_relative(e, &event->timeout_warning_event, CLOCK_MONOTONIC,
- udev_warn_timeout(arg_event_timeout_usec), USEC_PER_SEC,
- on_event_timeout_warning, event);
+ manager->exit = true;
- (void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
- arg_event_timeout_usec, USEC_PER_SEC,
- on_event_timeout, event);
-}
+ sd_notify(false,
+ "STOPPING=1\n"
+ "STATUS=Starting shutdown...");
-static void manager_clear_for_worker(Manager *manager) {
- assert(manager);
+ /* close sources of new events and discard buffered events */
+ manager->ctrl = udev_ctrl_unref(manager->ctrl);
manager->inotify_event = sd_event_source_unref(manager->inotify_event);
- manager->kill_workers_event = sd_event_source_unref(manager->kill_workers_event);
-
- manager->event = sd_event_unref(manager->event);
-
- manager->workers = hashmap_free(manager->workers);
- event_queue_cleanup(manager, EVENT_UNDEF);
+ manager->inotify_fd = safe_close(manager->inotify_fd);
manager->monitor = sd_device_monitor_unref(manager->monitor);
- manager->ctrl = udev_ctrl_unref(manager->ctrl);
- manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
+ /* discard queued events and kill workers */
+ event_queue_cleanup(manager, EVENT_QUEUED);
+ manager_kill_workers(manager, true);
}
-static Manager* manager_free(Manager *manager) {
- if (!manager)
- return NULL;
+/* reload requested, HUP signal received, rules changed, builtin changed */
+static void manager_reload(Manager *manager) {
- udev_builtin_exit();
+ assert(manager);
- if (manager->pid == getpid_cached())
- udev_ctrl_cleanup(manager->ctrl);
+ sd_notify(false,
+ "RELOADING=1\n"
+ "STATUS=Flushing configuration...");
- manager_clear_for_worker(manager);
+ manager_kill_workers(manager, false);
+ manager->rules = udev_rules_free(manager->rules);
+ udev_builtin_exit();
- sd_netlink_unref(manager->rtnl);
+ sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Processing with %u children at max", arg_children_max);
+}
- hashmap_free_free_free(manager->properties);
- udev_rules_free(manager->rules);
+static int on_kill_workers_event(sd_event_source *s, uint64_t usec, void *userdata) {
+ Manager *manager = userdata;
- safe_close(manager->inotify_fd);
- safe_close_pair(manager->worker_watch);
+ assert(manager);
- return mfree(manager);
-}
+ log_debug("Cleanup idle workers");
+ manager_kill_workers(manager, false);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+ return 1;
+}
static int worker_send_message(int fd) {
- struct worker_message message = {};
+ WorkerMessage message = {};
return loop_write(fd, &message, sizeof(message), false);
}
return 0;
}
-static int worker_spawn(Manager *manager, struct event *event) {
+static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ Event *event = userdata;
+
+ assert(event);
+ assert(event->worker);
+
+ kill_and_sigcont(event->worker->pid, arg_timeout_signal);
+ event->worker->state = WORKER_KILLED;
+
+ log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum);
+
+ return 1;
+}
+
+static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
+ Event *event = userdata;
+
+ assert(event);
+ assert(event->worker);
+
+ log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time", event->worker->pid, event->seqnum);
+
+ return 1;
+}
+
+static void worker_attach_event(Worker *worker, Event *event) {
+ sd_event *e;
+
+ assert(worker);
+ assert(worker->manager);
+ assert(event);
+ assert(!event->worker);
+ assert(!worker->event);
+
+ worker->state = WORKER_RUNNING;
+ worker->event = event;
+ event->state = EVENT_RUNNING;
+ event->worker = worker;
+
+ e = worker->manager->event;
+
+ (void) sd_event_add_time_relative(e, &event->timeout_warning_event, CLOCK_MONOTONIC,
+ udev_warn_timeout(arg_event_timeout_usec), USEC_PER_SEC,
+ on_event_timeout_warning, event);
+
+ (void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
+ arg_event_timeout_usec, USEC_PER_SEC,
+ on_event_timeout, event);
+}
+
+static int worker_spawn(Manager *manager, Event *event) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *worker_monitor = NULL;
- struct worker *worker;
+ Worker *worker;
pid_t pid;
int r;
return 0;
}
-static void event_run(Manager *manager, struct event *event) {
+static int event_run(Event *event) {
static bool log_children_max_reached = true;
- struct worker *worker;
+ Manager *manager;
+ Worker *worker;
int r;
- assert(manager);
assert(event);
+ assert(event->manager);
log_device_uevent(event->dev, "Device ready for processing");
+ manager = event->manager;
HASHMAP_FOREACH(worker, manager->workers) {
if (worker->state != WORKER_IDLE)
continue;
continue;
}
worker_attach_event(worker, event);
- return;
+ return 1; /* event is now processing. */
}
if (hashmap_size(manager->workers) >= arg_children_max) {
-
/* Avoid spamming the debug logs if the limit is already reached and
* many events still need to be processed */
if (log_children_max_reached && arg_children_max > 1) {
log_debug("Maximum number (%u) of children reached.", hashmap_size(manager->workers));
log_children_max_reached = false;
}
- return;
+ return 0; /* no free worker */
}
/* Re-enable the debug message for the next batch of events */
log_children_max_reached = true;
/* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
- and, until the next SELinux policy changes, we safe further reloads in future children */
+ * and, until the next SELinux policy changes, we safe further reloads in future children */
mac_selinux_maybe_reload();
/* start new worker and pass initial device */
- worker_spawn(manager, event);
-}
-
-static int event_queue_insert(Manager *manager, sd_device *dev) {
- _cleanup_(sd_device_unrefp) sd_device *clone = NULL;
- struct event *event;
- uint64_t seqnum;
- int r;
-
- assert(manager);
- assert(dev);
-
- /* only one process can add events to the queue */
- assert(manager->pid == getpid_cached());
-
- /* We only accepts devices received by device monitor. */
- r = sd_device_get_seqnum(dev, &seqnum);
+ r = worker_spawn(manager, event);
if (r < 0)
return r;
- /* Save original device to restore the state on failures. */
- r = device_shallow_clone(dev, &clone);
- if (r < 0)
- return r;
-
- r = device_copy_properties(clone, dev);
- if (r < 0)
- return r;
-
- event = new(struct event, 1);
- if (!event)
- return -ENOMEM;
-
- *event = (struct event) {
- .manager = manager,
- .dev = sd_device_ref(dev),
- .dev_kernel = TAKE_PTR(clone),
- .seqnum = seqnum,
- .state = EVENT_QUEUED,
- };
-
- if (LIST_IS_EMPTY(manager->events)) {
- r = touch("/run/udev/queue");
- if (r < 0)
- log_warning_errno(r, "Failed to touch /run/udev/queue: %m");
- }
-
- LIST_APPEND(event, manager->events, event);
+ return 1; /* event is now processing. */
+}
- log_device_uevent(dev, "Device is queued");
+static int event_is_blocked(Event *event) {
+ const char *subsystem, *devpath, *devpath_old = NULL;
+ dev_t devnum = makedev(0, 0);
+ Event *loop_event;
+ size_t devpath_len;
+ int r, ifindex = 0;
+ bool is_block;
- return 0;
-}
+ /* lookup event for identical, parent, child device */
-static void manager_kill_workers(Manager *manager, bool force) {
- struct worker *worker;
+ assert(event);
+ assert(event->manager);
+ assert(event->blocker_seqnum <= event->seqnum);
- assert(manager);
+ if (event->blocker_seqnum == event->seqnum)
+ /* we have checked previously and no blocker found */
+ return false;
- HASHMAP_FOREACH(worker, manager->workers) {
- if (worker->state == WORKER_KILLED)
+ LIST_FOREACH(event, loop_event, event->manager->events) {
+ /* we already found a later event, earlier cannot block us, no need to check again */
+ if (loop_event->seqnum < event->blocker_seqnum)
continue;
- if (worker->state == WORKER_RUNNING && !force) {
- worker->state = WORKER_KILLING;
- continue;
- }
+ /* event we checked earlier still exists, no need to check again */
+ if (loop_event->seqnum == event->blocker_seqnum)
+ return true;
- worker->state = WORKER_KILLED;
- (void) kill(worker->pid, SIGTERM);
+ /* found ourself, no later event can block us */
+ if (loop_event->seqnum >= event->seqnum)
+ goto no_blocker;
+
+ /* found event we have not checked */
+ break;
}
-}
-/* lookup event for identical, parent, child device */
-static int is_device_busy(Manager *manager, struct event *event) {
- const char *subsystem, *devpath, *devpath_old = NULL;
- dev_t devnum = makedev(0, 0);
- struct event *loop_event;
- size_t devpath_len;
- int r, ifindex = 0;
- bool is_block;
+ assert(loop_event);
+ assert(loop_event->seqnum > event->blocker_seqnum &&
+ loop_event->seqnum < event->seqnum);
r = sd_device_get_subsystem(event->dev, &subsystem);
if (r < 0)
return r;
/* check if queue contains events we depend on */
- LIST_FOREACH(event, loop_event, manager->events) {
+ LIST_FOREACH(event, loop_event, loop_event) {
size_t loop_devpath_len, common;
const char *loop_devpath;
- /* we already found a later event, earlier cannot block us, no need to check again */
- if (loop_event->seqnum < event->delaying_seqnum)
- continue;
-
- /* event we checked earlier still exists, no need to check again */
- if (loop_event->seqnum == event->delaying_seqnum)
- return true;
-
/* found ourself, no later event can block us */
if (loop_event->seqnum >= event->seqnum)
- break;
+ goto no_blocker;
/* check major/minor */
if (major(devnum) != 0) {
if (sd_device_get_devnum(loop_event->dev, &d) >= 0 &&
devnum == d && is_block == streq(s, "block"))
- goto set_delaying_seqnum;
+ break;
}
/* check network device ifindex */
if (sd_device_get_ifindex(loop_event->dev, &i) >= 0 &&
ifindex == i)
- goto set_delaying_seqnum;
+ break;
}
if (sd_device_get_devpath(loop_event->dev, &loop_devpath) < 0)
/* check our old name */
if (devpath_old && streq(devpath_old, loop_devpath))
- goto set_delaying_seqnum;
+ break;
loop_devpath_len = strlen(loop_devpath);
/* identical device event found */
if (devpath_len == loop_devpath_len)
- goto set_delaying_seqnum;
+ break;
/* parent device event found */
if (devpath[common] == '/')
- goto set_delaying_seqnum;
+ break;
/* child device event found */
if (loop_devpath[common] == '/')
- goto set_delaying_seqnum;
+ break;
}
- return false;
+ assert(loop_event);
-set_delaying_seqnum:
log_device_debug(event->dev, "SEQNUM=%" PRIu64 " blocked by SEQNUM=%" PRIu64,
event->seqnum, loop_event->seqnum);
- event->delaying_seqnum = loop_event->seqnum;
+ event->blocker_seqnum = loop_event->seqnum;
return true;
-}
-
-static void manager_exit(Manager *manager) {
- assert(manager);
-
- manager->exit = true;
-
- sd_notify(false,
- "STOPPING=1\n"
- "STATUS=Starting shutdown...");
-
- /* close sources of new events and discard buffered events */
- manager->ctrl = udev_ctrl_unref(manager->ctrl);
-
- manager->inotify_event = sd_event_source_unref(manager->inotify_event);
- manager->inotify_fd = safe_close(manager->inotify_fd);
-
- manager->monitor = sd_device_monitor_unref(manager->monitor);
-
- /* discard queued events and kill workers */
- event_queue_cleanup(manager, EVENT_QUEUED);
- manager_kill_workers(manager, true);
-}
-
-/* reload requested, HUP signal received, rules changed, builtin changed */
-static void manager_reload(Manager *manager) {
-
- assert(manager);
-
- sd_notify(false,
- "RELOADING=1\n"
- "STATUS=Flushing configuration...");
-
- manager_kill_workers(manager, false);
- manager->rules = udev_rules_free(manager->rules);
- udev_builtin_exit();
-
- sd_notifyf(false,
- "READY=1\n"
- "STATUS=Processing with %u children at max", arg_children_max);
-}
-static int on_kill_workers_event(sd_event_source *s, uint64_t usec, void *userdata) {
- Manager *manager = userdata;
-
- assert(manager);
-
- log_debug("Cleanup idle workers");
- manager_kill_workers(manager, false);
-
- return 1;
+no_blocker:
+ event->blocker_seqnum = event->seqnum;
+ return false;
}
-static void event_queue_start(Manager *manager) {
- struct event *event;
+static int event_queue_start(Manager *manager) {
+ Event *event, *event_next;
usec_t usec;
int r;
if (LIST_IS_EMPTY(manager->events) ||
manager->exit || manager->stop_exec_queue)
- return;
+ return 0;
assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &usec) >= 0);
/* check for changed config, every 3 seconds at most */
if (manager->last_usec == 0 ||
- usec - manager->last_usec > 3 * USEC_PER_SEC) {
+ usec > usec_add(manager->last_usec, 3 * USEC_PER_SEC)) {
if (udev_rules_check_timestamp(manager->rules) ||
udev_builtin_validate())
manager_reload(manager);
if (!manager->rules) {
r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
- if (r < 0) {
- log_warning_errno(r, "Failed to read udev rules: %m");
- return;
- }
+ if (r < 0)
+ return log_warning_errno(r, "Failed to read udev rules: %m");
}
- LIST_FOREACH(event, event, manager->events) {
+ LIST_FOREACH_SAFE(event, event, event_next, manager->events) {
if (event->state != EVENT_QUEUED)
continue;
- /* do not start event if parent or child event is still running */
- if (is_device_busy(manager, event) != 0)
+ /* do not start event if parent or child event is still running or queued */
+ r = event_is_blocked(event);
+ if (r < 0) {
+ sd_device_action_t a = _SD_DEVICE_ACTION_INVALID;
+
+ (void) sd_device_get_action(event->dev, &a);
+ log_device_warning_errno(event->dev, r,
+ "Failed to check event dependency, "
+ "skipping event (SEQNUM=%"PRIu64", ACTION=%s)",
+ event->seqnum,
+ strna(device_action_to_string(a)));
+
+ event_free(event);
+ return r;
+ }
+ if (r > 0)
continue;
- event_run(manager, event);
+ r = event_run(event);
+ if (r <= 0)
+ return r;
}
+
+ return 0;
}
-static void event_queue_cleanup(Manager *manager, enum event_state match_type) {
- struct event *event, *tmp;
+static int event_queue_insert(Manager *manager, sd_device *dev) {
+ _cleanup_(sd_device_unrefp) sd_device *clone = NULL;
+ Event *event;
+ uint64_t seqnum;
+ int r;
- LIST_FOREACH_SAFE(event, event, tmp, manager->events) {
- if (match_type != EVENT_UNDEF && match_type != event->state)
- continue;
+ assert(manager);
+ assert(dev);
- event_free(event);
+ /* only one process can add events to the queue */
+ assert(manager->pid == getpid_cached());
+
+ /* We only accepts devices received by device monitor. */
+ r = sd_device_get_seqnum(dev, &seqnum);
+ if (r < 0)
+ return r;
+
+ /* Save original device to restore the state on failures. */
+ r = device_shallow_clone(dev, &clone);
+ if (r < 0)
+ return r;
+
+ r = device_copy_properties(clone, dev);
+ if (r < 0)
+ return r;
+
+ event = new(Event, 1);
+ if (!event)
+ return -ENOMEM;
+
+ *event = (Event) {
+ .manager = manager,
+ .dev = sd_device_ref(dev),
+ .dev_kernel = TAKE_PTR(clone),
+ .seqnum = seqnum,
+ .state = EVENT_QUEUED,
+ };
+
+ if (LIST_IS_EMPTY(manager->events)) {
+ r = touch("/run/udev/queue");
+ if (r < 0)
+ log_warning_errno(r, "Failed to touch /run/udev/queue, ignoring: %m");
+ }
+
+ LIST_APPEND(event, manager->events, event);
+
+ log_device_uevent(dev, "Device is queued");
+
+ return 0;
+}
+
+static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
+ Manager *manager = userdata;
+ int r;
+
+ assert(manager);
+
+ DEVICE_TRACE_POINT(kernel_uevent_received, dev);
+
+ device_ensure_usec_initialized(dev, NULL);
+
+ r = event_queue_insert(manager, dev);
+ if (r < 0) {
+ log_device_error_errno(dev, r, "Failed to insert device into event queue: %m");
+ return 1;
}
+
+ /* we have fresh events, try to schedule them */
+ event_queue_start(manager);
+
+ return 1;
}
static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
assert(manager);
for (;;) {
- struct worker_message msg;
+ WorkerMessage msg;
struct iovec iovec = {
.iov_base = &msg,
.iov_len = sizeof(msg),
};
ssize_t size;
struct ucred *ucred;
- struct worker *worker;
+ Worker *worker;
size = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT);
if (size == -EINTR)
cmsg_close_all(&msghdr);
- if (size != sizeof(struct worker_message)) {
+ if (size != sizeof(WorkerMessage)) {
log_warning("Ignoring worker message with invalid size %zi bytes", size);
continue;
}
return 1;
}
-static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
- Manager *manager = userdata;
- int r;
-
- assert(manager);
-
- DEVICE_TRACE_POINT(kernel_uevent_received, dev);
-
- device_ensure_usec_initialized(dev, NULL);
-
- r = event_queue_insert(manager, dev);
- if (r < 0) {
- log_device_error_errno(dev, r, "Failed to insert device into event queue: %m");
- return 1;
- }
-
- /* we have fresh events, try to schedule them */
- event_queue_start(manager);
-
- return 1;
-}
-
/* receive the udevd message from userspace */
-static int on_ctrl_msg(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, const union udev_ctrl_msg_value *value, void *userdata) {
+static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrlMessageValue *value, void *userdata) {
Manager *manager = userdata;
int r;
for (;;) {
pid_t pid;
int status;
- struct worker *worker;
+ Worker *worker;
pid = waitpid(-1, &status, WNOHANG);
if (pid <= 0)
case '?':
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
device = argv[optind];
break;
default:
- assert_not_reached("Unexpected output mode");
+ assert_not_reached();
}
return 0;
break;
default:
- assert_not_reached("Unexpected display mode");
+ assert_not_reached();
}
return 0;
break;
default:
- assert_not_reached("Unexpected output mode");
+ assert_not_reached();
}
return 0;
if (r < 0)
return log_error_errno(r, "Failed to enumerate groups of user: %m");
} else
- assert_not_reached("Unexpected verb");
+ assert_not_reached();
for (;;) {
_cleanup_free_ char *user = NULL, *group = NULL;
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached();
}
}
static int add_nss_service(JsonVariant **v) {
_cleanup_(json_variant_unrefp) JsonVariant *status = NULL, *z = NULL;
- char buf[SD_ID128_STRING_MAX];
sd_id128_t mid;
int r;
return r;
status = json_variant_ref(json_variant_by_key(*v, "status"));
- z = json_variant_ref(json_variant_by_key(status, sd_id128_to_string(mid, buf)));
+ z = json_variant_ref(json_variant_by_key(status, SD_ID128_TO_STRING(mid)));
if (json_variant_by_key(z, "service"))
return 0;
if (r < 0)
return r;
- r = json_variant_set_field(&status, buf, z);
+ r = json_variant_set_field(&status, SD_ID128_TO_STRING(mid), z);
if (r < 0)
return r;
static int determine_devices(void) {
_cleanup_free_ void *m = NULL;
sd_id128_t root_uuid, verity_uuid;
- char ids[ID128_UUID_STRING_MAX];
size_t l;
int r;
if (!arg_data_what) {
memcpy(&root_uuid, m, sizeof(root_uuid));
- arg_data_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(root_uuid, ids));
+ arg_data_what = path_join("/dev/disk/by-partuuid", ID128_TO_UUID_STRING(root_uuid));
if (!arg_data_what)
return log_oom();
}
if (!arg_hash_what) {
memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
- arg_hash_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(verity_uuid, ids));
+ arg_hash_what = path_join("/dev/disk/by-partuuid", ID128_TO_UUID_STRING(verity_uuid));
if (!arg_hash_what)
return log_oom();
}
printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]\n"
"%s detach VOLUME\n\n"
- "Attaches or detaches an integrity protected block device.\n"
+ "Attach or detach an integrity protected block device.\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
program_invocation_short_name,
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
int r;
- if (argc <= 1)
+ if (argc <= 1 ||
+ strv_contains(strv_skip(argv, 1), "--help") ||
+ strv_contains(strv_skip(argv, 1), "-h") ||
+ streq(argv[1], "help"))
return help();
if (argc < 3)
if (!escaped)
return log_oom();
- free_and_replace(exec_split[n], escaped);
- n++;
+ free_and_replace(exec_split[n++], escaped);
continue;
}
if (!quoted)
return log_oom();
- free_and_replace(exec_split[n], quoted);
- n++;
+ free_and_replace(exec_split[n++], quoted);
}
for (; exec_split[n]; n++)
exec_split[n] = mfree(exec_split[n]);
if install_sysconfdir
meson.add_install_script('sh', '-c',
- mkdir_p.format(join_paths(sysconfdir, 'sysctl.d')))
+ mkdir_p.format(sysconfdir / 'sysctl.d'))
endif
(
local workspace="${1:?}"
+ # On openSUSE the static linked version of busybox is named "busybox-static".
+ busybox="$(type -P busybox-static || type -P busybox)"
+ inst_simple "$busybox" "$(dirname $busybox)/busybox"
+
if selinuxenabled >/dev/null; then
dracut_install selinuxenabled
cp -ar /etc/selinux "$workspace/etc/selinux"
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+
+TEST_NO_NSPAWN=1
+
+set -e
+TEST_DESCRIPTION="test RestrictNetworkInterfaces="
+. $TEST_BASE_DIR/test-functions
+
+do_test "$@" 62
root="${1:?Usage $0 container-root}"
mkdir -p "$root"
mkdir "$root/bin"
-cp $(type -P busybox) "$root/bin"
-os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)
-ID_LIKE=$(awk -F= '$1=="ID_LIKE" { print $2 ;}' $os_release)
-if [[ "$ID_LIKE" = *"suse"* ]]; then
- mkdir -p "$root/lib"
- mkdir -p "$root/lib64"
- for lib in $(find /lib*/ld*); do
- [[ -d $root/$(dirname $lib) ]] || mkdir -p $root/$(dirname $lib)
- cp $lib $root/$lib
- done
-fi
+# On openSUSE the static linked version of busybox is named "busybox-static".
+busybox="$(type -P busybox-static || type -P busybox)"
+cp "$busybox" "$root/bin/busybox"
mkdir -p "$root/usr/lib"
touch "$root/usr/lib/os-release"
ln -s busybox "$root/bin/ip"
ln -s busybox "$root/bin/seq"
ln -s busybox "$root/bin/sleep"
+ln -s busybox "$root/bin/usleep"
ln -s busybox "$root/bin/test"
mkdir -p "$root/sbin"
TCP6SegmentationOffload=
UDPSegmentationOffload=
GenericReceiveOffload=
+GenericReceiveOffloadHardware=
LargeReceiveOffload=
RxChannels=
TxChannels=
AutoNegotiationFlowControl=
GenericSegmentOffloadMaxBytes=
GenericSegmentOffloadMaxSegments=
+RxCoalesceSec=
+RxMaxCoalescedFrames=
+RxCoalesceIrqSec=
+RxMaxCoalescedIrqFrames=
+TxCoalesceSec=
+TxMaxCoalescedFrames=
+TxCoalesceIrqSec=
+TxMaxCoalescedIrqFrames=
+StatisticsBlockCoalesceSec=
+UseAdaptiveRxCoalesce=
+UseAdaptiveTxCoalesce=
+CoalescePacketRateLow=
+RxCoalesceLowSec=
+RxMaxCoalescedLowFrames=
+TxCoalesceLowSec=
+TxMaxCoalescedLowFrames=
+CoalescePacketRateHigh=
+RxCoalesceHighSec=
+RxMaxCoalescedHighFrames=
+TxCoalesceHighSec=
+TxMaxCoalescedHighFrames=
+CoalescePacketRateSampleIntervalSec=
Hostname=
DUIDType=
UseHostname=
+Label=
CriticalConnection=
DUIDRawData=
RequestBroadcast=
[CAN]
SamplePoint=
BitRate=
+TimeQuantaNSec=
+PropagationSegment=
+PhaseBufferSegment1=
+PhaseBufferSegment2=
+SyncJumpWidth=
DataSamplePoint=
DataBitRate=
+DataTimeQuantaNSec=
+DataPropagationSegment=
+DataPhaseBufferSegment1=
+DataPhaseBufferSegment2=
+DataSyncJumpWidth=
FDMode=
FDNonISO=
RestartSec=
BusErrorReporting=
Termination=
ListenOnly=
+Loopback=
+OneShot=
+PresumeACK=
+ClassicDataLengthCode=
[Address]
DuplicateAddressDetection=
AutoJoin=
EmitDomains=
Managed=
OtherInformation=
+UplinkInterface=
[IPv6PrefixDelegation]
RouterPreference=
DNSLifetimeSec=
Prefix=
[IPv6AcceptRA]
UseDomains=
+UseMTU=
RouteTable=
RouteMetric=
UseDNS=
ReserveVT=
RestrictAddressFamilies=
RestrictNamespaces=
+RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
RuntimeDirectory=
RestartKillSignal=
RestrictAddressFamilies=
RestrictNamespaces=
+RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
RootDirectory=
MemorySwapMax=
NetClass=
RestartKillSignal=
+RestrictNetworkInterfaces=
RuntimeMaxSec=
SendSIGHUP=
SendSIGKILL=
RestartSec=
RestrictAddressFamilies=
RestrictNamespaces=
+RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
RootDirectory=
MemoryMin=
MemorySwapMax=
NetClass=
+RestrictNetworkInterfaces=
Slice=
SocketBindAllow=
SocketBindDeny=
RestartKillSignal=
RestrictAddressFamilies=
RestrictNamespaces=
+RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
ReusePort=
RestartKillSignal=
RestrictAddressFamilies=
RestrictNamespaces=
+RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
RootDirectory=
endif
foreach p : out.stdout().split()
- source = join_paths(project_source_root, p)
+ source = project_source_root / p
name = 'dmidecode_' + p.split('/')[-1].split('.')[0]
test(name,
udev_dmi_memory_id_test,
- args : [dmi_memory_id_path, source, source + '.txt'])
+ args : [udev_prog_paths['dmi_memory_id'],
+ source,
+ source + '.txt'])
endforeach
endif
export TEST_BASE_DIR TEST_UNITS_DIR SOURCE_DIR TOOLS_DIR
# note that find-build-dir.sh will return $BUILD_DIR if provided, else it will try to find it
-if ! BUILD_DIR="$("$TOOLS_DIR"/find-build-dir.sh)"; then
- if get_bool "${NO_BUILD:=}"; then
- BUILD_DIR="$SOURCE_DIR"
- else
- echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2
- exit 1
- fi
+if get_bool "${NO_BUILD:=}"; then
+ BUILD_DIR="$SOURCE_DIR"
+elif ! BUILD_DIR="$("$TOOLS_DIR"/find-build-dir.sh)"; then
+ echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2
+ exit 1
fi
PATH_TO_INIT="$ROOTLIBDIR/systemd"
base64
basename
bash
- busybox
capsh
cat
chmod
# Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
local _asan_calls
- _asan_calls="$(objdump -dC "$SYSTEMD_JOURNALD" | grep -E "callq?\s+[0-9a-f]+\s+<__asan" -c)"
+ _asan_calls="$(objdump -dC "$SYSTEMD_JOURNALD" | grep -E "(callq?|brasl?|bl)\s.+__asan" -c)"
if ((_asan_calls < 1000)); then
return 1
else
[ "$ARCH" ] || ARCH=$(uname -m)
case $ARCH in
ppc64*)
- KERNEL_BIN="/boot/vmlinux-$KERNEL_VER"
- CONSOLE=hvc0
- ;;
+ # Ubuntu ppc64* calls the kernel binary as vmlinux-*, RHEL/CentOS
+ # uses the "standard" vmlinuz- prefix
+ [[ -e "/boot/vmlinux-$KERNEL_VER" ]] && KERNEL_BIN="/boot/vmlinux-$KERNEL_VER" || KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER"
+ CONSOLE=hvc0
+ ;;
*)
- KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER"
- ;;
+ KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER"
+ ;;
esac
fi
fi
export initdir="$TESTDIR/app1"
mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
- grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app1"
- echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app1"
+ grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2"
+ echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app2"
+ setfattr -n user.extension-release.strict -v false "$initdir/usr/lib/extension-release.d/extension-release.app2"
cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF
[Service]
Type=oneshot
#!/bin/bash
set -e
test -e /usr/lib/os-release
-cat /usr/lib/extension-release.d/extension-release.app1
+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"
install_fs_tools
install_modules
install_plymouth
+ install_haveged
install_debug_tools
install_ld_so_conf
install_testuser
has_user_dbus_socket && install_user_dbus
setup_selinux
strip_binaries
+ instmods veth
install_depmod_files
generate_module_dependencies
if get_bool "$IS_BUILT_WITH_ASAN"; then
done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2)
}
+install_suse_systemd() {
+ local testsdir=/usr/lib/systemd/tests
+ local pkgs
+
+ dinfo "Install SUSE systemd"
+
+ pkgs=(
+ systemd
+ systemd-container
+ systemd-coredump
+ systemd-experimental
+ systemd-journal-remote
+ systemd-portable
+ udev
+ )
+
+ for p in "${pkgs[@]}"; do
+ rpm -q "$p" &>/dev/null || continue
+
+ ddebug "Install files from package $p"
+ while read -r f; do
+ [ -e "$f" ] || continue
+ [ -d "$f" ] && continue
+ inst "$f"
+ done < <(rpm -ql "$p")
+ done
+
+ # we only need testsdata dir as well as the unit tests (for
+ # TEST-02-UNITTESTS) in the image.
+ dinfo "Install unit tests and testdata directory"
+
+ mkdir -p "$initdir/$testsdir"
+ cp "$testsdir"/test-* "$initdir/$testsdir/"
+ cp -a "$testsdir/testdata" "$initdir/$testsdir/"
+
+ # On openSUSE, these dirs are not created at package install for now on.
+ mkdir -p "$initdir/var/log/journal/remote"
+}
+
install_distro_systemd() {
dinfo "Install distro systemd"
if get_bool "$LOOKS_LIKE_DEBIAN"; then
install_debian_systemd
+ elif get_bool "$LOOKS_LIKE_SUSE"; then
+ install_suse_systemd
else
dfatal "NO_BUILD not supported for this distro"
exit 1
# remove unneeded documentation
rm -fr "$initdir"/usr/share/{man,doc}
- get_bool "$LOOKS_LIKE_SUSE" && setup_suse
-
# enable debug logging in PID1
echo LogLevel=debug >>"$initdir/etc/systemd/system.conf"
# store coredumps in journal
setfacl -m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}/"failed
fi
ret=1
+ elif get_bool "$TIMED_OUT"; then
+ echo "(timeout)" >"${TESTDIR:?}/failed"
+ ret=2
elif [ -e "$workspace/testok" ]; then
# …/testok always counts (but with lower priority than …/failed)
ret=0
echo "${TESTNAME:?} was skipped:"
cat "$workspace/skipped"
ret=0
- elif get_bool "$TIMED_OUT"; then
- echo "(timeout)" >"${TESTDIR:?}/failed"
- ret=2
else
echo "(failed; see logs)" >"${TESTDIR:?}/failed"
ret=3
# fi
}
+install_haveged() {
+ # If haveged is installed and probably included in initrd, it needs to be
+ # installed in the image too.
+ if [ -x /usr/sbin/haveged ]; then
+ dinfo "Install haveged files"
+ inst /usr/sbin/haveged
+ inst /usr/lib/systemd/system/haveged.service
+ fi
+}
+
install_ld_so_conf() {
dinfo "Install /etc/ld.so.conf*"
cp -a /etc/ld.so.conf* "${initdir:?}/etc"
paths+=(/lib*/security)
fi
- for d in /etc/pam.d /etc/security /usr/lib/pam.d; do
+ for d in /etc/pam.d /etc/security /usr/{etc,lib}/pam.d; do
[ -d "$d" ] && paths+=("$d")
done
# set empty root password for easy debugging
sed -i 's/^root:x:/root::/' "${initdir:?}/etc/passwd"
+
+ # And make sure pam_unix will accept it by making sure that
+ # the PAM module has the nullok option.
+ for d in /etc/pam.d /usr/{etc,lib}/pam.d; do
+ [ -d "$initdir/$d" ] || continue
+ sed -i '/^auth.*pam_unix.so/s/$/ nullok/' "$initdir/$d"/*
+ done
}
install_keymaps() {
while read -r line; do
[[ "$line" = 'not a dynamic executable' ]] && break
+ # Skip a harmless error when running the tests on a system with a significantly
+ # older systemd version (ldd tries to resolve the unprefixed RPATH for libsystemd.so.0,
+ # which is in this case older than the already installed libsystemd.so.0 in $initdir).
+ # The issue is triggered by installing test dependencies in install_missing_libraries().
+ [[ "$line" =~ libsystemd.so.*:\ version\ .*\ not\ found ]] && continue
if [[ "$line" =~ $so_regex ]]; then
file="${BASH_REMATCH[1]}"
else
(
[[ "$mpargs" ]] && echo "$mpargs"
- find "$mod_dir" -path "*/${mod#=}/*" -type f -printf '%f\n'
+ find "$mod_dir" -path "*/${mod#=}/*" -name "*.ko*" -type f -printf '%f\n'
) | instmods
fi
;;
return 0
}
-setup_suse() {
- ln -fs ../usr/bin/systemctl "${initdir:?}/bin/"
- ln -fs ../usr/lib/systemd "$initdir/lib/"
- inst_simple "/usr/lib/systemd/system/haveged.service"
-}
-
_umount_dir() {
local mountpoint="${1:?}"
if mountpoint -q "$mountpoint"; then
--- /dev/null
+[NetDev]
+Name=vxlan97
+Kind=vxlan
+
+[VXLAN]
+VNI=4831583
+Local=fe80::281:8eff:fef0:73aa
--- /dev/null
+[NetDev]
+Name=bridge99
+Kind=bridge
+
+[Bridge]
+MulticastQuerier=yes
+MulticastSnooping=yes
+Priority=10
+STP=yes
+ForwardDelaySec=5
+MulticastIGMPVersion=2
+VLANFiltering=yes
--- /dev/null
+[Match]
+Name=bridge99
+
+[Network]
+VLAN=vlan99
+IPForward=yes
+ConfigureWithoutCarrier=yes
+LLDP=yes
+IPv6AcceptRA=false
+
+[Bridge]
+Learning=yes
+MulticastRouter=no
+UseBPDU=yes
+
+[BridgeVLAN]
+VLAN=100
+
+[BridgeVLAN]
+VLAN=600
--- /dev/null
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+IPForward=yes
+Bridge=bridge99
+LinkLocalAddressing=no
+EmitLLDP=nearest-bridge
+LLDP=yes
+
+[Link]
+RequiredForOnline=no
+
+[Bridge]
+Learning=yes
+MulticastRouter=query
+UseBPDU=yes
+
+[BridgeVLAN]
+VLAN=100
+EgressUntagged=100
+PVID=100
+
+[BridgeVLAN]
+VLAN=560
+
+[BridgeVLAN]
+VLAN=600
ClientIdentifier=mac
VendorClassIdentifier=SusantVendorTest
RouteTable=211
+Label=test-label
[DHCPServerStaticLease]
MACAddress=12:34:56:78:9a:bc
-Address=10.1.1.3
+Address=10.1.1.200
[DHCPServer]
DNS=192.168.5.1
NTP=192.168.5.1
+
+[IPv6SendRA]
+EmitDNS=no
+EmitDomains=no
RouterDenyList=2001::1
PrefixDenyList=2001:db8:0:2::
RouteDenyList=2001:db1:fff::
+UseDomains=yes
PrefixDenyList=2001:db8:0:1:: 2001:db8:0:1::
RouteAllowList=2001:db0:fff:: 2001:db0:fff::
RouteDenyList=2001:db0:fff:: 2001:db0:fff::
+UseDomains=yes
DHCP=no
IPv6SendRA=yes
+[IPv6SendRA]
+UplinkInterface=dummy98
+
[IPv6Prefix]
Prefix=2001:db8:0:1::/64
--- /dev/null
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:db8:1:1::1/64
+DNS=2001:db8:1:1::2
+Domains=example.com
--- /dev/null
+[Match]
+Name=vxlan97
+
+[Network]
+IPv6AcceptRA=no
+LinkLocalAddressing=yes
+
+[BridgeFDB]
+MACAddress=00:00:00:00:00:00
+Destination=fe80::27c:16ff:fec0:6c74
+OutgoingInterface=test1
+VNI=4831583
+
+[BridgeFDB]
+MACAddress=00:00:00:00:00:00
+Destination=fe80::2a2:e4ff:fef9:2269
+OutgoingInterface=test1
+VNI=4831583
+
+[BridgeFDB]
+MACAddress=00:00:00:00:00:00
+Destination=fe80::23b:d2ff:fe95:967f
+OutgoingInterface=test1
+VNI=4831583
IPv6AcceptRA=false
LinkLocalAddressing=yes
VXLAN=vxlan99
+VXLAN=vxlan97
output = check_output('ip -4 address show dev dummy98')
print(output)
- self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
- self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
- self.assertRegex(output, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
+ self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
+ self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
+ self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
check_output('ip address del 10.1.2.3/16 dev dummy98')
check_output('ip address del 10.1.2.4/16 dev dummy98')
output = check_output('ip -4 address show dev dummy98')
print(output)
- self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
- self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
- self.assertRegex(output, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
+ self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
+ self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
+ self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
+
+ remove_unit_from_networkd_path(['25-address-static.network'])
+
+ check_output(*networkctl_cmd, 'reload', env=env)
+ self.wait_operstate('dummy98', 'degraded', setup_state='unmanaged')
+
+ output = check_output('ip -4 address show dev dummy98')
+ print(output)
+ self.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
+ self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
+ self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
+
+ copy_unit_to_networkd_unit_path('25-address-static.network')
+ check_output(*networkctl_cmd, 'reload', env=env)
+ self.wait_online(['dummy98:routable'])
+
+ output = check_output('ip -4 address show dev dummy98')
+ print(output)
+ self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
+ self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
+ self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
def test_reload(self):
start_networkd(3)
'vtitun98',
'vtitun99',
'vxcan99',
+ 'vxlan97',
'vxlan98',
'vxlan99',
'wg97',
'25-vti-tunnel.netdev',
'25-vxcan.netdev',
'25-vxlan-independent.netdev',
+ '25-vxlan-ipv6.netdev',
'25-vxlan.netdev',
'25-wireguard-23-peers.netdev',
'25-wireguard-23-peers.network',
'sit.network',
'vti6.network',
'vti.network',
+ 'vxlan-ipv6.network',
'vxlan-test1.network',
'vxlan.network',
'xfrm.network',
self.assertRegex(output, ' mtu 2000 ')
self.assertRegex(output, 'macvlan mode ' + mode + ' ')
+ rc = call("ip link del test1")
+ self.assertEqual(rc, 0)
+ time.sleep(1)
+
+ rc = call("ip link add test1 type dummy")
+ self.assertEqual(rc, 0)
+ time.sleep(1)
+
+ self.wait_online(['macvlan99:degraded', 'test1:degraded'])
+
+ output = check_output('ip -d link show test1')
+ print(output)
+ self.assertRegex(output, ' mtu 2000 ')
+
+ output = check_output('ip -d link show macvlan99')
+ print(output)
+ self.assertRegex(output, ' mtu 2000 ')
+ self.assertRegex(output, 'macvlan mode ' + mode + ' ')
+
@expectedFailureIfModuleIsNotAvailable('ipvlan')
def test_ipvlan(self):
for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
def test_vxlan(self):
copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network',
+ '25-vxlan-ipv6.netdev', 'vxlan-ipv6.network',
'25-vxlan-independent.netdev', 'netdev-link-local-addressing-yes.network',
'11-dummy.netdev', 'vxlan-test1.network')
start_networkd()
- self.wait_online(['test1:degraded', 'vxlan99:degraded', 'vxlan98:degraded'])
+ self.wait_online(['test1:degraded', 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded'])
output = check_output('ip -d link show vxlan99')
print(output)
- self.assertRegex(output, '999')
- self.assertRegex(output, '5555')
- self.assertRegex(output, 'l2miss')
- self.assertRegex(output, 'l3miss')
- self.assertRegex(output, 'udpcsum')
- self.assertRegex(output, 'udp6zerocsumtx')
- self.assertRegex(output, 'udp6zerocsumrx')
- self.assertRegex(output, 'remcsumtx')
- self.assertRegex(output, 'remcsumrx')
- self.assertRegex(output, 'gbp')
+ self.assertIn('999', output)
+ self.assertIn('5555', output)
+ self.assertIn('l2miss', output)
+ self.assertIn('l3miss', output)
+ self.assertIn('udpcsum', output)
+ self.assertIn('udp6zerocsumtx', output)
+ self.assertIn('udp6zerocsumrx', output)
+ self.assertIn('remcsumtx', output)
+ self.assertIn('remcsumrx', output)
+ self.assertIn('gbp', output)
output = check_output('bridge fdb show dev vxlan99')
print(output)
- self.assertRegex(output, '00:11:22:33:44:55 dst 10.0.0.5 self permanent')
- self.assertRegex(output, '00:11:22:33:44:66 dst 10.0.0.6 self permanent')
- self.assertRegex(output, '00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent')
+ self.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output)
+ self.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output)
+ self.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output)
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'vxlan99', env=env)
print(output)
- self.assertRegex(output, 'VNI: 999')
- self.assertRegex(output, 'Destination Port: 5555')
- self.assertRegex(output, 'Underlying Device: test1')
+ self.assertIn('VNI: 999', output)
+ self.assertIn('Destination Port: 5555', output)
+ self.assertIn('Underlying Device: test1', output)
+
+ output = check_output('bridge fdb show dev vxlan97')
+ print(output)
+ self.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output)
+ self.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output)
+ self.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output)
def test_macsec(self):
copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
links = [
'bridge99',
'dummy98',
- 'test1']
+ 'test1',
+ 'vlan99',
+ ]
units = [
'11-dummy.netdev',
'12-dummy.netdev',
+ '21-vlan.netdev',
+ '21-vlan.network',
'26-bridge.netdev',
'26-bridge-configure-without-carrier.network',
+ '26-bridge-issue-20373.netdev',
'26-bridge-mdb-master.network',
'26-bridge-mdb-slave.network',
'26-bridge-slave-interface-1.network',
'26-bridge-slave-interface-2.network',
+ '26-bridge-vlan-master-issue-20373.network',
'26-bridge-vlan-master.network',
+ '26-bridge-vlan-slave-issue-20373.network',
'26-bridge-vlan-slave.network',
'bridge99-ignore-carrier-loss.network',
- 'bridge99.network']
+ 'bridge99.network'
+ ]
routing_policy_rule_tables = ['100']
self.assertRegex(output, f'{i}')
self.assertNotRegex(output, '4095')
+ def test_bridge_vlan_issue_20373(self):
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
+ '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
+ '21-vlan.netdev', '21-vlan.network')
+ start_networkd()
+ self.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
+
+ output = check_output('bridge vlan show dev test1')
+ print(output)
+ self.assertIn('100 PVID Egress Untagged', output)
+ self.assertIn('560', output)
+ self.assertIn('600', output)
+
+ output = check_output('bridge vlan show dev bridge99')
+ print(output)
+ self.assertIn('1 PVID Egress Untagged', output)
+ self.assertIn('100', output)
+ self.assertIn('600', output)
+
def test_bridge_mdb(self):
copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-mdb-slave.network',
'26-bridge.netdev', '26-bridge-mdb-master.network')
output = check_output('ip rule list table 100')
print(output)
- self.assertIn('0: from all to 8.8.8.8 lookup 100', output)
+ self.assertIn('from all to 8.8.8.8 lookup 100', output)
class NetworkdLLDPTests(unittest.TestCase, Utilities):
links = ['veth99']
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
print(output)
- self.assertIn('10.1.1.3 (DHCP4 via 10.1.1.1)', output)
+ self.assertIn('10.1.1.200 (DHCP4 via 10.1.1.1)', output)
class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
links = [
self.assertRegex(output, '12:34:56:78:9a:bc')
self.assertRegex(output, '192.168.5')
self.assertRegex(output, '1492')
+ self.assertRegex(output, 'test-label')
print('## ip route show table main dev veth99')
output = check_output('ip route show table main dev veth99')
self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
- links = ['veth99']
+ links = [
+ 'dummy98',
+ 'veth99',
+ ]
units = [
+ '12-dummy.netdev',
'25-veth.netdev',
'ipv6ra-prefix-client-deny-list.network',
'ipv6ra-prefix-client.network',
- 'ipv6ra-prefix.network'
- ]
+ 'ipv6ra-prefix.network',
+ 'ipv6ra-uplink.network',
+ ]
def setUp(self):
remove_links(self.links)
stop_networkd(show_logs=True)
def test_ipv6_route_prefix(self):
- copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client.network', 'ipv6ra-prefix.network')
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client.network', 'ipv6ra-prefix.network',
+ '12-dummy.netdev', 'ipv6ra-uplink.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
output = check_output('ip address show dev veth-peer')
print(output)
self.assertNotIn('inet6 2001:db8:0:1:', output)
self.assertIn('inet6 2001:db8:0:2:', output)
+ output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env)
+ print(output)
+ self.assertRegex(output, '2001:db8:1:1::2')
+
+ output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env)
+ print(output)
+ self.assertIn('example.com', output)
+
def test_ipv6_route_prefix_deny_list(self):
- copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client-deny-list.network', 'ipv6ra-prefix.network')
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client-deny-list.network', 'ipv6ra-prefix.network',
+ '12-dummy.netdev', 'ipv6ra-uplink.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
output = check_output('ip address show dev veth-peer')
print(output)
self.assertNotIn('inet6 2001:db8:0:1:', output)
self.assertIn('inet6 2001:db8:0:2:', output)
+ output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env)
+ print(output)
+ self.assertRegex(output, '2001:db8:1:1::2')
+
+ output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env)
+ print(output)
+ self.assertIn('example.com', output)
+
class NetworkdMTUTests(unittest.TestCase, Utilities):
links = ['dummy98']
[Service]
ExecStartPre=rm -f /failed /testok
Type=oneshot
-ExecStart=sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test10.socket; printf x >test.file; socat -t20 OPEN:test.file UNIX-CONNECT:/run/test.ctl; >/testok'
+ExecStart=rm -f /tmp/nonexistent
+ExecStart=systemctl start test10.socket
+ExecStart=sh -x -c 'printf x >test.file'
+ExecStart=-socat -T20 OPEN:test.file UNIX-CONNECT:/run/test.ctl
+# TriggerLimitIntervalSec= by default is set to 2s. A "sleep 10" should give
+# systemd enough time even on slower machines, to reach the trigger limit.
+ExecStart=sleep 10
+ExecStart=sh -x -c 'test "$(systemctl show test10.socket -P ActiveState)" = failed'
+ExecStart=sh -x -c 'test "$(systemctl show test10.socket -P Result)" = trigger-limit-hit'
+ExecStart=sh -x -c 'echo OK >/testok'
is_user_ns_supported=yes
fi
-SUSE_OPTS=()
-ID_LIKE=$(awk -F= '$1=="ID_LIKE" { print $2 ;}' /etc/os-release)
-if [[ "$ID_LIKE" = *"suse"* ]]; then
- SUSE_OPTS+=(--bind /lib64 --bind /usr/lib64)
-fi
-
function check_bind_tmp_path {
# https://github.com/systemd/systemd/issues/4789
local _root="/var/lib/machines/testsuite-13.bind-tmp-path"
rm -rf "$_root"
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
: >/tmp/bind
- systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
+ systemd-nspawn --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
}
function check_norbind {
mount -t tmpfs tmpfs /tmp/binddir/subdir
echo -n "inner" >/tmp/binddir/subdir/file
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
- systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
+ systemd-nspawn --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
}
function check_notification_socket {
# https://github.com/systemd/systemd/issues/4944
local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/host/notify'
# /testsuite-13.nc-container is prepared by test.sh
- systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd"
- systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd"
+ systemd-nspawn --register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd"
+ systemd-nspawn --register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd"
}
function check_os_release {
echo MARKER=1 >>/etc/os-release
fi
- systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
+ systemd-nspawn --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
if grep -q MARKER /etc/os-release; then
rm /etc/os-release
}
function check_machinectl_bind {
- local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep 0.5; done; exit 1;'
+ local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; usleep 500000; done; exit 1;'
cat >/run/systemd/system/nspawn_machinectl_bind.service <<EOF
[Service]
local _root="/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3"
rm -rf "$_root"
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
- SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b
- SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --private-network -b
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -b
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -U -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -U -b; then
[[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1
else
[[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1
fi
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --private-network -U -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -U -b; then
[[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1
else
[[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1
# --network-namespace-path and network-related options cannot be used together
for netopt in "${_net_opts[@]}"; do
echo "$_netns_opt in combination with $netopt should fail"
- if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b "$_netns_opt" "$netopt"; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b "$_netns_opt" "$netopt"; then
echo >&2 "unexpected pass"
return 1
fi
done
# allow combination of --network-namespace-path and --private-network
- if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b "$_netns_opt" --private-network; then
+ if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b "$_netns_opt" --private-network; then
return 1
fi
# test --network-namespace-path works with a network namespace created by "ip netns"
ip netns add nspawn_test
_netns_opt="--network-namespace-path=/run/netns/nspawn_test"
- SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
local r=$?
ip netns del nspawn_test
set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
+mkdir -p /run/systemd/system/systemd-portabled.service.d/
+cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
portablectl attach --now --runtime /usr/share/minimal_0.raw app0
portablectl list | grep -q -F "No images."
-root="/usr/share/minimal_0.raw"
-app1="/usr/share/app1.raw"
+portablectl attach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0
-portablectl attach --now --runtime --extension ${app1} ${root} app1
+systemctl is-active app0.service
+
+portablectl reattach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
+
+systemctl is-active app0.service
+
+portablectl detach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
+
+portablectl attach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
systemctl is-active app1.service
-portablectl reattach --now --runtime --extension ${app1} ${root} app1
+portablectl reattach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
systemctl is-active app1.service
-portablectl detach --now --runtime --extension ${app1} ${root} app1
+portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
# portablectl also works with directory paths rather than images
mkdir /tmp/rootdir /tmp/app1 /tmp/overlay
-mount ${app1} /tmp/app1
-mount ${root} /tmp/rootdir
+mount /usr/share/app1.raw /tmp/app1
+mount /usr/share/minimal_0.raw /tmp/rootdir
mount -t overlay overlay -o lowerdir=/tmp/app1:/tmp/rootdir /tmp/overlay
portablectl attach --copy=symlink --now --runtime /tmp/overlay app1
systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app1"
+systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
cat >/run/systemd/system/testservice-50e.service <<EOF
[Service]
--- /dev/null
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-all-pings-work
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=
+Type=oneshot
--- /dev/null
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-allow-list
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=veth1
+Type=oneshot
--- /dev/null
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-deny-list
+[Service]
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=~veth0
+RestrictNetworkInterfaces=~veth1
+Type=oneshot
--- /dev/null
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-empty-assigment
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=
+Type=oneshot
--- /dev/null
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-invert-assigment
+[Service]
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=veth0 veth1
+RestrictNetworkInterfaces=~veth0
+Type=oneshot
--- /dev/null
+Description=TEST-62-RESTRICT-IFACES
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+setup() {
+ systemd-analyze log-level debug
+ systemd-analyze log-target console
+
+ for i in `seq 0 3`;
+ do
+ ip netns del ns${i} || true
+ ip link del veth${i} || true
+ ip netns add ns${i}
+ ip link add veth${i} type veth peer name veth${i}_
+ ip link set veth${i}_ netns ns${i}
+ ip -n ns${i} link set dev veth${i}_ up
+ ip -n ns${i} link set dev lo up
+ ip -n ns${i} addr add "192.168.113."$((4*i+1))/30 dev veth${i}_
+ ip link set dev veth${i} up
+ ip addr add "192.168.113."$((4*i+2))/30 dev veth${i}
+ done
+}
+
+teardown() {
+ set +e
+
+ for i in `seq 0 3`;
+ do
+ ip netns del ns${i}
+ ip link del veth${i}
+ done
+
+ systemd-analyze log-level info
+}
+
+KERNEL_VERSION="$(uname -r)"
+KERNEL_MAJOR="${KERNEL_VERSION%%.*}"
+KERNEL_MINOR="${KERNEL_VERSION#$KERNEL_MAJOR.}"
+KERNEL_MINOR="${KERNEL_MINOR%%.*}"
+
+MAJOR_REQUIRED=5
+MINOR_REQUIRED=7
+
+if [[ "$KERNEL_MAJOR" -lt $MAJOR_REQUIRED || ("$KERNEL_MAJOR" -eq $MAJOR_REQUIRED && "$KERNEL_MINOR" -lt $MINOR_REQUIRED) ]]; then
+ echo "kernel is not 5.7+" >>/skipped
+ exit 0
+fi
+
+trap teardown EXIT
+setup
+
+systemctl start --wait testsuite-62-1.service
+systemctl start --wait testsuite-62-2.service
+systemctl start --wait testsuite-62-3.service
+systemctl start --wait testsuite-62-4.service
+systemctl start --wait testsuite-62-5.service
+
+echo OK > /testok
+
+exit 0
# do nothing
elif pair[1] == '' or conf.get(pair[1]) == 1
custom_target(
- # XXX: workaround for old meson. Drop when upgrading.
- 'tmpfiles+' + pair[0],
+ pair[0],
input : pair[0] + '.in',
output: pair[0],
command : [meson_render_jinja2, config_h, '@INPUT@'],
if enable_tmpfiles and install_sysconfdir
meson.add_install_script(
- 'sh', '-c',
- mkdir_p.format(join_paths(sysconfdir, 'tmpfiles.d')))
+ 'sh', '-c', mkdir_p.format(sysconfdir / 'tmpfiles.d'))
endif
--- /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=Target that triggers factory reset. Does nothing by default.
+Documentation=man:systemd.special(7)
'sysinit.target.wants/'],
['emergency.target', ''],
['exit.target', ''],
+ ['factory-reset.target', ''],
['final.target', ''],
['first-boot-complete.target', ''],
['getty.target', '',
if install_sysconfdir
meson.add_install_script(meson_make_symlink,
- join_paths(pkgsysconfdir, 'user'),
- join_paths(sysconfdir, 'xdg/systemd/user'))
+ pkgsysconfdir / 'user',
+ sysconfdir / 'xdg/systemd/user')
endif
meson.add_install_script(meson_make_symlink,
- join_paths(dbussystemservicedir, 'org.freedesktop.systemd1.service'),
- join_paths(dbussessionservicedir, 'org.freedesktop.systemd1.service'))
+ dbussystemservicedir / 'org.freedesktop.systemd1.service',
+ dbussessionservicedir / 'org.freedesktop.systemd1.service')
if conf.get('HAVE_SYSV_COMPAT') == 1
foreach i : [1, 2, 3, 4, 5]
meson.add_install_script(
'sh', '-c',
- mkdir_p.format(join_paths(systemunitdir, 'runlevel@0@.target.wants'.format(i))))
+ mkdir_p.format(systemunitdir / 'runlevel@0@.target.wants'.format(i)))
endforeach
endif
--- /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=Automatic Boot Loader Update
+Documentation=man:bootctl(1)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=local-fs.target
+Before=sysinit.target shutdown.target systemd-update-done.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=bootctl --no-variables --graceful update
+
+[Install]
+WantedBy=sysinit.target
Description=Cleanup of Temporary Directories
Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8)
DefaultDependencies=no
-Conflicts=shutdown.target
+Conflicts=shutdown.target initrd-switch-root.service
After=local-fs.target time-set.target
Before=shutdown.target
Description=Create Volatile Files and Directories
Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8)
DefaultDependencies=no
-Conflicts=shutdown.target
+Conflicts=shutdown.target initrd-switch-root.service
After=local-fs.target systemd-sysusers.service systemd-journald.service
Before=sysinit.target shutdown.target
RefuseManualStop=yes