- 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)
pcre-devel
python3
python3-lxml
- python3-jinja2
+ python3-Jinja2
qrencode-devel
system-user-nobody
systemd-sysvinit
libapparmor1
libcrypt1
libcryptsetup12
+ libgcrypt20
libkmod2
liblz4-1
libmount1
Consult the kernel documentation for details on this sysctl:
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
+
+ * The v239 change to turn on "net.ipv4.tcp_ecn" by default has been
+ reverted.
* CPUAccounting=yes no longer enables the CPU controller when using
kernel 4.15+ and the unified cgroup hierarchy, as required accounting
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:
-* use credentials logic/TPM2 logic to store homed signing key
+* PAM: pick auf one authentication token from credentials
+
+* 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.
* New udev block device symlink names:
/dev/disk/by-parttypelabel/<pttype>/<ptlabel>. Use case: if pt label is used
* 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
- when homed is in use, maybe start the user session manager in a mount namespace with MS_SLAVE,
so that mounts propagate down but not up - eg, user A setting up a backup volume
doesn't mean user B sees it
-
-* homed: during login resize fs automatically towards size goal. Specifically,
- resize to diskSize if possible, but leave a certain amount (configured by a
- new value diskLeaveFreeSize) of space free on the backing fs.
-
-* homed: permit multiple user record signing keys to be used locally, and pick
- the right one for signing records automatically depending on a pre-existing
- signature
-
-* homed: add a way to "adopt" a home directory, i.e. strip foreign signatures
- and insert a local signature instead.
-
-* homed: as an extension to the directory+subvolume backend: if located on
- especially marked fs, then sync down password into LUKS header of that fs,
- and always verify passwords against it too. Bootstrapping is a problem
- though: if no one is logged in (or no other user even exists yet), how do you
- unlock the volume in order to create the first user and add the first pw.
-
-* homed: support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt
-
-* homed: maybe pre-create ~/.cache as subvol so that it can have separate quota
- easily?
-
-* homed: if kernel 5.12 uid mapping mounts exist, use that instead of recursive
- chowns.
-
-* add a switch to homectl (maybe called --first-boot) where it will check if
- any non-system users exist, and if not prompts interactively for basic user
- info, mimicking systemd-firstboot. Then, place this in a service that runs
- after systemd-homed, but before gdm and friends, as a simple, barebones
- fallback logic to get a regular user created on uninitialized systems.
-
-* homed: store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with
- systemd-cryptsetup, so that it can unlock homed volumes
-
-* homed: try to unmount in regular intervals when home dir was busy when we
- tried because idle.
-
-* homed: keep an fd to the homedir open at all times, to keep the fs pinned
- (autofs and such) while user is logged in.
-
-* when we resize disks (homed?) always round up to 4K sectors, not 512K
+ - use credentials logic/TPM2 logic to store homed signing key
+ - during login resize fs automatically towards size goal. Specifically,
+ resize to diskSize if possible, but leave a certain amount (configured by a
+ new value diskLeaveFreeSize) of space free on the backing fs.
+ - permit multiple user record signing keys to be used locally, and pick
+ the right one for signing records automatically depending on a pre-existing
+ signature
+ - add a way to "adopt" a home directory, i.e. strip foreign signatures
+ and insert a local signature instead.
+ - as an extension to the directory+subvolume backend: if located on
+ especially marked fs, then sync down password into LUKS header of that fs,
+ and always verify passwords against it too. Bootstrapping is a problem
+ though: if no one is logged in (or no other user even exists yet), how do you
+ unlock the volume in order to create the first user and add the first pw.
+ - support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt
+ - maybe pre-create ~/.cache as subvol so that it can have separate quota
+ easily?
+ - if kernel 5.12 uid mapping mounts exist, use that instead of recursive
+ chowns.
+ - add a switch to homectl (maybe called --first-boot) where it will check if
+ any non-system users exist, and if not prompts interactively for basic user
+ info, mimicking systemd-firstboot. Then, place this in a service that runs
+ after systemd-homed, but before gdm and friends, as a simple, barebones
+ fallback logic to get a regular user created on uninitialized systems.
+ - store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with
+ systemd-cryptsetup, so that it can unlock homed volumes
+ - try to unmount in regular intervals when home dir was busy when we
+ tried because idle.
+ - keep an fd to the homedir open at all times, to keep the fs pinned
+ (autofs and such) while user is logged in.
* add a new switch --auto-definitions=yes/no or so to systemd-repart. If
specified, synthesize a definition automatically if we can: enlarge last
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
| `77055800-792c-4f94-b39a-98c91b762bb6` | _Root Partition (LoongArch 64-bit)_ | ditto | ditto |
| `60d5a7fe-8e7d-435c-b714-3dd8162144e1` | _Root Partition (RISC-V 32-bit)_ | ditto | ditto |
| `72ec70a6-cf74-40e6-bd49-4bda08e8f224` | _Root Partition (RISC-V 64-bit)_ | ditto | ditto |
-| `d13c5d3b-b5d1-422a-b29f-9454fdc89d76` | _Root Verity Partition (x86)_ | A dm-verity superblock followed by hash data | On systems with matching architecture, contains dm-verity integrity hash data for the matching root partition. If this feature is used the partition UUID of the root partition should be the first 128bit of the root hash of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the final 128bit of it, so that the root partition and its verity partition can be discovered easily, simply by specifying the root hash. |
+| `d13c5d3b-b5d1-422a-b29f-9454fdc89d76` | _Root Verity Partition (x86)_ | A dm-verity superblock followed by hash data | Contains dm-verity integrity hash data for the matching root partition. If this feature is used the partition UUID of the root partition should be the first 128 bits of the root hash of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the final 128 bits of it, so that the root partition and its verity partition can be discovered easily, simply by specifying the root hash. |
| `2c7357ed-ebd2-46d9-aec1-23d437ec2bf5` | _Root Verity Partition (x86-64)_ | ditto | ditto |
| `7386cdf2-203c-47a9-a498-f2ecce45a2d6` | _Root Verity Partition (32-bit ARM)_ | ditto | ditto |
| `df3300ce-d69f-4c92-978c-9bfb0f38d820` | _Root Verity Partition (64-bit ARM/AArch64)_ | ditto | ditto |
| `8f1056be-9b05-47c4-81d6-be53128e5b54` | _`/usr/` Verity Partition (RISC-V 64-bit)_ | ditto | ditto |
| `933ac7e1-2eb4-4f13-b844-0e14e2aef915` | _Home Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/home/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/home`. |
| `3b8f8425-20e0-4f3b-907f-1a25a76f98e8` | _Server Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/srv/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/srv`. |
-| `4d21b016-b534-45c2-a9fb-5c16e091fd2d` | _Variable Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/` — under the condition that its partition UUID matches the first 128 bit of `HMAC-SHA256(machine-id, 0x4d21b016b53445c2a9fb5c16e091fd2d)` (i.e. the SHA256 HMAC hash of the binary type UUID keyed by the machine ID as read from [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html). This special requirement is made because `/var/` (unlike the other partition types listed here) is inherently private to a specific installation and cannot possibly be shared between multiple OS installations on the same disk, and thus should be bound to a specific instance of the OS, identified by its machine ID. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`. |
+| `4d21b016-b534-45c2-a9fb-5c16e091fd2d` | _Variable Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/` — under the condition that its partition UUID matches the first 128 bits of `HMAC-SHA256(machine-id, 0x4d21b016b53445c2a9fb5c16e091fd2d)` (i.e. the SHA256 HMAC hash of the binary type UUID keyed by the machine ID as read from [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html). This special requirement is made because `/var/` (unlike the other partition types listed here) is inherently private to a specific installation and cannot possibly be shared between multiple OS installations on the same disk, and thus should be bound to a specific instance of the OS, identified by its machine ID. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`. |
| `7ec6f557-3bc5-4aca-b293-16ef5df639d1` | _Temporary Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/tmp/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/tmp`. Note that the intended mount point is indeed `/var/tmp/`, not `/tmp/`. The latter is typically maintained in memory via <tt>tmpfs</tt> and does not require a partition on disk. In some cases it might be desirable to make `/tmp/` persistent too, in which case it is recommended to make it a symlink or bind mount to `/var/tmp/`, thus not requiring its own partition type UUID. |
| `0657fd6d-a4ab-43c4-84e5-0933c84b4f4f` | _Swap_ | Swap, optionally in LUKS | All swap partitions on the disk containing the root partition are automatically enabled. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/swap`. This partition type predates the Discoverable Partitions Specification. |
| `0fc63daf-8483-4772-8e79-3d69d8477de4` | _Generic Linux Data Partitions_ | Any native, optionally in LUKS | No automatic mounting takes place for other Linux data partitions. This partition type should be used for all partitions that carry Linux file systems. The installer needs to mount them explicitly via entries in <tt>/etc/fstab</tt>. Optionally, these partitions may be encrypted with LUKS. This partition type predates the Discoverable Partitions Specification. |
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
evdev:name:gpio-keys:phys:gpio-keys/input0:ev:23:dmi:*:svnHewlett-Packard:pnHPStream7Tablet:*
KEYBOARD_KEY_0=unknown
+# HP Omen 15
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pnOMENLaptop15*:pvr*
+ KEYBOARD_KEY_a1=!calc
+
##########################################################
# Huawei
##########################################################
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrY13D_KB133.103:bd06/01/2018:svnHampoo:pnDefaultstring:pvrV100:rvnHampoo:rnY13D_KB133:rvrV100:cvnDefaultstring:ct9:cvrDefaultstring:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+# Chuwi SurBook Mini (CWI540)
+sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo*:pnC3W6_AP108_4GB:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+
#########################################
# Connect
#########################################
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/25/2017:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
ACCEL_LOCATION=base
+#########################################
+# Hometech
+########################################
+
+# Nobody bothered to use Linux on any device of this manufacturer
+# so current marks might be too general and need fixes.
+# These values are based on Wi101 model.
+sensor:modalias:acpi:BMA250E*:dmi:*:svnInsyde*:pni101c:*
+ ACCEL_MOUNT_MATRIX=0,1,0;-1,0,0;-1,0,0
+
#########################################
# HP
#########################################
# udevadm info /dev/input/eventXX.
#
# Allowed properties are:
+# ID_INPUT_POINTINGSTICK
# POINTINGSTICK_CONST_ACCEL (deprecated)
# POINTINGSTICK_SENSITIVITY
#
# Sort by brand, model
+##########################################
+# Generic
+##########################################
+evdev:name:*[tT]rack[pP]oint*:*
+ ID_INPUT_POINTINGSTICK=1
+
#########################################
# Dell
#########################################
# 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
<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>
</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>
<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>
@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>
main system. Additionally, the presence of that file means that the system is in the initrd phase.
<filename>/etc/os-release</filename> should be symlinked to <filename>/etc/initrd-release</filename>
(or vice versa), so programs that only look for <filename>/etc/os-release</filename> (as described
- 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>
+ above) work correctly.</para>
+
+ <para>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>
+ plays the same role for extension images as <filename>os-release</filename> for the main system, and
+ follows the 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 identify the extension and to allow the operating system to verify that the extension image
+ matches the base OS. This is typically implemented by checking that the <varname>ID=</varname> options
+ match, and either <varname>SYSEXT_LEVEL=</varname> exists and matches too, or if it is not present,
+ <varname>VERSION_ID=</varname> exists and matches. This ensures ABI/API compatibility between the
+ layers and prevents merging of an incompatible image in an overlay.</para>
+
+ <para>In the <filename>extension-release.<replaceable>IMAGE</replaceable></filename> filename, the
+ <replaceable>IMAGE</replaceable> part must exactly match the file name of the containing image with the
+ suffix removed. 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, it is possible to relax this check: if exactly one
+ file whose name matches <literal><filename>extension-release.*</filename></literal> is present in this
+ directory, and the file 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>, it will be used instead.</para>
+
+ <para>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>
<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>
#include <stdio.h>
+#include <stdlib.h>
#include <sd-path.h>
int main(void) {
+ int r;
char *t;
- sd_path_lookup(SD_PATH_USER_DOCUMENTS, NULL, &t);
+ r = sd_path_lookup(SD_PATH_USER_DOCUMENTS, NULL, &t);
+ if (r < 0)
+ return EXIT_FAILURE;
+
printf("~/Documents: %s\n", t);
+ free(t);
+
+ return EXIT_SUCCESS;
}
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>
['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" />
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
For more information, see <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
+ <para>The root partition can be specified by symlinking <filename>/run/systemd/volatile-root</filename>
+ to <filename>/dev/block/$major:$minor</filename>. This is especially useful if the root mount has been
+ replaced by some form of volatile file system (overlayfs).
+ </para>
+
<para>Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP
is mounted to <filename>/boot/</filename> (except if an Extended Boot Loader partition exists, see
below), unless a mount point directory <filename>/efi/</filename> exists, in which case it is mounted
</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>
</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>
<para>During boot OS extension images are activated automatically, if the
<filename>systemd-sysext.service</filename> is enabled. Note that this service runs only after the
- underlying file systems where system extensions are searched are mounted. This means they are not
+ underlying file systems where system extensions may be located have been mounted. This means they are not
suitable for shipping resources that are processed by subsystems running in earliest boot. Specifically,
OS extension images are not suitable for shipping system services or
<citerefentry><refentrytitle>systemd-sysusers</refentrytitle><manvolnum>8</manvolnum></citerefentry>
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>
<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>
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,
<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>LargeReceiveOffload=</varname></term>
+ <term><varname>GenericReceiveOffloadHardware=</varname></term>
<listitem>
- <para>Takes a boolean. If set to true, the Large Receive Offload (LRO) is enabled.
- When unset, the kernel's default will be used.</para>
+ <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>RxChannels=</varname></term>
+ <term><varname>LargeReceiveOffload=</varname></term>
<listitem>
- <para>Sets the number of receive channels (a number between 1 and 4294967295) .</para>
+ <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>
- <listitem>
- <para>Sets the number of transmit channels (a number between 1 and 4294967295).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
<term><varname>OtherChannels=</varname></term>
- <listitem>
- <para>Sets the number of other channels (a number between 1 and 4294967295).</para>
- </listitem>
- </varlistentry>
- <varlistentry>
<term><varname>CombinedChannels=</varname></term>
<listitem>
- <para>Sets the number of combined set 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>RxBufferSize=</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>
- </listitem>
- </varlistentry>
- <varlistentry>
<term><varname>RxMiniBufferSize=</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>
- </listitem>
- </varlistentry>
- <varlistentry>
<term><varname>RxJumboBufferSize=</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>
- </listitem>
- </varlistentry>
- <varlistentry>
<term><varname>TxBufferSize=</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>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>RxFlowControl=</varname></term>
<listitem>
- <para>Takes a boolean. When set, enables the receive flow control, also known as the ethernet
+ <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>
<term><varname>TxFlowControl=</varname></term>
<listitem>
- <para>Takes a boolean. When set, enables the transmit flow control, also known as the ethernet
+ <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>
<term><varname>AutoNegotiationFlowControl=</varname></term>
<listitem>
- <para>Takes a boolean. When set, the auto negotiation enables the interface to exchange state
+ <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>
accept. An unsigned integer in the range 1…65535. Defaults to unset.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>UseAdaptiveRxCoalesce=</varname></term>
+ <term><varname>UseAdaptiveTxCoalesce=</varname></term>
+ <listitem>
+ <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>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>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>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>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>CoalescePacketRateLow=</varname></term>
+ <term><varname>CoalescePacketRateHigh=</varname></term>
+ <listitem>
+ <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>CoalescePacketRateSampleIntervalSec=</varname></term>
+ <listitem>
+ <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>StatisticsBlockCoalesceSec=</varname></term>
+ <listitem>
+ <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>
</variablelist>
</refsect1>
<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>
has been unsuccessful for some time. (IPv4 link-local address autoconfiguration will usually
happen in parallel with repeated attempts to acquire a DHCPv4 lease).</para>
- <para>Defaults to <option>no</option> when <varname>Bridge=yes</varname> is set, and
+ <para>Defaults to <option>no</option> when <varname>Bridge=</varname> is set or when the specified
+ <varname>MACVLAN=</varname>/<varname>MACVTAP=</varname> has <varname>Mode=passthru</varname>, or
<option>ipv6</option> otherwise.</para>
</listitem>
</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>
<!-- 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>
<varlistentry>
<term><varname>DenyList=</varname></term>
<listitem>
- <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are
+ <para>A whitespace-separated list of IPv4 addresses. Each address can optionally take a
+ prefix length after <literal>/</literal>. DHCP offers from servers in the list are
rejected. Note that if <varname>AllowList=</varname> is configured then
<varname>DenyList=</varname> is ignored.</para>
</listitem>
<varlistentry>
<term><varname>AllowList=</varname></term>
<listitem>
- <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are
+ <para>A whitespace-separated list of IPv4 addresses. Each address can optionally take a
+ prefix length after <literal>/</literal>. DHCP offers from servers in the list are
accepted.</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>RouterDenyList=</varname></term>
<listitem>
- <para>A whitespace-separated list of IPv6 router addresses. Any information advertised by
- the listed router is ignored.</para>
+ <para>A whitespace-separated list of IPv6 router addresses. Each address can optionally
+ take a prefix length after <literal>/</literal>. Any information advertised by the listed
+ router is ignored.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RouterAllowList=</varname></term>
<listitem>
- <para>A whitespace-separated list of IPv6 router addresses. Only information advertised by
- the listed router is accepted. Note that if <varname>RouterAllowList=</varname> is
- configured then <varname>RouterDenyList=</varname> is ignored.</para>
+ <para>A whitespace-separated list of IPv6 router addresses. Each address can optionally
+ take a prefix length after <literal>/</literal>. Only information advertised by the listed
+ router is accepted. Note that if <varname>RouterAllowList=</varname> is configured then
+ <varname>RouterDenyList=</varname> is ignored.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>PrefixDenyList=</varname></term>
<listitem>
- <para>A whitespace-separated list of IPv6 prefixes. IPv6 prefixes supplied via router
- advertisements in the list are ignored.</para>
+ <para>A whitespace-separated list of IPv6 prefixes. Each prefix can optionally take its
+ prefix length after <literal>/</literal>. IPv6 prefixes supplied via router advertisements
+ in the list are ignored.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>PrefixAllowList=</varname></term>
<listitem>
- <para>A whitespace-separated list of IPv6 prefixes. IPv6 prefixes supplied via router
- advertisements in the list are allowed. Note that if <varname>PrefixAllowList=</varname> is
- configured then <varname>PrefixDenyList=</varname> is ignored.</para>
+ <para>A whitespace-separated list of IPv6 prefixes. Each prefix can optionally take its
+ prefix length after <literal>/</literal>. IPv6 prefixes supplied via router advertisements
+ in the list are allowed. Note that if <varname>PrefixAllowList=</varname> is configured
+ then <varname>PrefixDenyList=</varname> is ignored.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RouteDenyList=</varname></term>
<listitem>
- <para>A whitespace-separated list of IPv6 route prefixes. IPv6 route prefixes supplied via
- router advertisements in the list are ignored.</para>
+ <para>A whitespace-separated list of IPv6 route prefixes. Each prefix can optionally take
+ its prefix length after <literal>/</literal>. IPv6 route prefixes supplied via router
+ advertisements in the list are ignored.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RouteAllowList=</varname></term>
<listitem>
- <para>A whitespace-separated list of IPv6 route prefixes. IPv6 route prefixes supplied via
- router advertisements in the list are allowed. Note that if <varname>RouteAllowList=</varname> is
+ <para>A whitespace-separated list of IPv6 route prefixes. Each prefix can optionally take
+ its prefix length after <literal>/</literal>. IPv6 route prefixes supplied via router
+ advertisements in the list are allowed. Note that if <varname>RouteAllowList=</varname> is
configured then <varname>RouteDenyList=</varname> is ignored.</para>
</listitem>
</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>
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>
</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 assignments 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>
<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>
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>
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>
'-Werror=shift-count-overflow',
'-Werror=shift-overflow=2',
'-Werror=undef',
+ '-Werror=unused-function',
'-Wfloat-equal',
'-Wimplicit-fallthrough=5',
'-Winit-self',
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],
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
############################################################
# LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS}
if conf.get('ENABLE_EFI') == 1
- summary({'efi arch' : efi_arch},
+ summary({'EFI arch' : efi_arch},
section : 'Extensible Firmware Interface')
if have_gnu_efi
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]')
# Benjamin Steinwender <b@stbe.at>, 2014.
# Bernd Homuth <dev@hmt.im>, 2015.
# Fabian Affolter <mail@fabian-affolter.ch>, 2020.
+# Ettore Atalan <atalanttore@googlemail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: systemd master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2020-12-13 14:36+0000\n"
-"Last-Translator: Fabian Affolter <mail@fabian-affolter.ch>\n"
+"PO-Revision-Date: 2021-08-23 18:04+0000\n"
+"Last-Translator: Ettore Atalan <atalanttore@googlemail.com>\n"
"Language-Team: German <https://translate.fedoraproject.org/projects/systemd/"
"master/de/>\n"
"Language: de\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.3.2\n"
+"X-Generator: Weblate 4.8\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
"anforderte es zu unterbinden."
#: src/login/org.freedesktop.login1.policy:235
-#, fuzzy
-#| msgid "Hibernate the system"
msgid "Halt the system"
-msgstr "Den Ruhezustand des Systems aktivieren"
+msgstr "Das System anhalten"
#: src/login/org.freedesktop.login1.policy:236
#, fuzzy
#: src/login/org.freedesktop.login1.policy:406
msgid "Change Session"
-msgstr ""
+msgstr "Sitzung ändern"
#: src/login/org.freedesktop.login1.policy:407
#, fuzzy
#: src/network/org.freedesktop.network1.policy:22
msgid "Set NTP servers"
-msgstr ""
+msgstr "NTP-Server festlegen"
#: src/network/org.freedesktop.network1.policy:23
#, fuzzy
#: src/network/org.freedesktop.network1.policy:33
#: src/resolve/org.freedesktop.resolve1.policy:44
msgid "Set DNS servers"
-msgstr ""
+msgstr "DNS-Server festlegen"
#: src/network/org.freedesktop.network1.policy:34
#: src/resolve/org.freedesktop.resolve1.policy:45
#: src/network/org.freedesktop.network1.policy:55
#: src/resolve/org.freedesktop.resolve1.policy:66
msgid "Set default route"
-msgstr ""
+msgstr "Standardroute festlegen"
#: src/network/org.freedesktop.network1.policy:56
#: src/resolve/org.freedesktop.resolve1.policy:67
#: src/network/org.freedesktop.network1.policy:66
#: src/resolve/org.freedesktop.resolve1.policy:77
msgid "Enable/disable LLMNR"
-msgstr ""
+msgstr "LLMNR aktivieren/deaktivieren"
#: src/network/org.freedesktop.network1.policy:67
#: src/resolve/org.freedesktop.resolve1.policy:78
#: src/network/org.freedesktop.network1.policy:77
#: src/resolve/org.freedesktop.resolve1.policy:88
msgid "Enable/disable multicast DNS"
-msgstr ""
+msgstr "Multicast-DNS aktivieren/deaktivieren"
#: src/network/org.freedesktop.network1.policy:78
#: src/resolve/org.freedesktop.resolve1.policy:89
#: src/network/org.freedesktop.network1.policy:88
#: src/resolve/org.freedesktop.resolve1.policy:99
msgid "Enable/disable DNS over TLS"
-msgstr ""
+msgstr "DNS over TLS aktivieren/deaktivieren"
#: src/network/org.freedesktop.network1.policy:89
#: src/resolve/org.freedesktop.resolve1.policy:100
#: src/network/org.freedesktop.network1.policy:99
#: src/resolve/org.freedesktop.resolve1.policy:110
msgid "Enable/disable DNSSEC"
-msgstr ""
+msgstr "DNSSEC aktivieren/deaktivieren"
#: src/network/org.freedesktop.network1.policy:100
#: src/resolve/org.freedesktop.resolve1.policy:111
#: src/network/org.freedesktop.network1.policy:110
#: src/resolve/org.freedesktop.resolve1.policy:121
msgid "Set DNSSEC Negative Trust Anchors"
-msgstr ""
+msgstr "Negative Vertrauensanker für DNSSEC festlegen"
#: src/network/org.freedesktop.network1.policy:111
#: src/resolve/org.freedesktop.resolve1.policy:122
#: src/network/org.freedesktop.network1.policy:121
msgid "Revert NTP settings"
-msgstr ""
+msgstr "NTP-Einstellungen zurücksetzen"
#: src/network/org.freedesktop.network1.policy:122
#, fuzzy
#: src/network/org.freedesktop.network1.policy:132
msgid "Revert DNS settings"
-msgstr ""
+msgstr "DNS-Einstellungen zurücksetzen"
#: src/network/org.freedesktop.network1.policy:133
#, fuzzy
#: src/network/org.freedesktop.network1.policy:154
msgid "Renew dynamic addresses"
-msgstr ""
+msgstr "Dynamische Adressen erneuern"
#: src/network/org.freedesktop.network1.policy:155
#, fuzzy
#: src/network/org.freedesktop.network1.policy:165
msgid "Reload network settings"
-msgstr ""
+msgstr "Netzwerkeinstellungen neu laden"
#: src/network/org.freedesktop.network1.policy:166
#, fuzzy
#: src/network/org.freedesktop.network1.policy:176
msgid "Reconfigure network interface"
-msgstr ""
+msgstr "Netzwerkschnittstelle neu konfigurieren"
#: src/network/org.freedesktop.network1.policy:177
#, fuzzy
#: src/resolve/org.freedesktop.resolve1.policy:22
msgid "Register a DNS-SD service"
-msgstr ""
+msgstr "Einen DNS-SD-Dienst registrieren"
#: src/resolve/org.freedesktop.resolve1.policy:23
#, fuzzy
#: src/resolve/org.freedesktop.resolve1.policy:33
msgid "Unregister a DNS-SD service"
-msgstr ""
+msgstr "Einen DNS-SD-Dienst deregistrieren"
#: src/resolve/org.freedesktop.resolve1.policy:34
#, fuzzy
#: src/resolve/org.freedesktop.resolve1.policy:132
msgid "Revert name resolution settings"
-msgstr ""
+msgstr "Namensauflösungseinstellungen zurücksetzen"
#: src/resolve/org.freedesktop.resolve1.policy:133
#, fuzzy
# Alex Puchades <alex94puchades@gmail.com>, 2015.
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2015.
# Álex Puchades <alex94puchades@gmail.com>, 2015.
-# Adolfo Jayme Barrientos <fitoschido@gmail.com>, 2020.
+# Adolfo Jayme Barrientos <fitoschido@gmail.com>, 2020, 2021.
# Emilio Herrera <ehespinosa57@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: systemd master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2021-06-08 09:04+0000\n"
-"Last-Translator: Emilio Herrera <ehespinosa57@gmail.com>\n"
+"PO-Revision-Date: 2021-08-26 18:05+0000\n"
+"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
"Language-Team: Spanish <https://translate.fedoraproject.org/projects/systemd/"
"master/es/>\n"
"Language: es\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.6.2\n"
+"X-Generator: Weblate 4.8\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
msgid ""
"Authentication is required to check credentials against a user's home area."
msgstr ""
-"Se requiere autenticación para comprobar las credenciales contra un área "
-"home de usuario."
+"Necesita autenticarse para comprobar las credenciales del espacio personal "
+"de un usuario."
#: src/home/org.freedesktop.home1.policy:43
msgid "Update a home area"
-msgstr "Actualizar un área home"
+msgstr "Actualizar un espacio personal"
#: src/home/org.freedesktop.home1.policy:44
msgid "Authentication is required to update a user's home area."
-msgstr "Se requiere autenticación para actualizar un área home de usuario."
+msgstr ""
+"Necesita autenticarse para actualizar el espacio personal de un usuario."
#: src/home/org.freedesktop.home1.policy:53
msgid "Resize a home area"
-msgstr ""
+msgstr "Redimensionar un espacio personal"
#: src/home/org.freedesktop.home1.policy:54
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to resize a user's home area."
-msgstr "Se requiere autenticación para establecer un muro de texto"
+msgstr ""
+"Necesita autenticarse para redimensionar el espacio personal de un usuario."
#: src/home/org.freedesktop.home1.policy:63
msgid "Change password of a home area"
-msgstr ""
+msgstr "Cambiar contraseña de un espacio personal"
#: 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 ""
-"Se requiere autenticación para administrar las sesiones activas, usuarios y "
-"puestos de trabajo."
+"Necesita autenticarse para cambiar la contraseña del espacio personal de un "
+"usuario."
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set hostname"
#: src/hostname/org.freedesktop.hostname1.policy:51
msgid "Get product UUID"
-msgstr ""
+msgstr "Obtener UUID del producto"
#: src/hostname/org.freedesktop.hostname1.policy:52
-#, fuzzy
-#| msgid "Authentication is required to reload '$(unit)'."
msgid "Authentication is required to get product UUID."
-msgstr "Se requiere autenticación para recargar '$(unit)'."
+msgstr "Necesita autenticarse para obtener el UUID de un producto."
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
#: src/login/org.freedesktop.login1.policy:352
msgid "Set the reboot \"reason\" in the kernel"
-msgstr ""
+msgstr "Establecer la «razón» de reinicio en el núcleo"
#: src/login/org.freedesktop.login1.policy:353
-#, fuzzy
-#| msgid "Authentication is required to set the system timezone."
msgid "Authentication is required to set the reboot \"reason\" in the kernel."
-msgstr "Se requiere autenticación para establecer la zona horaria del sistema."
+msgstr ""
+"Necesita autenticarse para establecer la «razón» de reinicio en el núcleo."
#: src/login/org.freedesktop.login1.policy:363
#, fuzzy
#: src/login/org.freedesktop.login1.policy:374
msgid "Indicate to the boot loader to boot to the boot loader menu"
-msgstr ""
+msgstr "Indicar al cargador de arranque que inicie el menú de selección"
#: src/login/org.freedesktop.login1.policy:375
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to indicate to the firmware to boot to setup "
-#| "interface."
msgid ""
"Authentication is required to indicate to the boot loader to boot to the "
"boot loader menu."
msgstr ""
-"Se requiere autenticación para indicar al firmware que arranque la interfaz "
-"de configuración."
+"Necesita autenticarse para indicar al cargador de arranque que inicie el "
+"menú de selección."
#: src/login/org.freedesktop.login1.policy:385
msgid "Indicate to the boot loader to boot a specific entry"
-msgstr ""
+msgstr "Indicar al cargador de arranque que inicie una entrada concreta"
#: src/login/org.freedesktop.login1.policy:386
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to indicate to the firmware to boot to setup "
-#| "interface."
msgid ""
"Authentication is required to indicate to the boot loader to boot into a "
"specific boot loader entry."
msgstr ""
-"Se requiere autenticación para indicar al firmware que arranque la interfaz "
-"de configuración."
+"Necesita autenticarse para indicar al cargador de arranque que inicie una "
+"entrada concreta."
#: src/login/org.freedesktop.login1.policy:396
msgid "Set a wall message"
#: src/login/org.freedesktop.login1.policy:406
msgid "Change Session"
-msgstr ""
+msgstr "Cambiar sesión"
#: src/login/org.freedesktop.login1.policy:407
-#, fuzzy
-#| msgid "Authentication is required to set the local hostname."
msgid "Authentication is required to change the virtual terminal."
-msgstr "Se requiere autenticación para establecer el nombre del equipo local."
+msgstr "Necesita autenticarse para cambiar la terminal virtual."
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
#: src/machine/org.freedesktop.machine1.policy:32
msgid "Log into the local host"
-msgstr "Conectarse al equipo local"
+msgstr "Acceder al anfitrión local"
#: src/machine/org.freedesktop.machine1.policy:33
msgid "Authentication is required to log into the local host."
-msgstr "Necesita autenticarse para conectarse al equipo local."
+msgstr "Necesita autenticarse para acceder al anfitrión local."
#: src/machine/org.freedesktop.machine1.policy:42
msgid "Acquire a shell in a local container"
#: src/network/org.freedesktop.network1.policy:22
msgid "Set NTP servers"
-msgstr ""
+msgstr "Establecer servidores NTP"
#: src/network/org.freedesktop.network1.policy:23
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
msgid "Authentication is required to set NTP servers."
-msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+msgstr "Necesita autenticarse para establecer servidores NTP."
#: src/network/org.freedesktop.network1.policy:33
#: src/resolve/org.freedesktop.resolve1.policy:44
msgid "Set DNS servers"
-msgstr ""
+msgstr "Establecer servidores DNS"
#: src/network/org.freedesktop.network1.policy:34
#: src/resolve/org.freedesktop.resolve1.policy:45
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
msgid "Authentication is required to set DNS servers."
-msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+msgstr "Necesita autenticarse para establecer servidores DNS."
#: src/network/org.freedesktop.network1.policy:44
#: src/resolve/org.freedesktop.resolve1.policy:55
msgid "Set domains"
-msgstr ""
+msgstr "Establecer dominios"
#: src/network/org.freedesktop.network1.policy:45
#: src/resolve/org.freedesktop.resolve1.policy:56
-#, fuzzy
-#| msgid "Authentication is required to stop '$(unit)'."
msgid "Authentication is required to set domains."
-msgstr "Se requiere autenticación para detener '$(unit)'."
+msgstr "Necesita autenticarse para establecer dominios."
#: src/network/org.freedesktop.network1.policy:55
#: src/resolve/org.freedesktop.resolve1.policy:66
msgid "Set default route"
-msgstr ""
+msgstr "Establecer ruta predeterminada"
#: src/network/org.freedesktop.network1.policy:56
#: src/resolve/org.freedesktop.resolve1.policy:67
-#, fuzzy
-#| msgid "Authentication is required to set the local hostname."
msgid "Authentication is required to set default route."
-msgstr "Se requiere autenticación para establecer el nombre del equipo local."
+msgstr "Necesita autenticarse para establecer la ruta predeterminada."
#: src/network/org.freedesktop.network1.policy:66
#: src/resolve/org.freedesktop.resolve1.policy:77
msgid "Enable/disable LLMNR"
-msgstr ""
+msgstr "Activar/desactivar LLMNR"
#: src/network/org.freedesktop.network1.policy:67
#: src/resolve/org.freedesktop.resolve1.policy:78
-#, fuzzy
-#| msgid "Authentication is required to hibernate the system."
msgid "Authentication is required to enable or disable LLMNR."
-msgstr "Se requiere autenticación para hibernar el sistema."
+msgstr "Necesita autenticarse para activar o desactivar LLMNR."
#: src/network/org.freedesktop.network1.policy:77
#: src/resolve/org.freedesktop.resolve1.policy:88
msgid "Enable/disable multicast DNS"
-msgstr ""
+msgstr "Activar/desactivar DNS multidifusión"
#: src/network/org.freedesktop.network1.policy:78
#: src/resolve/org.freedesktop.resolve1.policy:89
-#, fuzzy
-#| msgid "Authentication is required to log into the local host."
msgid "Authentication is required to enable or disable multicast DNS."
-msgstr "Se requiere autenticación para conectarse al equipo local."
+msgstr "Necesita autenticarse para activar o desactivar DNS multidifusión."
#: src/network/org.freedesktop.network1.policy:88
#: src/resolve/org.freedesktop.resolve1.policy:99
msgid "Enable/disable DNS over TLS"
-msgstr ""
+msgstr "Activar/desactivar DNS por TLS"
#: src/network/org.freedesktop.network1.policy:89
#: src/resolve/org.freedesktop.resolve1.policy:100
-#, fuzzy
-#| msgid "Authentication is required to set the local hostname."
msgid "Authentication is required to enable or disable DNS over TLS."
-msgstr "Se requiere autenticación para establecer el nombre del equipo local."
+msgstr "Necesita autenticarse para activar o desactivar DNS por TLS."
#: src/network/org.freedesktop.network1.policy:99
#: src/resolve/org.freedesktop.resolve1.policy:110
msgid "Enable/disable DNSSEC"
-msgstr ""
+msgstr "Activar/desactivar DNSSEC"
#: src/network/org.freedesktop.network1.policy:100
#: src/resolve/org.freedesktop.resolve1.policy:111
-#, fuzzy
-#| msgid "Authentication is required to hibernate the system."
msgid "Authentication is required to enable or disable DNSSEC."
-msgstr "Se requiere autenticación para hibernar el sistema."
+msgstr "Necesita autenticarse para activar o desactivar DNSSEC."
#: src/network/org.freedesktop.network1.policy:110
#: src/resolve/org.freedesktop.resolve1.policy:121
msgid "Set DNSSEC Negative Trust Anchors"
-msgstr ""
+msgstr "Establecer anclas de confianza negativas de DNSSEC"
#: src/network/org.freedesktop.network1.policy:111
#: src/resolve/org.freedesktop.resolve1.policy:122
-#, fuzzy
-#| msgid "Authentication is required to set the system locale."
msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
-msgstr "Se requiere autenticación para establecer la región del sistema."
+msgstr ""
+"Necesita autenticarse para establecer las anclas de confianza negativas de "
+"DNSSEC."
#: src/network/org.freedesktop.network1.policy:121
msgid "Revert NTP settings"
-msgstr ""
+msgstr "Revertir configuración de NTP"
#: src/network/org.freedesktop.network1.policy:122
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
msgid "Authentication is required to reset NTP settings."
-msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+msgstr "Necesita autenticarse para restablecer la configuración de NTP."
#: src/network/org.freedesktop.network1.policy:132
msgid "Revert DNS settings"
-msgstr ""
+msgstr "Revertir configuración de DNS"
#: src/network/org.freedesktop.network1.policy:133
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
msgid "Authentication is required to reset DNS settings."
-msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+msgstr "Necesita autenticarse para restablecer la configuración de DNS."
#: src/network/org.freedesktop.network1.policy:143
msgid "DHCP server sends force renew message"
-msgstr ""
+msgstr "El servidor DCHP envía un mensaje de renovación forzada"
#: 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 "Se requiere autenticación para establecer un muro de texto"
+msgstr "Necesita autenticarse para enviar el mensaje de renovación forzada."
#: src/network/org.freedesktop.network1.policy:154
msgid "Renew dynamic addresses"
-msgstr ""
+msgstr "Renovar direcciones dinámicas"
#: src/network/org.freedesktop.network1.policy:155
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to renew dynamic addresses."
-msgstr "Se requiere autenticación para establecer un muro de texto"
+msgstr "Necesita autenticarse para renovar las direcciones dinámicas."
#: src/network/org.freedesktop.network1.policy:165
msgid "Reload network settings"
-msgstr ""
+msgstr "Recargar configuración de red"
#: src/network/org.freedesktop.network1.policy:166
-#, fuzzy
-#| msgid "Authentication is required to reload the systemd state."
msgid "Authentication is required to reload network settings."
-msgstr "Se requiere autenticación para recargar el estado de systemd."
+msgstr "Necesita autenticarse para recargar la configuración de red."
#: src/network/org.freedesktop.network1.policy:176
msgid "Reconfigure network interface"
-msgstr ""
+msgstr "Reconfigurar interfaz de red"
#: 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 "Se requiere autenticación para reiniciar el sistema."
+msgstr "Necesita autenticarse para reconfigurar la interfaz de red."
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
-msgstr ""
+msgstr "Inspeccionar una imagen de servicio portátil"
#: src/portable/org.freedesktop.portable1.policy:14
#, fuzzy
#: src/portable/org.freedesktop.portable1.policy:23
msgid "Attach or detach a portable service image"
-msgstr ""
+msgstr "Adjuntar o desadjuntar una imagen de servicio portátil"
#: src/portable/org.freedesktop.portable1.policy:24
#, fuzzy
#: src/portable/org.freedesktop.portable1.policy:34
msgid "Delete or modify portable service image"
-msgstr ""
+msgstr "Eliminar o modificar imagen de servicio portátil"
#: src/portable/org.freedesktop.portable1.policy:35
#, fuzzy
#: src/resolve/org.freedesktop.resolve1.policy:22
msgid "Register a DNS-SD service"
-msgstr ""
+msgstr "Registrar un servicio DNS-SD"
#: src/resolve/org.freedesktop.resolve1.policy:23
#, fuzzy
#: src/resolve/org.freedesktop.resolve1.policy:33
msgid "Unregister a DNS-SD service"
-msgstr ""
+msgstr "Desregistrar un servicio DNS-SD"
#: src/resolve/org.freedesktop.resolve1.policy:34
#, fuzzy
#: src/resolve/org.freedesktop.resolve1.policy:132
msgid "Revert name resolution settings"
-msgstr ""
+msgstr "Revertir configuración de resolución de nombres"
#: src/resolve/org.freedesktop.resolve1.policy:133
#, fuzzy
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the systemd package.
+# Jan Kuparinen <copper_fin@hotmail.com>, 2021.
+msgid ""
+msgstr ""
+"Project-Id-Version: systemd\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2021-01-08 17:48+0100\n"
+"PO-Revision-Date: 2021-08-26 18:05+0000\n"
+"Last-Translator: Jan Kuparinen <copper_fin@hotmail.com>\n"
+"Language-Team: Finnish <https://translate.fedoraproject.org/projects/systemd/"
+"master/fi/>\n"
+"Language: fi\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.8\n"
+
+#: src/core/org.freedesktop.systemd1.policy.in:22
+msgid "Send passphrase back to system"
+msgstr "Lähetä tunnuslause takaisin järjestelmään"
+
+#: src/core/org.freedesktop.systemd1.policy.in:23
+msgid ""
+"Authentication is required to send the entered passphrase back to the system."
+msgstr ""
+"Todennus vaaditaan jotta syötetty tunnuslause lähetetään takaisin "
+"järjestelmään."
+
+#: src/core/org.freedesktop.systemd1.policy.in:33
+msgid "Manage system services or other units"
+msgstr "Hallinnoi järjestelmäpalveluja tai muita yksiköitä"
+
+#: src/core/org.freedesktop.systemd1.policy.in:34
+msgid "Authentication is required to manage system services or other units."
+msgstr ""
+"Todennus vaaditaan järjestelmäpalvelujen tai muiden yksiköiden hallintaan."
+
+#: src/core/org.freedesktop.systemd1.policy.in:43
+msgid "Manage system service or unit files"
+msgstr "Hallitse järjestelmäpalvelu- tai yksikkötiedostoja"
+
+#: src/core/org.freedesktop.systemd1.policy.in:44
+msgid "Authentication is required to manage system service or unit files."
+msgstr ""
+"Todennus vaaditaan järjestelmän palvelu- tai yksikkötiedostojen hallintaan."
+
+#: src/core/org.freedesktop.systemd1.policy.in:54
+msgid "Set or unset system and service manager environment variables"
+msgstr "Aseta tai poista järjestelmän ja palvelunhallinnan ympäristömuuttujia"
+
+#: src/core/org.freedesktop.systemd1.policy.in:55
+msgid ""
+"Authentication is required to set or unset system and service manager "
+"environment variables."
+msgstr ""
+"Todennus vaaditaan järjestelmän ja palvelunhallinnan ympäristömuuttujien "
+"asettamiseen tai poistamiseen."
+
+#: src/core/org.freedesktop.systemd1.policy.in:64
+msgid "Reload the systemd state"
+msgstr "Lataa järjestelmätila uudelleen"
+
+#: src/core/org.freedesktop.systemd1.policy.in:65
+msgid "Authentication is required to reload the systemd state."
+msgstr "Todennus vaaditaan, jotta järjestelmätila voidaan ladata uudelleen."
+
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Luo kotialue"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "Todennus vaaditaan käyttäjän kotialueen luomiseksi."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Poista kotialue"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "Todennus vaaditaan käyttäjän kotialueen poistamiseksi."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Tarkista kotialueen valtuudet"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Todennus vaaditaan, jotta käyttäjän kotialueen valtuuksia voi tarkistaa."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Päivitä kotialue"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr ""
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr ""
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr ""
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:20
+msgid "Set hostname"
+msgstr "Määritä isäntänimi"
+
+#: src/hostname/org.freedesktop.hostname1.policy:21
+msgid "Authentication is required to set the local hostname."
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:30
+msgid "Set static hostname"
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:31
+msgid ""
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:41
+msgid "Set machine information"
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:42
+msgid "Authentication is required to set local machine information."
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:22
+msgid "Import a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:23
+msgid "Authentication is required to import a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:32
+msgid "Export a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:33
+msgid "Authentication is required to export a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:42
+msgid "Download a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:43
+msgid "Authentication is required to download a VM or container image"
+msgstr ""
+
+#: src/locale/org.freedesktop.locale1.policy:22
+msgid "Set system locale"
+msgstr ""
+
+#: src/locale/org.freedesktop.locale1.policy:23
+msgid "Authentication is required to set the system locale."
+msgstr ""
+
+#: src/locale/org.freedesktop.locale1.policy:33
+msgid "Set system keyboard settings"
+msgstr ""
+
+#: src/locale/org.freedesktop.locale1.policy:34
+msgid "Authentication is required to set the system keyboard settings."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:22
+msgid "Allow applications to inhibit system shutdown"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:23
+msgid ""
+"Authentication is required for an application to inhibit system shutdown."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:33
+msgid "Allow applications to delay system shutdown"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:34
+msgid "Authentication is required for an application to delay system shutdown."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:44
+msgid "Allow applications to inhibit system sleep"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:45
+msgid "Authentication is required for an application to inhibit system sleep."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:55
+msgid "Allow applications to delay system sleep"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:56
+msgid "Authentication is required for an application to delay system sleep."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:65
+msgid "Allow applications to inhibit automatic system suspend"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:66
+msgid ""
+"Authentication is required for an application to inhibit automatic system "
+"suspend."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:75
+msgid "Allow applications to inhibit system handling of the power key"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:76
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the power key."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:86
+msgid "Allow applications to inhibit system handling of the suspend key"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:87
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the suspend key."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:97
+msgid "Allow applications to inhibit system handling of the hibernate key"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:98
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the hibernate key."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:107
+msgid "Allow applications to inhibit system handling of the lid switch"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:108
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the lid switch."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:117
+msgid "Allow applications to inhibit system handling of the reboot key"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:118
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the reboot key."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:128
+msgid "Allow non-logged-in user to run programs"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:129
+msgid "Explicit request is required to run programs as a non-logged-in user."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:138
+msgid "Allow non-logged-in users to run programs"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:139
+msgid "Authentication is required to run programs as a non-logged-in user."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:148
+msgid "Allow attaching devices to seats"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:149
+msgid "Authentication is required to attach a device to a seat."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:159
+msgid "Flush device to seat attachments"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:160
+msgid "Authentication is required to reset how devices are attached to seats."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:169
+msgid "Power off the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:170
+msgid "Authentication is required to power off the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:180
+msgid "Power off the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:181
+msgid ""
+"Authentication is required to power off the system while other users are "
+"logged in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:191
+msgid "Power off the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:192
+msgid ""
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:202
+msgid "Reboot the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:203
+msgid "Authentication is required to reboot the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:213
+msgid "Reboot the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:214
+msgid ""
+"Authentication is required to reboot the system while other users are logged "
+"in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:224
+msgid "Reboot the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:225
+msgid ""
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:235
+msgid "Halt the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:236
+msgid "Authentication is required to halt the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:246
+msgid "Halt the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:247
+msgid ""
+"Authentication is required to halt the system while other users are logged "
+"in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:257
+msgid "Halt the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:258
+msgid ""
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:268
+msgid "Suspend the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:269
+msgid "Authentication is required to suspend the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:278
+msgid "Suspend the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:279
+msgid ""
+"Authentication is required to suspend the system while other users are "
+"logged in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:289
+msgid "Suspend the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:290
+msgid ""
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:300
+msgid "Hibernate the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:301
+msgid "Authentication is required to hibernate the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:310
+msgid "Hibernate the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:311
+msgid ""
+"Authentication is required to hibernate the system while other users are "
+"logged in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:321
+msgid "Hibernate the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:322
+msgid ""
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:332
+msgid "Manage active sessions, users and seats"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:333
+msgid "Authentication is required to manage active sessions, users and seats."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:342
+msgid "Lock or unlock active sessions"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:343
+msgid "Authentication is required to lock or unlock active sessions."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:352
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:353
+msgid "Authentication is required to set the reboot \"reason\" in the kernel."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:363
+msgid "Indicate to the firmware to boot to setup interface"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:364
+msgid ""
+"Authentication is required to indicate to the firmware to boot to setup "
+"interface."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:374
+msgid "Indicate to the boot loader to boot to the boot loader menu"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:375
+msgid ""
+"Authentication is required to indicate to the boot loader to boot to the "
+"boot loader menu."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:385
+msgid "Indicate to the boot loader to boot a specific entry"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:386
+msgid ""
+"Authentication is required to indicate to the boot loader to boot into a "
+"specific boot loader entry."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Set a wall message"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:397
+msgid "Authentication is required to set a wall message"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:406
+msgid "Change Session"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:407
+msgid "Authentication is required to change the virtual terminal."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:22
+msgid "Log into a local container"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:23
+msgid "Authentication is required to log into a local container."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:32
+msgid "Log into the local host"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:33
+msgid "Authentication is required to log into the local host."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:42
+msgid "Acquire a shell in a local container"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:43
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:53
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:54
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:64
+msgid "Acquire a pseudo TTY in a local container"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:65
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:74
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:75
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:84
+msgid "Manage local virtual machines and containers"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:85
+msgid ""
+"Authentication is required to manage local virtual machines and containers."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:95
+msgid "Manage local virtual machine and container images"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:96
+msgid ""
+"Authentication is required to manage local virtual machine and container "
+"images."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "DHCP server sends force renew message"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to send force renew message."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Renew dynamic addresses"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to renew dynamic addresses."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reload network settings"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reload network settings."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:176
+msgid "Reconfigure network interface"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:177
+msgid "Authentication is required to reconfigure network interface."
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:13
+msgid "Inspect a portable service image"
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:14
+msgid "Authentication is required to inspect a portable service image."
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:23
+msgid "Attach or detach a portable service image"
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:24
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:34
+msgid "Delete or modify portable service image"
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:35
+msgid ""
+"Authentication is required to delete or modify a portable service image."
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:22
+msgid "Register a DNS-SD service"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:23
+msgid "Authentication is required to register a DNS-SD service"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:33
+msgid "Unregister a DNS-SD service"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:34
+msgid "Authentication is required to unregister a DNS-SD service"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:22
+msgid "Set system time"
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:23
+msgid "Authentication is required to set the system time."
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:33
+msgid "Set system timezone"
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:34
+msgid "Authentication is required to set the system timezone."
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:43
+msgid "Set RTC to local timezone or UTC"
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:44
+msgid ""
+"Authentication is required to control whether the RTC stores the local or "
+"UTC time."
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:53
+msgid "Turn network time synchronization on or off"
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:54
+msgid ""
+"Authentication is required to control whether network time synchronization "
+"shall be enabled."
+msgstr ""
+
+#: src/core/dbus-unit.c:359
+msgid "Authentication is required to start '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:360
+msgid "Authentication is required to stop '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:361
+msgid "Authentication is required to reload '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:362 src/core/dbus-unit.c:363
+msgid "Authentication is required to restart '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:535
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:566
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:599
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:708
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:757
+msgid ""
+"Authentication is required to freeze or thaw the processes of '$(unit)' unit."
+msgstr ""
"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
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
}
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
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI__LINUX_GENERIC_NETLINK_H
+#define _UAPI__LINUX_GENERIC_NETLINK_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+#define GENL_NAMSIZ 16 /* length of family name */
+
+#define GENL_MIN_ID NLMSG_MIN_TYPE
+#define GENL_MAX_ID 1023
+
+struct genlmsghdr {
+ __u8 cmd;
+ __u8 version;
+ __u16 reserved;
+};
+
+#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr))
+
+#define GENL_ADMIN_PERM 0x01
+#define GENL_CMD_CAP_DO 0x02
+#define GENL_CMD_CAP_DUMP 0x04
+#define GENL_CMD_CAP_HASPOL 0x08
+#define GENL_UNS_ADMIN_PERM 0x10
+
+/*
+ * List of reserved static generic netlink identifiers:
+ */
+#define GENL_ID_CTRL NLMSG_MIN_TYPE
+#define GENL_ID_VFS_DQUOT (NLMSG_MIN_TYPE + 1)
+#define GENL_ID_PMCRAID (NLMSG_MIN_TYPE + 2)
+/* must be last reserved + 1 */
+#define GENL_START_ALLOC (NLMSG_MIN_TYPE + 3)
+
+/**************************************************************************
+ * Controller
+ **************************************************************************/
+
+enum {
+ CTRL_CMD_UNSPEC,
+ CTRL_CMD_NEWFAMILY,
+ CTRL_CMD_DELFAMILY,
+ CTRL_CMD_GETFAMILY,
+ CTRL_CMD_NEWOPS,
+ CTRL_CMD_DELOPS,
+ CTRL_CMD_GETOPS,
+ CTRL_CMD_NEWMCAST_GRP,
+ CTRL_CMD_DELMCAST_GRP,
+ CTRL_CMD_GETMCAST_GRP, /* unused */
+ CTRL_CMD_GETPOLICY,
+ __CTRL_CMD_MAX,
+};
+
+#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1)
+
+enum {
+ CTRL_ATTR_UNSPEC,
+ CTRL_ATTR_FAMILY_ID,
+ CTRL_ATTR_FAMILY_NAME,
+ CTRL_ATTR_VERSION,
+ CTRL_ATTR_HDRSIZE,
+ CTRL_ATTR_MAXATTR,
+ CTRL_ATTR_OPS,
+ CTRL_ATTR_MCAST_GROUPS,
+ CTRL_ATTR_POLICY,
+ CTRL_ATTR_OP_POLICY,
+ CTRL_ATTR_OP,
+ __CTRL_ATTR_MAX,
+};
+
+#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1)
+
+enum {
+ CTRL_ATTR_OP_UNSPEC,
+ CTRL_ATTR_OP_ID,
+ CTRL_ATTR_OP_FLAGS,
+ __CTRL_ATTR_OP_MAX,
+};
+
+#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+
+enum {
+ CTRL_ATTR_MCAST_GRP_UNSPEC,
+ CTRL_ATTR_MCAST_GRP_NAME,
+ CTRL_ATTR_MCAST_GRP_ID,
+ __CTRL_ATTR_MCAST_GRP_MAX,
+};
+
+enum {
+ CTRL_ATTR_POLICY_UNSPEC,
+ CTRL_ATTR_POLICY_DO,
+ CTRL_ATTR_POLICY_DUMP,
+
+ __CTRL_ATTR_POLICY_DUMP_MAX,
+ CTRL_ATTR_POLICY_DUMP_MAX = __CTRL_ATTR_POLICY_DUMP_MAX - 1
+};
+
+#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
+
+
+#endif /* _UAPI__LINUX_GENERIC_NETLINK_H */
#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)
linux/can/vxcan.h
linux/fib_rules.h
linux/fou.h
+ linux/genetlink.h
linux/hdlc/ioctl.h
linux/if.h
linux/if_addr.h
/* 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 r;
r = access_fd(fd, X_OK);
- if (r < 0)
+ if (r == -ENOSYS) {
+ /* /proc is not mounted. Fallback to access(). */
+ if (access(path, X_OK) < 0)
+ return -errno;
+ } else if (r < 0)
return r;
if (ret_fd)
return 0;
}
-int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) {
- int last_error, r;
- const char *p = NULL;
+static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *path_name = NULL;
+ int r;
assert(name);
- if (is_path(name)) {
- _cleanup_close_ int fd = -1;
-
- r = check_x_access(name, ret_fd ? &fd : 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;
- if (ret_filename) {
- r = path_make_absolute_cwd(name, ret_filename);
- if (r < 0)
- return r;
- }
+ name = path_name;
+ }
- if (ret_fd)
- *ret_fd = TAKE_FD(fd);
+ r = check_x_access(name, ret_fd ? &fd : NULL);
+ if (r < 0)
+ return r;
- return 0;
+ if (ret_filename) {
+ r = path_make_absolute_cwd(name, ret_filename);
+ if (r < 0)
+ return r;
}
+ if (ret_fd)
+ *ret_fd = TAKE_FD(fd);
+
+ return 0;
+}
+
+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;
+
+ assert(name);
+
+ if (is_path(name))
+ return find_executable_impl(name, root, ret_filename, ret_fd);
+
if (use_path_envvar)
/* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
* binary. */
/* Resolve a single-component name to a full path */
for (;;) {
_cleanup_free_ char *element = NULL;
- _cleanup_close_ int fd = -1;
r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
if (!path_extend(&element, name))
return -ENOMEM;
- r = check_x_access(element, ret_fd ? &fd : NULL);
+ r = find_executable_impl(element, root, ret_filename, ret_fd);
if (r < 0) {
/* PATH entries which we don't have access to are ignored, as per tradition. */
if (r != -EACCES)
}
/* Found it! */
- if (ret_filename)
- *ret_filename = path_simplify(TAKE_PTR(element));
- if (ret_fd)
- *ret_fd = TAKE_FD(fd);
-
return 0;
}
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; \
})
int socket_bind_to_ifname(int fd, const char *ifname);
int socket_bind_to_ifindex(int fd, int ifindex);
+/* Define a 64bit version of timeval/timespec in any case, even on 32bit userspace. */
+struct timeval_large {
+ uint64_t tvl_sec, tvl_usec;
+};
+struct timespec_large {
+ uint64_t tvl_sec, tvl_nsec;
+};
+
+/* glibc duplicates timespec/timeval on certain 32bit archs, once in 32bit and once in 64bit.
+ * See __convert_scm_timestamps() in glibc souce code. Hence, we need additional buffer space for them
+ * to prevent from recvmsg_safe() returning -EXFULL. */
+#define CMSG_SPACE_TIMEVAL \
+ ((sizeof(struct timeval) == sizeof(struct timeval_large)) ? \
+ CMSG_SPACE(sizeof(struct timeval)) : \
+ CMSG_SPACE(sizeof(struct timeval)) + \
+ CMSG_SPACE(sizeof(struct timeval_large)))
+#define CMSG_SPACE_TIMESPEC \
+ ((sizeof(struct timespec) == sizeof(struct timespec_large)) ? \
+ CMSG_SPACE(sizeof(struct timespec)) : \
+ CMSG_SPACE(sizeof(struct timespec)) + \
+ CMSG_SPACE(sizeof(struct timespec_large)))
+
ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags);
int socket_get_family(int fd, int *ret);
#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_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())
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)
if (r < 0)
return r;
- r = unit_test_start_limit(u);
- if (r < 0) {
- automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
- return r;
- }
-
r = unit_acquire_invocation_id(u);
if (r < 0)
return r;
return supported;
}
+static int automount_test_start_limit(Unit *u) {
+ Automount *a = AUTOMOUNT(u);
+ int r;
+
+ assert(a);
+
+ r = unit_test_start_limit(u);
+ if (r < 0) {
+ automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ return 0;
+}
+
static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
[AUTOMOUNT_SUCCESS] = "success",
[AUTOMOUNT_FAILURE_RESOURCES] = "resources",
[JOB_FAILED] = "Failed to unset automount %s.",
},
},
+
+ .test_start_limit = automount_test_start_limit,
};
#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();
}
}
if (!console_only) { /* Skip printing if output goes to the console, and job_print_status_message()
* will actually print something to the console. */
-
+ Condition *c;
const char *mid = job_done_mid(t, result); /* mid may be NULL. log_unit_struct() will ignore it. */
- const char *msg_fmt = strjoina("MESSAGE=", format);
- DISABLE_WARNING_FORMAT_NONLITERAL;
- log_unit_struct(u, job_done_messages[result].log_level,
- msg_fmt, ident,
- "JOB_ID=%" PRIu32, job_id,
- "JOB_TYPE=%s", job_type_to_string(t),
- "JOB_RESULT=%s", job_result_to_string(result),
- LOG_UNIT_INVOCATION_ID(u),
- mid);
- REENABLE_WARNING;
+ c = t == JOB_START && result == JOB_DONE ? unit_find_failed_condition(u) : NULL;
+ if (c) {
+ /* Special case units that were skipped because of a failed condition check so that
+ * we can add more information to the message. */
+ if (c->trigger)
+ log_unit_struct(
+ u,
+ job_done_messages[result].log_level,
+ "MESSAGE=%s was skipped because all trigger condition checks failed.",
+ ident,
+ "JOB_ID=%" PRIu32, job_id,
+ "JOB_TYPE=%s", job_type_to_string(t),
+ "JOB_RESULT=%s", job_result_to_string(result),
+ LOG_UNIT_INVOCATION_ID(u),
+ mid);
+ else
+ log_unit_struct(
+ u,
+ job_done_messages[result].log_level,
+ "MESSAGE=%s was skipped because of a failed condition check (%s=%s%s).",
+ ident,
+ condition_type_to_string(c->type),
+ c->negate ? "!" : "",
+ c->parameter,
+ "JOB_ID=%" PRIu32, job_id,
+ "JOB_TYPE=%s", job_type_to_string(t),
+ "JOB_RESULT=%s", job_result_to_string(result),
+ LOG_UNIT_INVOCATION_ID(u),
+ mid);
+ } else {
+ const char *msg_fmt = strjoina("MESSAGE=", format);
+
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ log_unit_struct(u, job_done_messages[result].log_level,
+ msg_fmt, ident,
+ "JOB_ID=%" PRIu32, job_id,
+ "JOB_TYPE=%s", job_type_to_string(t),
+ "JOB_RESULT=%s", job_result_to_string(result),
+ LOG_UNIT_INVOCATION_ID(u),
+ mid);
+ REENABLE_WARNING;
+ }
}
if (do_console) {
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;
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");
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;
/* A generic implementation, covering both manager_unref_uid() and manager_unref_gid(), under the assumption
* that uid_t and gid_t are actually defined the same way, with the same validity rules.
*
- * We store a hashmap where the UID/GID is they key and the value is a 32bit reference counter, whose highest
+ * We store a hashmap where the key is the UID/GID and the value is a 32bit reference counter, whose highest
* bit is used as flag for marking UIDs/GIDs whose IPC objects to remove when the last reference to the UID/GID
* is dropped. The flag is set to on, once at least one reference from a unit where RemoveIPC= is set is added
* on a UID/GID. It is reset when the UID's/GID's reference counter drops to 0 again. */
#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',
output: file,
command : [meson_render_jinja2, config_h, '@INPUT@'],
capture : true,
- install : dir != 'no',
+ install : (dir == pkgsysconfdir and install_sysconfdir_samples) or (dir != pkgsysconfdir and dir != 'no'),
install_dir : dir)
endforeach
assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED));
- r = unit_test_start_limit(u);
- if (r < 0) {
- mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
- return r;
- }
-
r = unit_acquire_invocation_id(u);
if (r < 0)
return r;
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 exec_context_get_clean_mask(&m->exec_context, ret);
}
+static int mount_test_start_limit(Unit *u) {
+ Mount *m = MOUNT(u);
+ int r;
+
+ assert(m);
+
+ r = unit_test_start_limit(u);
+ if (r < 0) {
+ mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ return 0;
+}
+
static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
[MOUNT_EXEC_MOUNT] = "ExecMount",
[MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
[JOB_TIMEOUT] = "Timed out unmounting %s.",
},
},
+
+ .test_start_limit = mount_test_start_limit,
};
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;
if (r < 0)
return r;
- r = unit_test_start_limit(u);
- if (r < 0) {
- path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
- return r;
- }
-
r = unit_acquire_invocation_id(u);
if (r < 0)
return r;
p->result = PATH_SUCCESS;
}
+static int path_test_start_limit(Unit *u) {
+ Path *p = PATH(u);
+ int r;
+
+ assert(p);
+
+ r = unit_test_start_limit(u);
+ if (r < 0) {
+ path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ return 0;
+}
+
static const char* const path_type_table[_PATH_TYPE_MAX] = {
[PATH_EXISTS] = "PathExists",
[PATH_EXISTS_GLOB] = "PathExistsGlob",
.reset_failed = path_reset_failed,
.bus_set_property = bus_path_set_property,
+
+ .test_start_limit = path_test_start_limit,
};
--- /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;
assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED));
- /* Make sure we don't enter a busy loop of some kind. */
- r = unit_test_start_limit(u);
- if (r < 0) {
- service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false);
- return r;
- }
-
r = unit_acquire_invocation_id(u);
if (r < 0)
return r;
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;
return NULL;
}
+static int service_test_start_limit(Unit *u) {
+ Service *s = SERVICE(u);
+ int r;
+
+ assert(s);
+
+ /* Make sure we don't enter a busy loop of some kind. */
+ r = unit_test_start_limit(u);
+ if (r < 0) {
+ service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false);
+ return r;
+ }
+
+ return 0;
+}
+
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
},
.finished_job = service_finished_job,
},
+
+ .test_start_limit = service_test_start_limit,
};
#include "process-util.h"
#include "selinux-util.h"
#include "serialize.h"
+#include "service.h"
#include "signal-util.h"
#include "smack-util.h"
#include "socket.h"
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();
}
}
assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED));
- r = unit_test_start_limit(u);
- if (r < 0) {
- socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
- return r;
- }
-
r = unit_acquire_invocation_id(u);
if (r < 0)
return r;
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 exec_context_get_clean_mask(&s->exec_context, ret);
}
+static int socket_test_start_limit(Unit *u) {
+ Socket *s = SOCKET(u);
+ int r;
+
+ assert(s);
+
+ r = unit_test_start_limit(u);
+ if (r < 0) {
+ socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ return 0;
+}
+
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
[SOCKET_EXEC_START_PRE] = "ExecStartPre",
[SOCKET_EXEC_START_CHOWN] = "ExecStartChown",
[JOB_TIMEOUT] = "Timed out stopping %s.",
},
},
+
+ .test_start_limit = socket_test_start_limit,
};
typedef struct SocketPeer SocketPeer;
#include "mount.h"
-#include "service.h"
#include "socket-util.h"
#include "unit.h"
if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING)
return -EAGAIN;
- r = unit_test_start_limit(u);
- if (r < 0) {
- swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT);
- return r;
- }
-
r = unit_acquire_invocation_id(u);
if (r < 0)
return r;
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);
return exec_context_get_clean_mask(&s->exec_context, ret);
}
+static int swap_test_start_limit(Unit *u) {
+ Swap *s = SWAP(u);
+ int r;
+
+ assert(s);
+
+ r = unit_test_start_limit(u);
+ if (r < 0) {
+ swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ return 0;
+}
+
static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
[SWAP_EXEC_ACTIVATE] = "ExecActivate",
[SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
[JOB_TIMEOUT] = "Timed out deactivating swap %s.",
},
},
+
+ .test_start_limit = swap_test_start_limit,
};
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);
if (r < 0)
return r;
- r = unit_test_start_limit(u);
- if (r < 0) {
- timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
- return r;
- }
-
r = unit_acquire_invocation_id(u);
if (r < 0)
return r;
break;
default:
- assert_not_reached("Unknown timer state");
+ assert_not_reached();
}
}
return 0;
}
+static int timer_test_start_limit(Unit *u) {
+ Timer *t = TIMER(u);
+ int r;
+
+ assert(t);
+
+ r = unit_test_start_limit(u);
+ if (r < 0) {
+ timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ return 0;
+}
+
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
[TIMER_ACTIVE] = "OnActiveSec",
[TIMER_BOOT] = "OnBootSec",
.timezone_change = timer_timezone_change,
.bus_set_property = bus_timer_set_property,
+
+ .test_start_limit = timer_test_start_limit,
};
#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);
r = manager_get_effective_environment(u->manager, &env);
if (r < 0) {
log_unit_error_errno(u, r, "Failed to determine effective environment: %m");
- u->condition_result = CONDITION_ERROR;
+ u->condition_result = true;
} else
u->condition_result = condition_test_list(
u->conditions,
assert(u);
+ /* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */
+ if (UNIT_VTABLE(u)->test_start_limit) {
+ int r = UNIT_VTABLE(u)->test_start_limit(u);
+ if (r < 0)
+ return r;
+ }
+
/* If this is already started, then this will succeed. Note that this will even succeed if this unit
* is not startable by the user. This is relied on to detect when we need to wait for units and when
* waiting is finished. */
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;
return unit_cgroup_freezer_action(u, FREEZER_THAW);
}
+Condition *unit_find_failed_condition(Unit *u) {
+ Condition *c, *failed_trigger = NULL;
+ bool has_succeeded_trigger = false;
+
+ if (u->condition_result)
+ return NULL;
+
+ LIST_FOREACH(conditions, c, u->conditions)
+ if (c->trigger) {
+ if (c->result == CONDITION_SUCCEEDED)
+ has_succeeded_trigger = true;
+ else if (!failed_trigger)
+ failed_trigger = c;
+ } else if (c->result != CONDITION_SUCCEEDED)
+ return c;
+
+ return failed_trigger && !has_succeeded_trigger ? failed_trigger : NULL;
+}
+
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
[COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
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;
* of this type will immediately fail. */
bool (*supported)(void);
+ /* If this function is set, it's invoked first as part of starting a unit to allow start rate
+ * limiting checks to occur before we do anything else. */
+ int (*test_start_limit)(Unit *u);
+
/* The strings to print in status messages */
UnitStatusMessageFormats status_message_formats;
int unit_freeze_vtable_common(Unit *u);
int unit_thaw_vtable_common(Unit *u);
+Condition *unit_find_failed_condition(Unit *u);
+
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full_errno_zerook(unit, level, error, ...) \
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) {
program_header->p_offset,
program_header->p_filesz,
ELF_T_NHDR);
+ if (!data)
+ continue;
Elf *memelf = elf_memory(data->d_buf, data->d_size);
if (!memelf)
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 function (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 function (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_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, "Failed to 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-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])))
#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;
+}
}
static int add_mounts(void) {
- dev_t devno;
+ _cleanup_free_ char *p = NULL;
int r;
+ dev_t devno;
- r = get_block_device_harder("/", &devno);
- if (r == -EUCLEAN)
- return btrfs_log_dev_root(LOG_ERR, r, "root file system");
- if (r < 0)
- return log_error_errno(r, "Failed to determine block device of root file system: %m");
- if (r == 0) { /* Not backed by block device */
- r = get_block_device_harder("/usr", &devno);
+ /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
+ * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
+ * here. */
+ r = readlink_malloc("/run/systemd/volatile-root", &p);
+ if (r == -ENOENT) { /* volatile-root not found */
+ r = get_block_device_harder("/", &devno);
if (r == -EUCLEAN)
- return btrfs_log_dev_root(LOG_ERR, r, "/usr");
+ return btrfs_log_dev_root(LOG_ERR, r, "root file system");
if (r < 0)
- return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
- if (r == 0) {
- _cleanup_free_ char *p = NULL;
- mode_t m;
-
- /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
- * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
- * here. */
- r = readlink_malloc("/run/systemd/volatile-root", &p);
- if (r == -ENOENT) {
- log_debug("Neither root nor /usr file system are on a (single) block device.");
- return 0;
- }
+ return log_error_errno(r, "Failed to determine block device of root file system: %m");
+ if (r == 0) { /* Not backed by block device */
+ r = get_block_device_harder("/usr", &devno);
+ if (r == -EUCLEAN)
+ return btrfs_log_dev_root(LOG_ERR, r, "/usr");
if (r < 0)
- return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
-
- r = device_path_parse_major_minor(p, &m, &devno);
- if (r < 0)
- return log_error_errno(r, "Failed to parse major/minor device node: %m");
- if (!S_ISBLK(m))
- return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
+ return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
}
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
+ else {
+ mode_t m;
+ r = device_path_parse_major_minor(p, &m, &devno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse major/minor device node: %m");
+ if (!S_ISBLK(m))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
}
return enumerate_partitions(devno);
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 */
#include "strv.h"
#include "tmpfile-util.h"
-/* Round down to the nearest 1K size. Note that Linux generally handles block devices with 512 blocks only,
- * but actually doesn't accept uneven numbers in many cases. To avoid any confusion around this we'll
- * strictly round disk sizes down to the next 1K boundary. */
-#define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(1023))
+/* Round down to the nearest 4K size. Given that newer hardware generally prefers 4K sectors, let's align our
+ * partitions to that too. In the worst case we'll waste 3.5K per partition that way, but I think I can live
+ * with that. */
+#define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(4095))
+
+/* Rounds up to the nearest 4K boundary. Returns UINT64_MAX on overflow */
+#define DISK_SIZE_ROUND_UP(x) \
+ ({ \
+ uint64_t _x = (x); \
+ _x > UINT64_MAX - 4095U ? UINT64_MAX : (_x + 4095U) & ~UINT64_C(4095); \
+ })
+
int run_mark_dirty(int fd, bool b) {
char x = '1';
_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_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
_cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
- uint64_t offset, size;
+ uint64_t offset, size, first_lba, start, last_lba, end;
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 type: %m");
- r = fdisk_partition_start_follow_default(p, 1);
- if (r < 0)
- return log_error_errno(r, "Failed to place partition at beginning of space: %m");
-
r = fdisk_partition_partno_follow_default(p, 1);
if (r < 0)
return log_error_errno(r, "Failed to place partition at first free partition index: %m");
- r = fdisk_partition_end_follow_default(p, 1);
+ first_lba = fdisk_get_first_lba(c); /* Boundary where usable space starts */
+ assert(first_lba <= UINT64_MAX/512);
+ start = DISK_SIZE_ROUND_UP(first_lba * 512); /* Round up to multiple of 4K */
+
+ if (start == UINT64_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Overflow while rounding up start LBA.");
+
+ last_lba = fdisk_get_last_lba(c); /* One sector before boundary where usable space ends */
+ assert(last_lba < UINT64_MAX/512);
+ end = DISK_SIZE_ROUND_DOWN((last_lba + 1) * 512); /* Round down to multiple of 4K */
+
+ if (end <= start)
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Resulting partition size zero or negative.");
+
+ r = fdisk_partition_set_start(p, start / 512);
if (r < 0)
- return log_error_errno(r, "Failed to make partition cover all free space: %m");
+ return log_error_errno(r, "Failed to place partition at offset %" PRIu64 ": %m", start);
+
+ r = fdisk_partition_set_size(p, (end - start) / 512);
+ if (r < 0)
+ return log_error_errno(r, "Failed to end partition at offset %" PRIu64 ": %m", end);
r = fdisk_partition_set_name(p, label);
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");
new_image_size = old_image_size; /* we can't resize physical block devices */
} else {
+ uint64_t new_image_size_rounded;
+
r = stat_verify_regular(&st);
if (r < 0)
return log_error_errno(r, "Image %s is not a block device nor regular file: %m", ip);
* apply onto the loopback file as a whole. When we operate on block devices we instead apply
* to the partition itself only. */
- new_image_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
- if (new_image_size == old_image_size) {
+ new_image_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size);
+
+ if (old_image_size == h->disk_size ||
+ old_image_size == new_image_size_rounded) {
+ /* If exact match, or a match after we rounded down, don't do a thing */
log_info("Image size already matching, skipping operation.");
return 0;
}
+
+ new_image_size = new_image_size_rounded;
}
r = home_prepare_luks(h, already_activated, whole_disk, cache, setup, &header_home);
if (new_image_size <= partition_table_extra)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than partition table metadata.");
- new_partition_size = new_image_size - partition_table_extra;
+ new_partition_size = DISK_SIZE_ROUND_DOWN(new_image_size - partition_table_extra);
} else {
+ uint64_t new_partition_size_rounded;
+
assert(S_ISBLK(st.st_mode));
- new_partition_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
- if (new_partition_size == setup->partition_size) {
+ new_partition_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size);
+
+ if (h->disk_size == setup->partition_size ||
+ new_partition_size_rounded == setup->partition_size) {
log_info("Partition size already matching, skipping operation.");
return 0;
}
+
+ new_partition_size = new_partition_size_rounded;
}
if ((UINT64_MAX - setup->partition_offset) < new_partition_size ||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than crypto payload offset?");
old_fs_size = (setup->partition_size / 512U - crypto_offset) * 512U;
- new_fs_size = (new_partition_size / 512U - crypto_offset) * 512U;
+ new_fs_size = DISK_SIZE_ROUND_DOWN((new_partition_size / 512U - crypto_offset) * 512U);
/* Before we start doing anything, let's figure out if we actually can */
resize_type = can_resize_fs(setup->root_fd, old_fs_size, new_fs_size);
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
" --version Show package version\n"
" -s --strict When updating, return non-zero exit value on any parsing error\n"
" --usr Generate in " UDEVLIBEXECDIR " instead of /etc/udev\n"
- " -r --root=PATH Alternative root path in the filesystem\n\n"
+ " -r --root=PATH Alternative root path in the filesystem\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
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)
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;
* identical to NAME_MAX. For now we use that, but this should be updated one day when the final
* limit is known. */
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
- CMSG_SPACE(sizeof(struct timeval)) +
+ CMSG_SPACE_TIMEVAL +
CMSG_SPACE(sizeof(int)) + /* fd */
CMSG_SPACE(NAME_MAX) /* selinux label */) control;
return stdout_stream_log(s, orig, line_break);
}
- assert_not_reached("Unknown stream state");
+ assert_not_reached();
}
static int stdout_stream_found(
} 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);
triple_timestamp *ret_timestamp) {
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
- CMSG_SPACE(sizeof(struct timeval))) control;
+ CMSG_SPACE_TIMEVAL) control;
struct iovec iov = {};
union sockaddr_union sa = {};
struct msghdr msg = {
/* 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();
}
}
sd-device/sd-device.c
sd-hwdb/hwdb-internal.h
sd-hwdb/sd-hwdb.c
- sd-netlink/generic-netlink.c
- sd-netlink/generic-netlink.h
+ sd-netlink/netlink-genl.c
+ sd-netlink/netlink-genl.h
sd-netlink/netlink-internal.h
+ sd-netlink/netlink-message-nfnl.c
+ sd-netlink/netlink-message-rtnl.c
sd-netlink/netlink-message.c
sd-netlink/netlink-slot.c
sd-netlink/netlink-slot.h
sd-netlink/netlink-socket.c
+ sd-netlink/netlink-types-genl.c
+ sd-netlink/netlink-types-internal.h
+ sd-netlink/netlink-types-nfnl.c
+ sd-netlink/netlink-types-rtnl.c
sd-netlink/netlink-types.c
sd-netlink/netlink-types.h
sd-netlink/netlink-util.c
sd-netlink/netlink-util.h
- sd-netlink/nfnl-message.c
- sd-netlink/rtnl-message.c
sd-netlink/sd-netlink.c
sd-network/network-util.c
sd-network/network-util.h
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;
}
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <linux/genetlink.h>
-
-#include "sd-netlink.h"
-
-#include "alloc-util.h"
-#include "generic-netlink.h"
-#include "netlink-internal.h"
-
-typedef struct {
- const char* name;
- uint8_t version;
-} genl_family;
-
-static const genl_family genl_families[] = {
- [SD_GENL_ID_CTRL] = { .name = "", .version = 1 },
- [SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 },
- [SD_GENL_FOU] = { .name = "fou", .version = 1 },
- [SD_GENL_L2TP] = { .name = "l2tp", .version = 1 },
- [SD_GENL_MACSEC] = { .name = "macsec", .version = 1 },
- [SD_GENL_NL80211] = { .name = "nl80211", .version = 1 },
- [SD_GENL_BATADV] = { .name = "batadv", .version = 1 },
-};
-
-int sd_genl_socket_open(sd_netlink **ret) {
- return netlink_open_family(ret, NETLINK_GENERIC);
-}
-
-static int genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- const NLType *genl_cmd_type, *nl_type;
- const NLTypeSystem *type_system;
- size_t size;
- int r;
-
- assert(nl);
- assert(nl->protocol == NETLINK_GENERIC);
- assert(ret);
-
- r = type_system_get_type(&genl_family_type_system_root, &genl_cmd_type, family);
- if (r < 0)
- return r;
-
- r = message_new_empty(nl, &m);
- if (r < 0)
- return r;
-
- size = NLMSG_SPACE(sizeof(struct genlmsghdr));
- m->hdr = malloc0(size);
- if (!m->hdr)
- return -ENOMEM;
-
- m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
-
- type_get_type_system(genl_cmd_type, &type_system);
-
- r = type_system_get_type(type_system, &nl_type, cmd);
- if (r < 0)
- return r;
-
- m->hdr->nlmsg_len = size;
- m->hdr->nlmsg_type = nlmsg_type;
-
- type_get_type_system(nl_type, &m->containers[0].type_system);
-
- *(struct genlmsghdr *) NLMSG_DATA(m->hdr) = (struct genlmsghdr) {
- .cmd = cmd,
- .version = genl_families[family].version,
- };
-
- *ret = TAKE_PTR(m);
-
- return 0;
-}
-
-static int lookup_nlmsg_type(sd_netlink *nl, sd_genl_family_t family, uint16_t *ret) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
- uint16_t u;
- void *v;
- int r;
-
- assert(nl);
- assert(nl->protocol == NETLINK_GENERIC);
- assert(ret);
-
- if (family == SD_GENL_ID_CTRL) {
- *ret = GENL_ID_CTRL;
- return 0;
- }
-
- v = hashmap_get(nl->genl_family_to_nlmsg_type, INT_TO_PTR(family));
- if (v) {
- *ret = PTR_TO_UINT(v);
- return 0;
- }
-
- r = genl_message_new(nl, SD_GENL_ID_CTRL, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &req);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, genl_families[family].name);
- if (r < 0)
- return r;
-
- r = sd_netlink_call(nl, req, 0, &reply);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, &u);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_put(&nl->genl_family_to_nlmsg_type, NULL, INT_TO_PTR(family), UINT_TO_PTR(u));
- if (r < 0)
- return r;
-
- r = hashmap_ensure_put(&nl->nlmsg_type_to_genl_family, NULL, UINT_TO_PTR(u), INT_TO_PTR(family));
- if (r < 0)
- return r;
-
- *ret = u;
- return 0;
-}
-
-int sd_genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint8_t cmd, sd_netlink_message **ret) {
- uint16_t nlmsg_type = 0; /* Unnecessary initialization to appease gcc */
- int r;
-
- assert_return(nl, -EINVAL);
- assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = lookup_nlmsg_type(nl, family, &nlmsg_type);
- if (r < 0)
- return r;
-
- return genl_message_new(nl, family, nlmsg_type, cmd, ret);
-}
-
-int nlmsg_type_to_genl_family(const sd_netlink *nl, uint16_t nlmsg_type, sd_genl_family_t *ret) {
- void *p;
-
- assert(nl);
- assert(nl->protocol == NETLINK_GENERIC);
- assert(ret);
-
- if (nlmsg_type == NLMSG_ERROR)
- *ret = SD_GENL_ERROR;
- else if (nlmsg_type == NLMSG_DONE)
- *ret = SD_GENL_DONE;
- else if (nlmsg_type == GENL_ID_CTRL)
- *ret = SD_GENL_ID_CTRL;
- else {
- p = hashmap_get(nl->nlmsg_type_to_genl_family, UINT_TO_PTR(nlmsg_type));
- if (!p)
- return -EOPNOTSUPP;
-
- *ret = PTR_TO_INT(p);
- }
-
- return 0;
-}
-
-int sd_genl_message_get_family(sd_netlink *nl, sd_netlink_message *m, sd_genl_family_t *ret) {
- uint16_t nlmsg_type;
- int r;
-
- assert_return(nl, -EINVAL);
- assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
- assert_return(m, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = sd_netlink_message_get_type(m, &nlmsg_type);
- if (r < 0)
- return r;
-
- return nlmsg_type_to_genl_family(nl, nlmsg_type, ret);
-}
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "sd-netlink.h"
-
-int nlmsg_type_to_genl_family(const sd_netlink *nl, uint16_t type, sd_genl_family_t *ret);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <linux/genetlink.h>
+
+#include "sd-netlink.h"
+
+#include "alloc-util.h"
+#include "netlink-genl.h"
+#include "netlink-internal.h"
+#include "netlink-types.h"
+
+typedef struct GenericNetlinkFamily {
+ sd_netlink *genl;
+
+ const NLTypeSystem *type_system;
+
+ uint16_t id; /* a.k.a nlmsg_type */
+ char *name;
+ uint32_t version;
+ uint32_t additional_header_size;
+ Hashmap *multicast_group_by_name;
+} GenericNetlinkFamily;
+
+static const GenericNetlinkFamily nlctrl_static = {
+ .id = GENL_ID_CTRL,
+ .name = (char*) CTRL_GENL_NAME,
+ .version = 0x01,
+};
+
+static GenericNetlinkFamily *genl_family_free(GenericNetlinkFamily *f) {
+ if (!f)
+ return NULL;
+
+ if (f->genl) {
+ if (f->id > 0)
+ hashmap_remove(f->genl->genl_family_by_id, UINT_TO_PTR(f->id));
+ if (f->name)
+ hashmap_remove(f->genl->genl_family_by_name, f->name);
+ }
+
+ free(f->name);
+ hashmap_free(f->multicast_group_by_name);
+
+ return mfree(f);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(GenericNetlinkFamily*, genl_family_free);
+
+void genl_clear_family(sd_netlink *nl) {
+ assert(nl);
+
+ nl->genl_family_by_name = hashmap_free_with_destructor(nl->genl_family_by_name, genl_family_free);
+ nl->genl_family_by_id = hashmap_free_with_destructor(nl->genl_family_by_id, genl_family_free);
+}
+
+static int genl_family_new(
+ sd_netlink *nl,
+ const char *expected_family_name,
+ const NLTypeSystem *type_system,
+ sd_netlink_message *message,
+ const GenericNetlinkFamily **ret) {
+
+ _cleanup_(genl_family_freep) GenericNetlinkFamily *f = NULL;
+ const char *family_name;
+ uint8_t cmd;
+ int r;
+
+ assert(nl);
+ assert(expected_family_name);
+ assert(type_system);
+ assert(message);
+ assert(ret);
+
+ f = new(GenericNetlinkFamily, 1);
+ if (!f)
+ return -ENOMEM;
+
+ *f = (GenericNetlinkFamily) {
+ .type_system = type_system,
+ };
+
+ if (sd_netlink_message_is_error(message)) {
+ int e;
+
+ /* Kernel does not support the genl family? To prevent from resolving the family name
+ * again, let's store the family with zero id to indicate that. */
+
+ e = sd_netlink_message_get_errno(message);
+ if (e >= 0) /* Huh? */
+ e = -EOPNOTSUPP;
+
+ f->name = strdup(expected_family_name);
+ if (!f->name)
+ return e;
+
+ if (hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f) < 0)
+ return e;
+
+ f->genl = nl;
+ TAKE_PTR(f);
+ return e;
+ }
+
+ r = sd_genl_message_get_family_name(nl, message, &family_name);
+ if (r < 0)
+ return r;
+
+ if (!streq(family_name, CTRL_GENL_NAME))
+ return -EINVAL;
+
+ r = sd_genl_message_get_command(nl, message, &cmd);
+ if (r < 0)
+ return r;
+
+ if (cmd != CTRL_CMD_NEWFAMILY)
+ return -EINVAL;
+
+ r = sd_netlink_message_read_u16(message, CTRL_ATTR_FAMILY_ID, &f->id);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_read_string_strdup(message, CTRL_ATTR_FAMILY_NAME, &f->name);
+ if (r < 0)
+ return r;
+
+ if (!streq(f->name, expected_family_name))
+ return -EINVAL;
+
+ r = sd_netlink_message_read_u32(message, CTRL_ATTR_VERSION, &f->version);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_read_u32(message, CTRL_ATTR_HDRSIZE, &f->additional_header_size);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_enter_container(message, CTRL_ATTR_MCAST_GROUPS);
+ if (r >= 0) {
+ for (uint16_t i = 0; i < UINT16_MAX; i++) {
+ _cleanup_free_ char *group_name = NULL;
+ uint32_t group_id;
+
+ r = sd_netlink_message_enter_array(message, i + 1);
+ if (r == -ENODATA)
+ break;
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_read_u32(message, CTRL_ATTR_MCAST_GRP_ID, &group_id);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_read_string_strdup(message, CTRL_ATTR_MCAST_GRP_NAME, &group_name);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (group_id == 0) {
+ log_debug("sd-netlink: received multicast group '%s' for generic netlink family '%s' with id == 0, ignoring",
+ group_name, f->name);
+ continue;
+ }
+
+ r = hashmap_ensure_put(&f->multicast_group_by_name, &string_hash_ops_free, group_name, UINT32_TO_PTR(group_id));
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(group_name);
+ }
+
+ r = sd_netlink_message_exit_container(message);
+ if (r < 0)
+ return r;
+ }
+
+ r = hashmap_ensure_put(&nl->genl_family_by_id, NULL, UINT_TO_PTR(f->id), f);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f);
+ if (r < 0) {
+ hashmap_remove(nl->genl_family_by_id, UINT_TO_PTR(f->id));
+ return r;
+ }
+
+ f->genl = nl;
+ *ret = TAKE_PTR(f);
+ return 0;
+}
+
+static const NLTypeSystem *genl_family_get_type_system(const GenericNetlinkFamily *family) {
+ assert(family);
+
+ if (family->type_system)
+ return family->type_system;
+
+ return genl_get_type_system_by_name(family->name);
+}
+
+static int genl_message_new(
+ sd_netlink *nl,
+ const GenericNetlinkFamily *family,
+ uint8_t cmd,
+ sd_netlink_message **ret) {
+
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ const NLTypeSystem *type_system;
+ int r;
+
+ assert(nl);
+ assert(nl->protocol == NETLINK_GENERIC);
+ assert(family);
+ assert(ret);
+
+ type_system = genl_family_get_type_system(family);
+ if (!type_system)
+ return -EOPNOTSUPP;
+
+ r = message_new_full(nl, family->id, type_system,
+ sizeof(struct genlmsghdr) + family->additional_header_size, &m);
+ if (r < 0)
+ return r;
+
+ *(struct genlmsghdr *) NLMSG_DATA(m->hdr) = (struct genlmsghdr) {
+ .cmd = cmd,
+ .version = family->version,
+ };
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+
+static int genl_family_get_by_name_internal(
+ sd_netlink *nl,
+ const GenericNetlinkFamily *ctrl,
+ const char *name,
+ const GenericNetlinkFamily **ret) {
+
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ const NLTypeSystem *type_system;
+ int r;
+
+ assert(nl);
+ assert(nl->protocol == NETLINK_GENERIC);
+ assert(ctrl);
+ assert(name);
+ assert(ret);
+
+ type_system = genl_get_type_system_by_name(name);
+ if (!type_system)
+ return -EOPNOTSUPP;
+
+ r = genl_message_new(nl, ctrl, CTRL_CMD_GETFAMILY, &req);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, name);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(nl, req, 0, &reply);
+ if (r < 0)
+ return r;
+
+ return genl_family_new(nl, name, type_system, reply, ret);
+}
+
+static int genl_family_get_by_name(sd_netlink *nl, const char *name, const GenericNetlinkFamily **ret) {
+ const GenericNetlinkFamily *f, *ctrl;
+ int r;
+
+ assert(nl);
+ assert(nl->protocol == NETLINK_GENERIC);
+ assert(name);
+ assert(ret);
+
+ f = hashmap_get(nl->genl_family_by_name, name);
+ if (f) {
+ if (f->id == 0) /* kernel does not support the family. */
+ return -EOPNOTSUPP;
+
+ *ret = f;
+ return 0;
+ }
+
+ if (streq(name, CTRL_GENL_NAME))
+ return genl_family_get_by_name_internal(nl, &nlctrl_static, CTRL_GENL_NAME, ret);
+
+ ctrl = hashmap_get(nl->genl_family_by_name, CTRL_GENL_NAME);
+ if (!ctrl) {
+ r = genl_family_get_by_name_internal(nl, &nlctrl_static, CTRL_GENL_NAME, &ctrl);
+ if (r < 0)
+ return r;
+ }
+
+ return genl_family_get_by_name_internal(nl, ctrl, name, ret);
+}
+
+static int genl_family_get_by_id(sd_netlink *nl, uint16_t id, const GenericNetlinkFamily **ret) {
+ const GenericNetlinkFamily *f;
+
+ assert(nl);
+ assert(nl->protocol == NETLINK_GENERIC);
+ assert(ret);
+
+ f = hashmap_get(nl->genl_family_by_id, UINT_TO_PTR(id));
+ if (f) {
+ *ret = f;
+ return 0;
+ }
+
+ if (id == GENL_ID_CTRL) {
+ *ret = &nlctrl_static;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int genl_get_type_system_and_header_size(
+ sd_netlink *nl,
+ uint16_t id,
+ const NLTypeSystem **ret_type_system,
+ size_t *ret_header_size) {
+
+ const GenericNetlinkFamily *f;
+ int r;
+
+ assert(nl);
+ assert(nl->protocol == NETLINK_GENERIC);
+
+ r = genl_family_get_by_id(nl, id, &f);
+ if (r < 0)
+ return r;
+
+ if (ret_type_system) {
+ const NLTypeSystem *t;
+
+ t = genl_family_get_type_system(f);
+ if (!t)
+ return -EOPNOTSUPP;
+
+ *ret_type_system = t;
+ }
+ if (ret_header_size)
+ *ret_header_size = sizeof(struct genlmsghdr) + f->additional_header_size;
+ return 0;
+}
+
+int sd_genl_message_new(sd_netlink *nl, const char *family_name, uint8_t cmd, sd_netlink_message **ret) {
+ const GenericNetlinkFamily *family;
+ int r;
+
+ assert_return(nl, -EINVAL);
+ assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+ assert_return(family_name, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = genl_family_get_by_name(nl, family_name, &family);
+ if (r < 0)
+ return r;
+
+ return genl_message_new(nl, family, cmd, ret);
+}
+
+int sd_genl_message_get_family_name(sd_netlink *nl, sd_netlink_message *m, const char **ret) {
+ const GenericNetlinkFamily *family;
+ uint16_t nlmsg_type;
+ int r;
+
+ assert_return(nl, -EINVAL);
+ assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+ assert_return(m, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = sd_netlink_message_get_type(m, &nlmsg_type);
+ if (r < 0)
+ return r;
+
+ r = genl_family_get_by_id(nl, nlmsg_type, &family);
+ if (r < 0)
+ return r;
+
+ *ret = family->name;
+ return 0;
+}
+
+int sd_genl_message_get_command(sd_netlink *nl, sd_netlink_message *m, uint8_t *ret) {
+ struct genlmsghdr *h;
+ uint16_t nlmsg_type;
+ size_t size;
+ int r;
+
+ assert_return(nl, -EINVAL);
+ assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+ assert_return(m, -EINVAL);
+ assert_return(m->protocol == NETLINK_GENERIC, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = sd_netlink_message_get_type(m, &nlmsg_type);
+ if (r < 0)
+ return r;
+
+ r = genl_get_type_system_and_header_size(nl, nlmsg_type, NULL, &size);
+ if (r < 0)
+ return r;
+
+ if (m->hdr->nlmsg_len < NLMSG_LENGTH(size))
+ return -EBADMSG;
+
+ h = NLMSG_DATA(m->hdr);
+
+ *ret = h->cmd;
+ return 0;
+}
+
+static int genl_family_get_multicast_group_id_by_name(const GenericNetlinkFamily *f, const char *name, uint32_t *ret) {
+ void *p;
+
+ assert(f);
+ assert(name);
+
+ p = hashmap_get(f->multicast_group_by_name, name);
+ if (!p)
+ return -ENOENT;
+
+ if (ret)
+ *ret = PTR_TO_UINT32(p);
+ return 0;
+}
+
+int sd_genl_add_match(
+ sd_netlink *nl,
+ sd_netlink_slot **ret_slot,
+ const char *family_name,
+ const char *multicast_group_name,
+ uint8_t command,
+ sd_netlink_message_handler_t callback,
+ sd_netlink_destroy_t destroy_callback,
+ void *userdata,
+ const char *description) {
+
+ const GenericNetlinkFamily *f;
+ uint32_t multicast_group_id;
+ int r;
+
+ assert_return(nl, -EINVAL);
+ assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(family_name, -EINVAL);
+ assert_return(multicast_group_name, -EINVAL);
+
+ /* If command == 0, then all commands belonging to the multicast group trigger the callback. */
+
+ r = genl_family_get_by_name(nl, family_name, &f);
+ if (r < 0)
+ return r;
+
+ r = genl_family_get_multicast_group_id_by_name(f, multicast_group_name, &multicast_group_id);
+ if (r < 0)
+ return r;
+
+ return netlink_add_match_internal(nl, ret_slot, &multicast_group_id, 1, f->id, command,
+ callback, destroy_callback, userdata, description);
+}
+
+int sd_genl_socket_open(sd_netlink **ret) {
+ return netlink_open_family(ret, NETLINK_GENERIC);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-netlink.h"
+
+#define CTRL_GENL_NAME "nlctrl"
+
+void genl_clear_family(sd_netlink *nl);
#include "prioq.h"
#include "time-util.h"
-#define RTNL_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
+#define NETLINK_DEFAULT_TIMEOUT_USEC ((usec_t) (25 * USEC_PER_SEC))
-#define RTNL_RQUEUE_MAX 64*1024
+#define NETLINK_RQUEUE_MAX 64*1024
-#define RTNL_CONTAINER_DEPTH 32
+#define NETLINK_CONTAINER_DEPTH 32
struct reply_callback {
sd_netlink_message_handler_t callback;
struct match_callback {
sd_netlink_message_handler_t callback;
+ uint32_t *groups;
+ size_t n_groups;
uint16_t type;
+ uint8_t cmd; /* used by genl */
LIST_FIELDS(struct match_callback, match_callbacks);
};
sd_event_source *exit_event_source;
sd_event *event;
- Hashmap *genl_family_to_nlmsg_type;
- Hashmap *nlmsg_type_to_genl_family;
+ Hashmap *genl_family_by_name;
+ Hashmap *genl_family_by_id;
};
struct netlink_attribute {
int protocol;
struct nlmsghdr *hdr;
- struct netlink_container containers[RTNL_CONTAINER_DEPTH];
+ struct netlink_container containers[NETLINK_CONTAINER_DEPTH];
unsigned n_containers; /* number of containers */
bool sealed:1;
bool broadcast:1;
sd_netlink_message *next; /* next in a chain of multi-part messages */
};
-int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type);
-int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret);
+int message_new_empty(sd_netlink *nl, sd_netlink_message **ret);
+int message_new_full(
+ sd_netlink *nl,
+ uint16_t nlmsg_type,
+ const NLTypeSystem *type_system,
+ size_t header_size,
+ sd_netlink_message **ret);
+int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type);
+int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret);
+uint32_t message_get_serial(sd_netlink_message *m);
+void message_seal(sd_netlink_message *m);
int netlink_open_family(sd_netlink **ret, int family);
+bool netlink_pid_changed(sd_netlink *nl);
+int netlink_rqueue_make_room(sd_netlink *nl);
+int netlink_rqueue_partial_make_room(sd_netlink *nl);
int socket_open(int family);
int socket_bind(sd_netlink *nl);
int socket_writev_message(sd_netlink *nl, sd_netlink_message **m, size_t msgcount);
int socket_read_message(sd_netlink *nl);
-int rtnl_rqueue_make_room(sd_netlink *rtnl);
-int rtnl_rqueue_partial_make_room(sd_netlink *rtnl);
-
-/* Make sure callbacks don't destroy the rtnl connection */
-#define NETLINK_DONT_DESTROY(rtnl) \
- _cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##rtnl = sd_netlink_ref(rtnl)
+int netlink_add_match_internal(
+ sd_netlink *nl,
+ sd_netlink_slot **ret_slot,
+ const uint32_t *groups,
+ size_t n_groups,
+ uint16_t type,
+ uint8_t cmd,
+ sd_netlink_message_handler_t callback,
+ sd_netlink_destroy_t destroy_callback,
+ void *userdata,
+ const char *description);
+
+/* Make sure callbacks don't destroy the netlink connection */
+#define NETLINK_DONT_DESTROY(nl) \
+ _cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##nl = sd_netlink_ref(nl)
#include "format-util.h"
#include "netlink-internal.h"
#include "netlink-types.h"
-#include "netlink-util.h"
#include "socket-util.h"
-#include "util.h"
-static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t type, uint16_t flags) {
+static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t msg_type, uint16_t flags) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- const NLType *nl_type;
- size_t size;
int r;
assert_return(nfnl, -EINVAL);
+ assert_return(ret, -EINVAL);
- r = type_system_root_get_type(nfnl, &nl_type, NFNL_SUBSYS_NFTABLES);
+ r = message_new(nfnl, &m, NFNL_SUBSYS_NFTABLES << 8 | msg_type);
if (r < 0)
return r;
- if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
- return -EINVAL;
-
- r = message_new_empty(nfnl, &m);
- if (r < 0)
- return r;
-
- size = NLMSG_SPACE(type_get_size(nl_type));
-
- assert(size >= sizeof(struct nlmsghdr));
- m->hdr = malloc0(size);
- if (!m->hdr)
- return -ENOMEM;
-
- m->hdr->nlmsg_flags = NLM_F_REQUEST | flags;
-
- type_get_type_system(nl_type, &m->containers[0].type_system);
-
- r = type_system_get_type_system(m->containers[0].type_system,
- &m->containers[0].type_system,
- type);
- if (r < 0)
- return r;
-
- m->hdr->nlmsg_len = size;
- m->hdr->nlmsg_type = NFNL_SUBSYS_NFTABLES << 8 | type;
+ m->hdr->nlmsg_flags |= flags;
*(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
.nfgen_family = family,
return 0;
}
-static int sd_nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, int v) {
+static int nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, uint16_t msg_type) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = message_new(nfnl, &m, v);
+ r = message_new(nfnl, &m, NFNL_SUBSYS_NONE << 8 | msg_type);
if (r < 0)
return r;
};
*ret = TAKE_PTR(m);
- return r;
+ return 0;
}
int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret) {
- return sd_nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_BEGIN);
+ return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_BEGIN);
}
int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret) {
- return sd_nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_END);
+ return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_END);
}
-int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret,
- int family,
- const char *table, const char *chain,
- const char *type,
- uint8_t hook, int prio) {
+int sd_nfnl_nft_message_new_basechain(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *table,
+ const char *chain,
+ const char *type,
+ uint8_t hook,
+ int prio) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWCHAIN, NLM_F_CREATE | NLM_F_ACK);
+ r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWCHAIN, NLM_F_CREATE);
if (r < 0)
return r;
return 0;
}
-int sd_nfnl_nft_message_del_table(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table) {
+int sd_nfnl_nft_message_del_table(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *table) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_DELTABLE, NLM_F_CREATE | NLM_F_ACK);
+ r = nft_message_new(nfnl, &m, family, NFT_MSG_DELTABLE, NLM_F_CREATE);
if (r < 0)
return r;
return r;
}
-int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, uint16_t flags) {
+int sd_nfnl_nft_message_new_table(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *table) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWTABLE, NLM_F_CREATE | flags);
+ r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL);
if (r < 0)
return r;
return r;
}
-int sd_nfnl_nft_message_new_rule(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, const char *chain) {
+int sd_nfnl_nft_message_new_rule(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *table,
+ const char *chain) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWRULE, NLM_F_CREATE | NLM_F_ACK);
+ r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWRULE, NLM_F_CREATE);
if (r < 0)
return r;
return r;
}
-int sd_nfnl_nft_message_new_set(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, const char *set_name,
- uint32_t set_id, uint32_t klen) {
+int sd_nfnl_nft_message_new_set(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *table,
+ const char *set_name,
+ uint32_t set_id,
+ uint32_t klen) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSET, NLM_F_CREATE | NLM_F_ACK);
+ r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSET, NLM_F_CREATE);
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_SET_KEY_LEN, htobe32(klen));
if (r < 0)
return r;
+
*ret = TAKE_PTR(m);
return r;
}
-int sd_nfnl_nft_message_new_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, const char *set_name) {
+int sd_nfnl_nft_message_new_setelems_begin(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *table,
+ const char *set_name) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSETELEM, NLM_F_CREATE | NLM_F_ACK);
+ r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSETELEM, NLM_F_CREATE);
if (r < 0)
return r;
return r;
}
-int sd_nfnl_nft_message_del_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, const char *set_name) {
+int sd_nfnl_nft_message_del_setelems_begin(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *table,
+ const char *set_name) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_DELSETELEM, NLM_F_ACK);
+ r = nft_message_new(nfnl, &m, family, NFT_MSG_DELSETELEM, 0);
if (r < 0)
return r;
}
static int sd_nfnl_add_data(sd_netlink_message *m, uint16_t attr, const void *data, uint32_t dlen) {
- int r = sd_netlink_message_open_container(m, attr);
+ int r;
+
+ r = sd_netlink_message_open_container(m, attr);
if (r < 0)
return r;
return sd_netlink_message_close_container(m); /* attr */
}
-int sd_nfnl_nft_message_add_setelem(sd_netlink_message *m, uint32_t num,
- const void *key, uint32_t klen,
- const void *data, uint32_t dlen) {
+int sd_nfnl_nft_message_add_setelem(
+ sd_netlink_message *m,
+ uint32_t num,
+ const void *key,
+ uint32_t klen,
+ const void *data,
+ uint32_t dlen) {
+
int r;
r = sd_netlink_message_open_array(m, num);
goto cancel;
}
- return r;
+ return 0;
+
cancel:
sd_netlink_message_cancel_array(m);
return r;
}
int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret,
- uint16_t nhmsg_type, int nh_family,
+ uint16_t nlmsg_type, int nh_family,
unsigned char nh_protocol) {
struct nhmsg *nhm;
int r;
- assert_return(rtnl_message_type_is_nexthop(nhmsg_type), -EINVAL);
- switch(nhmsg_type) {
+ assert_return(rtnl_message_type_is_nexthop(nlmsg_type), -EINVAL);
+ switch(nlmsg_type) {
case RTM_DELNEXTHOP:
assert_return(nh_family == AF_UNSPEC, -EINVAL);
_fallthrough_;
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);
- r = message_new(rtnl, ret, nhmsg_type);
+ r = message_new(rtnl, ret, nlmsg_type);
if (r < 0)
return r;
- if (nhmsg_type == RTM_NEWNEXTHOP)
+ if (nlmsg_type == RTM_NEWNEXTHOP)
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
nhm = NLMSG_DATA((*ret)->hdr);
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);
#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
-int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
+int message_new_empty(sd_netlink *nl, sd_netlink_message **ret) {
sd_netlink_message *m;
- assert_return(ret, -EINVAL);
+ assert(nl);
+ assert(ret);
- /* Note that 'rtnl' is currently unused, if we start using it internally
- we must take care to avoid problems due to mutual references between
- buses and their queued messages. See sd-bus.
- */
+ /* Note that 'nl' is currently unused, if we start using it internally we must take care to
+ * avoid problems due to mutual references between buses and their queued messages. See sd-bus. */
m = new(sd_netlink_message, 1);
if (!m)
*m = (sd_netlink_message) {
.n_ref = 1,
- .protocol = rtnl->protocol,
+ .protocol = nl->protocol,
.sealed = false,
};
*ret = m;
-
return 0;
}
-int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) {
+int message_new_full(
+ sd_netlink *nl,
+ uint16_t nlmsg_type,
+ const NLTypeSystem *type_system,
+ size_t header_size,
+ sd_netlink_message **ret) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- const NLType *nl_type;
size_t size;
int r;
- assert_return(rtnl, -EINVAL);
+ assert(nl);
+ assert(type_system);
+ assert(ret);
- r = type_system_root_get_type(rtnl, &nl_type, type);
- if (r < 0)
- return r;
-
- if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
- return -EINVAL;
+ size = NLMSG_SPACE(header_size);
+ assert(size >= sizeof(struct nlmsghdr));
- r = message_new_empty(rtnl, &m);
+ r = message_new_empty(nl, &m);
if (r < 0)
return r;
- size = NLMSG_SPACE(type_get_size(nl_type));
+ m->containers[0].type_system = type_system;
- assert(size >= sizeof(struct nlmsghdr));
m->hdr = malloc0(size);
if (!m->hdr)
return -ENOMEM;
m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
-
- type_get_type_system(nl_type, &m->containers[0].type_system);
m->hdr->nlmsg_len = size;
- m->hdr->nlmsg_type = type;
+ m->hdr->nlmsg_type = nlmsg_type;
*ret = TAKE_PTR(m);
+ return 0;
+}
+
+int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type) {
+ const NLTypeSystem *type_system;
+ size_t size;
+ int r;
+
+ assert_return(nl, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = type_system_root_get_type_system_and_header_size(nl, type, &type_system, &size);
+ if (r < 0)
+ return r;
+
+ return message_new_full(nl, type, type_system, size, ret);
+}
+
+int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret) {
+ struct nlmsgerr *err;
+ int r;
+
+ assert(error <= 0);
+
+ r = message_new(nl, ret, NLMSG_ERROR);
+ if (r < 0)
+ return r;
+
+ message_seal(*ret);
+ (*ret)->hdr->nlmsg_seq = serial;
+
+ err = NLMSG_DATA((*ret)->hdr);
+ err->error = error;
return 0;
}
static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) {
const NLType *type;
- int r;
assert(m);
- r = type_system_get_type(m->containers[m->n_containers].type_system, &type, attribute_type);
- if (r < 0)
- return r;
+ type = type_system_get_type(m->containers[m->n_containers].type_system, attribute_type);
+ if (!type)
+ return -EOPNOTSUPP;
if (type_get_type(type) != data_type)
return -EINVAL;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
/* m->containers[m->n_containers + 1] is accessed both in read and write. Prevent access out of bound */
- assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -ERANGE);
+ assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED);
if (r < 0) {
if (r < 0)
return r;
- r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type);
- if (r < 0)
- return r;
+ type_system_union = type_system_get_type_system_union(
+ m->containers[m->n_containers].type_system,
+ type);
+ if (!type_system_union)
+ return -EOPNOTSUPP;
- r = type_system_union_protocol_get_type_system(type_system_union,
- &m->containers[m->n_containers + 1].type_system,
- family);
- if (r < 0)
- return r;
- } else {
- r = type_system_get_type_system(m->containers[m->n_containers].type_system,
- &m->containers[m->n_containers + 1].type_system,
- type);
- if (r < 0)
- return r;
- }
+ m->containers[m->n_containers + 1].type_system =
+ type_system_union_get_type_system_by_protocol(
+ type_system_union,
+ family);
+ } else
+ m->containers[m->n_containers + 1].type_system =
+ type_system_get_type_system(
+ m->containers[m->n_containers].type_system,
+ type);
+ if (!m->containers[m->n_containers + 1].type_system)
+ return -EOPNOTSUPP;
r = add_rtattr(m, type | NLA_F_NESTED, NULL, size);
if (r < 0)
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -ERANGE);
+ assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
- r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type);
- if (r < 0)
- return r;
+ type_system_union = type_system_get_type_system_union(
+ m->containers[m->n_containers].type_system,
+ type);
+ if (!type_system_union)
+ return -EOPNOTSUPP;
- r = type_system_union_get_type_system(type_system_union,
- &m->containers[m->n_containers + 1].type_system,
- key);
- if (r < 0)
- return r;
+ m->containers[m->n_containers + 1].type_system =
+ type_system_union_get_type_system_by_string(
+ type_system_union,
+ key);
+ if (!m->containers[m->n_containers + 1].type_system)
+ return -EOPNOTSUPP;
- r = sd_netlink_message_append_string(m, type_system_union->match, key);
+ r = sd_netlink_message_append_string(m, type_system_union_get_match_attribute(type_system_union), key);
if (r < 0)
return r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -ERANGE);
+ assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
if (r < 0)
assert_return(m, -EINVAL);
assert_return(m->sealed, -EPERM);
- assert(m->n_containers < RTNL_CONTAINER_DEPTH);
+ assert(m->n_containers < NETLINK_CONTAINER_DEPTH);
if (!m->containers[m->n_containers].attributes)
return -ENODATA;
int r;
assert_return(m, -EINVAL);
- assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL);
+ assert_return(m->n_containers < NETLINK_CONTAINER_DEPTH, -EINVAL);
- r = type_system_get_type(m->containers[m->n_containers].type_system,
- &nl_type,
- container_type);
- if (r < 0)
- return r;
+ nl_type = type_system_get_type(
+ m->containers[m->n_containers].type_system,
+ container_type);
+ if (!nl_type)
+ return -EOPNOTSUPP;
if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
return -EINVAL;
- r = type_system_get_type_system(m->containers[m->n_containers].type_system,
- &type_system,
- container_type);
- if (r < 0)
- return r;
+ type_system = type_system_get_type_system(
+ m->containers[m->n_containers].type_system,
+ container_type);
+ if (!type_system)
+ return -EOPNOTSUPP;
- r = type_system_get_type(type_system, &nl_type, type_id);
- if (r < 0)
- return r;
+ nl_type = type_system_get_type(type_system, type_id);
+ if (!nl_type)
+ return -EOPNOTSUPP;
if (type_get_type(nl_type) != NETLINK_TYPE_STRING)
return -EINVAL;
return -ENOMEM;
if (attributes[type].offset != 0)
- log_debug("rtnl: message parse - overwriting repeated attribute");
+ log_debug("sd-netlink: message parse - overwriting repeated attribute");
attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED;
int r;
assert_return(m, -EINVAL);
- assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -EINVAL);
+ assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
- r = type_system_get_type(m->containers[m->n_containers].type_system,
- &nl_type,
- type_id);
- if (r < 0)
- return r;
+ nl_type = type_system_get_type(
+ m->containers[m->n_containers].type_system,
+ type_id);
+ if (!nl_type)
+ return -EOPNOTSUPP;
type = type_get_type(nl_type);
if (type == NETLINK_TYPE_NESTED) {
- r = type_system_get_type_system(m->containers[m->n_containers].type_system,
- &type_system,
- type_id);
- if (r < 0)
- return r;
+ type_system = type_system_get_type_system(
+ m->containers[m->n_containers].type_system,
+ type_id);
+ if (!type_system)
+ return -EOPNOTSUPP;
} else if (type == NETLINK_TYPE_UNION) {
const NLTypeSystemUnion *type_system_union;
- r = type_system_get_type_system_union(m->containers[m->n_containers].type_system,
- &type_system_union,
- type_id);
- if (r < 0)
- return r;
+ type_system_union = type_system_get_type_system_union(
+ m->containers[m->n_containers].type_system,
+ type_id);
+ if (!type_system_union)
+ return -EOPNOTSUPP;
- switch (type_system_union->match_type) {
- case NL_MATCH_SIBLING:
- {
+ switch (type_system_union_get_match_type(type_system_union)) {
+ case NL_MATCH_SIBLING: {
const char *key;
- r = sd_netlink_message_read_string(m, type_system_union->match, &key);
+ r = sd_netlink_message_read_string(
+ m,
+ type_system_union_get_match_attribute(type_system_union),
+ &key);
if (r < 0)
return r;
- r = type_system_union_get_type_system(type_system_union,
- &type_system,
- key);
- if (r < 0)
- return r;
+ type_system = type_system_union_get_type_system_by_string(
+ type_system_union,
+ key);
+ if (!type_system)
+ return -EOPNOTSUPP;
break;
}
- case NL_MATCH_PROTOCOL:
- {
+ case NL_MATCH_PROTOCOL: {
int family;
r = sd_rtnl_message_get_family(m, &family);
if (r < 0)
return r;
- r = type_system_union_protocol_get_type_system(type_system_union,
- &type_system,
- family);
- if (r < 0)
- return r;
+ type_system = type_system_union_get_type_system_by_protocol(
+ type_system_union,
+ family);
+ if (!type_system)
+ return -EOPNOTSUPP;
break;
}
default:
- assert_not_reached("sd-netlink: invalid type system union type");
+ assert_not_reached();
}
} else
return -EINVAL;
int r;
assert_return(m, -EINVAL);
- assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -EINVAL);
+ assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
r = netlink_message_read_internal(m, type_id, &container, NULL);
if (r < 0)
return 0;
}
-uint32_t rtnl_message_get_serial(sd_netlink_message *m) {
+uint32_t message_get_serial(sd_netlink_message *m) {
assert(m);
assert(m->hdr);
NLMSG_PAYLOAD(m->hdr, hlen));
}
-int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl) {
- const NLType *nl_type;
- uint16_t type;
+int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) {
size_t size;
int r;
assert_return(m, -EINVAL);
- assert_return(genl || m->protocol != NETLINK_GENERIC, -EINVAL);
+ assert_return(nl, -EINVAL);
/* don't allow appending to message once parsed */
- if (!m->sealed)
- rtnl_message_seal(m);
+ message_seal(m);
for (unsigned i = 1; i <= m->n_containers; i++)
m->containers[i].attributes = mfree(m->containers[i].attributes);
assert(m->hdr);
- r = type_system_root_get_type(genl, &nl_type, m->hdr->nlmsg_type);
+ r = type_system_root_get_type_system_and_header_size(nl, m->hdr->nlmsg_type,
+ &m->containers[0].type_system, &size);
if (r < 0)
return r;
- type = type_get_type(nl_type);
- size = type_get_size(nl_type);
-
- if (type == NETLINK_TYPE_NESTED) {
- const NLTypeSystem *type_system;
-
- type_get_type_system(nl_type, &type_system);
-
- m->containers[0].type_system = type_system;
-
- if (sd_netlink_message_is_error(m))
- r = netlink_message_parse_error(m);
- else
- r = netlink_container_parse(m,
- &m->containers[m->n_containers],
- (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
- NLMSG_PAYLOAD(m->hdr, size));
- if (r < 0)
- return r;
- }
+ if (sd_netlink_message_is_error(m))
+ return netlink_message_parse_error(m);
- return 0;
+ return netlink_container_parse(m,
+ &m->containers[0],
+ (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
+ NLMSG_PAYLOAD(m->hdr, size));
}
-void rtnl_message_seal(sd_netlink_message *m) {
+void message_seal(sd_netlink_message *m) {
assert(m);
- assert(!m->sealed);
m->sealed = true;
}
case NETLINK_MATCH_CALLBACK:
LIST_REMOVE(match_callbacks, nl->match_callbacks, &slot->match_callback);
- switch (slot->match_callback.type) {
- case RTM_NEWLINK:
- case RTM_DELLINK:
- (void) socket_broadcast_group_unref(nl, RTNLGRP_LINK);
-
- break;
- case RTM_NEWADDR:
- case RTM_DELADDR:
- (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_IFADDR);
- (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_IFADDR);
-
- break;
- case RTM_NEWROUTE:
- case RTM_DELROUTE:
- (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_ROUTE);
- (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_ROUTE);
-
- break;
- }
+ for (size_t i = 0; i < slot->match_callback.n_groups; i++)
+ (void) socket_broadcast_group_unref(nl, slot->match_callback.groups[i]);
+
+ slot->match_callback.n_groups = 0;
+ slot->match_callback.groups = mfree(slot->match_callback.groups);
break;
default:
- assert_not_reached("Wut? Unknown slot type?");
+ assert_not_reached();
}
slot->type = _NETLINK_SLOT_INVALID;
n = recvmsg_safe(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
if (n == -ENOBUFS)
- return log_debug_errno(n, "rtnl: kernel receive buffer overrun");
+ return log_debug_errno(n, "sd-netlink: kernel receive buffer overrun");
if (IN_SET(n, -EAGAIN, -EINTR))
return 0;
if (n < 0)
if (sender.nl.nl_pid != 0) {
/* not from the kernel, ignore */
- log_debug("rtnl: ignoring message from PID %"PRIu32, sender.nl.nl_pid);
+ log_debug("sd-netlink: ignoring message from PID %"PRIu32, sender.nl.nl_pid);
if (peek) {
/* drop the message */
* If nothing useful was received 0 is returned.
* On failure, a negative error code is returned.
*/
-int socket_read_message(sd_netlink *rtnl) {
+int socket_read_message(sd_netlink *nl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL;
bool multi_part = false, done = false;
size_t len, allocated;
unsigned i = 0;
int r;
- assert(rtnl);
- assert(rtnl->rbuffer);
+ assert(nl);
+ assert(nl->rbuffer);
/* read nothing, just get the pending message size */
- r = socket_recv_message(rtnl->fd, &iov, NULL, true);
+ r = socket_recv_message(nl->fd, &iov, NULL, true);
if (r <= 0)
return r;
else
len = (size_t) r;
/* make room for the pending message */
- if (!greedy_realloc((void **)&rtnl->rbuffer, len, sizeof(uint8_t)))
+ if (!greedy_realloc((void**) &nl->rbuffer, len, sizeof(uint8_t)))
return -ENOMEM;
- allocated = MALLOC_SIZEOF_SAFE(rtnl->rbuffer);
- iov = IOVEC_MAKE(rtnl->rbuffer, allocated);
+ allocated = MALLOC_SIZEOF_SAFE(nl->rbuffer);
+ iov = IOVEC_MAKE(nl->rbuffer, allocated);
/* read the pending message */
- r = socket_recv_message(rtnl->fd, &iov, &group, false);
+ r = socket_recv_message(nl->fd, &iov, &group, false);
if (r <= 0)
return r;
else
/* message did not fit in read buffer */
return -EIO;
- if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
+ if (NLMSG_OK(nl->rbuffer, len) && nl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
multi_part = true;
- for (i = 0; i < rtnl->rqueue_partial_size; i++)
- if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
- rtnl->rbuffer->nlmsg_seq) {
- first = rtnl->rqueue_partial[i];
+ for (i = 0; i < nl->rqueue_partial_size; i++)
+ if (message_get_serial(nl->rqueue_partial[i]) ==
+ nl->rbuffer->nlmsg_seq) {
+ first = nl->rqueue_partial[i];
break;
}
}
- for (struct nlmsghdr *new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
+ for (struct nlmsghdr *new_msg = nl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- const NLType *nl_type;
+ size_t size;
- if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
+ if (!group && new_msg->nlmsg_pid != nl->sockaddr.nl.nl_pid)
/* not broadcast and not for us */
continue;
}
/* check that we support this message type */
- r = type_system_root_get_type(rtnl, &nl_type, new_msg->nlmsg_type);
+ r = type_system_root_get_type_system_and_header_size(nl, new_msg->nlmsg_type, NULL, &size);
if (r < 0) {
if (r == -EOPNOTSUPP)
log_debug("sd-netlink: ignored message with unknown type: %i",
}
/* check that the size matches the message type */
- if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
+ if (new_msg->nlmsg_len < NLMSG_LENGTH(size)) {
log_debug("sd-netlink: message is shorter than expected, dropping");
continue;
}
- r = message_new_empty(rtnl, &m);
+ r = message_new_empty(nl, &m);
if (r < 0)
return r;
return -ENOMEM;
/* seal and parse the top-level message */
- r = sd_netlink_message_rewind(m, rtnl);
+ r = sd_netlink_message_rewind(m, nl);
if (r < 0)
return r;
if (!multi_part || done) {
/* we got a complete message, push it on the read queue */
- r = rtnl_rqueue_make_room(rtnl);
+ r = netlink_rqueue_make_room(nl);
if (r < 0)
return r;
- rtnl->rqueue[rtnl->rqueue_size++] = TAKE_PTR(first);
+ nl->rqueue[nl->rqueue_size++] = TAKE_PTR(first);
- if (multi_part && (i < rtnl->rqueue_partial_size)) {
+ if (multi_part && (i < nl->rqueue_partial_size)) {
/* remove the message form the partial read queue */
- memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
- sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
- rtnl->rqueue_partial_size--;
+ memmove(nl->rqueue_partial + i, nl->rqueue_partial + i + 1,
+ sizeof(sd_netlink_message*) * (nl->rqueue_partial_size - i - 1));
+ nl->rqueue_partial_size--;
}
return 1;
} else {
/* we only got a partial multi-part message, push it on the
partial read queue */
- if (i < rtnl->rqueue_partial_size)
- rtnl->rqueue_partial[i] = TAKE_PTR(first);
+ if (i < nl->rqueue_partial_size)
+ nl->rqueue_partial[i] = TAKE_PTR(first);
else {
- r = rtnl_rqueue_partial_make_room(rtnl);
+ r = netlink_rqueue_partial_make_room(nl);
if (r < 0)
return r;
- rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = TAKE_PTR(first);
+ nl->rqueue_partial[nl->rqueue_partial_size++] = TAKE_PTR(first);
}
return 0;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <linux/batman_adv.h>
+#include <linux/fou.h>
+#include <linux/genetlink.h>
+#include <linux/if.h>
+#include <linux/if_macsec.h>
+#include <linux/l2tp.h>
+#include <linux/nl80211.h>
+#include <linux/wireguard.h>
+
+#include "netlink-genl.h"
+#include "netlink-types-internal.h"
+
+/***************** genl ctrl type systems *****************/
+static const NLType genl_ctrl_mcast_group_types[] = {
+ [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NETLINK_TYPE_STRING },
+ [CTRL_ATTR_MCAST_GRP_ID] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(genl_ctrl_mcast_group);
+
+static const NLType genl_ctrl_ops_types[] = {
+ [CTRL_ATTR_OP_ID] = { .type = NETLINK_TYPE_U32 },
+ [CTRL_ATTR_OP_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(genl_ctrl_ops);
+
+static const NLType genl_ctrl_types[] = {
+ [CTRL_ATTR_FAMILY_ID] = { .type = NETLINK_TYPE_U16 },
+ [CTRL_ATTR_FAMILY_NAME] = { .type = NETLINK_TYPE_STRING },
+ [CTRL_ATTR_VERSION] = { .type = NETLINK_TYPE_U32 },
+ [CTRL_ATTR_HDRSIZE] = { .type = NETLINK_TYPE_U32 },
+ [CTRL_ATTR_MAXATTR] = { .type = NETLINK_TYPE_U32 },
+ [CTRL_ATTR_OPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_ops_type_system },
+ [CTRL_ATTR_MCAST_GROUPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_mcast_group_type_system },
+ /*
+ [CTRL_ATTR_POLICY] = { .type = NETLINK_TYPE_NESTED, },
+ [CTRL_ATTR_OP_POLICY] = { .type = NETLINK_TYPE_NESTED, }
+ */
+ [CTRL_ATTR_OP] = { .type = NETLINK_TYPE_U32 },
+};
+
+/***************** genl batadv type systems *****************/
+static const NLType genl_batadv_types[] = {
+ [BATADV_ATTR_VERSION] = { .type = NETLINK_TYPE_STRING },
+ [BATADV_ATTR_ALGO_NAME] = { .type = NETLINK_TYPE_STRING },
+ [BATADV_ATTR_MESH_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_MESH_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
+ [BATADV_ATTR_MESH_ADDRESS] = { .size = ETH_ALEN },
+ [BATADV_ATTR_HARD_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_HARD_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
+ [BATADV_ATTR_HARD_ADDRESS] = { .size = ETH_ALEN },
+ [BATADV_ATTR_ORIG_ADDRESS] = { .size = ETH_ALEN },
+ [BATADV_ATTR_TPMETER_RESULT] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_TPMETER_BYTES] = { .type = NETLINK_TYPE_U64 },
+ [BATADV_ATTR_TPMETER_COOKIE] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_PAD] = { .type = NETLINK_TYPE_UNSPEC },
+ [BATADV_ATTR_ACTIVE] = { .type = NETLINK_TYPE_FLAG },
+ [BATADV_ATTR_TT_ADDRESS] = { .size = ETH_ALEN },
+ [BATADV_ATTR_TT_TTVN] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_TT_LAST_TTVN] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_TT_CRC32] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_TT_VID] = { .type = NETLINK_TYPE_U16 },
+ [BATADV_ATTR_TT_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_FLAG_BEST] = { .type = NETLINK_TYPE_FLAG },
+ [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_NEIGH_ADDRESS] = { .size = ETH_ALEN },
+ [BATADV_ATTR_TQ] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_THROUGHPUT] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_BANDWIDTH_UP] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_ROUTER] = { .size = ETH_ALEN },
+ [BATADV_ATTR_BLA_OWN] = { .type = NETLINK_TYPE_FLAG },
+ [BATADV_ATTR_BLA_ADDRESS] = { .size = ETH_ALEN },
+ [BATADV_ATTR_BLA_VID] = { .type = NETLINK_TYPE_U16 },
+ [BATADV_ATTR_BLA_BACKBONE] = { .size = ETH_ALEN },
+ [BATADV_ATTR_BLA_CRC] = { .type = NETLINK_TYPE_U16 },
+ [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_DAT_CACHE_HWADDRESS] = { .size = ETH_ALEN },
+ [BATADV_ATTR_DAT_CACHE_VID] = { .type = NETLINK_TYPE_U16 },
+ [BATADV_ATTR_MCAST_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_MCAST_FLAGS_PRIV] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_VLANID] = { .type = NETLINK_TYPE_U16 },
+ [BATADV_ATTR_AGGREGATED_OGMS_ENABLED] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_AP_ISOLATION_ENABLED] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_ISOLATION_MARK] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_ISOLATION_MASK] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_BONDING_ENABLED] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_FRAGMENTATION_ENABLED] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_GW_BANDWIDTH_DOWN] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_GW_BANDWIDTH_UP] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_GW_MODE] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_GW_SEL_CLASS] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_HOP_PENALTY] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_LOG_LEVEL] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_MULTICAST_FANOUT] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_NETWORK_CODING_ENABLED] = { .type = NETLINK_TYPE_U8 },
+ [BATADV_ATTR_ORIG_INTERVAL] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_ELP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
+ [BATADV_ATTR_THROUGHPUT_OVERRIDE] = { .type = NETLINK_TYPE_U32 },
+};
+
+/***************** genl fou type systems *****************/
+static const NLType genl_fou_types[] = {
+ [FOU_ATTR_PORT] = { .type = NETLINK_TYPE_U16 },
+ [FOU_ATTR_AF] = { .type = NETLINK_TYPE_U8 },
+ [FOU_ATTR_IPPROTO] = { .type = NETLINK_TYPE_U8 },
+ [FOU_ATTR_TYPE] = { .type = NETLINK_TYPE_U8 },
+ [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
+ [FOU_ATTR_LOCAL_V4] = { .type = NETLINK_TYPE_IN_ADDR },
+ [FOU_ATTR_PEER_V4] = { .type = NETLINK_TYPE_IN_ADDR },
+ [FOU_ATTR_LOCAL_V6] = { .type = NETLINK_TYPE_IN_ADDR },
+ [FOU_ATTR_PEER_V6] = { .type = NETLINK_TYPE_IN_ADDR},
+ [FOU_ATTR_PEER_PORT] = { .type = NETLINK_TYPE_U16},
+ [FOU_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32},
+};
+
+/***************** genl l2tp type systems *****************/
+static const NLType genl_l2tp_types[] = {
+ [L2TP_ATTR_PW_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [L2TP_ATTR_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [L2TP_ATTR_OFFSET] = { .type = NETLINK_TYPE_U16 },
+ [L2TP_ATTR_DATA_SEQ] = { .type = NETLINK_TYPE_U16 },
+ [L2TP_ATTR_L2SPEC_TYPE] = { .type = NETLINK_TYPE_U8 },
+ [L2TP_ATTR_L2SPEC_LEN] = { .type = NETLINK_TYPE_U8 },
+ [L2TP_ATTR_PROTO_VERSION] = { .type = NETLINK_TYPE_U8 },
+ [L2TP_ATTR_IFNAME] = { .type = NETLINK_TYPE_STRING },
+ [L2TP_ATTR_CONN_ID] = { .type = NETLINK_TYPE_U32 },
+ [L2TP_ATTR_PEER_CONN_ID] = { .type = NETLINK_TYPE_U32 },
+ [L2TP_ATTR_SESSION_ID] = { .type = NETLINK_TYPE_U32 },
+ [L2TP_ATTR_PEER_SESSION_ID] = { .type = NETLINK_TYPE_U32 },
+ [L2TP_ATTR_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
+ [L2TP_ATTR_VLAN_ID] = { .type = NETLINK_TYPE_U16 },
+ [L2TP_ATTR_RECV_SEQ] = { .type = NETLINK_TYPE_U8 },
+ [L2TP_ATTR_SEND_SEQ] = { .type = NETLINK_TYPE_U8 },
+ [L2TP_ATTR_LNS_MODE] = { .type = NETLINK_TYPE_U8 },
+ [L2TP_ATTR_USING_IPSEC] = { .type = NETLINK_TYPE_U8 },
+ [L2TP_ATTR_FD] = { .type = NETLINK_TYPE_U32 },
+ [L2TP_ATTR_IP_SADDR] = { .type = NETLINK_TYPE_IN_ADDR },
+ [L2TP_ATTR_IP_DADDR] = { .type = NETLINK_TYPE_IN_ADDR },
+ [L2TP_ATTR_UDP_SPORT] = { .type = NETLINK_TYPE_U16 },
+ [L2TP_ATTR_UDP_DPORT] = { .type = NETLINK_TYPE_U16 },
+ [L2TP_ATTR_IP6_SADDR] = { .type = NETLINK_TYPE_IN_ADDR },
+ [L2TP_ATTR_IP6_DADDR] = { .type = NETLINK_TYPE_IN_ADDR },
+ [L2TP_ATTR_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_FLAG },
+ [L2TP_ATTR_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_FLAG },
+};
+
+/***************** genl macsec type systems *****************/
+static const NLType genl_macsec_rxsc_types[] = {
+ [MACSEC_RXSC_ATTR_SCI] = { .type = NETLINK_TYPE_U64 },
+};
+
+DEFINE_TYPE_SYSTEM(genl_macsec_rxsc);
+
+static const NLType genl_macsec_sa_types[] = {
+ [MACSEC_SA_ATTR_AN] = { .type = NETLINK_TYPE_U8 },
+ [MACSEC_SA_ATTR_ACTIVE] = { .type = NETLINK_TYPE_U8 },
+ [MACSEC_SA_ATTR_PN] = { .type = NETLINK_TYPE_U32 },
+ [MACSEC_SA_ATTR_KEYID] = { .size = MACSEC_KEYID_LEN },
+ [MACSEC_SA_ATTR_KEY] = { .size = MACSEC_MAX_KEY_LEN },
+};
+
+DEFINE_TYPE_SYSTEM(genl_macsec_sa);
+
+static const NLType genl_macsec_types[] = {
+ [MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+ [MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsc_type_system },
+ [MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_type_system },
+};
+
+/***************** genl nl80211 type systems *****************/
+static const NLType genl_nl80211_types[] = {
+ [NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+ [NL80211_ATTR_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR },
+ [NL80211_ATTR_SSID] = { .type = NETLINK_TYPE_STRING },
+ [NL80211_ATTR_IFTYPE] = { .type = NETLINK_TYPE_U32 },
+};
+
+/***************** genl wireguard type systems *****************/
+static const NLType genl_wireguard_allowedip_types[] = {
+ [WGALLOWEDIP_A_FAMILY] = { .type = NETLINK_TYPE_U16 },
+ [WGALLOWEDIP_A_IPADDR] = { .type = NETLINK_TYPE_IN_ADDR },
+ [WGALLOWEDIP_A_CIDR_MASK] = { .type = NETLINK_TYPE_U8 },
+};
+
+DEFINE_TYPE_SYSTEM(genl_wireguard_allowedip);
+
+static const NLType genl_wireguard_peer_types[] = {
+ [WGPEER_A_PUBLIC_KEY] = { .size = WG_KEY_LEN },
+ [WGPEER_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [WGPEER_A_PRESHARED_KEY] = { .size = WG_KEY_LEN },
+ [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U16 },
+ [WGPEER_A_ENDPOINT] = { .type = NETLINK_TYPE_SOCKADDR },
+ [WGPEER_A_ALLOWEDIPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_allowedip_type_system },
+};
+
+DEFINE_TYPE_SYSTEM(genl_wireguard_peer);
+
+static const NLType genl_wireguard_types[] = {
+ [WGDEVICE_A_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+ [WGDEVICE_A_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
+ [WGDEVICE_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [WGDEVICE_A_PRIVATE_KEY] = { .size = WG_KEY_LEN },
+ [WGDEVICE_A_LISTEN_PORT] = { .type = NETLINK_TYPE_U16 },
+ [WGDEVICE_A_FWMARK] = { .type = NETLINK_TYPE_U32 },
+ [WGDEVICE_A_PEERS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system },
+};
+
+/***************** genl families *****************/
+static const NLTypeSystemUnionElement genl_type_systems[] = {
+ { .name = CTRL_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_ctrl), },
+ { .name = BATADV_NL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_batadv), },
+ { .name = FOU_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_fou), },
+ { .name = L2TP_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_l2tp), },
+ { .name = MACSEC_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_macsec), },
+ { .name = NL80211_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_nl80211), },
+ { .name = WG_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_wireguard), },
+};
+
+/* This is the root type system union, so match_attribute is not necessary. */
+DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(genl, 0);
+
+const NLTypeSystem *genl_get_type_system_by_name(const char *name) {
+ return type_system_union_get_type_system_by_string(&genl_type_system_union, name);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro.h"
+#include "netlink-types.h"
+
+struct NLType {
+ uint16_t type;
+ size_t size;
+ const NLTypeSystem *type_system;
+ const NLTypeSystemUnion *type_system_union;
+};
+
+struct NLTypeSystem {
+ uint16_t count;
+ const NLType *types;
+};
+
+typedef struct NLTypeSystemUnionElement {
+ union {
+ int protocol;
+ const char *name;
+ };
+ NLTypeSystem type_system;
+} NLTypeSystemUnionElement;
+
+struct NLTypeSystemUnion {
+ size_t count;
+ const NLTypeSystemUnionElement *elements;
+ NLMatchType match_type;
+ uint16_t match_attribute;
+};
+
+#define TYPE_SYSTEM_FROM_TYPE(name) \
+ { .count = ELEMENTSOF(name##_types), .types = name##_types }
+#define DEFINE_TYPE_SYSTEM(name) \
+ static const NLTypeSystem name##_type_system = TYPE_SYSTEM_FROM_TYPE(name)
+
+#define _DEFINE_TYPE_SYSTEM_UNION(name, type, attr) \
+ static const NLTypeSystemUnion name##_type_system_union = { \
+ .count = ELEMENTSOF(name##_type_systems), \
+ .elements = name##_type_systems, \
+ .match_type = type, \
+ .match_attribute = attr, \
+ }
+#define DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(name) \
+ _DEFINE_TYPE_SYSTEM_UNION(name, NL_MATCH_PROTOCOL, 0)
+#define DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(name, attr) \
+ _DEFINE_TYPE_SYSTEM_UNION(name, NL_MATCH_SIBLING, attr)
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nfnetlink.h>
+
+#include "netlink-types-internal.h"
+#include "string-table.h"
+
+static const NLType nfnl_nft_table_types[] = {
+ [NFTA_TABLE_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+ [NFTA_TABLE_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_table);
+
+static const NLType nfnl_nft_chain_hook_types[] = {
+ [NFTA_HOOK_HOOKNUM] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_HOOK_PRIORITY] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_HOOK_DEV] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_chain_hook);
+
+static const NLType nfnl_nft_chain_types[] = {
+ [NFTA_CHAIN_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+ [NFTA_CHAIN_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+ [NFTA_CHAIN_HOOK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_hook_type_system },
+ [NFTA_CHAIN_TYPE] = { .type = NETLINK_TYPE_STRING, .size = 16 },
+ [NFTA_CHAIN_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_chain);
+
+static const NLType nfnl_nft_expr_meta_types[] = {
+ [NFTA_META_DREG] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_META_KEY] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_META_SREG] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_expr_payload_types[] = {
+ [NFTA_PAYLOAD_DREG] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_PAYLOAD_BASE] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_PAYLOAD_OFFSET] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_PAYLOAD_LEN] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_expr_nat_types[] = {
+ [NFTA_NAT_TYPE] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_NAT_FAMILY] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_NAT_REG_ADDR_MIN] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_NAT_REG_ADDR_MAX] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_NAT_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_NAT_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_NAT_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_data_types[] = {
+ [NFTA_DATA_VALUE] = { .type = NETLINK_TYPE_BINARY },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_data);
+
+static const NLType nfnl_nft_expr_bitwise_types[] = {
+ [NFTA_BITWISE_SREG] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_BITWISE_DREG] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_BITWISE_LEN] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_BITWISE_MASK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+ [NFTA_BITWISE_XOR] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+};
+
+static const NLType nfnl_nft_expr_cmp_types[] = {
+ [NFTA_CMP_SREG] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_CMP_OP] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_CMP_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+};
+
+static const NLType nfnl_nft_expr_fib_types[] = {
+ [NFTA_FIB_DREG] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_FIB_RESULT] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_FIB_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_expr_lookup_types[] = {
+ [NFTA_LOOKUP_SET] = { .type = NETLINK_TYPE_STRING },
+ [NFTA_LOOKUP_SREG] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_LOOKUP_DREG] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_LOOKUP_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_expr_masq_types[] = {
+ [NFTA_MASQ_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_MASQ_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_MASQ_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLTypeSystemUnionElement nfnl_expr_data_type_systems[] = {
+ { .name = "bitwise", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_bitwise), },
+ { .name = "cmp", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_cmp), },
+ { .name = "fib", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_fib), },
+ { .name = "lookup", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_lookup), },
+ { .name = "masq", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_masq), },
+ { .name = "meta", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_meta), },
+ { .name = "nat", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_nat), },
+ { .name = "payload", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_payload), },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(nfnl_expr_data, NFTA_EXPR_NAME);
+
+static const NLType nfnl_nft_rule_expr_types[] = {
+ [NFTA_EXPR_NAME] = { .type = NETLINK_TYPE_STRING, .size = 16 },
+ [NFTA_EXPR_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &nfnl_expr_data_type_system_union },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_rule_expr);
+
+static const NLType nfnl_nft_rule_types[] = {
+ [NFTA_RULE_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+ [NFTA_RULE_CHAIN] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+ [NFTA_RULE_EXPRESSIONS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_expr_type_system }
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_rule);
+
+static const NLType nfnl_nft_set_types[] = {
+ [NFTA_SET_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+ [NFTA_SET_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+ [NFTA_SET_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_SET_KEY_TYPE] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_SET_KEY_LEN] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_SET_DATA_TYPE] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_SET_DATA_LEN] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_SET_POLICY] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_SET_ID] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_set);
+
+static const NLType nfnl_nft_setelem_types[] = {
+ [NFTA_SET_ELEM_KEY] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+ [NFTA_SET_ELEM_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+ [NFTA_SET_ELEM_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_setelem);
+
+static const NLType nfnl_nft_setelem_list_types[] = {
+ [NFTA_SET_ELEM_LIST_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+ [NFTA_SET_ELEM_LIST_SET] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+ [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_type_system },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_setelem_list);
+
+static const NLType nfnl_subsys_nft_types [] = {
+ [NFT_MSG_DELTABLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
+ [NFT_MSG_NEWTABLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
+ [NFT_MSG_NEWCHAIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_type_system, .size = sizeof(struct nfgenmsg) },
+ [NFT_MSG_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_type_system, .size = sizeof(struct nfgenmsg) },
+ [NFT_MSG_NEWSET] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_set_type_system, .size = sizeof(struct nfgenmsg) },
+ [NFT_MSG_NEWSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
+ [NFT_MSG_DELSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_subsys_nft);
+
+static const NLType nfnl_msg_batch_types [] = {
+ [NFNL_BATCH_GENID] = { .type = NETLINK_TYPE_U32 }
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_msg_batch);
+
+static const NLType nfnl_subsys_none_types[] = {
+ [NFNL_MSG_BATCH_BEGIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
+ [NFNL_MSG_BATCH_END] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_subsys_none);
+
+static const NLType nfnl_types[] = {
+ [NFNL_SUBSYS_NONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_subsys_none_type_system },
+ [NFNL_SUBSYS_NFTABLES] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_subsys_nft_type_system },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl);
+
+const NLType *nfnl_get_type(uint16_t nlmsg_type) {
+ const NLTypeSystem *subsys;
+
+ subsys = type_system_get_type_system(&nfnl_type_system, nlmsg_type >> 8);
+ if (!subsys)
+ return NULL;
+
+ return type_system_get_type(subsys, nlmsg_type & ((1U << 8) - 1));
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <linux/batman_adv.h>
+#include <linux/can/netlink.h>
+#include <linux/can/vxcan.h>
+#include <linux/fib_rules.h>
+#include <linux/fou.h>
+#include <linux/if.h>
+#include <linux/if_addr.h>
+#include <linux/if_addrlabel.h>
+#include <linux/if_bridge.h>
+#include <linux/if_link.h>
+#include <linux/if_macsec.h>
+#include <linux/if_tunnel.h>
+#include <linux/ip.h>
+#include <linux/l2tp.h>
+#include <linux/netlink.h>
+#include <linux/nexthop.h>
+#include <linux/nl80211.h>
+#include <linux/pkt_sched.h>
+#include <linux/rtnetlink.h>
+#include <linux/veth.h>
+#include <linux/wireguard.h>
+
+#include "sd-netlink.h"
+
+#include "netlink-types-internal.h"
+#include "string-table.h"
+
+/* Maximum ARP IP target defined in kernel */
+#define BOND_MAX_ARP_TARGETS 16
+
+typedef enum {
+ BOND_ARP_TARGETS_0,
+ BOND_ARP_TARGETS_1,
+ BOND_ARP_TARGETS_2,
+ BOND_ARP_TARGETS_3,
+ BOND_ARP_TARGETS_4,
+ BOND_ARP_TARGETS_5,
+ BOND_ARP_TARGETS_6,
+ BOND_ARP_TARGETS_7,
+ BOND_ARP_TARGETS_8,
+ BOND_ARP_TARGETS_9,
+ BOND_ARP_TARGETS_10,
+ BOND_ARP_TARGETS_11,
+ BOND_ARP_TARGETS_12,
+ BOND_ARP_TARGETS_13,
+ BOND_ARP_TARGETS_14,
+ BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS,
+} BondArpTargets;
+
+static const NLTypeSystem rtnl_link_type_system;
+
+static const NLType rtnl_link_info_data_batadv_types[] = {
+ [IFLA_BATADV_ALGO_NAME] = { .type = NETLINK_TYPE_STRING, .size = 20 },
+};
+
+static const NLType rtnl_link_info_data_veth_types[] = {
+ [VETH_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+};
+
+static const NLType rtnl_link_info_data_vxcan_types[] = {
+ [VXCAN_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+};
+
+static const NLType rtnl_link_info_data_ipvlan_types[] = {
+ [IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_IPVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
+};
+
+static const NLType rtnl_macvlan_macaddr_types[] = {
+ [IFLA_MACVLAN_MACADDR] = { .type = NETLINK_TYPE_ETHER_ADDR },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_macvlan_macaddr);
+
+static const NLType rtnl_link_info_data_macvlan_types[] = {
+ [IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_MACVLAN_MACADDR_MODE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_MACVLAN_MACADDR_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_macvlan_macaddr_type_system },
+ [IFLA_MACVLAN_MACADDR_COUNT] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_MACVLAN_BC_QUEUE_LEN] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = { .type = NETLINK_TYPE_REJECT },
+};
+
+static const NLType rtnl_link_info_data_bridge_types[] = {
+ [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BR_MCAST_IGMP_VERSION] = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_vlan_qos_map_types[] = {
+ [IFLA_VLAN_QOS_MAPPING] = { .size = sizeof(struct ifla_vlan_qos_mapping) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_vlan_qos_map);
+
+static const NLType rtnl_link_info_data_vlan_types[] = {
+ [IFLA_VLAN_ID] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_VLAN_FLAGS] = { .size = sizeof(struct ifla_vlan_flags) },
+ [IFLA_VLAN_EGRESS_QOS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
+ [IFLA_VLAN_INGRESS_QOS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
+ [IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 },
+};
+
+static const NLType rtnl_link_info_data_vxlan_types[] = {
+ [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_AGEING] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VXLAN_LIMIT] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VXLAN_PORT_RANGE] = { .type = NETLINK_TYPE_U32},
+ [IFLA_VXLAN_PROXY] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_RSC] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_L2MISS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_L3MISS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_PORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_VXLAN_GROUP6] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_VXLAN_LOCAL6] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_VXLAN_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_REMCSUM_TX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_REMCSUM_RX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_GBP] = { .type = NETLINK_TYPE_FLAG },
+ [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
+ [IFLA_VXLAN_COLLECT_METADATA] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_VXLAN_LABEL] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VXLAN_GPE] = { .type = NETLINK_TYPE_FLAG },
+ [IFLA_VXLAN_TTL_INHERIT] = { .type = NETLINK_TYPE_FLAG },
+ [IFLA_VXLAN_DF] = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_bond_arp_target_types[] = {
+ [BOND_ARP_TARGETS_0] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_1] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_2] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_3] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_4] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_5] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_6] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_7] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_8] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_9] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_10] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_11] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_12] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_13] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_14] = { .type = NETLINK_TYPE_U32 },
+ [BOND_ARP_TARGETS_MAX] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_bond_arp_target);
+
+static const NLType rtnl_link_info_data_bond_types[] = {
+ [IFLA_BOND_MODE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BOND_ACTIVE_SLAVE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_MIIMON] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_UPDELAY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_DOWNDELAY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_USE_CARRIER] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BOND_ARP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_ARP_IP_TARGET] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_arp_target_type_system },
+ [IFLA_BOND_ARP_VALIDATE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_PRIMARY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_PRIMARY_RESELECT] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BOND_FAIL_OVER_MAC] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BOND_RESEND_IGMP] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_NUM_PEER_NOTIF] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BOND_ALL_SLAVES_ACTIVE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BOND_MIN_LINKS] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_LP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_PACKETS_PER_SLAVE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BOND_AD_LACP_RATE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BOND_AD_SELECT] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED },
+ [IFLA_BOND_AD_ACTOR_SYS_PRIO] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BOND_AD_USER_PORT_KEY] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BOND_AD_ACTOR_SYSTEM] = { .type = NETLINK_TYPE_ETHER_ADDR },
+ [IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_link_info_data_iptun_types[] = {
+ [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_IPTUN_TOS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_IPTUN_PMTUDISC] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_IPTUN_6RD_PREFIX] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_IPTUN_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_IPTUN_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
+};
+
+static const NLType rtnl_link_info_data_ipgre_types[] = {
+ [IFLA_GRE_LINK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_GRE_IFLAGS] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_GRE_OFLAGS] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_GRE_IKEY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_GRE_OKEY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_GRE_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_GRE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_GRE_TTL] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_GRE_TOS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_GRE_PMTUDISC] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_GRE_FLOWINFO] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_GRE_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_GRE_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_GRE_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_GRE_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_GRE_ERSPAN_INDEX] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_link_info_data_ipvti_types[] = {
+ [IFLA_VTI_LINK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VTI_IKEY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VTI_OKEY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VTI_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
+};
+
+static const NLType rtnl_link_info_data_ip6tnl_types[] = {
+ [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_IPTUN_FLOWINFO] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_link_info_data_vrf_types[] = {
+ [IFLA_VRF_TABLE] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_link_info_data_geneve_types[] = {
+ [IFLA_GENEVE_ID] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_GENEVE_TTL] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_GENEVE_TOS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_GENEVE_PORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_GENEVE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_GENEVE_REMOTE6] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_GENEVE_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_GENEVE_LABEL] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_GENEVE_TTL_INHERIT] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_GENEVE_DF] = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_link_info_data_can_types[] = {
+ [IFLA_CAN_BITTIMING] = { .size = sizeof(struct can_bittiming) },
+ [IFLA_CAN_RESTART_MS] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_CAN_CTRLMODE] = { .size = sizeof(struct can_ctrlmode) },
+ [IFLA_CAN_TERMINATION] = { .type = NETLINK_TYPE_U16 },
+};
+
+static const NLType rtnl_link_info_data_macsec_types[] = {
+ [IFLA_MACSEC_SCI] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_MACSEC_PORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_MACSEC_ICV_LEN] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_MACSEC_CIPHER_SUITE] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_MACSEC_WINDOW] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_MACSEC_ENCODING_SA] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_MACSEC_ENCRYPT] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_MACSEC_PROTECT] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_MACSEC_INC_SCI] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_MACSEC_ES] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_MACSEC_SCB] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_MACSEC_VALIDATION] = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_link_info_data_xfrm_types[] = {
+ [IFLA_XFRM_LINK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_XFRM_IF_ID] = { .type = NETLINK_TYPE_U32 }
+};
+
+static const NLType rtnl_link_info_data_bareudp_types[] = {
+ [IFLA_BAREUDP_PORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BAREUDP_ETHERTYPE] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BAREUDP_SRCPORT_MIN] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BAREUDP_MULTIPROTO_MODE] = { .type = NETLINK_TYPE_FLAG },
+};
+
+static const NLTypeSystemUnionElement rtnl_link_info_data_type_systems[] = {
+ { .name = "bareudp", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_bareudp), },
+ { .name = "batadv", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_batadv), },
+ { .name = "bond", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_bond), },
+ { .name = "bridge", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_bridge), },
+ { .name = "can", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_can), },
+ { .name = "dummy", },
+ { .name = "erspan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre), },
+ { .name = "geneve", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_geneve), },
+ { .name = "gre", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre), },
+ { .name = "gretap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre), },
+ { .name = "ifb", },
+ { .name = "ip6gre", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre), },
+ { .name = "ip6gretap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre), },
+ { .name = "ip6tnl", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ip6tnl), },
+ { .name = "ipip", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_iptun), },
+ { .name = "ipvlan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvlan), },
+ { .name = "ipvtap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvlan), },
+ { .name = "macsec", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_macsec), },
+ { .name = "macvlan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_macvlan), },
+ { .name = "macvtap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_macvlan), },
+ { .name = "netdevsim", },
+ { .name = "nlmon", },
+ { .name = "sit", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_iptun), },
+ { .name = "vcan", },
+ { .name = "veth", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_veth), },
+ { .name = "vlan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vlan), },
+ { .name = "vrf", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vrf), },
+ { .name = "vti", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvti), },
+ { .name = "vti6", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvti), },
+ { .name = "vxcan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vxcan), },
+ { .name = "vxlan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vxlan), },
+ { .name = "wireguard", },
+ { .name = "xfrm", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_xfrm), },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(rtnl_link_info_data, IFLA_INFO_KIND);
+
+static const NLType rtnl_link_info_types[] = {
+ [IFLA_INFO_KIND] = { .type = NETLINK_TYPE_STRING },
+ [IFLA_INFO_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union },
+/*
+ [IFLA_INFO_XSTATS],
+ [IFLA_INFO_SLAVE_KIND] = { .type = NETLINK_TYPE_STRING },
+ [IFLA_INFO_SLAVE_DATA] = { .type = NETLINK_TYPE_NESTED },
+*/
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_link_info);
+
+static const struct NLType rtnl_prot_info_bridge_port_types[] = {
+ [IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_MODE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_GUARD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_PROTECT] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_FAST_LEAVE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_LEARNING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_PROXYARP] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_ROOT_ID] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_BRIDGE_ID] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_DESIGNATED_PORT] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_DESIGNATED_COST] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_ID] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_NO] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_TOPOLOGY_CHANGE_ACK] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_CONFIG_PENDING] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_MESSAGE_AGE_TIMER] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BRPORT_FORWARD_DELAY_TIMER] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BRPORT_HOLD_TIMER] = { .type = NETLINK_TYPE_U64 },
+ [IFLA_BRPORT_FLUSH] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_PAD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_MCAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_VLAN_TUNNEL] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_BCAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_ISOLATED] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_BRPORT_BACKUP_PORT] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLTypeSystemUnionElement rtnl_prot_info_type_systems[] = {
+ { .protocol = AF_BRIDGE, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_prot_info_bridge_port), },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(rtnl_prot_info);
+
+static const struct NLType rtnl_af_spec_inet6_types[] = {
+ [IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 },
+/*
+ IFLA_INET6_CONF,
+ IFLA_INET6_STATS,
+ IFLA_INET6_MCAST,
+ IFLA_INET6_CACHEINFO,
+ IFLA_INET6_ICMP6STATS,
+*/
+ [IFLA_INET6_TOKEN] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFLA_INET6_ADDR_GEN_MODE] = { .type = NETLINK_TYPE_U8 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_af_spec_inet6);
+
+static const NLType rtnl_af_spec_unspec_types[] = {
+ [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system },
+};
+
+static const NLType rtnl_af_spec_bridge_types[] = {
+ [IFLA_BRIDGE_FLAGS] = { .type = NETLINK_TYPE_U16 },
+ [IFLA_BRIDGE_VLAN_INFO] = { .size = sizeof(struct bridge_vlan_info) },
+};
+
+static const NLTypeSystemUnionElement rtnl_af_spec_type_systems[] = {
+ { .protocol = AF_UNSPEC, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_af_spec_unspec), },
+ { .protocol = AF_BRIDGE, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_af_spec_bridge), },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(rtnl_af_spec);
+
+static const NLType rtnl_prop_list_types[] = {
+ [IFLA_ALT_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_prop_list);
+
+static const NLType rtnl_vf_vlan_list_types[] = {
+ [IFLA_VF_VLAN_INFO] = { .size = sizeof(struct ifla_vf_vlan_info) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_vf_vlan_list);
+
+static const NLType rtnl_vf_info_types[] = {
+ [IFLA_VF_MAC] = { .size = sizeof(struct ifla_vf_mac) },
+ [IFLA_VF_VLAN] = { .size = sizeof(struct ifla_vf_vlan) },
+ [IFLA_VF_VLAN_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_list_type_system},
+ [IFLA_VF_TX_RATE] = { .size = sizeof(struct ifla_vf_tx_rate) },
+ [IFLA_VF_SPOOFCHK] = { .size = sizeof(struct ifla_vf_spoofchk) },
+ [IFLA_VF_RATE] = { .size = sizeof(struct ifla_vf_rate) },
+ [IFLA_VF_LINK_STATE] = { .size = sizeof(struct ifla_vf_link_state) },
+ [IFLA_VF_RSS_QUERY_EN] = { .size = sizeof(struct ifla_vf_rss_query_en) },
+ [IFLA_VF_TRUST] = { .size = sizeof(struct ifla_vf_trust) },
+ [IFLA_VF_IB_NODE_GUID] = { .size = sizeof(struct ifla_vf_guid) },
+ [IFLA_VF_IB_PORT_GUID] = { .size = sizeof(struct ifla_vf_guid) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_vf_info);
+
+static const NLType rtnl_vfinfo_list_types[] = {
+ [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_info_type_system },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_vfinfo_list);
+
+static const NLType rtnl_link_types[] = {
+ [IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR },
+ [IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR },
+ [IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
+ [IFLA_MTU] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_LINK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_QDISC] = { .type = NETLINK_TYPE_STRING },
+ [IFLA_STATS] = { .size = sizeof(struct rtnl_link_stats) },
+/*
+ [IFLA_COST],
+ [IFLA_PRIORITY],
+*/
+ [IFLA_MASTER] = { .type = NETLINK_TYPE_U32 },
+/*
+ [IFLA_WIRELESS],
+*/
+ [IFLA_PROTINFO] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union },
+ [IFLA_TXQLEN] = { .type = NETLINK_TYPE_U32 },
+/*
+ [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) },
+*/
+ [IFLA_WEIGHT] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_OPERSTATE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_LINKMODE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system },
+ [IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 },
+ [IFLA_NUM_VF] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VFINFO_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vfinfo_list_type_system },
+ [IFLA_STATS64] = { .size = sizeof(struct rtnl_link_stats64) },
+/*
+ [IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED },
+ [IFLA_PORT_SELF] = { .type = NETLINK_TYPE_NESTED },
+*/
+ [IFLA_AF_SPEC] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_af_spec_type_system_union },
+/*
+ [IFLA_VF_PORTS],
+ [IFLA_PORT_SELF],
+*/
+ [IFLA_GROUP] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_NET_NS_FD] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_EXT_MASK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_PROMISCUITY] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_NUM_TX_QUEUES] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_NUM_RX_QUEUES] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_GSO_MAX_SEGS] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_GSO_MAX_SIZE] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_CARRIER] = { .type = NETLINK_TYPE_U8 },
+/*
+ [IFLA_PHYS_PORT_ID] = { .type = NETLINK_TYPE_BINARY, .len = MAX_PHYS_PORT_ID_LEN },
+*/
+ [IFLA_MIN_MTU] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_MAX_MTU] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_PROP_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_prop_list_type_system },
+ [IFLA_ALT_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_link);
+
+/* IFA_FLAGS was defined in kernel 3.14, but we still support older
+ * kernels where IFA_MAX is lower. */
+static const NLType rtnl_address_types[] = {
+ [IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
+ [IFA_BROADCAST] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFA_ANYCAST] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) },
+ [IFA_MULTICAST] = { .type = NETLINK_TYPE_IN_ADDR },
+ [IFA_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [IFA_RT_PRIORITY] = { .type = NETLINK_TYPE_U32 },
+ [IFA_TARGET_NETNSID] = { .type = NETLINK_TYPE_S32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_address);
+
+/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
+
+static const NLType rtnl_route_metrics_types[] = {
+ [RTAX_MTU] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_WINDOW] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_RTT] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_RTTVAR] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_SSTHRESH] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_CWND] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_ADVMSS] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_REORDERING] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_HOPLIMIT] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_INITCWND] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_FEATURES] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_RTO_MIN] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_INITRWND] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_QUICKACK] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_CC_ALGO] = { .type = NETLINK_TYPE_U32 },
+ [RTAX_FASTOPEN_NO_COOKIE] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_route_metrics);
+
+static const NLType rtnl_route_types[] = {
+ [RTA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
+ [RTA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
+ [RTA_IIF] = { .type = NETLINK_TYPE_U32 },
+ [RTA_OIF] = { .type = NETLINK_TYPE_U32 },
+ [RTA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR },
+ [RTA_PRIORITY] = { .type = NETLINK_TYPE_U32 },
+ [RTA_PREFSRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
+ [RTA_METRICS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_metrics_type_system},
+ [RTA_MULTIPATH] = { .size = sizeof(struct rtnexthop) },
+ [RTA_FLOW] = { .type = NETLINK_TYPE_U32 }, /* 6? */
+ [RTA_CACHEINFO] = { .size = sizeof(struct rta_cacheinfo) },
+ [RTA_TABLE] = { .type = NETLINK_TYPE_U32 },
+ [RTA_MARK] = { .type = NETLINK_TYPE_U32 },
+ [RTA_MFC_STATS] = { .type = NETLINK_TYPE_U64 },
+ [RTA_VIA] = { /* See struct rtvia */ },
+ [RTA_NEWDST] = { .type = NETLINK_TYPE_U32 },
+ [RTA_PREF] = { .type = NETLINK_TYPE_U8 },
+ [RTA_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [RTA_ENCAP] = { .type = NETLINK_TYPE_NESTED }, /* Multiple type systems i.e. LWTUNNEL_ENCAP_MPLS/LWTUNNEL_ENCAP_IP/LWTUNNEL_ENCAP_ILA etc... */
+ [RTA_EXPIRES] = { .type = NETLINK_TYPE_U32 },
+ [RTA_UID] = { .type = NETLINK_TYPE_U32 },
+ [RTA_TTL_PROPAGATE] = { .type = NETLINK_TYPE_U8 },
+ [RTA_IP_PROTO] = { .type = NETLINK_TYPE_U8 },
+ [RTA_SPORT] = { .type = NETLINK_TYPE_U16 },
+ [RTA_DPORT] = { .type = NETLINK_TYPE_U16 },
+ [RTA_NH_ID] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_route);
+
+static const NLType rtnl_neigh_types[] = {
+ [NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR },
+ [NDA_LLADDR] = { /* struct ether_addr, struct in_addr, or struct in6_addr */ },
+ [NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) },
+ [NDA_PROBES] = { .type = NETLINK_TYPE_U32 },
+ [NDA_VLAN] = { .type = NETLINK_TYPE_U16 },
+ [NDA_PORT] = { .type = NETLINK_TYPE_U16 },
+ [NDA_VNI] = { .type = NETLINK_TYPE_U32 },
+ [NDA_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_neigh);
+
+static const NLType rtnl_addrlabel_types[] = {
+ [IFAL_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
+ [IFAL_LABEL] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_addrlabel);
+
+static const NLType rtnl_routing_policy_rule_types[] = {
+ [FRA_DST] = { .type = NETLINK_TYPE_IN_ADDR },
+ [FRA_SRC] = { .type = NETLINK_TYPE_IN_ADDR },
+ [FRA_IIFNAME] = { .type = NETLINK_TYPE_STRING },
+ [FRA_GOTO] = { .type = NETLINK_TYPE_U32 },
+ [FRA_PRIORITY] = { .type = NETLINK_TYPE_U32 },
+ [FRA_FWMARK] = { .type = NETLINK_TYPE_U32 },
+ [FRA_FLOW] = { .type = NETLINK_TYPE_U32 },
+ [FRA_TUN_ID] = { .type = NETLINK_TYPE_U64 },
+ [FRA_SUPPRESS_IFGROUP] = { .type = NETLINK_TYPE_U32 },
+ [FRA_SUPPRESS_PREFIXLEN] = { .type = NETLINK_TYPE_U32 },
+ [FRA_TABLE] = { .type = NETLINK_TYPE_U32 },
+ [FRA_FWMASK] = { .type = NETLINK_TYPE_U32 },
+ [FRA_OIFNAME] = { .type = NETLINK_TYPE_STRING },
+ [FRA_PAD] = { .type = NETLINK_TYPE_U32 },
+ [FRA_L3MDEV] = { .type = NETLINK_TYPE_U8 },
+ [FRA_UID_RANGE] = { .size = sizeof(struct fib_rule_uid_range) },
+ [FRA_PROTOCOL] = { .type = NETLINK_TYPE_U8 },
+ [FRA_IP_PROTO] = { .type = NETLINK_TYPE_U8 },
+ [FRA_SPORT_RANGE] = { .size = sizeof(struct fib_rule_port_range) },
+ [FRA_DPORT_RANGE] = { .size = sizeof(struct fib_rule_port_range) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_routing_policy_rule);
+
+static const NLType rtnl_nexthop_types[] = {
+ [NHA_ID] = { .type = NETLINK_TYPE_U32 },
+ [NHA_GROUP] = { /* array of struct nexthop_grp */ },
+ [NHA_GROUP_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [NHA_BLACKHOLE] = { .type = NETLINK_TYPE_FLAG },
+ [NHA_OIF] = { .type = NETLINK_TYPE_U32 },
+ [NHA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR },
+ [NHA_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [NHA_ENCAP] = { .type = NETLINK_TYPE_NESTED },
+ [NHA_GROUPS] = { .type = NETLINK_TYPE_FLAG },
+ [NHA_MASTER] = { .type = NETLINK_TYPE_U32 },
+ [NHA_FDB] = { .type = NETLINK_TYPE_FLAG },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_nexthop);
+
+static const NLType rtnl_tca_option_data_cake_types[] = {
+ [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
+ [TCA_CAKE_OVERHEAD] = { .type = NETLINK_TYPE_S32 },
+ [TCA_CAKE_MPU] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_codel_types[] = {
+ [TCA_CODEL_TARGET] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CODEL_LIMIT] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CODEL_INTERVAL] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CODEL_ECN] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CODEL_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_drr_types[] = {
+ [TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_ets_quanta_types[] = {
+ [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32, },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_tca_option_data_ets_quanta);
+
+static const NLType rtnl_tca_option_data_ets_prio_types[] = {
+ [TCA_ETS_PRIOMAP_BAND] = { .type = NETLINK_TYPE_U8, },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_tca_option_data_ets_prio);
+
+static const NLType rtnl_tca_option_data_ets_types[] = {
+ [TCA_ETS_NBANDS] = { .type = NETLINK_TYPE_U8 },
+ [TCA_ETS_NSTRICT] = { .type = NETLINK_TYPE_U8 },
+ [TCA_ETS_QUANTA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_quanta_type_system },
+ [TCA_ETS_PRIOMAP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_prio_type_system },
+ [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_fq_types[] = {
+ [TCA_FQ_PLIMIT] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_FLOW_PLIMIT] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_QUANTUM] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_INITIAL_QUANTUM] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_RATE_ENABLE] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_FLOW_DEFAULT_RATE] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_FLOW_MAX_RATE] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_BUCKETS_LOG] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_FLOW_REFILL_DELAY] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_LOW_RATE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_ORPHAN_MASK] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_fq_codel_types[] = {
+ [TCA_FQ_CODEL_TARGET] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_CODEL_LIMIT] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_CODEL_INTERVAL] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_CODEL_ECN] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_CODEL_FLOWS] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_CODEL_QUANTUM] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_CODEL_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NETLINK_TYPE_U32 },
+ [TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_fq_pie_types[] = {
+ [TCA_FQ_PIE_LIMIT] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_gred_types[] = {
+ [TCA_GRED_DPS] = { .size = sizeof(struct tc_gred_sopt) },
+};
+
+static const NLType rtnl_tca_option_data_hhf_types[] = {
+ [TCA_HHF_BACKLOG_LIMIT] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_htb_types[] = {
+ [TCA_HTB_PARMS] = { .size = sizeof(struct tc_htb_opt) },
+ [TCA_HTB_INIT] = { .size = sizeof(struct tc_htb_glob) },
+ [TCA_HTB_CTAB] = { .size = TC_RTAB_SIZE },
+ [TCA_HTB_RTAB] = { .size = TC_RTAB_SIZE },
+ [TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 },
+ [TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 },
+};
+
+static const NLType rtnl_tca_option_data_pie_types[] = {
+ [TCA_PIE_LIMIT] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_qfq_types[] = {
+ [TCA_QFQ_WEIGHT] = { .type = NETLINK_TYPE_U32 },
+ [TCA_QFQ_LMAX] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_sfb_types[] = {
+ [TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) },
+};
+
+static const NLType rtnl_tca_option_data_tbf_types[] = {
+ [TCA_TBF_PARMS] = { .size = sizeof(struct tc_tbf_qopt) },
+ [TCA_TBF_RTAB] = { .size = TC_RTAB_SIZE },
+ [TCA_TBF_PTAB] = { .size = TC_RTAB_SIZE },
+ [TCA_TBF_RATE64] = { .type = NETLINK_TYPE_U64 },
+ [TCA_TBF_PRATE64] = { .type = NETLINK_TYPE_U64 },
+ [TCA_TBF_BURST] = { .type = NETLINK_TYPE_U32 },
+ [TCA_TBF_PBURST] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLTypeSystemUnionElement rtnl_tca_option_data_type_systems[] = {
+ { .name = "cake", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_cake), },
+ { .name = "codel", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_codel), },
+ { .name = "drr", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_drr), },
+ { .name = "ets", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_ets), },
+ { .name = "fq", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_fq), },
+ { .name = "fq_codel", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_fq_codel), },
+ { .name = "fq_pie", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_fq_pie), },
+ { .name = "gred", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_gred), },
+ { .name = "hhf", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_hhf), },
+ { .name = "htb", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_htb), },
+ { .name = "pie", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_pie), },
+ { .name = "qfq", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_qfq), },
+ { .name = "sfb", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_sfb), },
+ { .name = "tbf", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_tbf), },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(rtnl_tca_option_data, TCA_KIND);
+
+static const NLType rtnl_tca_types[] = {
+ [TCA_KIND] = { .type = NETLINK_TYPE_STRING },
+ [TCA_OPTIONS] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_tca_option_data_type_system_union },
+ [TCA_INGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
+ [TCA_EGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_tca);
+
+static const NLType rtnl_mdb_types[] = {
+ [MDBA_SET_ENTRY] = { .size = sizeof(struct br_port_msg) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_mdb);
+
+static const NLType rtnl_types[] = {
+ [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+ [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+ [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+ [RTM_SETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+ [RTM_NEWLINKPROP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+ [RTM_DELLINKPROP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+ [RTM_GETLINKPROP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+ [RTM_NEWADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
+ [RTM_DELADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
+ [RTM_GETADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
+ [RTM_NEWROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
+ [RTM_DELROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
+ [RTM_GETROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
+ [RTM_NEWNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
+ [RTM_DELNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
+ [RTM_GETNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
+ [RTM_NEWADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
+ [RTM_DELADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
+ [RTM_GETADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
+ [RTM_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
+ [RTM_DELRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
+ [RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
+ [RTM_NEWNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
+ [RTM_DELNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
+ [RTM_GETNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
+ [RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_NEWTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_DELTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_GETTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_NEWMDB] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
+ [RTM_DELMDB] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
+ [RTM_GETMDB] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl);
+
+const NLType *rtnl_get_type(uint16_t nlmsg_type) {
+ return type_system_get_type(&rtnl_type_system, nlmsg_type);
+}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <netinet/in.h>
-#include <stdint.h>
-#include <sys/socket.h>
-#include <linux/can/vxcan.h>
#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <linux/genetlink.h>
-#include <linux/ip.h>
-#include <linux/if.h>
-#include <linux/batman_adv.h>
-#include <linux/can/netlink.h>
-#include <linux/fib_rules.h>
-#include <linux/fou.h>
-#include <linux/if_addr.h>
-#include <linux/if_addrlabel.h>
-#include <linux/if_bridge.h>
-#include <linux/if_link.h>
-#include <linux/if_macsec.h>
-#include <linux/if_tunnel.h>
-#include <linux/l2tp.h>
-#include <linux/netfilter/nf_tables.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/nexthop.h>
-#include <linux/nl80211.h>
-#include <linux/pkt_sched.h>
-#include <linux/veth.h>
-#include <linux/wireguard.h>
-#include "sd-netlink.h"
-
-#include "generic-netlink.h"
-#include "hashmap.h"
-#include "macro.h"
+#include "netlink-genl.h"
#include "netlink-internal.h"
-#include "netlink-types.h"
-#include "string-table.h"
-#include "util.h"
-
-/* Maximum ARP IP target defined in kernel */
-#define BOND_MAX_ARP_TARGETS 16
-
-typedef enum {
- BOND_ARP_TARGETS_0,
- BOND_ARP_TARGETS_1,
- BOND_ARP_TARGETS_2,
- BOND_ARP_TARGETS_3,
- BOND_ARP_TARGETS_4,
- BOND_ARP_TARGETS_5,
- BOND_ARP_TARGETS_6,
- BOND_ARP_TARGETS_7,
- BOND_ARP_TARGETS_8,
- BOND_ARP_TARGETS_9,
- BOND_ARP_TARGETS_10,
- BOND_ARP_TARGETS_11,
- BOND_ARP_TARGETS_12,
- BOND_ARP_TARGETS_13,
- BOND_ARP_TARGETS_14,
- BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS,
-} BondArpTargets;
-
-struct NLType {
- uint16_t type;
- size_t size;
- const NLTypeSystem *type_system;
- const NLTypeSystemUnion *type_system_union;
-};
-
-struct NLTypeSystem {
- uint16_t count;
- const NLType *types;
-};
-
-static const NLTypeSystem rtnl_link_type_system;
+#include "netlink-types-internal.h"
static const NLType empty_types[1] = {
/* fake array to avoid .types==NULL, which denotes invalid type-systems */
};
-static const NLTypeSystem empty_type_system = {
- .count = 0,
- .types = empty_types,
-};
-
-static const NLType rtnl_link_info_data_batadv_types[] = {
- [IFLA_BATADV_ALGO_NAME] = { .type = NETLINK_TYPE_STRING, .size = 20 },
-};
-
-static const NLType rtnl_link_info_data_veth_types[] = {
- [VETH_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-};
-
-static const NLType rtnl_link_info_data_vxcan_types[] = {
- [VXCAN_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-};
-
-static const NLType rtnl_link_info_data_ipvlan_types[] = {
- [IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_macvlan_macaddr_types[] = {
- [IFLA_MACVLAN_MACADDR] = { .type = NETLINK_TYPE_ETHER_ADDR },
-};
-
-static const NLTypeSystem rtnl_macvlan_macaddr_type_system = {
- .count = ELEMENTSOF(rtnl_macvlan_macaddr_types),
- .types = rtnl_macvlan_macaddr_types,
-};
-
-static const NLType rtnl_link_info_data_macvlan_types[] = {
- [IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_MACVLAN_MACADDR_MODE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACVLAN_MACADDR_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_macvlan_macaddr_type_system },
- [IFLA_MACVLAN_MACADDR_COUNT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACVLAN_BC_QUEUE_LEN] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = { .type = NETLINK_TYPE_REJECT },
-};
-
-static const NLType rtnl_link_info_data_bridge_types[] = {
- [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_MCAST_IGMP_VERSION] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_vlan_qos_map_types[] = {
- [IFLA_VLAN_QOS_MAPPING] = { .size = sizeof(struct ifla_vlan_qos_mapping) },
-};
-
-static const NLTypeSystem rtnl_vlan_qos_map_type_system = {
- .count = ELEMENTSOF(rtnl_vlan_qos_map_types),
- .types = rtnl_vlan_qos_map_types,
-};
-
-static const NLType rtnl_link_info_data_vlan_types[] = {
- [IFLA_VLAN_ID] = { .type = NETLINK_TYPE_U16 },
- [IFLA_VLAN_FLAGS] = { .size = sizeof(struct ifla_vlan_flags) },
- [IFLA_VLAN_EGRESS_QOS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
- [IFLA_VLAN_INGRESS_QOS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
- [IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_link_info_data_vxlan_types[] = {
- [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_AGEING] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_LIMIT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_PORT_RANGE] = { .type = NETLINK_TYPE_U32},
- [IFLA_VXLAN_PROXY] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_RSC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_L2MISS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_L3MISS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_VXLAN_GROUP6] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_VXLAN_LOCAL6] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_VXLAN_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_REMCSUM_TX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_REMCSUM_RX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_GBP] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_VXLAN_COLLECT_METADATA] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_LABEL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_GPE] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_VXLAN_TTL_INHERIT] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_VXLAN_DF] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_bond_arp_target_types[] = {
- [BOND_ARP_TARGETS_0] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_1] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_2] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_3] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_4] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_5] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_6] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_7] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_8] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_9] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_10] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_11] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_12] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_13] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_14] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_MAX] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_bond_arp_type_system = {
- .count = ELEMENTSOF(rtnl_bond_arp_target_types),
- .types = rtnl_bond_arp_target_types,
-};
-
-static const NLType rtnl_link_info_data_bond_types[] = {
- [IFLA_BOND_MODE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_ACTIVE_SLAVE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_MIIMON] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_UPDELAY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_DOWNDELAY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_USE_CARRIER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_ARP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_ARP_IP_TARGET] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_arp_type_system },
- [IFLA_BOND_ARP_VALIDATE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_PRIMARY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_PRIMARY_RESELECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_FAIL_OVER_MAC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_RESEND_IGMP] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_NUM_PEER_NOTIF] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_ALL_SLAVES_ACTIVE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_MIN_LINKS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_LP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_PACKETS_PER_SLAVE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_AD_LACP_RATE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_AD_SELECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED },
- [IFLA_BOND_AD_ACTOR_SYS_PRIO] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BOND_AD_USER_PORT_KEY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BOND_AD_ACTOR_SYSTEM] = { .type = NETLINK_TYPE_ETHER_ADDR },
- [IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_link_info_data_iptun_types[] = {
- [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_IPTUN_TOS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_IPTUN_PMTUDISC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 },
- [IFLA_IPTUN_6RD_PREFIX] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NETLINK_TYPE_U32 },
- [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPTUN_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPTUN_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_link_info_data_ipgre_types[] = {
- [IFLA_GRE_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_IFLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_OFLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_IKEY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_OKEY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_GRE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_GRE_TTL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_TOS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_PMTUDISC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_FLOWINFO] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_ERSPAN_INDEX] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_ipvti_types[] = {
- [IFLA_VTI_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VTI_IKEY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VTI_OKEY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VTI_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
-};
-
-static const NLType rtnl_link_info_data_ip6tnl_types[] = {
- [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 },
- [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_IPTUN_FLOWINFO] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_vrf_types[] = {
- [IFLA_VRF_TABLE] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_geneve_types[] = {
- [IFLA_GENEVE_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GENEVE_TTL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_TOS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GENEVE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_GENEVE_REMOTE6] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_GENEVE_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_LABEL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GENEVE_TTL_INHERIT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_DF] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_link_info_data_can_types[] = {
- [IFLA_CAN_BITTIMING] = { .size = sizeof(struct can_bittiming) },
- [IFLA_CAN_RESTART_MS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_CAN_CTRLMODE] = { .size = sizeof(struct can_ctrlmode) },
- [IFLA_CAN_TERMINATION] = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_link_info_data_macsec_types[] = {
- [IFLA_MACSEC_SCI] = { .type = NETLINK_TYPE_U64 },
- [IFLA_MACSEC_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_MACSEC_ICV_LEN] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_CIPHER_SUITE] = { .type = NETLINK_TYPE_U64 },
- [IFLA_MACSEC_WINDOW] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACSEC_ENCODING_SA] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_ENCRYPT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_PROTECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_INC_SCI] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_ES] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_SCB] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_VALIDATION] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_link_info_data_xfrm_types[] = {
- [IFLA_XFRM_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_XFRM_IF_ID] = { .type = NETLINK_TYPE_U32 }
-};
-
-static const NLType rtnl_link_info_data_bareudp_types[] = {
- [IFLA_BAREUDP_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BAREUDP_ETHERTYPE] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BAREUDP_SRCPORT_MIN] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BAREUDP_MULTIPROTO_MODE] = { .type = NETLINK_TYPE_FLAG },
-};
-
-/* these strings must match the .kind entries in the kernel */
-static const char* const nl_union_link_info_data_table[] = {
- [NL_UNION_LINK_INFO_DATA_BOND] = "bond",
- [NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge",
- [NL_UNION_LINK_INFO_DATA_VLAN] = "vlan",
- [NL_UNION_LINK_INFO_DATA_VETH] = "veth",
- [NL_UNION_LINK_INFO_DATA_DUMMY] = "dummy",
- [NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan",
- [NL_UNION_LINK_INFO_DATA_MACVTAP] = "macvtap",
- [NL_UNION_LINK_INFO_DATA_IPVLAN] = "ipvlan",
- [NL_UNION_LINK_INFO_DATA_IPVTAP] = "ipvtap",
- [NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan",
- [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip",
- [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = "gre",
- [NL_UNION_LINK_INFO_DATA_ERSPAN] = "erspan",
- [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = "gretap",
- [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = "ip6gre",
- [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = "ip6gretap",
- [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = "sit",
- [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = "vti",
- [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6",
- [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl",
- [NL_UNION_LINK_INFO_DATA_VRF] = "vrf",
- [NL_UNION_LINK_INFO_DATA_VCAN] = "vcan",
- [NL_UNION_LINK_INFO_DATA_GENEVE] = "geneve",
- [NL_UNION_LINK_INFO_DATA_VXCAN] = "vxcan",
- [NL_UNION_LINK_INFO_DATA_WIREGUARD] = "wireguard",
- [NL_UNION_LINK_INFO_DATA_NETDEVSIM] = "netdevsim",
- [NL_UNION_LINK_INFO_DATA_CAN] = "can",
- [NL_UNION_LINK_INFO_DATA_MACSEC] = "macsec",
- [NL_UNION_LINK_INFO_DATA_NLMON] = "nlmon",
- [NL_UNION_LINK_INFO_DATA_XFRM] = "xfrm",
- [NL_UNION_LINK_INFO_DATA_IFB] = "ifb",
- [NL_UNION_LINK_INFO_DATA_BAREUDP] = "bareudp",
- [NL_UNION_LINK_INFO_DATA_BATADV] = "batadv",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
-
-static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
- [NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types),
- .types = rtnl_link_info_data_bond_types },
- [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types),
- .types = rtnl_link_info_data_bridge_types },
- [NL_UNION_LINK_INFO_DATA_VLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types),
- .types = rtnl_link_info_data_vlan_types },
- [NL_UNION_LINK_INFO_DATA_VETH] = { .count = ELEMENTSOF(rtnl_link_info_data_veth_types),
- .types = rtnl_link_info_data_veth_types },
- [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
- .types = rtnl_link_info_data_macvlan_types },
- [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
- .types = rtnl_link_info_data_macvlan_types },
- [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types),
- .types = rtnl_link_info_data_ipvlan_types },
- [NL_UNION_LINK_INFO_DATA_IPVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types),
- .types = rtnl_link_info_data_ipvlan_types },
- [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types),
- .types = rtnl_link_info_data_vxlan_types },
- [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types),
- .types = rtnl_link_info_data_iptun_types },
- [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
- .types = rtnl_link_info_data_ipgre_types },
- [NL_UNION_LINK_INFO_DATA_ERSPAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
- .types = rtnl_link_info_data_ipgre_types },
- [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
- .types = rtnl_link_info_data_ipgre_types },
- [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
- .types = rtnl_link_info_data_ipgre_types },
- [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
- .types = rtnl_link_info_data_ipgre_types },
- [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types),
- .types = rtnl_link_info_data_iptun_types },
- [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types),
- .types = rtnl_link_info_data_ipvti_types },
- [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types),
- .types = rtnl_link_info_data_ipvti_types },
- [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types),
- .types = rtnl_link_info_data_ip6tnl_types },
- [NL_UNION_LINK_INFO_DATA_VRF] = { .count = ELEMENTSOF(rtnl_link_info_data_vrf_types),
- .types = rtnl_link_info_data_vrf_types },
- [NL_UNION_LINK_INFO_DATA_GENEVE] = { .count = ELEMENTSOF(rtnl_link_info_data_geneve_types),
- .types = rtnl_link_info_data_geneve_types },
- [NL_UNION_LINK_INFO_DATA_VXCAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxcan_types),
- .types = rtnl_link_info_data_vxcan_types },
- [NL_UNION_LINK_INFO_DATA_CAN] = { .count = ELEMENTSOF(rtnl_link_info_data_can_types),
- .types = rtnl_link_info_data_can_types },
- [NL_UNION_LINK_INFO_DATA_MACSEC] = { .count = ELEMENTSOF(rtnl_link_info_data_macsec_types),
- .types = rtnl_link_info_data_macsec_types },
- [NL_UNION_LINK_INFO_DATA_XFRM] = { .count = ELEMENTSOF(rtnl_link_info_data_xfrm_types),
- .types = rtnl_link_info_data_xfrm_types },
- [NL_UNION_LINK_INFO_DATA_BAREUDP] = { .count = ELEMENTSOF(rtnl_link_info_data_bareudp_types),
- .types = rtnl_link_info_data_bareudp_types },
- [NL_UNION_LINK_INFO_DATA_BATADV] = { .count = ELEMENTSOF(rtnl_link_info_data_batadv_types),
- .types = rtnl_link_info_data_batadv_types },
-};
-
-static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
- .num = _NL_UNION_LINK_INFO_DATA_MAX,
- .lookup = nl_union_link_info_data_from_string,
- .type_systems = rtnl_link_info_data_type_systems,
- .match_type = NL_MATCH_SIBLING,
- .match = IFLA_INFO_KIND,
-};
-
-static const NLType rtnl_link_info_types[] = {
- [IFLA_INFO_KIND] = { .type = NETLINK_TYPE_STRING },
- [IFLA_INFO_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union },
-/*
- [IFLA_INFO_XSTATS],
- [IFLA_INFO_SLAVE_KIND] = { .type = NETLINK_TYPE_STRING },
- [IFLA_INFO_SLAVE_DATA] = { .type = NETLINK_TYPE_NESTED },
-*/
-};
-
-static const NLTypeSystem rtnl_link_info_type_system = {
- .count = ELEMENTSOF(rtnl_link_info_types),
- .types = rtnl_link_info_types,
-};
-
-static const struct NLType rtnl_prot_info_bridge_port_types[] = {
- [IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_MODE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_GUARD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PROTECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_FAST_LEAVE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_LEARNING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PROXYARP] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_ROOT_ID] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_BRIDGE_ID] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_DESIGNATED_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_DESIGNATED_COST] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_ID] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_NO] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_TOPOLOGY_CHANGE_ACK] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_CONFIG_PENDING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MESSAGE_AGE_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BRPORT_FORWARD_DELAY_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BRPORT_HOLD_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BRPORT_FLUSH] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PAD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MCAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_VLAN_TUNNEL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_BCAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_ISOLATED] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_BACKUP_PORT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_prot_info_type_systems[] = {
- [AF_BRIDGE] = { .count = ELEMENTSOF(rtnl_prot_info_bridge_port_types),
- .types = rtnl_prot_info_bridge_port_types },
-};
-
-static const NLTypeSystemUnion rtnl_prot_info_type_system_union = {
- .num = AF_MAX,
- .type_systems = rtnl_prot_info_type_systems,
- .match_type = NL_MATCH_PROTOCOL,
-};
-
-static const struct NLType rtnl_af_spec_inet6_types[] = {
- [IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 },
-/*
- IFLA_INET6_CONF,
- IFLA_INET6_STATS,
- IFLA_INET6_MCAST,
- IFLA_INET6_CACHEINFO,
- IFLA_INET6_ICMP6STATS,
-*/
- [IFLA_INET6_TOKEN] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_INET6_ADDR_GEN_MODE] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLTypeSystem rtnl_af_spec_inet6_type_system = {
- .count = ELEMENTSOF(rtnl_af_spec_inet6_types),
- .types = rtnl_af_spec_inet6_types,
-};
-
-static const NLType rtnl_af_spec_unspec_types[] = {
- [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system },
-};
-
-static const NLType rtnl_af_spec_bridge_types[] = {
- [IFLA_BRIDGE_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRIDGE_VLAN_INFO] = { .size = sizeof(struct bridge_vlan_info) },
-};
-
-static const NLTypeSystem rtnl_af_spec_type_systems[] = {
- [AF_UNSPEC] = { .count = ELEMENTSOF(rtnl_af_spec_unspec_types),
- .types = rtnl_af_spec_unspec_types },
- [AF_BRIDGE] = { .count = ELEMENTSOF(rtnl_af_spec_bridge_types),
- .types = rtnl_af_spec_bridge_types },
-};
-
-static const NLTypeSystemUnion rtnl_af_spec_type_system_union = {
- .num = AF_MAX,
- .type_systems = rtnl_af_spec_type_systems,
- .match_type = NL_MATCH_PROTOCOL,
-};
-
-static const NLType rtnl_prop_list_types[] = {
- [IFLA_ALT_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
-};
-
-static const NLTypeSystem rtnl_prop_list_type_system = {
- .count = ELEMENTSOF(rtnl_prop_list_types),
- .types = rtnl_prop_list_types,
-};
-
-static const NLType rtnl_vf_vlan_list_types[] = {
- [IFLA_VF_VLAN_INFO] = { .size = sizeof(struct ifla_vf_vlan_info) },
-};
-
-static const NLTypeSystem rtnl_vf_vlan_type_system = {
- .count = ELEMENTSOF(rtnl_vf_vlan_list_types),
- .types = rtnl_vf_vlan_list_types,
-};
-
-static const NLType rtnl_vf_vlan_info_types[] = {
- [IFLA_VF_MAC] = { .size = sizeof(struct ifla_vf_mac) },
- [IFLA_VF_VLAN] = { .size = sizeof(struct ifla_vf_vlan) },
- [IFLA_VF_VLAN_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_type_system},
- [IFLA_VF_TX_RATE] = { .size = sizeof(struct ifla_vf_tx_rate) },
- [IFLA_VF_SPOOFCHK] = { .size = sizeof(struct ifla_vf_spoofchk) },
- [IFLA_VF_RATE] = { .size = sizeof(struct ifla_vf_rate) },
- [IFLA_VF_LINK_STATE] = { .size = sizeof(struct ifla_vf_link_state) },
- [IFLA_VF_RSS_QUERY_EN] = { .size = sizeof(struct ifla_vf_rss_query_en) },
- [IFLA_VF_TRUST] = { .size = sizeof(struct ifla_vf_trust) },
- [IFLA_VF_IB_NODE_GUID] = { .size = sizeof(struct ifla_vf_guid) },
- [IFLA_VF_IB_PORT_GUID] = { .size = sizeof(struct ifla_vf_guid) },
-};
-
-static const NLTypeSystem rtnl_vf_vlan_info_type_system = {
- .count = ELEMENTSOF(rtnl_vf_vlan_info_types),
- .types = rtnl_vf_vlan_info_types,
-};
-
-static const NLType rtnl_link_io_srv_types[] = {
- [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_info_type_system },
-};
-
-static const NLTypeSystem rtnl_io_srv_type_system = {
- .count = ELEMENTSOF(rtnl_link_io_srv_types),
- .types = rtnl_link_io_srv_types,
-};
-
-static const NLType rtnl_link_types[] = {
- [IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR },
- [IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR },
- [IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
- [IFLA_MTU] = { .type = NETLINK_TYPE_U32 },
- [IFLA_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_QDISC] = { .type = NETLINK_TYPE_STRING },
- [IFLA_STATS] = { .size = sizeof(struct rtnl_link_stats) },
-/*
- [IFLA_COST],
- [IFLA_PRIORITY],
-*/
- [IFLA_MASTER] = { .type = NETLINK_TYPE_U32 },
-/*
- [IFLA_WIRELESS],
-*/
- [IFLA_PROTINFO] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union },
- [IFLA_TXQLEN] = { .type = NETLINK_TYPE_U32 },
-/*
- [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) },
-*/
- [IFLA_WEIGHT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_OPERSTATE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_LINKMODE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system },
- [IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 },
- [IFLA_NUM_VF] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VFINFO_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_io_srv_type_system },
- [IFLA_STATS64] = { .size = sizeof(struct rtnl_link_stats64) },
-/*
- [IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED },
- [IFLA_PORT_SELF] = { .type = NETLINK_TYPE_NESTED },
-*/
- [IFLA_AF_SPEC] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_af_spec_type_system_union },
-/*
- [IFLA_VF_PORTS],
- [IFLA_PORT_SELF],
-*/
- [IFLA_GROUP] = { .type = NETLINK_TYPE_U32 },
- [IFLA_NET_NS_FD] = { .type = NETLINK_TYPE_U32 },
- [IFLA_EXT_MASK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_PROMISCUITY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_NUM_TX_QUEUES] = { .type = NETLINK_TYPE_U32 },
- [IFLA_NUM_RX_QUEUES] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GSO_MAX_SEGS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GSO_MAX_SIZE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_CARRIER] = { .type = NETLINK_TYPE_U8 },
-/*
- [IFLA_PHYS_PORT_ID] = { .type = NETLINK_TYPE_BINARY, .len = MAX_PHYS_PORT_ID_LEN },
-*/
- [IFLA_MIN_MTU] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MAX_MTU] = { .type = NETLINK_TYPE_U32 },
- [IFLA_PROP_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_prop_list_type_system },
- [IFLA_ALT_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
-};
-
-static const NLTypeSystem rtnl_link_type_system = {
- .count = ELEMENTSOF(rtnl_link_types),
- .types = rtnl_link_types,
-};
-
-/* IFA_FLAGS was defined in kernel 3.14, but we still support older
- * kernels where IFA_MAX is lower. */
-static const NLType rtnl_address_types[] = {
- [IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
- [IFA_BROADCAST] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFA_ANYCAST] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) },
- [IFA_MULTICAST] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFA_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [IFA_RT_PRIORITY] = { .type = NETLINK_TYPE_U32 },
- [IFA_TARGET_NETNSID] = { .type = NETLINK_TYPE_S32 },
-};
-
-static const NLTypeSystem rtnl_address_type_system = {
- .count = ELEMENTSOF(rtnl_address_types),
- .types = rtnl_address_types,
-};
-
-/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
-
-static const NLType rtnl_route_metrics_types[] = {
- [RTAX_MTU] = { .type = NETLINK_TYPE_U32 },
- [RTAX_WINDOW] = { .type = NETLINK_TYPE_U32 },
- [RTAX_RTT] = { .type = NETLINK_TYPE_U32 },
- [RTAX_RTTVAR] = { .type = NETLINK_TYPE_U32 },
- [RTAX_SSTHRESH] = { .type = NETLINK_TYPE_U32 },
- [RTAX_CWND] = { .type = NETLINK_TYPE_U32 },
- [RTAX_ADVMSS] = { .type = NETLINK_TYPE_U32 },
- [RTAX_REORDERING] = { .type = NETLINK_TYPE_U32 },
- [RTAX_HOPLIMIT] = { .type = NETLINK_TYPE_U32 },
- [RTAX_INITCWND] = { .type = NETLINK_TYPE_U32 },
- [RTAX_FEATURES] = { .type = NETLINK_TYPE_U32 },
- [RTAX_RTO_MIN] = { .type = NETLINK_TYPE_U32 },
- [RTAX_INITRWND] = { .type = NETLINK_TYPE_U32 },
- [RTAX_QUICKACK] = { .type = NETLINK_TYPE_U32 },
- [RTAX_CC_ALGO] = { .type = NETLINK_TYPE_U32 },
- [RTAX_FASTOPEN_NO_COOKIE] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_route_metrics_type_system = {
- .count = ELEMENTSOF(rtnl_route_metrics_types),
- .types = rtnl_route_metrics_types,
-};
-
-static const NLType rtnl_route_types[] = {
- [RTA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
- [RTA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
- [RTA_IIF] = { .type = NETLINK_TYPE_U32 },
- [RTA_OIF] = { .type = NETLINK_TYPE_U32 },
- [RTA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR },
- [RTA_PRIORITY] = { .type = NETLINK_TYPE_U32 },
- [RTA_PREFSRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
- [RTA_METRICS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_metrics_type_system},
- [RTA_MULTIPATH] = { .size = sizeof(struct rtnexthop) },
- [RTA_FLOW] = { .type = NETLINK_TYPE_U32 }, /* 6? */
- [RTA_CACHEINFO] = { .size = sizeof(struct rta_cacheinfo) },
- [RTA_TABLE] = { .type = NETLINK_TYPE_U32 },
- [RTA_MARK] = { .type = NETLINK_TYPE_U32 },
- [RTA_MFC_STATS] = { .type = NETLINK_TYPE_U64 },
- [RTA_VIA] = { /* See struct rtvia */ },
- [RTA_NEWDST] = { .type = NETLINK_TYPE_U32 },
- [RTA_PREF] = { .type = NETLINK_TYPE_U8 },
- [RTA_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
- [RTA_ENCAP] = { .type = NETLINK_TYPE_NESTED }, /* Multiple type systems i.e. LWTUNNEL_ENCAP_MPLS/LWTUNNEL_ENCAP_IP/LWTUNNEL_ENCAP_ILA etc... */
- [RTA_EXPIRES] = { .type = NETLINK_TYPE_U32 },
- [RTA_UID] = { .type = NETLINK_TYPE_U32 },
- [RTA_TTL_PROPAGATE] = { .type = NETLINK_TYPE_U8 },
- [RTA_IP_PROTO] = { .type = NETLINK_TYPE_U8 },
- [RTA_SPORT] = { .type = NETLINK_TYPE_U16 },
- [RTA_DPORT] = { .type = NETLINK_TYPE_U16 },
- [RTA_NH_ID] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_route_type_system = {
- .count = ELEMENTSOF(rtnl_route_types),
- .types = rtnl_route_types,
-};
-
-static const NLType rtnl_neigh_types[] = {
- [NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR },
- [NDA_LLADDR] = { /* struct ether_addr, struct in_addr, or struct in6_addr */ },
- [NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) },
- [NDA_PROBES] = { .type = NETLINK_TYPE_U32 },
- [NDA_VLAN] = { .type = NETLINK_TYPE_U16 },
- [NDA_PORT] = { .type = NETLINK_TYPE_U16 },
- [NDA_VNI] = { .type = NETLINK_TYPE_U32 },
- [NDA_IFINDEX] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_neigh_type_system = {
- .count = ELEMENTSOF(rtnl_neigh_types),
- .types = rtnl_neigh_types,
-};
-
-static const NLType rtnl_addrlabel_types[] = {
- [IFAL_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [IFAL_LABEL] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_addrlabel_type_system = {
- .count = ELEMENTSOF(rtnl_addrlabel_types),
- .types = rtnl_addrlabel_types,
-};
-
-static const NLType rtnl_routing_policy_rule_types[] = {
- [FRA_DST] = { .type = NETLINK_TYPE_IN_ADDR },
- [FRA_SRC] = { .type = NETLINK_TYPE_IN_ADDR },
- [FRA_IIFNAME] = { .type = NETLINK_TYPE_STRING },
- [FRA_GOTO] = { .type = NETLINK_TYPE_U32 },
- [FRA_PRIORITY] = { .type = NETLINK_TYPE_U32 },
- [FRA_FWMARK] = { .type = NETLINK_TYPE_U32 },
- [FRA_FLOW] = { .type = NETLINK_TYPE_U32 },
- [FRA_TUN_ID] = { .type = NETLINK_TYPE_U64 },
- [FRA_SUPPRESS_IFGROUP] = { .type = NETLINK_TYPE_U32 },
- [FRA_SUPPRESS_PREFIXLEN] = { .type = NETLINK_TYPE_U32 },
- [FRA_TABLE] = { .type = NETLINK_TYPE_U32 },
- [FRA_FWMASK] = { .type = NETLINK_TYPE_U32 },
- [FRA_OIFNAME] = { .type = NETLINK_TYPE_STRING },
- [FRA_PAD] = { .type = NETLINK_TYPE_U32 },
- [FRA_L3MDEV] = { .type = NETLINK_TYPE_U8 },
- [FRA_UID_RANGE] = { .size = sizeof(struct fib_rule_uid_range) },
- [FRA_PROTOCOL] = { .type = NETLINK_TYPE_U8 },
- [FRA_IP_PROTO] = { .type = NETLINK_TYPE_U8 },
- [FRA_SPORT_RANGE] = { .size = sizeof(struct fib_rule_port_range) },
- [FRA_DPORT_RANGE] = { .size = sizeof(struct fib_rule_port_range) },
-};
-
-static const NLTypeSystem rtnl_routing_policy_rule_type_system = {
- .count = ELEMENTSOF(rtnl_routing_policy_rule_types),
- .types = rtnl_routing_policy_rule_types,
-};
-
-static const NLType rtnl_nexthop_types[] = {
- [NHA_ID] = { .type = NETLINK_TYPE_U32 },
- [NHA_GROUP] = { /* array of struct nexthop_grp */ },
- [NHA_GROUP_TYPE] = { .type = NETLINK_TYPE_U16 },
- [NHA_BLACKHOLE] = { .type = NETLINK_TYPE_FLAG },
- [NHA_OIF] = { .type = NETLINK_TYPE_U32 },
- [NHA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR },
- [NHA_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
- [NHA_ENCAP] = { .type = NETLINK_TYPE_NESTED },
- [NHA_GROUPS] = { .type = NETLINK_TYPE_FLAG },
- [NHA_MASTER] = { .type = NETLINK_TYPE_U32 },
- [NHA_FDB] = { .type = NETLINK_TYPE_FLAG },
-};
-
-static const NLTypeSystem rtnl_nexthop_type_system = {
- .count = ELEMENTSOF(rtnl_nexthop_types),
- .types = rtnl_nexthop_types,
-};
-
-static const NLType rtnl_tca_option_data_cake_types[] = {
- [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
- [TCA_CAKE_OVERHEAD] = { .type = NETLINK_TYPE_S32 },
- [TCA_CAKE_MPU] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_codel_types[] = {
- [TCA_CODEL_TARGET] = { .type = NETLINK_TYPE_U32 },
- [TCA_CODEL_LIMIT] = { .type = NETLINK_TYPE_U32 },
- [TCA_CODEL_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [TCA_CODEL_ECN] = { .type = NETLINK_TYPE_U32 },
- [TCA_CODEL_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_drr_types[] = {
- [TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_ets_quanta_types[] = {
- [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32, },
-};
-
-static const NLTypeSystem rtnl_tca_option_data_ets_quanta_type_system = {
- .count = ELEMENTSOF(rtnl_tca_option_data_ets_quanta_types),
- .types = rtnl_tca_option_data_ets_quanta_types,
-};
-
-static const NLType rtnl_tca_option_data_ets_prio_types[] = {
- [TCA_ETS_PRIOMAP_BAND] = { .type = NETLINK_TYPE_U8, },
-};
-
-static const NLTypeSystem rtnl_tca_option_data_ets_prio_type_system = {
- .count = ELEMENTSOF(rtnl_tca_option_data_ets_prio_types),
- .types = rtnl_tca_option_data_ets_prio_types,
-};
-
-static const NLType rtnl_tca_option_data_ets_types[] = {
- [TCA_ETS_NBANDS] = { .type = NETLINK_TYPE_U8 },
- [TCA_ETS_NSTRICT] = { .type = NETLINK_TYPE_U8 },
- [TCA_ETS_QUANTA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_quanta_type_system },
- [TCA_ETS_PRIOMAP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_prio_type_system },
- [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_fq_types[] = {
- [TCA_FQ_PLIMIT] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_FLOW_PLIMIT] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_QUANTUM] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_INITIAL_QUANTUM] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_RATE_ENABLE] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_FLOW_DEFAULT_RATE] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_FLOW_MAX_RATE] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_BUCKETS_LOG] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_FLOW_REFILL_DELAY] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_LOW_RATE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_ORPHAN_MASK] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_fq_codel_types[] = {
- [TCA_FQ_CODEL_TARGET] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_LIMIT] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_ECN] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_FLOWS] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_QUANTUM] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_fq_pie_types[] = {
- [TCA_FQ_PIE_LIMIT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_gred_types[] = {
- [TCA_GRED_DPS] = { .size = sizeof(struct tc_gred_sopt) },
-};
-
-static const NLType rtnl_tca_option_data_hhf_types[] = {
- [TCA_HHF_BACKLOG_LIMIT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_htb_types[] = {
- [TCA_HTB_PARMS] = { .size = sizeof(struct tc_htb_opt) },
- [TCA_HTB_INIT] = { .size = sizeof(struct tc_htb_glob) },
- [TCA_HTB_CTAB] = { .size = TC_RTAB_SIZE },
- [TCA_HTB_RTAB] = { .size = TC_RTAB_SIZE },
- [TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 },
- [TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 },
-};
-
-static const NLType rtnl_tca_option_data_pie_types[] = {
- [TCA_PIE_LIMIT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_qfq_types[] = {
- [TCA_QFQ_WEIGHT] = { .type = NETLINK_TYPE_U32 },
- [TCA_QFQ_LMAX] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_sfb_types[] = {
- [TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) },
-};
-
-static const NLType rtnl_tca_option_data_tbf_types[] = {
- [TCA_TBF_PARMS] = { .size = sizeof(struct tc_tbf_qopt) },
- [TCA_TBF_RTAB] = { .size = TC_RTAB_SIZE },
- [TCA_TBF_PTAB] = { .size = TC_RTAB_SIZE },
- [TCA_TBF_RATE64] = { .type = NETLINK_TYPE_U64 },
- [TCA_TBF_PRATE64] = { .type = NETLINK_TYPE_U64 },
- [TCA_TBF_BURST] = { .type = NETLINK_TYPE_U32 },
- [TCA_TBF_PBURST] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const char* const nl_union_tca_option_data_table[] = {
- [NL_UNION_TCA_OPTION_DATA_CAKE] = "cake",
- [NL_UNION_TCA_OPTION_DATA_CODEL] = "codel",
- [NL_UNION_TCA_OPTION_DATA_DRR] = "drr",
- [NL_UNION_TCA_OPTION_DATA_ETS] = "ets",
- [NL_UNION_TCA_OPTION_DATA_FQ] = "fq",
- [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = "fq_codel",
- [NL_UNION_TCA_OPTION_DATA_FQ_PIE] = "fq_pie",
- [NL_UNION_TCA_OPTION_DATA_GRED] = "gred",
- [NL_UNION_TCA_OPTION_DATA_HHF] = "hhf",
- [NL_UNION_TCA_OPTION_DATA_HTB] = "htb",
- [NL_UNION_TCA_OPTION_DATA_PIE] = "pie",
- [NL_UNION_TCA_OPTION_DATA_QFQ] = "qfq",
- [NL_UNION_TCA_OPTION_DATA_SFB] = "sfb",
- [NL_UNION_TCA_OPTION_DATA_TBF] = "tbf",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(nl_union_tca_option_data, NLUnionTCAOptionData);
-
-static const NLTypeSystem rtnl_tca_option_data_type_systems[] = {
- [NL_UNION_TCA_OPTION_DATA_CAKE] = { .count = ELEMENTSOF(rtnl_tca_option_data_cake_types),
- .types = rtnl_tca_option_data_cake_types },
- [NL_UNION_TCA_OPTION_DATA_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_codel_types),
- .types = rtnl_tca_option_data_codel_types },
- [NL_UNION_TCA_OPTION_DATA_DRR] = { .count = ELEMENTSOF(rtnl_tca_option_data_drr_types),
- .types = rtnl_tca_option_data_drr_types },
- [NL_UNION_TCA_OPTION_DATA_ETS] = { .count = ELEMENTSOF(rtnl_tca_option_data_ets_types),
- .types = rtnl_tca_option_data_ets_types },
- [NL_UNION_TCA_OPTION_DATA_FQ] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_types),
- .types = rtnl_tca_option_data_fq_types },
- [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_codel_types),
- .types = rtnl_tca_option_data_fq_codel_types },
- [NL_UNION_TCA_OPTION_DATA_FQ_PIE] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_pie_types),
- .types = rtnl_tca_option_data_fq_pie_types },
- [NL_UNION_TCA_OPTION_DATA_GRED] = { .count = ELEMENTSOF(rtnl_tca_option_data_gred_types),
- .types = rtnl_tca_option_data_gred_types },
- [NL_UNION_TCA_OPTION_DATA_HHF] = { .count = ELEMENTSOF(rtnl_tca_option_data_hhf_types),
- .types = rtnl_tca_option_data_hhf_types },
- [NL_UNION_TCA_OPTION_DATA_HTB] = { .count = ELEMENTSOF(rtnl_tca_option_data_htb_types),
- .types = rtnl_tca_option_data_htb_types },
- [NL_UNION_TCA_OPTION_DATA_PIE] = { .count = ELEMENTSOF(rtnl_tca_option_data_pie_types),
- .types = rtnl_tca_option_data_pie_types },
- [NL_UNION_TCA_OPTION_DATA_QFQ] = { .count = ELEMENTSOF(rtnl_tca_option_data_qfq_types),
- .types = rtnl_tca_option_data_qfq_types },
- [NL_UNION_TCA_OPTION_DATA_SFB] = { .count = ELEMENTSOF(rtnl_tca_option_data_sfb_types),
- .types = rtnl_tca_option_data_sfb_types },
- [NL_UNION_TCA_OPTION_DATA_TBF] = { .count = ELEMENTSOF(rtnl_tca_option_data_tbf_types),
- .types = rtnl_tca_option_data_tbf_types },
-};
-
-static const NLTypeSystemUnion rtnl_tca_option_data_type_system_union = {
- .num = _NL_UNION_TCA_OPTION_DATA_MAX,
- .lookup = nl_union_tca_option_data_from_string,
- .type_systems = rtnl_tca_option_data_type_systems,
- .match_type = NL_MATCH_SIBLING,
- .match = TCA_KIND,
-};
-
-static const NLType rtnl_tca_types[] = {
- [TCA_KIND] = { .type = NETLINK_TYPE_STRING },
- [TCA_OPTIONS] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_tca_option_data_type_system_union },
- [TCA_INGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
- [TCA_EGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_tca_type_system = {
- .count = ELEMENTSOF(rtnl_tca_types),
- .types = rtnl_tca_types,
-};
-
-static const NLType mdb_types[] = {
- [MDBA_SET_ENTRY] = { .size = sizeof(struct br_port_msg) },
-};
-
-static const NLTypeSystem rtnl_mdb_type_system = {
- .count = ELEMENTSOF(mdb_types),
- .types = mdb_types,
-};
+DEFINE_TYPE_SYSTEM(empty);
static const NLType error_types[] = {
[NLMSGERR_ATTR_MSG] = { .type = NETLINK_TYPE_STRING },
[NLMSGERR_ATTR_OFFS] = { .type = NETLINK_TYPE_U32 },
};
-static const NLTypeSystem error_type_system = {
- .count = ELEMENTSOF(error_types),
- .types = error_types,
-};
-
-static const NLType rtnl_types[] = {
- [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
- [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
- [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_SETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_NEWLINKPROP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_DELLINKPROP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_GETLINKPROP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_NEWADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
- [RTM_DELADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
- [RTM_GETADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
- [RTM_NEWROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
- [RTM_DELROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
- [RTM_GETROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
- [RTM_NEWNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
- [RTM_DELNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
- [RTM_GETNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
- [RTM_NEWADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
- [RTM_DELADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
- [RTM_GETADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
- [RTM_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
- [RTM_DELRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
- [RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
- [RTM_NEWNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
- [RTM_DELNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
- [RTM_GETNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
- [RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_NEWTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_DELTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_GETTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_NEWMDB] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
- [RTM_DELMDB] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
- [RTM_GETMDB] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
-};
-
-const NLTypeSystem rtnl_type_system_root = {
- .count = ELEMENTSOF(rtnl_types),
- .types = rtnl_types,
-};
-
-static const NLType genl_wireguard_allowedip_types[] = {
- [WGALLOWEDIP_A_FAMILY] = { .type = NETLINK_TYPE_U16 },
- [WGALLOWEDIP_A_IPADDR] = { .type = NETLINK_TYPE_IN_ADDR },
- [WGALLOWEDIP_A_CIDR_MASK] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLTypeSystem genl_wireguard_allowedip_type_system = {
- .count = ELEMENTSOF(genl_wireguard_allowedip_types),
- .types = genl_wireguard_allowedip_types,
-};
-
-static const NLType genl_wireguard_peer_types[] = {
- [WGPEER_A_PUBLIC_KEY] = { .size = WG_KEY_LEN },
- [WGPEER_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [WGPEER_A_PRESHARED_KEY] = { .size = WG_KEY_LEN },
- [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U16 },
- [WGPEER_A_ENDPOINT] = { .type = NETLINK_TYPE_SOCKADDR },
- [WGPEER_A_ALLOWEDIPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_allowedip_type_system },
-};
-
-static const NLTypeSystem genl_wireguard_peer_type_system = {
- .count = ELEMENTSOF(genl_wireguard_peer_types),
- .types = genl_wireguard_peer_types,
-};
-
-static const NLType genl_wireguard_set_device_types[] = {
- [WGDEVICE_A_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [WGDEVICE_A_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
- [WGDEVICE_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [WGDEVICE_A_PRIVATE_KEY] = { .size = WG_KEY_LEN },
- [WGDEVICE_A_LISTEN_PORT] = { .type = NETLINK_TYPE_U16 },
- [WGDEVICE_A_FWMARK] = { .type = NETLINK_TYPE_U32 },
- [WGDEVICE_A_PEERS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system },
-};
-
-static const NLTypeSystem genl_wireguard_set_device_type_system = {
- .count = ELEMENTSOF(genl_wireguard_set_device_types),
- .types = genl_wireguard_set_device_types,
-};
-
-static const NLType genl_wireguard_cmds[] = {
- [WG_CMD_SET_DEVICE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_set_device_type_system },
-};
-
-static const NLTypeSystem genl_wireguard_type_system = {
- .count = ELEMENTSOF(genl_wireguard_cmds),
- .types = genl_wireguard_cmds,
-};
-
-static const NLType genl_mcast_group_types[] = {
- [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NETLINK_TYPE_STRING },
- [CTRL_ATTR_MCAST_GRP_ID] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem genl_mcast_group_type_system = {
- .count = ELEMENTSOF(genl_mcast_group_types),
- .types = genl_mcast_group_types,
-};
-
-static const NLType genl_get_family_types[] = {
- [CTRL_ATTR_FAMILY_NAME] = { .type = NETLINK_TYPE_STRING },
- [CTRL_ATTR_FAMILY_ID] = { .type = NETLINK_TYPE_U16 },
- [CTRL_ATTR_MCAST_GROUPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_mcast_group_type_system },
-};
-
-static const NLTypeSystem genl_get_family_type_system = {
- .count = ELEMENTSOF(genl_get_family_types),
- .types = genl_get_family_types,
-};
-
-static const NLType genl_ctrl_id_ctrl_cmds[] = {
- [CTRL_CMD_GETFAMILY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system },
-};
-
-static const NLTypeSystem genl_ctrl_id_ctrl_type_system = {
- .count = ELEMENTSOF(genl_ctrl_id_ctrl_cmds),
- .types = genl_ctrl_id_ctrl_cmds,
-};
-
-static const NLType genl_fou_types[] = {
- [FOU_ATTR_PORT] = { .type = NETLINK_TYPE_U16 },
- [FOU_ATTR_AF] = { .type = NETLINK_TYPE_U8 },
- [FOU_ATTR_IPPROTO] = { .type = NETLINK_TYPE_U8 },
- [FOU_ATTR_TYPE] = { .type = NETLINK_TYPE_U8 },
- [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
- [FOU_ATTR_LOCAL_V4] = { .type = NETLINK_TYPE_IN_ADDR },
- [FOU_ATTR_PEER_V4] = { .type = NETLINK_TYPE_IN_ADDR },
- [FOU_ATTR_LOCAL_V6] = { .type = NETLINK_TYPE_IN_ADDR },
- [FOU_ATTR_PEER_V6] = { .type = NETLINK_TYPE_IN_ADDR},
- [FOU_ATTR_PEER_PORT] = { .type = NETLINK_TYPE_U16},
- [FOU_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32},
-};
-
-static const NLTypeSystem genl_fou_type_system = {
- .count = ELEMENTSOF(genl_fou_types),
- .types = genl_fou_types,
-};
-
-static const NLType genl_fou_cmds[] = {
- [FOU_CMD_ADD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
- [FOU_CMD_DEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
- [FOU_CMD_GET] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
-};
-
-static const NLTypeSystem genl_fou_cmds_type_system = {
- .count = ELEMENTSOF(genl_fou_cmds),
- .types = genl_fou_cmds,
-};
-
-static const NLType genl_l2tp_types[] = {
- [L2TP_ATTR_PW_TYPE] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_OFFSET] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_DATA_SEQ] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_L2SPEC_TYPE] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_L2SPEC_LEN] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_PROTO_VERSION] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_IFNAME] = { .type = NETLINK_TYPE_STRING },
- [L2TP_ATTR_CONN_ID] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_PEER_CONN_ID] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_SESSION_ID] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_PEER_SESSION_ID] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_VLAN_ID] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_RECV_SEQ] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_SEND_SEQ] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_LNS_MODE] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_USING_IPSEC] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_FD] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_IP_SADDR] = { .type = NETLINK_TYPE_IN_ADDR },
- [L2TP_ATTR_IP_DADDR] = { .type = NETLINK_TYPE_IN_ADDR },
- [L2TP_ATTR_UDP_SPORT] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_UDP_DPORT] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_IP6_SADDR] = { .type = NETLINK_TYPE_IN_ADDR },
- [L2TP_ATTR_IP6_DADDR] = { .type = NETLINK_TYPE_IN_ADDR },
- [L2TP_ATTR_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_FLAG },
- [L2TP_ATTR_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_FLAG },
-};
-
-static const NLTypeSystem genl_l2tp_type_system = {
- .count = ELEMENTSOF(genl_l2tp_types),
- .types = genl_l2tp_types,
-};
-
-static const NLType genl_l2tp[] = {
- [L2TP_CMD_TUNNEL_CREATE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
- [L2TP_CMD_TUNNEL_DELETE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
- [L2TP_CMD_TUNNEL_MODIFY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
- [L2TP_CMD_TUNNEL_GET] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
- [L2TP_CMD_SESSION_CREATE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
- [L2TP_CMD_SESSION_DELETE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
- [L2TP_CMD_SESSION_MODIFY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
- [L2TP_CMD_SESSION_GET] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
-};
-
-static const NLTypeSystem genl_l2tp_tunnel_session_type_system = {
- .count = ELEMENTSOF(genl_l2tp),
- .types = genl_l2tp,
-};
-
-static const NLType genl_rxsc_types[] = {
- [MACSEC_RXSC_ATTR_SCI] = { .type = NETLINK_TYPE_U64 },
-};
-
-static const NLTypeSystem genl_rxsc_config_type_system = {
- .count = ELEMENTSOF(genl_rxsc_types),
- .types = genl_rxsc_types,
-};
-
-static const NLType genl_macsec_rxsc_types[] = {
- [MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_rxsc_config_type_system },
-};
-
-static const NLTypeSystem genl_macsec_rxsc_type_system = {
- .count = ELEMENTSOF(genl_macsec_rxsc_types),
- .types = genl_macsec_rxsc_types,
-};
-
-static const NLType genl_macsec_sa_config_types[] = {
- [MACSEC_SA_ATTR_AN] = { .type = NETLINK_TYPE_U8 },
- [MACSEC_SA_ATTR_ACTIVE] = { .type = NETLINK_TYPE_U8 },
- [MACSEC_SA_ATTR_PN] = { .type = NETLINK_TYPE_U32 },
- [MACSEC_SA_ATTR_KEYID] = { .size = MACSEC_KEYID_LEN },
- [MACSEC_SA_ATTR_KEY] = { .size = MACSEC_MAX_KEY_LEN },
-};
+DEFINE_TYPE_SYSTEM(error);
-static const NLTypeSystem genl_macsec_sa_config_type_system = {
- .count = ELEMENTSOF(genl_macsec_sa_config_types),
- .types = genl_macsec_sa_config_types,
+static const NLType basic_types[] = {
+ [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system },
+ [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
};
-static const NLType genl_macsec_rxsa_types[] = {
- [MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_config_type_system },
-};
-
-static const NLTypeSystem genl_macsec_rxsa_type_system = {
- .count = ELEMENTSOF(genl_macsec_rxsa_types),
- .types = genl_macsec_rxsa_types,
-};
-
-static const NLType genl_macsec_sa_types[] = {
- [MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_rxsc_config_type_system },
- [MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_config_type_system },
-};
-
-static const NLTypeSystem genl_macsec_sa_type_system = {
- .count = ELEMENTSOF(genl_macsec_sa_types),
- .types = genl_macsec_sa_types,
-};
-
-static const NLType genl_macsec[] = {
- [MACSEC_CMD_ADD_RXSC] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsc_type_system },
- [MACSEC_CMD_ADD_TXSA] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsa_type_system},
- [MACSEC_CMD_ADD_RXSA] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_type_system },
-};
-
-static const NLTypeSystem genl_macsec_device_type_system = {
- .count = ELEMENTSOF(genl_macsec),
- .types = genl_macsec,
-};
-
-static const NLType genl_nl80211_types[] = {
- [NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [NL80211_ATTR_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR },
- [NL80211_ATTR_SSID] = { .type = NETLINK_TYPE_STRING },
- [NL80211_ATTR_IFTYPE] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem genl_nl80211_type_system = {
- .count = ELEMENTSOF(genl_nl80211_types),
- .types = genl_nl80211_types,
-};
-
-static const NLType genl_nl80211_cmds[] = {
- [NL80211_CMD_GET_WIPHY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_SET_WIPHY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_NEW_WIPHY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_DEL_WIPHY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_GET_INTERFACE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_SET_INTERFACE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_NEW_INTERFACE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_DEL_INTERFACE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_GET_STATION] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_SET_STATION] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_NEW_STATION] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
- [NL80211_CMD_DEL_STATION] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-};
-
-static const NLTypeSystem genl_nl80211_cmds_type_system = {
- .count = ELEMENTSOF(genl_nl80211_cmds),
- .types = genl_nl80211_cmds,
-};
-
-static const NLType genl_batadv_types[] = {
- [BATADV_ATTR_VERSION] = { .type = NETLINK_TYPE_STRING },
- [BATADV_ATTR_ALGO_NAME] = { .type = NETLINK_TYPE_STRING },
- [BATADV_ATTR_MESH_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_MESH_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
- [BATADV_ATTR_MESH_ADDRESS] = { .size = ETH_ALEN },
- [BATADV_ATTR_HARD_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_HARD_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
- [BATADV_ATTR_HARD_ADDRESS] = { .size = ETH_ALEN },
- [BATADV_ATTR_ORIG_ADDRESS] = { .size = ETH_ALEN },
- [BATADV_ATTR_TPMETER_RESULT] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_TPMETER_BYTES] = { .type = NETLINK_TYPE_U64 },
- [BATADV_ATTR_TPMETER_COOKIE] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_PAD] = { .type = NETLINK_TYPE_UNSPEC },
- [BATADV_ATTR_ACTIVE] = { .type = NETLINK_TYPE_FLAG },
- [BATADV_ATTR_TT_ADDRESS] = { .size = ETH_ALEN },
- [BATADV_ATTR_TT_TTVN] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_TT_LAST_TTVN] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_TT_CRC32] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_TT_VID] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_TT_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_FLAG_BEST] = { .type = NETLINK_TYPE_FLAG },
- [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_NEIGH_ADDRESS] = { .size = ETH_ALEN },
- [BATADV_ATTR_TQ] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_THROUGHPUT] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_BANDWIDTH_UP] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_ROUTER] = { .size = ETH_ALEN },
- [BATADV_ATTR_BLA_OWN] = { .type = NETLINK_TYPE_FLAG },
- [BATADV_ATTR_BLA_ADDRESS] = { .size = ETH_ALEN },
- [BATADV_ATTR_BLA_VID] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_BLA_BACKBONE] = { .size = ETH_ALEN },
- [BATADV_ATTR_BLA_CRC] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_DAT_CACHE_HWADDRESS] = { .size = ETH_ALEN },
- [BATADV_ATTR_DAT_CACHE_VID] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_MCAST_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_MCAST_FLAGS_PRIV] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_VLANID] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_AGGREGATED_OGMS_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_AP_ISOLATION_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_ISOLATION_MARK] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_ISOLATION_MASK] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_BONDING_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_FRAGMENTATION_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_GW_BANDWIDTH_DOWN] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_GW_BANDWIDTH_UP] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_GW_MODE] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_GW_SEL_CLASS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_HOP_PENALTY] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_LOG_LEVEL] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_MULTICAST_FANOUT] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_NETWORK_CODING_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_ORIG_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_ELP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_THROUGHPUT_OVERRIDE] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem genl_batadv_type_system = {
- .count = ELEMENTSOF(genl_batadv_types),
- .types = genl_batadv_types,
-};
-
-static const NLType genl_batadv_cmds[] = {
- [BATADV_CMD_SET_MESH] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_batadv_type_system },
-};
-
-static const NLTypeSystem genl_batadv_cmds_type_system = {
- .count = ELEMENTSOF(genl_batadv_cmds),
- .types = genl_batadv_cmds,
-};
-
-static const NLType genl_families[] = {
- [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
- [SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system },
- [SD_GENL_FOU] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_cmds_type_system },
- [SD_GENL_L2TP] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_tunnel_session_type_system },
- [SD_GENL_MACSEC] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_device_type_system },
- [SD_GENL_NL80211] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_cmds_type_system },
- [SD_GENL_BATADV] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_batadv_cmds_type_system },
-};
-
-static const NLType nfnl_nft_table_types[] = {
- [NFTA_TABLE_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_TABLE_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_nft_table_type_system = {
- .count = ELEMENTSOF(nfnl_nft_table_types),
- .types = nfnl_nft_table_types,
-};
-
-static const NLType nfnl_nft_chain_hook_types[] = {
- [NFTA_HOOK_HOOKNUM] = { .type = NETLINK_TYPE_U32 },
- [NFTA_HOOK_PRIORITY] = { .type = NETLINK_TYPE_U32 },
- [NFTA_HOOK_DEV] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
-};
-
-static const NLTypeSystem nfnl_nft_chain_hook_type_system = {
- .count = ELEMENTSOF(nfnl_nft_chain_hook_types),
- .types = nfnl_nft_chain_hook_types,
-};
-
-static const NLType nfnl_nft_chain_types[] = {
- [NFTA_CHAIN_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_CHAIN_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_CHAIN_HOOK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_hook_type_system },
- [NFTA_CHAIN_TYPE] = { .type = NETLINK_TYPE_STRING, .size = 16 },
- [NFTA_CHAIN_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_nft_chain_type_system = {
- .count = ELEMENTSOF(nfnl_nft_chain_types),
- .types = nfnl_nft_chain_types,
-};
-
-static const NLType nfnl_nft_expr_meta_types[] = {
- [NFTA_META_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_META_KEY] = { .type = NETLINK_TYPE_U32 },
- [NFTA_META_SREG] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_expr_payload_types[] = {
- [NFTA_PAYLOAD_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_PAYLOAD_BASE] = { .type = NETLINK_TYPE_U32 },
- [NFTA_PAYLOAD_OFFSET] = { .type = NETLINK_TYPE_U32 },
- [NFTA_PAYLOAD_LEN] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_expr_nat_types[] = {
- [NFTA_NAT_TYPE] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_FAMILY] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_REG_ADDR_MIN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_REG_ADDR_MAX] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_data_types[] = {
- [NFTA_DATA_VALUE] = { .type = NETLINK_TYPE_BINARY },
-};
-
-static const NLTypeSystem nfnl_nft_data_type_system = {
- .count = ELEMENTSOF(nfnl_nft_data_types),
- .types = nfnl_nft_data_types,
-};
-
-static const NLType nfnl_nft_expr_bitwise_types[] = {
- [NFTA_BITWISE_SREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_BITWISE_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_BITWISE_LEN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_BITWISE_MASK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
- [NFTA_BITWISE_XOR] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
-};
-
-static const NLType nfnl_nft_expr_cmp_types[] = {
- [NFTA_CMP_SREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_CMP_OP] = { .type = NETLINK_TYPE_U32 },
- [NFTA_CMP_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
-};
-
-static const NLType nfnl_nft_expr_fib_types[] = {
- [NFTA_FIB_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_FIB_RESULT] = { .type = NETLINK_TYPE_U32 },
- [NFTA_FIB_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_expr_lookup_types[] = {
- [NFTA_LOOKUP_SET] = { .type = NETLINK_TYPE_STRING },
- [NFTA_LOOKUP_SREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_LOOKUP_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_LOOKUP_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_expr_masq_types[] = {
- [NFTA_MASQ_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [NFTA_MASQ_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_MASQ_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_expr_data_type_systems[] = {
- [NL_UNION_NFT_EXPR_DATA_BITWISE] = { .count = ELEMENTSOF(nfnl_nft_expr_bitwise_types),
- .types = nfnl_nft_expr_bitwise_types },
- [NL_UNION_NFT_EXPR_DATA_CMP] = { .count = ELEMENTSOF(nfnl_nft_expr_cmp_types),
- .types = nfnl_nft_expr_cmp_types },
- [NL_UNION_NFT_EXPR_DATA_FIB] = { .count = ELEMENTSOF(nfnl_nft_expr_fib_types),
- .types = nfnl_nft_expr_fib_types },
- [NL_UNION_NFT_EXPR_DATA_LOOKUP] = { .count = ELEMENTSOF(nfnl_nft_expr_lookup_types),
- .types = nfnl_nft_expr_lookup_types },
- [NL_UNION_NFT_EXPR_DATA_MASQ] = { .count = ELEMENTSOF(nfnl_nft_expr_masq_types),
- .types = nfnl_nft_expr_masq_types },
- [NL_UNION_NFT_EXPR_DATA_META] = { .count = ELEMENTSOF(nfnl_nft_expr_meta_types),
- .types = nfnl_nft_expr_meta_types },
- [NL_UNION_NFT_EXPR_DATA_NAT] = { .count = ELEMENTSOF(nfnl_nft_expr_nat_types),
- .types = nfnl_nft_expr_nat_types },
- [NL_UNION_NFT_EXPR_DATA_PAYLOAD] = { .count = ELEMENTSOF(nfnl_nft_expr_payload_types),
- .types = nfnl_nft_expr_payload_types },
-};
-
-static const char* const nl_union_nft_expr_data_table[] = {
- [NL_UNION_NFT_EXPR_DATA_BITWISE] = "bitwise",
- [NL_UNION_NFT_EXPR_DATA_CMP] = "cmp",
- [NL_UNION_NFT_EXPR_DATA_LOOKUP] = "lookup",
- [NL_UNION_NFT_EXPR_DATA_META] = "meta",
- [NL_UNION_NFT_EXPR_DATA_FIB] = "fib",
- [NL_UNION_NFT_EXPR_DATA_MASQ] = "masq",
- [NL_UNION_NFT_EXPR_DATA_NAT] = "nat",
- [NL_UNION_NFT_EXPR_DATA_PAYLOAD] = "payload",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(nl_union_nft_expr_data, NLUnionNFTExprData);
-
-static const NLTypeSystemUnion nfnl_nft_data_expr_type_system_union = {
- .num = _NL_UNION_NFT_EXPR_DATA_MAX,
- .lookup = nl_union_nft_expr_data_from_string,
- .type_systems = nfnl_expr_data_type_systems,
- .match_type = NL_MATCH_SIBLING,
- .match = NFTA_EXPR_NAME,
-};
-
-static const NLType nfnl_nft_rule_expr_types[] = {
- [NFTA_EXPR_NAME] = { .type = NETLINK_TYPE_STRING, .size = 16 },
- [NFTA_EXPR_DATA] = { .type = NETLINK_TYPE_UNION,
- .type_system_union = &nfnl_nft_data_expr_type_system_union },
-};
-
-static const NLTypeSystem nfnl_nft_rule_expr_type_system = {
- .count = ELEMENTSOF(nfnl_nft_rule_expr_types),
- .types = nfnl_nft_rule_expr_types,
-};
-
-static const NLType nfnl_nft_rule_types[] = {
- [NFTA_RULE_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_RULE_CHAIN] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_RULE_EXPRESSIONS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_expr_type_system }
-};
-
-static const NLTypeSystem nfnl_nft_rule_type_system = {
- .count = ELEMENTSOF(nfnl_nft_rule_types),
- .types = nfnl_nft_rule_types,
-};
-
-static const NLType nfnl_nft_set_types[] = {
- [NFTA_SET_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_SET_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_SET_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_KEY_TYPE] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_KEY_LEN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_DATA_TYPE] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_DATA_LEN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_POLICY] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_ID] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_nft_set_type_system = {
- .count = ELEMENTSOF(nfnl_nft_set_types),
- .types = nfnl_nft_set_types,
-};
-
-static const NLType nfnl_nft_setelem_types[] = {
- [NFTA_SET_ELEM_KEY] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
- [NFTA_SET_ELEM_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
- [NFTA_SET_ELEM_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_nft_setelem_type_system = {
- .count = ELEMENTSOF(nfnl_nft_setelem_types),
- .types = nfnl_nft_setelem_types,
-};
-
-static const NLType nfnl_nft_setelem_list_types[] = {
- [NFTA_SET_ELEM_LIST_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_SET_ELEM_LIST_SET] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_type_system },
-};
-
-static const NLTypeSystem nfnl_nft_setelem_list_type_system = {
- .count = ELEMENTSOF(nfnl_nft_setelem_list_types),
- .types = nfnl_nft_setelem_list_types,
-};
-
-static const NLType nfnl_nft_msg_types [] = {
- [NFT_MSG_DELTABLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWTABLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWCHAIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWSET] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_set_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_DELSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
-};
-
-static const NLTypeSystem nfnl_nft_msg_type_system = {
- .count = ELEMENTSOF(nfnl_nft_msg_types),
- .types = nfnl_nft_msg_types,
-};
-
-static const NLType nfnl_msg_batch_types [] = {
- [NFNL_BATCH_GENID] = { .type = NETLINK_TYPE_U32 }
-};
-
-static const NLTypeSystem nfnl_msg_batch_type_system = {
- .count = ELEMENTSOF(nfnl_msg_batch_types),
- .types = nfnl_msg_batch_types,
-};
-
-static const NLType nfnl_types[] = {
- [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
- [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
- [NFNL_MSG_BATCH_BEGIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
- [NFNL_MSG_BATCH_END] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
- [NFNL_SUBSYS_NFTABLES] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_msg_type_system, .size = sizeof(struct nfgenmsg) },
-};
-
-const NLTypeSystem nfnl_type_system_root = {
- .count = ELEMENTSOF(nfnl_types),
- .types = nfnl_types,
-};
-
-/* Mainly used when sending message */
-const NLTypeSystem genl_family_type_system_root = {
- .count = ELEMENTSOF(genl_families),
- .types = genl_families,
-};
-
-static const NLType genl_types[] = {
- [SD_GENL_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
- [SD_GENL_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system },
- [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system, .size = sizeof(struct genlmsghdr) },
- [SD_GENL_NL80211] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system, .size = sizeof(struct genlmsghdr) },
-};
-
-/* Mainly used when message received */
-const NLTypeSystem genl_type_system_root = {
- .count = ELEMENTSOF(genl_types),
- .types = genl_types,
-};
+DEFINE_TYPE_SYSTEM(basic);
uint16_t type_get_type(const NLType *type) {
assert(type);
return type->size;
}
-void type_get_type_system(const NLType *nl_type, const NLTypeSystem **ret) {
+const NLTypeSystem *type_get_type_system(const NLType *nl_type) {
assert(nl_type);
- assert(ret);
assert(nl_type->type == NETLINK_TYPE_NESTED);
assert(nl_type->type_system);
-
- *ret = nl_type->type_system;
+ return nl_type->type_system;
}
-void type_get_type_system_union(const NLType *nl_type, const NLTypeSystemUnion **ret) {
+const NLTypeSystemUnion *type_get_type_system_union(const NLType *nl_type) {
assert(nl_type);
- assert(ret);
assert(nl_type->type == NETLINK_TYPE_UNION);
assert(nl_type->type_system_union);
-
- *ret = nl_type->type_system_union;
-}
-
-uint16_t type_system_get_count(const NLTypeSystem *type_system) {
- assert(type_system);
- return type_system->count;
+ return nl_type->type_system_union;
}
-const NLTypeSystem *type_system_get_root(int protocol) {
- switch (protocol) {
- case NETLINK_GENERIC:
- return &genl_type_system_root;
- case NETLINK_NETFILTER:
- return &nfnl_type_system_root;
- default: /* NETLINK_ROUTE: */
- return &rtnl_type_system_root;
- }
-}
+int type_system_root_get_type_system_and_header_size(
+ sd_netlink *nl,
+ uint16_t type,
+ const NLTypeSystem **ret_type_system,
+ size_t *ret_header_size) {
-int type_system_root_get_type(sd_netlink *nl, const NLType **ret, uint16_t type) {
- sd_genl_family_t family;
const NLType *nl_type;
- int r;
-
- if (!nl)
- return type_system_get_type(&rtnl_type_system_root, ret, type);
-
- if (nl->protocol != NETLINK_GENERIC)
- return type_system_get_type(type_system_get_root(nl->protocol), ret, type);
- r = nlmsg_type_to_genl_family(nl, type, &family);
- if (r < 0)
- return r;
+ assert(nl);
- if (family >= genl_type_system_root.count)
+ if (IN_SET(type, NLMSG_DONE, NLMSG_ERROR))
+ nl_type = type_system_get_type(&basic_type_system, type);
+ else
+ switch(nl->protocol) {
+ case NETLINK_ROUTE:
+ nl_type = rtnl_get_type(type);
+ break;
+ case NETLINK_NETFILTER:
+ nl_type = nfnl_get_type(type);
+ break;
+ case NETLINK_GENERIC:
+ return genl_get_type_system_and_header_size(nl, type, ret_type_system, ret_header_size);
+ default:
+ return -EOPNOTSUPP;
+ }
+ if (!nl_type)
return -EOPNOTSUPP;
- nl_type = &genl_type_system_root.types[family];
-
- if (nl_type->type == NETLINK_TYPE_UNSPEC)
+ if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
return -EOPNOTSUPP;
- *ret = nl_type;
-
+ if (ret_type_system)
+ *ret_type_system = type_get_type_system(nl_type);
+ if (ret_header_size)
+ *ret_header_size = type_get_size(nl_type);
return 0;
}
-int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) {
+const NLType *type_system_get_type(const NLTypeSystem *type_system, uint16_t type) {
const NLType *nl_type;
- assert(ret);
assert(type_system);
assert(type_system->types);
if (type >= type_system->count)
- return -EOPNOTSUPP;
+ return NULL;
nl_type = &type_system->types[type];
if (nl_type->type == NETLINK_TYPE_UNSPEC)
- return -EOPNOTSUPP;
-
- *ret = nl_type;
+ return NULL;
- return 0;
+ return nl_type;
}
-int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type) {
+const NLTypeSystem *type_system_get_type_system(const NLTypeSystem *type_system, uint16_t type) {
const NLType *nl_type;
- int r;
- assert(ret);
+ nl_type = type_system_get_type(type_system, type);
+ if (!nl_type)
+ return NULL;
- r = type_system_get_type(type_system, &nl_type, type);
- if (r < 0)
- return r;
-
- type_get_type_system(nl_type, ret);
- return 0;
+ return type_get_type_system(nl_type);
}
-int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type) {
+const NLTypeSystemUnion *type_system_get_type_system_union(const NLTypeSystem *type_system, uint16_t type) {
const NLType *nl_type;
- int r;
- assert(ret);
+ nl_type = type_system_get_type(type_system, type);
+ if (!nl_type)
+ return NULL;
- r = type_system_get_type(type_system, &nl_type, type);
- if (r < 0)
- return r;
+ return type_get_type_system_union(nl_type);
+}
- type_get_type_system_union(nl_type, ret);
- return 0;
+NLMatchType type_system_union_get_match_type(const NLTypeSystemUnion *type_system_union) {
+ assert(type_system_union);
+ return type_system_union->match_type;
}
-int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key) {
- int type;
+uint16_t type_system_union_get_match_attribute(const NLTypeSystemUnion *type_system_union) {
+ assert(type_system_union);
+ assert(type_system_union->match_type == NL_MATCH_SIBLING);
+ return type_system_union->match_attribute;
+}
+const NLTypeSystem *type_system_union_get_type_system_by_string(const NLTypeSystemUnion *type_system_union, const char *key) {
assert(type_system_union);
+ assert(type_system_union->elements);
assert(type_system_union->match_type == NL_MATCH_SIBLING);
- assert(type_system_union->lookup);
- assert(type_system_union->type_systems);
- assert(ret);
assert(key);
- type = type_system_union->lookup(key);
- if (type < 0)
- return -EOPNOTSUPP;
-
- assert(type < type_system_union->num);
-
- *ret = &type_system_union->type_systems[type];
+ for (size_t i = 0; i < type_system_union->count; i++)
+ if (streq(type_system_union->elements[i].name, key))
+ return &type_system_union->elements[i].type_system;
- return 0;
+ return NULL;
}
-int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol) {
- const NLTypeSystem *type_system;
-
+const NLTypeSystem *type_system_union_get_type_system_by_protocol(const NLTypeSystemUnion *type_system_union, uint16_t protocol) {
assert(type_system_union);
- assert(type_system_union->type_systems);
+ assert(type_system_union->elements);
assert(type_system_union->match_type == NL_MATCH_PROTOCOL);
- assert(ret);
-
- if (protocol >= type_system_union->num)
- return -EOPNOTSUPP;
- type_system = &type_system_union->type_systems[protocol];
- if (!type_system->types)
- return -EOPNOTSUPP;
-
- *ret = type_system;
+ for (size_t i = 0; i < type_system_union->count; i++)
+ if (type_system_union->elements[i].protocol == protocol)
+ return &type_system_union->elements[i].type_system;
- return 0;
+ return NULL;
}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include "macro.h"
+#include "sd-netlink.h"
enum {
NETLINK_TYPE_UNSPEC,
typedef struct NLTypeSystem NLTypeSystem;
typedef struct NLType NLType;
-struct NLTypeSystemUnion {
- int num;
- NLMatchType match_type;
- uint16_t match;
- int (*lookup)(const char *);
- const NLTypeSystem *type_systems;
-};
-
-extern const NLTypeSystem genl_family_type_system_root;
+const NLType *rtnl_get_type(uint16_t nlmsg_type);
+const NLType *nfnl_get_type(uint16_t nlmsg_type);
+const NLTypeSystem *genl_get_type_system_by_name(const char *name);
+int genl_get_type_system_and_header_size(
+ sd_netlink *nl,
+ uint16_t id,
+ const NLTypeSystem **ret_type_system,
+ size_t *ret_header_size);
uint16_t type_get_type(const NLType *type);
size_t type_get_size(const NLType *type);
-void type_get_type_system(const NLType *type, const NLTypeSystem **ret);
-void type_get_type_system_union(const NLType *type, const NLTypeSystemUnion **ret);
-
-const NLTypeSystem* type_system_get_root(int protocol);
-uint16_t type_system_get_count(const NLTypeSystem *type_system);
-int type_system_root_get_type(sd_netlink *nl, const NLType **ret, uint16_t type);
-int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type);
-int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type);
-int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type);
-int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key);
-int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol);
-
-typedef enum NLUnionLinkInfoData {
- NL_UNION_LINK_INFO_DATA_BOND,
- NL_UNION_LINK_INFO_DATA_BRIDGE,
- NL_UNION_LINK_INFO_DATA_VLAN,
- NL_UNION_LINK_INFO_DATA_VETH,
- NL_UNION_LINK_INFO_DATA_DUMMY,
- NL_UNION_LINK_INFO_DATA_MACVLAN,
- NL_UNION_LINK_INFO_DATA_MACVTAP,
- NL_UNION_LINK_INFO_DATA_IPVLAN,
- NL_UNION_LINK_INFO_DATA_IPVTAP,
- NL_UNION_LINK_INFO_DATA_VXLAN,
- NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL,
- NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL,
- NL_UNION_LINK_INFO_DATA_ERSPAN,
- NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL,
- NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL,
- NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL,
- NL_UNION_LINK_INFO_DATA_SIT_TUNNEL,
- NL_UNION_LINK_INFO_DATA_VTI_TUNNEL,
- NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL,
- NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL,
- NL_UNION_LINK_INFO_DATA_VRF,
- NL_UNION_LINK_INFO_DATA_VCAN,
- NL_UNION_LINK_INFO_DATA_GENEVE,
- NL_UNION_LINK_INFO_DATA_VXCAN,
- NL_UNION_LINK_INFO_DATA_WIREGUARD,
- NL_UNION_LINK_INFO_DATA_NETDEVSIM,
- NL_UNION_LINK_INFO_DATA_CAN,
- NL_UNION_LINK_INFO_DATA_MACSEC,
- NL_UNION_LINK_INFO_DATA_NLMON,
- NL_UNION_LINK_INFO_DATA_XFRM,
- NL_UNION_LINK_INFO_DATA_IFB,
- NL_UNION_LINK_INFO_DATA_BAREUDP,
- NL_UNION_LINK_INFO_DATA_BATADV,
- _NL_UNION_LINK_INFO_DATA_MAX,
- _NL_UNION_LINK_INFO_DATA_INVALID = -EINVAL,
-} NLUnionLinkInfoData;
-
-const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_;
-NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_;
-
-typedef enum NLUnionTCAOptionData {
- NL_UNION_TCA_OPTION_DATA_CAKE,
- NL_UNION_TCA_OPTION_DATA_CODEL,
- NL_UNION_TCA_OPTION_DATA_DRR,
- NL_UNION_TCA_OPTION_DATA_ETS,
- NL_UNION_TCA_OPTION_DATA_FQ,
- NL_UNION_TCA_OPTION_DATA_FQ_CODEL,
- NL_UNION_TCA_OPTION_DATA_FQ_PIE,
- NL_UNION_TCA_OPTION_DATA_GRED,
- NL_UNION_TCA_OPTION_DATA_HHF,
- NL_UNION_TCA_OPTION_DATA_HTB,
- NL_UNION_TCA_OPTION_DATA_PIE,
- NL_UNION_TCA_OPTION_DATA_QFQ,
- NL_UNION_TCA_OPTION_DATA_SFB,
- NL_UNION_TCA_OPTION_DATA_TBF,
- _NL_UNION_TCA_OPTION_DATA_MAX,
- _NL_UNION_TCA_OPTION_DATA_INVALID = -EINVAL,
-} NLUnionTCAOptionData;
-
-const char *nl_union_tca_option_data_to_string(NLUnionTCAOptionData p) _const_;
-NLUnionTCAOptionData nl_union_tca_option_data_from_string(const char *p) _pure_;
+const NLTypeSystem *type_get_type_system(const NLType *type);
+const NLTypeSystemUnion *type_get_type_system_union(const NLType *type);
-typedef enum NLUnionNFTExprData {
- NL_UNION_NFT_EXPR_DATA_BITWISE,
- NL_UNION_NFT_EXPR_DATA_CMP,
- NL_UNION_NFT_EXPR_DATA_FIB,
- NL_UNION_NFT_EXPR_DATA_LOOKUP,
- NL_UNION_NFT_EXPR_DATA_PAYLOAD,
- NL_UNION_NFT_EXPR_DATA_MASQ,
- NL_UNION_NFT_EXPR_DATA_META,
- NL_UNION_NFT_EXPR_DATA_NAT,
- _NL_UNION_NFT_EXPR_DATA_MAX,
- _NL_UNION_NFT_EXPR_DATA_INVALID = -EINVAL,
-} NLUnionNFTExprData;
+int type_system_root_get_type_system_and_header_size(
+ sd_netlink *nl,
+ uint16_t type,
+ const NLTypeSystem **ret_type_system,
+ size_t *ret_header_size);
-const char *nl_union_nft_expr_data_to_string(NLUnionNFTExprData p) _const_;
-NLUnionNFTExprData nl_union_nft_expr_data_from_string(const char *p) _pure_;
+const NLType *type_system_get_type(const NLTypeSystem *type_system, uint16_t type);
+const NLTypeSystem *type_system_get_type_system(const NLTypeSystem *type_system, uint16_t type);
+const NLTypeSystemUnion *type_system_get_type_system_union(const NLTypeSystem *type_system, uint16_t type);
+NLMatchType type_system_union_get_match_type(const NLTypeSystemUnion *type_system_union);
+uint16_t type_system_union_get_match_attribute(const NLTypeSystemUnion *type_system_union);
+const NLTypeSystem *type_system_union_get_type_system_by_string(const NLTypeSystemUnion *type_system_union, const char *key);
+const NLTypeSystem *type_system_union_get_type_system_by_protocol(const NLTypeSystemUnion *type_system_union, uint16_t protocol);
return 0;
}
-int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
- struct nlmsgerr *err;
- int r;
-
- assert(error <= 0);
-
- r = message_new(rtnl, ret, NLMSG_ERROR);
- if (r < 0)
- return r;
-
- rtnl_message_seal(*ret);
- (*ret)->hdr->nlmsg_seq = serial;
-
- err = NLMSG_DATA((*ret)->hdr);
- err->error = error;
-
- return 0;
-}
-
int rtnl_log_parse_error(int r) {
return log_error_errno(r, "Failed to parse netlink message: %m");
}
int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret);
-int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret);
-uint32_t rtnl_message_get_serial(sd_netlink_message *m);
-void rtnl_message_seal(sd_netlink_message *m);
-
static inline bool rtnl_message_type_is_neigh(uint16_t type) {
return IN_SET(type, RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH);
}
#include "hashmap.h"
#include "io-util.h"
#include "macro.h"
+#include "netlink-genl.h"
#include "netlink-internal.h"
#include "netlink-slot.h"
-#include "netlink-util.h"
#include "process-util.h"
#include "socket-util.h"
#include "string-util.h"
-#include "util.h"
/* Some really high limit, to catch programming errors */
#define REPLY_CALLBACKS_MAX UINT16_MAX
-static int sd_netlink_new(sd_netlink **ret) {
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+static int netlink_new(sd_netlink **ret) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
assert_return(ret, -EINVAL);
- rtnl = new(sd_netlink, 1);
- if (!rtnl)
+ nl = new(sd_netlink, 1);
+ if (!nl)
return -ENOMEM;
- *rtnl = (sd_netlink) {
+ *nl = (sd_netlink) {
.n_ref = 1,
.fd = -1,
.sockaddr.nl.nl_family = AF_NETLINK,
.serial = (uint32_t) (now(CLOCK_MONOTONIC) % UINT32_MAX) + 1,
};
- /* We guarantee that the read buffer has at least space for
- * a message header */
- if (!greedy_realloc((void**)&rtnl->rbuffer, sizeof(struct nlmsghdr), sizeof(uint8_t)))
+ /* We guarantee that the read buffer has at least space for a message header */
+ if (!greedy_realloc((void**) &nl->rbuffer, sizeof(struct nlmsghdr), sizeof(uint8_t)))
return -ENOMEM;
- *ret = TAKE_PTR(rtnl);
-
+ *ret = TAKE_PTR(nl);
return 0;
}
-int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) {
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+int sd_netlink_new_from_fd(sd_netlink **ret, int fd) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
socklen_t addrlen;
int r;
assert_return(ret, -EINVAL);
- r = sd_netlink_new(&rtnl);
+ r = netlink_new(&nl);
if (r < 0)
return r;
- addrlen = sizeof(rtnl->sockaddr);
+ addrlen = sizeof(nl->sockaddr);
- r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
+ r = getsockname(fd, &nl->sockaddr.sa, &addrlen);
if (r < 0)
return -errno;
- if (rtnl->sockaddr.nl.nl_family != AF_NETLINK)
+ if (nl->sockaddr.nl.nl_family != AF_NETLINK)
return -EINVAL;
- rtnl->fd = fd;
-
- *ret = TAKE_PTR(rtnl);
+ nl->fd = fd;
+ *ret = TAKE_PTR(nl);
return 0;
}
-static bool rtnl_pid_changed(const sd_netlink *rtnl) {
- assert(rtnl);
-
- /* We don't support people creating an rtnl connection and
- * keeping it around over a fork(). Let's complain. */
-
- return rtnl->original_pid != getpid_cached();
-}
-
int sd_netlink_open_fd(sd_netlink **ret, int fd) {
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
int r, protocol;
assert_return(ret, -EINVAL);
assert_return(fd >= 0, -EBADF);
- r = sd_netlink_new(&rtnl);
+ r = netlink_new(&nl);
if (r < 0)
return r;
if (r < 0)
return r;
- rtnl->fd = fd;
- rtnl->protocol = protocol;
+ nl->fd = fd;
+ nl->protocol = protocol;
r = setsockopt_int(fd, SOL_NETLINK, NETLINK_EXT_ACK, true);
if (r < 0)
if (r < 0)
log_debug_errno(r, "sd-netlink: Failed to enable NETLINK_GET_STRICT_CHK option, ignoring: %m");
- r = socket_bind(rtnl);
+ r = socket_bind(nl);
if (r < 0) {
- rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */
- rtnl->protocol = -1;
+ nl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */
+ nl->protocol = -1;
return r;
}
- *ret = TAKE_PTR(rtnl);
+ *ret = TAKE_PTR(nl);
return 0;
}
return netlink_open_family(ret, NETLINK_ROUTE);
}
-int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) {
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+bool netlink_pid_changed(sd_netlink *nl) {
+ assert(nl);
- return fd_inc_rcvbuf(rtnl->fd, size);
+ /* We don't support people creating an nl connection and
+ * keeping it around over a fork(). Let's complain. */
+
+ return nl->original_pid != getpid_cached();
}
-static sd_netlink *netlink_free(sd_netlink *rtnl) {
+int sd_netlink_inc_rcvbuf(sd_netlink *nl, size_t size) {
+ assert_return(nl, -EINVAL);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
+
+ return fd_inc_rcvbuf(nl->fd, size);
+}
+
+static sd_netlink *netlink_free(sd_netlink *nl) {
sd_netlink_slot *s;
unsigned i;
- assert(rtnl);
+ assert(nl);
- for (i = 0; i < rtnl->rqueue_size; i++)
- sd_netlink_message_unref(rtnl->rqueue[i]);
- free(rtnl->rqueue);
+ for (i = 0; i < nl->rqueue_size; i++)
+ sd_netlink_message_unref(nl->rqueue[i]);
+ free(nl->rqueue);
- for (i = 0; i < rtnl->rqueue_partial_size; i++)
- sd_netlink_message_unref(rtnl->rqueue_partial[i]);
- free(rtnl->rqueue_partial);
+ for (i = 0; i < nl->rqueue_partial_size; i++)
+ sd_netlink_message_unref(nl->rqueue_partial[i]);
+ free(nl->rqueue_partial);
- free(rtnl->rbuffer);
+ free(nl->rbuffer);
- while ((s = rtnl->slots)) {
+ while ((s = nl->slots)) {
assert(s->floating);
netlink_slot_disconnect(s, true);
}
- hashmap_free(rtnl->reply_callbacks);
- prioq_free(rtnl->reply_callbacks_prioq);
+ hashmap_free(nl->reply_callbacks);
+ prioq_free(nl->reply_callbacks_prioq);
- sd_event_source_unref(rtnl->io_event_source);
- sd_event_source_unref(rtnl->time_event_source);
- sd_event_unref(rtnl->event);
+ sd_event_source_unref(nl->io_event_source);
+ sd_event_source_unref(nl->time_event_source);
+ sd_event_unref(nl->event);
- hashmap_free(rtnl->broadcast_group_refs);
+ hashmap_free(nl->broadcast_group_refs);
- hashmap_free(rtnl->genl_family_to_nlmsg_type);
- hashmap_free(rtnl->nlmsg_type_to_genl_family);
+ genl_clear_family(nl);
- safe_close(rtnl->fd);
- return mfree(rtnl);
+ safe_close(nl->fd);
+ return mfree(nl);
}
DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_netlink, sd_netlink, netlink_free);
-static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) {
+static void netlink_seal_message(sd_netlink *nl, sd_netlink_message *m) {
uint32_t picked;
- assert(rtnl);
- assert(!rtnl_pid_changed(rtnl));
+ assert(nl);
+ assert(!netlink_pid_changed(nl));
assert(m);
assert(m->hdr);
/* Avoid collisions with outstanding requests */
do {
- picked = rtnl->serial;
+ picked = nl->serial;
/* Don't use seq == 0, as that is used for broadcasts, so we would get confused by replies to
such messages */
- rtnl->serial = rtnl->serial == UINT32_MAX ? 1 : rtnl->serial + 1;
+ nl->serial = nl->serial == UINT32_MAX ? 1 : nl->serial + 1;
- } while (hashmap_contains(rtnl->reply_callbacks, UINT32_TO_PTR(picked)));
+ } while (hashmap_contains(nl->reply_callbacks, UINT32_TO_PTR(picked)));
m->hdr->nlmsg_seq = picked;
- rtnl_message_seal(m);
+ message_seal(m);
}
-int sd_netlink_send(sd_netlink *nl,
- sd_netlink_message *message,
- uint32_t *serial) {
+int sd_netlink_send(
+ sd_netlink *nl,
+ sd_netlink_message *message,
+ uint32_t *serial) {
+
int r;
assert_return(nl, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
assert_return(message, -EINVAL);
assert_return(!message->sealed, -EPERM);
- rtnl_seal_message(nl, message);
+ netlink_seal_message(nl, message);
r = socket_write_message(nl, message);
if (r < 0)
return r;
if (serial)
- *serial = rtnl_message_get_serial(message);
+ *serial = message_get_serial(message);
return 1;
}
int r;
assert_return(nl, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
assert_return(messages, -EINVAL);
assert_return(msgcount > 0, -EINVAL);
for (unsigned i = 0; i < msgcount; i++) {
assert_return(!messages[i]->sealed, -EPERM);
- rtnl_seal_message(nl, messages[i]);
+ netlink_seal_message(nl, messages[i]);
if (serials)
- serials[i] = rtnl_message_get_serial(messages[i]);
+ serials[i] = message_get_serial(messages[i]);
}
r = socket_writev_message(nl, messages, msgcount);
return r;
}
-int rtnl_rqueue_make_room(sd_netlink *rtnl) {
- assert(rtnl);
+int netlink_rqueue_make_room(sd_netlink *nl) {
+ assert(nl);
- if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX)
+ if (nl->rqueue_size >= NETLINK_RQUEUE_MAX)
return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
- "rtnl: exhausted the read queue size (%d)",
- RTNL_RQUEUE_MAX);
+ "sd-netlink: exhausted the read queue size (%d)",
+ NETLINK_RQUEUE_MAX);
- if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_size + 1))
+ if (!GREEDY_REALLOC(nl->rqueue, nl->rqueue_size + 1))
return -ENOMEM;
return 0;
}
-int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) {
- assert(rtnl);
+int netlink_rqueue_partial_make_room(sd_netlink *nl) {
+ assert(nl);
- if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX)
+ if (nl->rqueue_partial_size >= NETLINK_RQUEUE_MAX)
return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
- "rtnl: exhausted the partial read queue size (%d)",
- RTNL_RQUEUE_MAX);
+ "sd-netlink: exhausted the partial read queue size (%d)",
+ NETLINK_RQUEUE_MAX);
- if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_size + 1))
+ if (!GREEDY_REALLOC(nl->rqueue_partial, nl->rqueue_partial_size + 1))
return -ENOMEM;
return 0;
}
-static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) {
+static int dispatch_rqueue(sd_netlink *nl, sd_netlink_message **message) {
int r;
- assert(rtnl);
+ assert(nl);
assert(message);
- if (rtnl->rqueue_size <= 0) {
+ if (nl->rqueue_size <= 0) {
/* Try to read a new message */
- r = socket_read_message(rtnl);
+ r = socket_read_message(nl);
if (r == -ENOBUFS) { /* FIXME: ignore buffer overruns for now */
- log_debug_errno(r, "Got ENOBUFS from netlink socket, ignoring.");
+ log_debug_errno(r, "sd-netlink: Got ENOBUFS from netlink socket, ignoring.");
return 1;
}
if (r <= 0)
}
/* Dispatch a queued message */
- *message = rtnl->rqueue[0];
- rtnl->rqueue_size--;
- memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size);
+ *message = nl->rqueue[0];
+ nl->rqueue_size--;
+ memmove(nl->rqueue, nl->rqueue + 1, sizeof(sd_netlink_message*) * nl->rqueue_size);
return 1;
}
-static int process_timeout(sd_netlink *rtnl) {
+static int process_timeout(sd_netlink *nl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
struct reply_callback *c;
sd_netlink_slot *slot;
usec_t n;
int r;
- assert(rtnl);
+ assert(nl);
- c = prioq_peek(rtnl->reply_callbacks_prioq);
+ c = prioq_peek(nl->reply_callbacks_prioq);
if (!c)
return 0;
if (c->timeout > n)
return 0;
- r = rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, c->serial, &m);
+ r = message_new_synthetic_error(nl, -ETIMEDOUT, c->serial, &m);
if (r < 0)
return r;
- assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
+ assert_se(prioq_pop(nl->reply_callbacks_prioq) == c);
c->timeout = 0;
- hashmap_remove(rtnl->reply_callbacks, UINT32_TO_PTR(c->serial));
+ hashmap_remove(nl->reply_callbacks, UINT32_TO_PTR(c->serial));
slot = container_of(c, sd_netlink_slot, reply_callback);
- r = c->callback(rtnl, m, slot->userdata);
+ r = c->callback(nl, m, slot->userdata);
if (r < 0)
log_debug_errno(r, "sd-netlink: timedout callback %s%s%sfailed: %m",
slot->description ? "'" : "",
return 1;
}
-static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
+static int process_reply(sd_netlink *nl, sd_netlink_message *m) {
struct reply_callback *c;
sd_netlink_slot *slot;
uint32_t serial;
uint16_t type;
int r;
- assert(rtnl);
+ assert(nl);
assert(m);
- serial = rtnl_message_get_serial(m);
- c = hashmap_remove(rtnl->reply_callbacks, UINT32_TO_PTR(serial));
+ serial = message_get_serial(m);
+ c = hashmap_remove(nl->reply_callbacks, UINT32_TO_PTR(serial));
if (!c)
return 0;
if (c->timeout != 0) {
- prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
+ prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
c->timeout = 0;
}
slot = container_of(c, sd_netlink_slot, reply_callback);
- r = c->callback(rtnl, m, slot->userdata);
+ r = c->callback(nl, m, slot->userdata);
if (r < 0)
log_debug_errno(r, "sd-netlink: reply callback %s%s%sfailed: %m",
slot->description ? "'" : "",
return 1;
}
-static int process_match(sd_netlink *rtnl, sd_netlink_message *m) {
+static int process_match(sd_netlink *nl, sd_netlink_message *m) {
struct match_callback *c;
- sd_netlink_slot *slot;
uint16_t type;
+ uint8_t cmd;
int r;
- assert(rtnl);
+ assert(nl);
assert(m);
r = sd_netlink_message_get_type(m, &type);
if (r < 0)
return r;
- LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
- if (type != c->type)
+ if (m->protocol == NETLINK_GENERIC) {
+ r = sd_genl_message_get_command(nl, m, &cmd);
+ if (r < 0)
+ return r;
+ } else
+ cmd = 0;
+
+ LIST_FOREACH(match_callbacks, c, nl->match_callbacks) {
+ sd_netlink_slot *slot;
+
+ if (c->type != type)
+ continue;
+ if (c->cmd != 0 && c->cmd != cmd)
continue;
slot = container_of(c, sd_netlink_slot, match_callback);
- r = c->callback(rtnl, m, slot->userdata);
+ r = c->callback(nl, m, slot->userdata);
if (r < 0)
log_debug_errno(r, "sd-netlink: match callback %s%s%sfailed: %m",
slot->description ? "'" : "",
return 1;
}
-static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) {
+static int process_running(sd_netlink *nl, sd_netlink_message **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- assert(rtnl);
+ assert(nl);
- r = process_timeout(rtnl);
+ r = process_timeout(nl);
if (r != 0)
goto null_message;
- r = dispatch_rqueue(rtnl, &m);
+ r = dispatch_rqueue(nl, &m);
if (r < 0)
return r;
if (!m)
goto null_message;
if (sd_netlink_message_is_broadcast(m)) {
- r = process_match(rtnl, m);
+ r = process_match(nl, m);
if (r != 0)
goto null_message;
} else {
- r = process_reply(rtnl, m);
+ r = process_reply(nl, m);
if (r != 0)
goto null_message;
}
return r;
}
-int sd_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) {
- NETLINK_DONT_DESTROY(rtnl);
+int sd_netlink_process(sd_netlink *nl, sd_netlink_message **ret) {
+ NETLINK_DONT_DESTROY(nl);
int r;
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
- assert_return(!rtnl->processing, -EBUSY);
+ assert_return(nl, -EINVAL);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
+ assert_return(!nl->processing, -EBUSY);
- rtnl->processing = true;
- r = process_running(rtnl, ret);
- rtnl->processing = false;
+ nl->processing = true;
+ r = process_running(nl, ret);
+ nl->processing = false;
return r;
}
return 0;
if (usec == 0)
- usec = RTNL_DEFAULT_TIMEOUT;
+ usec = NETLINK_DEFAULT_TIMEOUT_USEC;
return usec_add(now(CLOCK_MONOTONIC), usec);
}
-static int rtnl_poll(sd_netlink *rtnl, bool need_more, usec_t timeout_usec) {
+static int netlink_poll(sd_netlink *nl, bool need_more, usec_t timeout_usec) {
usec_t m = USEC_INFINITY;
int r, e;
- assert(rtnl);
+ assert(nl);
- e = sd_netlink_get_events(rtnl);
+ e = sd_netlink_get_events(nl);
if (e < 0)
return e;
/* Caller wants to process if there is something to
* process, but doesn't care otherwise */
- r = sd_netlink_get_timeout(rtnl, &until);
+ r = sd_netlink_get_timeout(nl, &until);
if (r < 0)
return r;
m = usec_sub_unsigned(until, now(CLOCK_MONOTONIC));
}
- r = fd_wait_for_event(rtnl->fd, e, MIN(m, timeout_usec));
+ r = fd_wait_for_event(nl->fd, e, MIN(m, timeout_usec));
if (r <= 0)
return r;
int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
assert_return(nl, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
if (nl->rqueue_size > 0)
return 0;
- return rtnl_poll(nl, false, timeout_usec);
+ return netlink_poll(nl, false, timeout_usec);
}
static int timeout_compare(const void *a, const void *b) {
assert_return(nl, -EINVAL);
assert_return(m, -EINVAL);
assert_return(callback, -EINVAL);
- assert_return(!rtnl_pid_changed(nl), -ECHILD);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
if (hashmap_size(nl->reply_callbacks) >= REPLY_CALLBACKS_MAX)
return -ERANGE;
}
int sd_netlink_read(
- sd_netlink *rtnl,
+ sd_netlink *nl,
uint32_t serial,
uint64_t usec,
sd_netlink_message **ret) {
usec_t timeout;
int r;
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+ assert_return(nl, -EINVAL);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
timeout = calc_elapse(usec);
for (;;) {
usec_t left;
- for (unsigned i = 0; i < rtnl->rqueue_size; i++) {
+ for (unsigned i = 0; i < nl->rqueue_size; i++) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL;
uint32_t received_serial;
uint16_t type;
- received_serial = rtnl_message_get_serial(rtnl->rqueue[i]);
+ received_serial = message_get_serial(nl->rqueue[i]);
if (received_serial != serial)
continue;
- incoming = rtnl->rqueue[i];
+ incoming = nl->rqueue[i];
/* found a match, remove from rqueue and return it */
- memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
- sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1));
- rtnl->rqueue_size--;
+ memmove(nl->rqueue + i, nl->rqueue + i + 1,
+ sizeof(sd_netlink_message*) * (nl->rqueue_size - i - 1));
+ nl->rqueue_size--;
r = sd_netlink_message_get_errno(incoming);
if (r < 0)
return 1;
}
- r = socket_read_message(rtnl);
+ r = socket_read_message(nl);
if (r < 0)
return r;
if (r > 0)
} else
left = USEC_INFINITY;
- r = rtnl_poll(rtnl, true, left);
+ r = netlink_poll(nl, true, left);
if (r < 0)
return r;
if (r == 0)
}
int sd_netlink_call(
- sd_netlink *rtnl,
+ sd_netlink *nl,
sd_netlink_message *message,
uint64_t usec,
sd_netlink_message **ret) {
uint32_t serial;
int r;
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+ assert_return(nl, -EINVAL);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
assert_return(message, -EINVAL);
- r = sd_netlink_send(rtnl, message, &serial);
+ r = sd_netlink_send(nl, message, &serial);
if (r < 0)
return r;
- return sd_netlink_read(rtnl, serial, usec, ret);
+ return sd_netlink_read(nl, serial, usec, ret);
}
-int sd_netlink_get_events(sd_netlink *rtnl) {
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+int sd_netlink_get_events(sd_netlink *nl) {
+ assert_return(nl, -EINVAL);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
- return rtnl->rqueue_size == 0 ? POLLIN : 0;
+ return nl->rqueue_size == 0 ? POLLIN : 0;
}
-int sd_netlink_get_timeout(sd_netlink *rtnl, uint64_t *timeout_usec) {
+int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
struct reply_callback *c;
- assert_return(rtnl, -EINVAL);
+ assert_return(nl, -EINVAL);
assert_return(timeout_usec, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+ assert_return(!netlink_pid_changed(nl), -ECHILD);
- if (rtnl->rqueue_size > 0) {
+ if (nl->rqueue_size > 0) {
*timeout_usec = 0;
return 1;
}
- c = prioq_peek(rtnl->reply_callbacks_prioq);
+ c = prioq_peek(nl->reply_callbacks_prioq);
if (!c) {
*timeout_usec = UINT64_MAX;
return 0;
}
static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_netlink *rtnl = userdata;
+ sd_netlink *nl = userdata;
int r;
- assert(rtnl);
+ assert(nl);
- r = sd_netlink_process(rtnl, NULL);
+ r = sd_netlink_process(nl, NULL);
if (r < 0)
return r;
}
static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_netlink *rtnl = userdata;
+ sd_netlink *nl = userdata;
int r;
- assert(rtnl);
+ assert(nl);
- r = sd_netlink_process(rtnl, NULL);
+ r = sd_netlink_process(nl, NULL);
if (r < 0)
return r;
}
static int prepare_callback(sd_event_source *s, void *userdata) {
- sd_netlink *rtnl = userdata;
+ sd_netlink *nl = userdata;
int r, e;
usec_t until;
assert(s);
- assert(rtnl);
+ assert(nl);
- e = sd_netlink_get_events(rtnl);
+ e = sd_netlink_get_events(nl);
if (e < 0)
return e;
- r = sd_event_source_set_io_events(rtnl->io_event_source, e);
+ r = sd_event_source_set_io_events(nl->io_event_source, e);
if (r < 0)
return r;
- r = sd_netlink_get_timeout(rtnl, &until);
+ r = sd_netlink_get_timeout(nl, &until);
if (r < 0)
return r;
if (r > 0) {
int j;
- j = sd_event_source_set_time(rtnl->time_event_source, until);
+ j = sd_event_source_set_time(nl->time_event_source, until);
if (j < 0)
return j;
}
- r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
+ r = sd_event_source_set_enabled(nl->time_event_source, r > 0);
if (r < 0)
return r;
return 1;
}
-int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t priority) {
+int sd_netlink_attach_event(sd_netlink *nl, sd_event *event, int64_t priority) {
int r;
- assert_return(rtnl, -EINVAL);
- assert_return(!rtnl->event, -EBUSY);
+ assert_return(nl, -EINVAL);
+ assert_return(!nl->event, -EBUSY);
- assert(!rtnl->io_event_source);
- assert(!rtnl->time_event_source);
+ assert(!nl->io_event_source);
+ assert(!nl->time_event_source);
if (event)
- rtnl->event = sd_event_ref(event);
+ nl->event = sd_event_ref(event);
else {
- r = sd_event_default(&rtnl->event);
+ r = sd_event_default(&nl->event);
if (r < 0)
return r;
}
- r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
+ r = sd_event_add_io(nl->event, &nl->io_event_source, nl->fd, 0, io_callback, nl);
if (r < 0)
goto fail;
- r = sd_event_source_set_priority(rtnl->io_event_source, priority);
+ r = sd_event_source_set_priority(nl->io_event_source, priority);
if (r < 0)
goto fail;
- r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
+ r = sd_event_source_set_description(nl->io_event_source, "netlink-receive-message");
if (r < 0)
goto fail;
- r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
+ r = sd_event_source_set_prepare(nl->io_event_source, prepare_callback);
if (r < 0)
goto fail;
- r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
+ r = sd_event_add_time(nl->event, &nl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, nl);
if (r < 0)
goto fail;
- r = sd_event_source_set_priority(rtnl->time_event_source, priority);
+ r = sd_event_source_set_priority(nl->time_event_source, priority);
if (r < 0)
goto fail;
- r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
+ r = sd_event_source_set_description(nl->time_event_source, "netlink-timer");
if (r < 0)
goto fail;
return 0;
fail:
- sd_netlink_detach_event(rtnl);
+ sd_netlink_detach_event(nl);
return r;
}
-int sd_netlink_detach_event(sd_netlink *rtnl) {
- assert_return(rtnl, -EINVAL);
- assert_return(rtnl->event, -ENXIO);
+int sd_netlink_detach_event(sd_netlink *nl) {
+ assert_return(nl, -EINVAL);
+ assert_return(nl->event, -ENXIO);
- rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
+ nl->io_event_source = sd_event_source_unref(nl->io_event_source);
- rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
+ nl->time_event_source = sd_event_source_unref(nl->time_event_source);
- rtnl->event = sd_event_unref(rtnl->event);
+ nl->event = sd_event_unref(nl->event);
return 0;
}
-int sd_netlink_add_match(
- sd_netlink *rtnl,
+int netlink_add_match_internal(
+ sd_netlink *nl,
sd_netlink_slot **ret_slot,
+ const uint32_t *groups,
+ size_t n_groups,
uint16_t type,
+ uint8_t cmd,
sd_netlink_message_handler_t callback,
sd_netlink_destroy_t destroy_callback,
void *userdata,
const char *description) {
+
_cleanup_free_ sd_netlink_slot *slot = NULL;
int r;
- assert_return(rtnl, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+ assert(groups);
+ assert(n_groups > 0);
+
+ for (size_t i = 0; i < n_groups; i++) {
+ r = socket_broadcast_group_ref(nl, groups[i]);
+ if (r < 0)
+ return r;
+ }
- r = netlink_slot_allocate(rtnl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback), userdata, description, &slot);
+ r = netlink_slot_allocate(nl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback),
+ userdata, description, &slot);
if (r < 0)
return r;
+ slot->match_callback.groups = newdup(uint32_t, groups, n_groups);
+ if (!slot->match_callback.groups)
+ return -ENOMEM;
+
+ slot->match_callback.n_groups = n_groups;
slot->match_callback.callback = callback;
slot->match_callback.type = type;
+ slot->match_callback.cmd = cmd;
+
+ LIST_PREPEND(match_callbacks, nl->match_callbacks, &slot->match_callback);
+
+ /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
+ slot->destroy_callback = destroy_callback;
+
+ if (ret_slot)
+ *ret_slot = slot;
+
+ TAKE_PTR(slot);
+ return 0;
+}
+
+int sd_netlink_add_match(
+ sd_netlink *rtnl,
+ sd_netlink_slot **ret_slot,
+ uint16_t type,
+ sd_netlink_message_handler_t callback,
+ sd_netlink_destroy_t destroy_callback,
+ void *userdata,
+ const char *description) {
+
+ static const uint32_t
+ address_groups[] = { RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, },
+ link_groups[] = { RTNLGRP_LINK, },
+ neighbor_groups[] = { RTNLGRP_NEIGH, },
+ nexthop_groups[] = { RTNLGRP_NEXTHOP, },
+ route_groups[] = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, },
+ rule_groups[] = { RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_RULE, };
+ const uint32_t *groups;
+ size_t n_groups;
+
+ assert_return(rtnl, -EINVAL);
+ assert_return(callback, -EINVAL);
+ assert_return(!netlink_pid_changed(rtnl), -ECHILD);
switch (type) {
case RTM_NEWLINK:
case RTM_DELLINK:
- r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK);
- if (r < 0)
- return r;
-
+ groups = link_groups;
+ n_groups = ELEMENTSOF(link_groups);
break;
case RTM_NEWADDR:
case RTM_DELADDR:
- r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR);
- if (r < 0)
- return r;
-
- r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR);
- if (r < 0)
- return r;
-
+ groups = address_groups;
+ n_groups = ELEMENTSOF(address_groups);
break;
case RTM_NEWNEIGH:
case RTM_DELNEIGH:
- r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEIGH);
- if (r < 0)
- return r;
-
+ groups = neighbor_groups;
+ n_groups = ELEMENTSOF(neighbor_groups);
break;
case RTM_NEWROUTE:
case RTM_DELROUTE:
- r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE);
- if (r < 0)
- return r;
-
- r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE);
- if (r < 0)
- return r;
+ groups = route_groups;
+ n_groups = ELEMENTSOF(route_groups);
break;
case RTM_NEWRULE:
case RTM_DELRULE:
- r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_RULE);
- if (r < 0)
- return r;
-
- r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_RULE);
- if (r < 0)
- return r;
+ groups = rule_groups;
+ n_groups = ELEMENTSOF(rule_groups);
break;
case RTM_NEWNEXTHOP:
case RTM_DELNEXTHOP:
- r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEXTHOP);
- if (r < 0)
- return r;
- break;
-
+ groups = nexthop_groups;
+ n_groups = ELEMENTSOF(nexthop_groups);
+ break;
default:
return -EOPNOTSUPP;
}
- LIST_PREPEND(match_callbacks, rtnl->match_callbacks, &slot->match_callback);
-
- /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
- slot->destroy_callback = destroy_callback;
-
- if (ret_slot)
- *ret_slot = slot;
-
- TAKE_PTR(slot);
-
- return 0;
+ return netlink_add_match_internal(rtnl, ret_slot, groups, n_groups, type, 0, callback,
+ destroy_callback, userdata, description);
}
#include <net/if.h>
#include <netinet/ether.h>
+#include <netinet/in.h>
+#include <linux/fou.h>
#include <linux/genetlink.h>
+#include <linux/if_macsec.h>
+#include <linux/l2tp.h>
+#include <linux/nl80211.h>
#include "sd-netlink.h"
#include "alloc-util.h"
#include "ether-addr-util.h"
#include "macro.h"
+#include "netlink-genl.h"
+#include "netlink-internal.h"
#include "netlink-util.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
-#include "util.h"
+#include "tests.h"
static void test_message_link_bridge(sd_netlink *rtnl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
uint32_t cost;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_NEWLINK, 1) >= 0);
assert_se(sd_rtnl_message_link_set_family(message, AF_BRIDGE) >= 0);
assert_se(sd_netlink_message_open_container(message, IFLA_PROTINFO) >= 0);
assert_se(sd_netlink_message_append_u32(message, IFLA_BRPORT_COST, 10) >= 0);
assert_se(sd_netlink_message_close_container(message) >= 0);
- assert_se(sd_netlink_message_rewind(message, NULL) >= 0);
+ assert_se(sd_netlink_message_rewind(message, rtnl) >= 0);
assert_se(sd_netlink_message_enter_container(message, IFLA_PROTINFO) >= 0);
assert_se(sd_netlink_message_read_u32(message, IFLA_BRPORT_COST, &cost) >= 0);
const char *name_out;
struct ether_addr mac_out;
+ log_debug("/* %s */", __func__);
+
/* we'd really like to test NEWLINK, but let's not mess with the running kernel */
assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, ifindex) >= 0);
uint32_t u32_data;
struct ether_addr eth_data;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
assert_se(m);
struct ifa_cacheinfo cache;
const char *label;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_rtnl_message_new_addr(rtnl, &m, RTM_GETADDR, ifindex, AF_INET) >= 0);
assert_se(m);
assert_se(sd_netlink_message_request_dump(m, true) >= 0);
uint32_t index = 2, u32_data;
int r;
+ log_debug("/* %s */", __func__);
+
r = sd_rtnl_message_new_route(rtnl, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC);
if (r < 0) {
log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
return;
}
- assert_se(sd_netlink_message_rewind(req, NULL) >= 0);
+ assert_se(sd_netlink_message_rewind(req, rtnl) >= 0);
assert_se(sd_netlink_message_read_in_addr(req, RTA_GATEWAY, &addr_data) >= 0);
assert_se(addr_data.s_addr == addr.s_addr);
static void test_multiple(void) {
sd_netlink *rtnl1, *rtnl2;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_netlink_open(&rtnl1) >= 0);
assert_se(sd_netlink_open(&rtnl2) >= 0);
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
char *ifname;
+ log_debug("/* %s */", __func__);
+
ifname = strdup("lo2");
assert_se(ifname);
const char *description;
char *ifname;
+ log_debug("/* %s */", __func__);
+
ifname = strdup("lo");
assert_se(ifname);
const char *description;
char *ifname;
+ log_debug("/* %s */", __func__);
+
ifname = strdup("lo");
assert_se(ifname);
_cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL;
char *ifname;
+ log_debug("/* %s */", __func__);
+
assert_se(t = new(struct test_async_object, 1));
assert_se(ifname = strdup("lo"));
*t = (struct test_async_object) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m1 = NULL, *m2 = NULL;
int counter = 0;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &m1, RTM_GETLINK, ifindex) >= 0);
uint32_t u32_data;
const char *string_data;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0) >= 0);
assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0);
assert_se(sd_netlink_message_close_container(m) >= 0);
assert_se(sd_netlink_message_close_container(m) == -EINVAL);
- assert_se(sd_netlink_message_rewind(m, NULL) >= 0);
+ assert_se(sd_netlink_message_rewind(m, rtnl) >= 0);
assert_se(sd_netlink_message_enter_container(m, IFLA_LINKINFO) >= 0);
assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0);
_cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *s1 = NULL, *s2 = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(sd_netlink_add_match(rtnl, &s1, RTM_NEWLINK, link_handler, NULL, NULL, NULL) >= 0);
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *m;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC) >= 0);
assert_se(sd_netlink_message_request_dump(req, true) >= 0);
assert_se(sd_netlink_call(rtnl, req, 0, &reply) >= 0);
static void test_message(sd_netlink *rtnl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- assert_se(rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, 1, &m) >= 0);
+ log_debug("/* %s */", __func__);
+
+ assert_se(message_new_synthetic_error(rtnl, -ETIMEDOUT, 1, &m) >= 0);
assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT);
}
_cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_genl_socket_open(&genl) >= 0);
- assert_se(sd_genl_message_new(genl, SD_GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &m) >= 0);
+ assert_se(sd_genl_message_new(genl, CTRL_GENL_NAME, CTRL_CMD_GETFAMILY, &m) >= 0);
assert_se(sd_netlink_message_open_container(m, CTRL_ATTR_MCAST_GROUPS) >= 0);
for (unsigned i = 0; i < 10; i++) {
}
assert_se(sd_netlink_message_close_container(m) >= 0);
- rtnl_message_seal(m);
+ message_seal(m);
assert_se(sd_netlink_message_rewind(m, genl) >= 0);
assert_se(sd_netlink_message_enter_container(m, CTRL_ATTR_MCAST_GROUPS) >= 0);
_cleanup_strv_free_ char **names_in = NULL, **names_out;
const char *p;
+ log_debug("/* %s */", __func__);
+
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINKPROP, 1) >= 0);
for (unsigned i = 0; i < 10; i++) {
assert_se(sd_netlink_message_append_strv(m, IFLA_ALT_IFNAME, names_in) >= 0);
assert_se(sd_netlink_message_close_container(m) >= 0);
- rtnl_message_seal(m);
- assert_se(sd_netlink_message_rewind(m, NULL) >= 0);
+ message_seal(m);
+ assert_se(sd_netlink_message_rewind(m, rtnl) >= 0);
assert_se(sd_netlink_message_read_strv(m, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names_out) >= 0);
assert_se(strv_equal(names_in, names_out));
assert_se(sd_netlink_message_exit_container(m) >= 0);
}
+static int genl_ctrl_match_callback(sd_netlink *genl, sd_netlink_message *m, void *userdata) {
+ const char *name;
+ uint16_t id;
+ uint8_t cmd;
+
+ assert(genl);
+ assert(m);
+
+ assert_se(sd_genl_message_get_family_name(genl, m, &name) >= 0);
+ assert_se(streq(name, CTRL_GENL_NAME));
+
+ assert_se(sd_genl_message_get_command(genl, m, &cmd) >= 0);
+
+ switch (cmd) {
+ case CTRL_CMD_NEWFAMILY:
+ case CTRL_CMD_DELFAMILY:
+ assert_se(sd_netlink_message_read_string(m, CTRL_ATTR_FAMILY_NAME, &name) >= 0);
+ assert_se(sd_netlink_message_read_u16(m, CTRL_ATTR_FAMILY_ID, &id) >= 0);
+ log_debug("%s: %s (id=%"PRIu16") family is %s.",
+ __func__, name, id, cmd == CTRL_CMD_NEWFAMILY ? "added" : "removed");
+ break;
+ case CTRL_CMD_NEWMCAST_GRP:
+ case CTRL_CMD_DELMCAST_GRP:
+ assert_se(sd_netlink_message_read_string(m, CTRL_ATTR_FAMILY_NAME, &name) >= 0);
+ assert_se(sd_netlink_message_read_u16(m, CTRL_ATTR_FAMILY_ID, &id) >= 0);
+ log_debug("%s: multicast group for %s (id=%"PRIu16") family is %s.",
+ __func__, name, id, cmd == CTRL_CMD_NEWMCAST_GRP ? "added" : "removed");
+ break;
+ default:
+ log_debug("%s: received nlctrl message with unknown command '%"PRIu8"'.", __func__, cmd);
+ }
+
+ return 0;
+}
+
+static void test_genl(void) {
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ const char *name;
+ uint8_t cmd;
+ int r;
+
+ log_debug("/* %s */", __func__);
+
+ assert_se(sd_genl_socket_open(&genl) >= 0);
+ assert_se(sd_event_default(&event) >= 0);
+ assert_se(sd_netlink_attach_event(genl, event, 0) >= 0);
+
+ assert_se(sd_genl_message_new(genl, CTRL_GENL_NAME, CTRL_CMD_GETFAMILY, &m) >= 0);
+ assert_se(sd_genl_message_get_family_name(genl, m, &name) >= 0);
+ assert_se(streq(name, CTRL_GENL_NAME));
+ assert_se(sd_genl_message_get_command(genl, m, &cmd) >= 0);
+ assert_se(cmd == CTRL_CMD_GETFAMILY);
+
+ assert_se(sd_genl_add_match(genl, NULL, CTRL_GENL_NAME, "notify", 0, genl_ctrl_match_callback, NULL, NULL, "genl-ctrl-notify") >= 0);
+
+ m = sd_netlink_message_unref(m);
+ assert_se(sd_genl_message_new(genl, "should-not-exist", CTRL_CMD_GETFAMILY, &m) < 0);
+ assert_se(sd_genl_message_new(genl, "should-not-exist", CTRL_CMD_GETFAMILY, &m) == -EOPNOTSUPP);
+
+ /* These families may not be supported by kernel. Hence, ignore results. */
+ (void) sd_genl_message_new(genl, FOU_GENL_NAME, 0, &m);
+ m = sd_netlink_message_unref(m);
+ (void) sd_genl_message_new(genl, L2TP_GENL_NAME, 0, &m);
+ m = sd_netlink_message_unref(m);
+ (void) sd_genl_message_new(genl, MACSEC_GENL_NAME, 0, &m);
+ m = sd_netlink_message_unref(m);
+ (void) sd_genl_message_new(genl, NL80211_GENL_NAME, 0, &m);
+
+ for (;;) {
+ r = sd_event_run(event, 500 * USEC_PER_MSEC);
+ assert_se(r >= 0);
+ if (r == 0)
+ return;
+ }
+}
+
int main(void) {
sd_netlink *rtnl;
sd_netlink_message *m;
int if_loopback;
uint16_t type;
+ test_setup_logging(LOG_DEBUG);
+
test_match();
test_multiple();
assert_se((r = sd_netlink_message_unref(r)) == NULL);
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
+ test_genl();
+
return EXIT_SUCCESS;
}
_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();
}
}
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;
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;
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
enable_logind = conf.get('ENABLE_LOGIND') == 1
in_files = [
- ['logind.conf', pkgsysconfdir, enable_logind],
+ ['logind.conf', pkgsysconfdir, enable_logind and install_sysconfdir_samples],
['70-uaccess.rules', udevrulesdir, enable_logind and conf.get('HAVE_ACL') == 1],
['71-seat.rules', udevrulesdir, enable_logind],
['73-seat-late.rules', udevrulesdir, enable_logind],
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_ask_password = false;
+
if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Execution in user context is not supported on non-local systems.");
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;
}
b = BATADV(netdev);
assert(b);
- r = sd_genl_message_new(netdev->manager->genl, SD_GENL_BATADV, BATADV_CMD_SET_MESH, &message);
+ r = sd_genl_message_new(netdev->manager->genl, BATADV_NL_NAME, BATADV_CMD_SET_MESH, &message);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
assert(t);
- r = sd_genl_message_new(netdev->manager->genl, SD_GENL_FOU, FOU_CMD_ADD, &m);
+ r = sd_genl_message_new(netdev->manager->genl, FOU_GENL_NAME, FOU_CMD_ADD, &m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
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;
assert(t);
- r = sd_genl_message_new(netdev->manager->genl, SD_GENL_L2TP, L2TP_CMD_TUNNEL_CREATE, &m);
+ r = sd_genl_message_new(netdev->manager->genl, L2TP_GENL_NAME, L2TP_CMD_TUNNEL_CREATE, &m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
assert(session);
assert(session->tunnel);
- r = sd_genl_message_new(netdev->manager->genl, SD_GENL_L2TP, L2TP_CMD_SESSION_CREATE, &m);
+ r = sd_genl_message_new(netdev->manager->genl, L2TP_GENL_NAME, L2TP_CMD_SESSION_CREATE, &m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
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;
}
assert(netdev);
assert(netdev->ifindex > 0);
- r = sd_genl_message_new(netdev->manager->genl, SD_GENL_MACSEC, command, &m);
+ r = sd_genl_message_new(netdev->manager->genl, MACSEC_GENL_NAME, command, &m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
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;
message = sd_netlink_message_unref(message);
- r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
+ r = sd_genl_message_new(netdev->manager->genl, WG_GENL_NAME, WG_CMD_SET_DEVICE, &message);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
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;
}
+bool address_is_filtered(int family, const union in_addr_union *address, uint8_t prefixlen, Set *allow_list, Set *deny_list) {
+ struct in_addr_prefix *p;
+
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(address);
+
+ if (allow_list) {
+ SET_FOREACH(p, allow_list)
+ if (p->family == family &&
+ p->prefixlen <= prefixlen &&
+ in_addr_prefix_covers(family, &p->address, p->prefixlen, address) > 0)
+ return false;
+
+ return true;
+ }
+
+ SET_FOREACH(p, deny_list)
+ if (p->family == family &&
+ in_addr_prefix_intersect(family, &p->address, p->prefixlen, address, prefixlen) > 0)
+ return true;
+
+ return false;
+}
+
int config_parse_dhcp(
const char* unit,
const char *filename,
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);
}
+
+int config_parse_address_filter(
+ 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) {
+
+ Set **list = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(IN_SET(ltype, AF_INET, AF_INET6));
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ *list = set_free(*list);
+ return 0;
+ }
+
+ for (const char *p = rvalue;;) {
+ _cleanup_free_ char *n = NULL;
+ _cleanup_free_ struct in_addr_prefix *a = NULL;
+ struct in_addr_prefix prefix;
+
+ r = extract_first_word(&p, &n, NULL, 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse NDisc %s=, ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+
+ r = in_addr_prefix_from_string(n, ltype, &prefix.address, &prefix.prefixlen);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "NDisc %s= entry is invalid, ignoring assignment: %s",
+ lvalue, n);
+ continue;
+ }
+
+ prefix.family = ltype;
+ a = newdup(struct in_addr_prefix, &prefix, 1);
+ if (!a)
+ return log_oom();
+
+ r = set_ensure_consume(list, &in_addr_prefix_hash_ops_free, TAKE_PTR(a));
+ if (r < 0)
+ return log_oom();
+ if (r == 0)
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "%s %s= entry is duplicated, ignoring assignment: %s",
+ section, lvalue, n);
+ }
+}
#include "conf-parser.h"
#include "dhcp-identifier.h"
+#include "in-addr-util.h"
+#include "set.h"
#include "time-util.h"
#define DHCP_ROUTE_METRIC 1024
int dhcp_configure_duid(Link *link, const DUID *duid);
int manager_request_product_uuid(Manager *m);
+bool address_is_filtered(int family, const union in_addr_union *address, uint8_t prefixlen, Set *allow_list, Set *deny_list);
+static inline bool in4_address_is_filtered(const struct in_addr *address, Set *allow_list, Set *deny_list) {
+ return address_is_filtered(AF_INET, &(union in_addr_union) { .in = *address }, 32, allow_list, deny_list);
+}
+static inline bool in6_prefix_is_filtered(const struct in6_addr *prefix, uint8_t prefixlen, Set *allow_list, Set *deny_list) {
+ return address_is_filtered(AF_INET6, &(union in_addr_union) { .in6 = *prefix }, prefixlen, allow_list, deny_list);
+}
+
const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_duid_rawdata);
CONFIG_PARSER_PROTOTYPE(config_parse_manager_duid_rawdata);
CONFIG_PARSER_PROTOTYPE(config_parse_network_duid_rawdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_address_filter);
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;
return r;
}
-static int dhcp_server_is_deny_listed(Link *link, sd_dhcp_client *client) {
+static int dhcp_server_is_filtered(Link *link, sd_dhcp_client *client) {
sd_dhcp_lease *lease;
struct in_addr addr;
int r;
if (r < 0)
return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m");
- if (set_contains(link->network->dhcp_deny_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
- log_struct(LOG_DEBUG,
- LOG_LINK_INTERFACE(link),
- LOG_LINK_MESSAGE(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" found in deny-list, ignoring offer",
- IPV4_ADDRESS_FMT_VAL(addr)));
- return true;
- }
-
- return false;
-}
-
-static int dhcp_server_is_allow_listed(Link *link, sd_dhcp_client *client) {
- sd_dhcp_lease *lease;
- struct in_addr addr;
- int r;
-
- assert(link);
- assert(link->network);
- assert(client);
-
- r = sd_dhcp_client_get_lease(client, &lease);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get DHCP lease: %m");
-
- r = sd_dhcp_lease_get_server_identifier(lease, &addr);
- if (r < 0)
- return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m");
+ if (in4_address_is_filtered(&addr, link->network->dhcp_allow_listed_ip, link->network->dhcp_deny_listed_ip)) {
+ if (DEBUG_LOGGING) {
+ if (link->network->dhcp_allow_listed_ip)
+ log_link_debug(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" not found in allow-list, ignoring offer.",
+ IPV4_ADDRESS_FMT_VAL(addr));
+ else
+ log_link_debug(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" found in deny-list, ignoring offer.",
+ IPV4_ADDRESS_FMT_VAL(addr));
+ }
- if (set_contains(link->network->dhcp_allow_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
- log_struct(LOG_DEBUG,
- LOG_LINK_INTERFACE(link),
- LOG_LINK_MESSAGE(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" found in allow-list, accepting offer",
- IPV4_ADDRESS_FMT_VAL(addr)));
return true;
}
}
break;
case SD_DHCP_CLIENT_EVENT_SELECTING:
- if (!set_isempty(link->network->dhcp_allow_listed_ip)) {
- r = dhcp_server_is_allow_listed(link, client);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENOMSG;
- } else {
- r = dhcp_server_is_deny_listed(link, client);
- if (r < 0)
- return r;
- if (r != 0)
- return -ENOMSG;
+ r = dhcp_server_is_filtered(link, client);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
}
+ if (r > 0)
+ return -ENOMSG;
break;
case SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE:
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 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");
- return sd_dhcp_client_start(link->dhcp_client);
+ log_link_debug(link, "Requested configuring of the DHCPv4 client.");
+ return 0;
}
int config_parse_dhcp_max_attempts(
return 0;
}
-int config_parse_dhcp_acl_ip_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) {
-
- Network *network = data;
- Set **acl;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- acl = STR_IN_SET(lvalue, "DenyList", "BlackList") ? &network->dhcp_deny_listed_ip : &network->dhcp_allow_listed_ip;
-
- if (isempty(rvalue)) {
- *acl = set_free(*acl);
- return 0;
- }
-
- for (const char *p = rvalue;;) {
- _cleanup_free_ char *n = NULL;
- union in_addr_union ip;
-
- r = extract_first_word(&p, &n, NULL, 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse DHCP '%s=' IP address, ignoring assignment: %s",
- lvalue, rvalue);
- return 0;
- }
- if (r == 0)
- return 0;
-
- r = in_addr_from_string(AF_INET, n, &ip);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "DHCP '%s=' IP address is invalid, ignoring assignment: %s", lvalue, n);
- continue;
- }
-
- r = set_ensure_put(acl, NULL, UINT32_TO_PTR(ip.in.s_addr));
- if (r < 0)
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to store DHCP '%s=' IP address '%s', ignoring assignment: %m", lvalue, n);
- }
-}
-
int config_parse_dhcp_ip_service_type(
const char *unit,
const char *filename,
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();
}
}
!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 (r < 0)
return log_link_error_errno(link, r, "Failed to get route destination address: %m");
- if ((!set_isempty(link->network->ndisc_allow_listed_route_prefix) &&
- !set_contains(link->network->ndisc_allow_listed_route_prefix, &dst)) ||
- set_contains(link->network->ndisc_deny_listed_route_prefix, &dst)) {
+ r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get route prefix length: %m");
+
+ if (in6_prefix_is_filtered(&dst, prefixlen, link->network->ndisc_allow_listed_route_prefix, link->network->ndisc_deny_listed_route_prefix)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
- (void) in6_addr_to_string(&dst, &buf);
+ (void) in6_addr_prefix_to_string(&dst, prefixlen, &buf);
if (!set_isempty(link->network->ndisc_allow_listed_route_prefix))
log_link_debug(link, "Route prefix '%s' is not in allow list, ignoring", strna(buf));
else
return 0;
}
- r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get route prefix length: %m");
-
r = sd_ndisc_router_route_get_preference(rt, &preference);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get default router preference from RA: %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)
switch (type) {
case SD_NDISC_OPTION_PREFIX_INFORMATION: {
+ unsigned prefixlen;
struct in6_addr a;
uint8_t flags;
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix address: %m");
- if ((!set_isempty(link->network->ndisc_allow_listed_prefix) &&
- !set_contains(link->network->ndisc_allow_listed_prefix, &a)) ||
- set_contains(link->network->ndisc_deny_listed_prefix, &a)) {
+ r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix length: %m");
+
+ if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *b = NULL;
- (void) in6_addr_to_string(&a, &b);
+ (void) in6_addr_prefix_to_string(&a, prefixlen, &b);
if (!set_isempty(link->network->ndisc_allow_listed_prefix))
log_link_debug(link, "Prefix '%s' is not in allow list, ignoring", strna(b));
else
if (r < 0)
return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
- if ((!set_isempty(link->network->ndisc_allow_listed_router) &&
- !set_contains(link->network->ndisc_allow_listed_router, &router)) ||
- set_contains(link->network->ndisc_deny_listed_router, &router)) {
+ if (in6_prefix_is_filtered(&router, 128, link->network->ndisc_allow_listed_router, link->network->ndisc_deny_listed_router)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
}
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);
ipv6_token_compare_func,
free);
-int config_parse_ndisc_address_filter(
- 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) {
-
- Set **list = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (isempty(rvalue)) {
- *list = set_free_free(*list);
- return 0;
- }
-
- for (const char *p = rvalue;;) {
- _cleanup_free_ char *n = NULL;
- _cleanup_free_ struct in6_addr *a = NULL;
- union in_addr_union ip;
-
- r = extract_first_word(&p, &n, NULL, 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse NDisc %s=, ignoring assignment: %s",
- lvalue, rvalue);
- return 0;
- }
- if (r == 0)
- return 0;
-
- r = in_addr_from_string(AF_INET6, n, &ip);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "NDisc %s= entry is invalid, ignoring assignment: %s",
- lvalue, n);
- continue;
- }
-
- a = newdup(struct in6_addr, &ip.in6, 1);
- if (!a)
- return log_oom();
-
- r = set_ensure_consume(list, &in6_addr_hash_ops, TAKE_PTR(a));
- if (r < 0)
- return log_oom();
- if (r == 0)
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "NDisc %s= entry is duplicated, ignoring assignment: %s",
- lvalue, n);
- }
-}
-
int config_parse_address_generation_type(
const char *unit,
const char *filename,
#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));
}
void ndisc_vacuum(Link *link);
void ndisc_flush(Link *link);
-CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_address_filter);
CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_use_domains);
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)
DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release)
DHCPv4.SendDecline, config_parse_bool, 0, offsetof(Network, dhcp_send_decline)
-DHCPv4.DenyList, config_parse_dhcp_acl_ip_address, 0, 0
-DHCPv4.AllowList, config_parse_dhcp_acl_ip_address, 0, 0
+DHCPv4.DenyList, config_parse_address_filter, AF_INET, offsetof(Network, dhcp_deny_listed_ip)
+DHCPv4.AllowList, config_parse_address_filter, AF_INET, offsetof(Network, dhcp_allow_listed_ip)
DHCPv4.IPServiceType, config_parse_dhcp_ip_service_type, 0, offsetof(Network, dhcp_ip_service_type)
DHCPv4.SendOption, config_parse_dhcp_send_option, AF_INET, offsetof(Network, dhcp_client_send_options)
DHCPv4.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_vendor_options)
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.RouterAllowList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_allow_listed_router)
-IPv6AcceptRA.RouterDenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_router)
-IPv6AcceptRA.PrefixAllowList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_allow_listed_prefix)
-IPv6AcceptRA.PrefixDenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_prefix)
-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)
+IPv6AcceptRA.RouterAllowList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_allow_listed_router)
+IPv6AcceptRA.RouterDenyList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_deny_listed_router)
+IPv6AcceptRA.PrefixAllowList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_allow_listed_prefix)
+IPv6AcceptRA.PrefixDenyList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix)
+IPv6AcceptRA.RouteAllowList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_allow_listed_route_prefix)
+IPv6AcceptRA.RouteDenyList, config_parse_address_filter, AF_INET6, 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
IPv6PrefixDelegation.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains)
IPv6PrefixDelegation.Domains, config_parse_radv_search_domains, 0, 0
IPv6PrefixDelegation.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec)
-DHCPv4.BlackList, config_parse_dhcp_acl_ip_address, 0, 0
+DHCPv4.BlackList, config_parse_address_filter, AF_INET, offsetof(Network, dhcp_deny_listed_ip)
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
DHCP.UseDNS, config_parse_dhcp_use_dns, 0, 0
DHCP.UseNTP, config_parse_dhcp_use_ntp, 0, 0
DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, 0
DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
DHCPv6.RouteMetric, config_parse_dhcp_route_metric, 0, 0
-IPv6AcceptRA.DenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_prefix)
-IPv6AcceptRA.BlackList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_prefix)
+IPv6AcceptRA.DenyList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix)
+IPv6AcceptRA.BlackList, config_parse_address_filter, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix)
TrafficControlQueueingDiscipline.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0
TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec, config_parse_network_emulator_delay, 0, 0
TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_network_emulator_delay, 0, 0
#include "hostname-util.h"
#include "in-addr-util.h"
#include "net-condition.h"
+#include "netdev/macvlan.h"
#include "networkd-address-label.h"
#include "networkd-address.h"
#include "networkd-bridge-fdb.h"
network->routes_by_section = hashmap_free_with_destructor(network->routes_by_section, route_free);
}
- if (network->link_local < 0)
- network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
+ if (network->link_local < 0) {
+ network->link_local = ADDRESS_FAMILY_IPV6;
+
+ if (network->bridge)
+ network->link_local = ADDRESS_FAMILY_NO;
+ else {
+ NetDev *netdev;
+
+ HASHMAP_FOREACH(netdev, network->stacked_netdevs) {
+ MacVlan *m;
+
+ if (netdev->kind == NETDEV_KIND_MACVLAN)
+ m = MACVLAN(netdev);
+ else if (netdev->kind == NETDEV_KIND_MACVTAP)
+ m = MACVTAP(netdev);
+ else
+ continue;
+
+ if (m->mode == NETDEV_MACVLAN_MODE_PASSTHRU)
+ network->link_local = ADDRESS_FAMILY_NO;
+
+ /* There won't be a passthru MACVLAN/MACVTAP if there's already one in another mode */
+ break;
+ }
+ }
+ }
+
if (network->ipv6ll_address_gen_mode == IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE)
SET_FLAG(network->link_local, ADDRESS_FAMILY_IPV6, false);
.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);
ordered_set_free(network->router_search_domains);
free(network->router_dns);
- set_free_free(network->ndisc_deny_listed_router);
- set_free_free(network->ndisc_allow_listed_router);
- set_free_free(network->ndisc_deny_listed_prefix);
- set_free_free(network->ndisc_allow_listed_prefix);
- set_free_free(network->ndisc_deny_listed_route_prefix);
- set_free_free(network->ndisc_allow_listed_route_prefix);
+ set_free(network->ndisc_deny_listed_router);
+ set_free(network->ndisc_allow_listed_router);
+ set_free(network->ndisc_deny_listed_prefix);
+ set_free(network->ndisc_allow_listed_prefix);
+ set_free(network->ndisc_deny_listed_route_prefix);
+ set_free(network->ndisc_allow_listed_route_prefix);
free(network->batadv_name);
free(network->bridge_name);
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);
}
}
- if (nexthop->onlink <= 0 &&
- in_addr_is_set(nexthop->family, &nexthop->gw) &&
- !manager_address_is_reachable(link->manager, nexthop->family, &nexthop->gw))
- return false;
-
- return true;
+ return gateway_is_ready(link, nexthop->onlink, nexthop->family, &nexthop->gw);
}
int request_process_nexthop(Request *req) {
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;
if (a->family != family)
return false;
+ if (!address_is_ready(a))
+ return false;
if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE))
return false;
if (in_addr_is_set(a->family, &a->in_addr_peer))
FAMILY_ADDRESS_SIZE(family) * 8) > 0;
}
-bool manager_address_is_reachable(Manager *manager, int family, const union in_addr_union *address) {
- Link *link;
+static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) {
+ Route *route;
- assert(manager);
+ assert(link);
+ assert(link->manager);
assert(IN_SET(family, AF_INET, AF_INET6));
assert(address);
- HASHMAP_FOREACH(link, manager->links_by_index) {
- Route *route;
- SET_FOREACH(route, link->routes)
- if (route_address_is_reachable(route, family, address))
- return true;
- SET_FOREACH(route, link->routes_foreign)
- if (route_address_is_reachable(route, family, address))
- return true;
- }
+ SET_FOREACH(route, link->routes)
+ if (route_address_is_reachable(route, family, address))
+ return true;
+ SET_FOREACH(route, link->routes_foreign)
+ if (route_address_is_reachable(route, family, address))
+ return true;
/* If we do not manage foreign routes, then there may exist a prefix route we do not know,
* which was created on configuring an address. Hence, also check the addresses. */
- if (!manager->manage_foreign_routes)
- HASHMAP_FOREACH(link, manager->links_by_index) {
- Address *a;
-
- SET_FOREACH(a, link->addresses)
- if (prefix_route_address_is_reachable(a, family, address))
- return true;
- SET_FOREACH(a, link->addresses_foreign)
- if (prefix_route_address_is_reachable(a, family, address))
- return true;
- }
+ if (!link->manager->manage_foreign_routes) {
+ Address *a;
+
+ SET_FOREACH(a, link->addresses)
+ if (prefix_route_address_is_reachable(a, family, address))
+ return true;
+ SET_FOREACH(a, link->addresses_foreign)
+ if (prefix_route_address_is_reachable(a, family, address))
+ return true;
+ }
return false;
}
return 0;
}
+bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_union *gw) {
+ assert(link);
+ assert(gw);
+
+ if (onlink > 0)
+ return true;
+
+ if (!in_addr_is_set(family, gw))
+ return true;
+
+ if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6))
+ return true;
+
+ return link_address_is_reachable(link, family, gw);
+}
+
static int route_is_ready_to_configure(const Route *route, Link *link) {
MultipathRoute *m;
NextHop *nh = NULL;
return r;
}
- if (route->gateway_onlink <= 0 &&
- in_addr_is_set(route->gw_family, &route->gw) > 0 &&
- !manager_address_is_reachable(link->manager, route->gw_family, &route->gw))
+ if (!gateway_is_ready(link, route->gateway_onlink, route->gw_family, &route->gw))
return false;
ORDERED_SET_FOREACH(m, route->multipath_routes) {
union in_addr_union a = m->gateway.address;
Link *l = NULL;
- if (route->gateway_onlink <= 0 &&
- !manager_address_is_reachable(link->manager, m->gateway.family, &a))
- return false;
-
if (m->ifname) {
if (link_get_by_name(link->manager, m->ifname, &l) < 0)
return false;
}
if (l && !link_is_ready_to_configure(l, true))
return false;
+
+ if (!gateway_is_ready(l ?: link, route->gateway_onlink, m->gateway.family, &a))
+ return false;
}
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);
int route_remove(const Route *route, Manager *manager, Link *link);
int link_has_route(Link *link, const Route *route);
-bool manager_address_is_reachable(Manager *manager, int family, const union in_addr_union *address);
int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret);
+bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_union *gw);
int link_drop_routes(Link *link);
int link_drop_foreign_routes(Link *link);
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();
}
}
#include "lldp-internal.h"
#include "macvlan.h"
#include "ndisc-internal.h"
-#include "netlink-internal.h"
#include "networkd-link.h"
#include "networkd-network.h"
#include "networkd-util.h"
/* test_table(link_state, LINK_STATE); — not a reversible mapping */
test_table(lldp_mode, LLDP_MODE);
test_table(netdev_kind, NETDEV_KIND);
- test_table(nl_union_link_info_data, NL_UNION_LINK_INFO_DATA);
test_table(radv_prefix_delegation, RADV_PREFIX_DELEGATION);
test_table(lldp_event, SD_LLDP_EVENT);
test_table(ndisc_event, SD_NDISC_EVENT);
assert_cc(sizeof(sd_lldp_event_t) == sizeof(int64_t));
assert_cc(sizeof(sd_ndisc_event_t) == sizeof(int64_t));
assert_cc(sizeof(sd_dhcp_lease_server_type_t) == sizeof(int64_t));
- assert_cc(sizeof(sd_genl_family_t) == sizeof(int64_t));
return EXIT_SUCCESS;
}
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;
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();
}
+ /* If we are talking to the per-user instance PolicyKit isn't going to help */
+ if (arg_user)
+ arg_ask_password = false;
+
with_trigger = !!arg_path_property || !!arg_socket_property || arg_with_timer;
/* currently, only single trigger (path, socket, timer) unit can be created simultaneously */
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 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(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);
+ 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;
- }
- if (k < 1) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Invalid %s= value, ignoring: %s", lvalue, rvalue);
+
+ 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;
- }
- 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;
- }
+ ecmd.cmd = ETHTOOL_SCOALESCE;
+ r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
+ if (r < 0)
+ return -errno;
return 0;
}
}
}
-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;
+}
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);
* stdin around. */
fd = open("/dev/tty", O_WRONLY);
if (fd < 0) {
- log_error_errno(errno, "Failed to open /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
+ if (errno != -ENXIO) {
+ log_error_errno(errno, "Failed to open /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
- if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
+ /* If we get ENXIO here we have no controlling TTY even though stdout/stderr are
+ * connected to a TTY. That's a weird setup, but let's handle it gracefully: let's
+ * skip the forking of the agents, given the TTY setup is not in order. */
+ } else {
+ if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
+ log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
- if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
+ if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
+ log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
- safe_close_above_stdio(fd);
+ fd = safe_close_above_stdio(fd);
+ }
}
(void) rlimit_nofile_safe();
msgcnt++;
assert(msgcnt < NFT_INIT_MSGS);
/* Set F_EXCL so table add fails if the table already exists. */
- r = sd_nfnl_nft_message_new_table(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME, NLM_F_EXCL | NLM_F_ACK);
+ r = sd_nfnl_nft_message_new_table(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME);
if (r < 0)
goto out_unref;
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) {
}
case _JSON_BUILD_ID128: {
- sd_id128_t id;
+ const sd_id128_t *id;
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
r = -EINVAL;
goto finish;
}
- id = va_arg(ap, sd_id128_t);
+ assert_se(id = va_arg(ap, sd_id128_t*));
if (current->n_suppress == 0) {
- r = json_variant_new_id128(&add, id);
+ r = json_variant_new_id128(&add, *id);
if (r < 0)
goto finish;
}
_JSON_BUILD_MAX,
};
-#define JSON_BUILD_STRING(s) _JSON_BUILD_STRING, ({ const char *_x = s; _x; })
-#define JSON_BUILD_INTEGER(i) _JSON_BUILD_INTEGER, ({ intmax_t _x = i; _x; })
-#define JSON_BUILD_UNSIGNED(u) _JSON_BUILD_UNSIGNED, ({ uintmax_t _x = u; _x; })
-#define JSON_BUILD_REAL(d) _JSON_BUILD_REAL, ({ long double _x = d; _x; })
-#define JSON_BUILD_BOOLEAN(b) _JSON_BUILD_BOOLEAN, ({ bool _x = b; _x; })
+#define JSON_BUILD_STRING(s) _JSON_BUILD_STRING, (const char*) { s }
+#define JSON_BUILD_INTEGER(i) _JSON_BUILD_INTEGER, (intmax_t) { i }
+#define JSON_BUILD_UNSIGNED(u) _JSON_BUILD_UNSIGNED, (uintmax_t) { u }
+#define JSON_BUILD_REAL(d) _JSON_BUILD_REAL, (long double) { d }
+#define JSON_BUILD_BOOLEAN(b) _JSON_BUILD_BOOLEAN, (bool) { b }
#define JSON_BUILD_ARRAY(...) _JSON_BUILD_ARRAY_BEGIN, __VA_ARGS__, _JSON_BUILD_ARRAY_END
#define JSON_BUILD_EMPTY_ARRAY _JSON_BUILD_ARRAY_BEGIN, _JSON_BUILD_ARRAY_END
#define JSON_BUILD_OBJECT(...) _JSON_BUILD_OBJECT_BEGIN, __VA_ARGS__, _JSON_BUILD_OBJECT_END
#define JSON_BUILD_EMPTY_OBJECT _JSON_BUILD_OBJECT_BEGIN, _JSON_BUILD_OBJECT_END
-#define JSON_BUILD_PAIR(n, ...) _JSON_BUILD_PAIR, ({ const char *_x = n; _x; }), __VA_ARGS__
-#define JSON_BUILD_PAIR_CONDITION(c, n, ...) _JSON_BUILD_PAIR_CONDITION, ({ bool _x = c; _x; }), ({ const char *_x = n; _x; }), __VA_ARGS__
+#define JSON_BUILD_PAIR(n, ...) _JSON_BUILD_PAIR, (const char*) { n }, __VA_ARGS__
+#define JSON_BUILD_PAIR_CONDITION(c, n, ...) _JSON_BUILD_PAIR_CONDITION, (bool) { c }, (const char*) { n }, __VA_ARGS__
#define JSON_BUILD_NULL _JSON_BUILD_NULL
-#define JSON_BUILD_VARIANT(v) _JSON_BUILD_VARIANT, ({ JsonVariant *_x = v; _x; })
-#define JSON_BUILD_VARIANT_ARRAY(v, n) _JSON_BUILD_VARIANT_ARRAY, ({ JsonVariant **_x = v; _x; }), ({ size_t _y = n; _y; })
-#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, ({ const char *_x = l; _x; })
-#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, ({ char **_x = l; _x; })
-#define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, ({ const void *_x = p; _x; }), ({ size_t _y = n; _y; })
-#define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, ({ const void *_x = p; _x; }), ({ size_t _y = n; _y; })
-#define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, ({ sd_id128_t _x = id; _x; })
-#define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, ({ const void *_x = v; _x; }), ({ size_t _y = n; _y; })
+#define JSON_BUILD_VARIANT(v) _JSON_BUILD_VARIANT, (JsonVariant*) { v }
+#define JSON_BUILD_VARIANT_ARRAY(v, n) _JSON_BUILD_VARIANT_ARRAY, (JsonVariant **) { v }, (size_t) { n }
+#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, (const char*) { l }
+#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, (char**) { l }
+#define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, (const void*) { p }, (size_t) { n }
+#define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n }
+#define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, (const sd_id128_t*) { &(id) }
+#define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, (const void*) { v }, (size_t) { n }
int json_build(JsonVariant **ret, ...);
int json_buildv(JsonVariant **ret, va_list ap);
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 */
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_;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "log.h"
+#include "string-util.h"
#include "wifi-util.h"
int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftype, char **ssid) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
- sd_genl_family_t family;
+ const char *family;
int r;
assert(genl);
assert(ifindex > 0);
- r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_INTERFACE, &m);
+ r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &m);
if (r < 0)
return log_debug_errno(r, "Failed to create generic netlink message: %m");
if (r < 0)
return log_debug_errno(r, "Failed to get information about wifi interface %d: %m", ifindex);
- r = sd_genl_message_get_family(genl, reply, &family);
+ r = sd_genl_message_get_family_name(genl, reply, &family);
if (r < 0)
return log_debug_errno(r, "Failed to determine genl family: %m");
- if (family != SD_GENL_NL80211) {
- log_debug("Received message of unexpected genl family %" PRIi64 ", ignoring.", family);
+ if (!streq(family, NL80211_GENL_NAME)) {
+ log_debug("Received message of unexpected genl family '%s', ignoring.", family);
goto nodata;
}
int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
- sd_genl_family_t family;
+ const char *family;
int r;
assert(genl);
assert(ifindex > 0);
assert(bssid);
- r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_STATION, &m);
+ r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &m);
if (r < 0)
return log_debug_errno(r, "Failed to create generic netlink message: %m");
if (r < 0)
return log_debug_errno(r, "Failed to get information about wifi station: %m");
- r = sd_genl_message_get_family(genl, reply, &family);
+ r = sd_genl_message_get_family_name(genl, reply, &family);
if (r < 0)
return log_debug_errno(r, "Failed to determine genl family: %m");
- if (family != SD_GENL_NL80211) {
- log_debug("Received message of unexpected genl family %" PRIi64 ", ignoring.", family);
+ if (!streq(family, NL80211_GENL_NAME)) {
+ log_debug("Received message of unexpected genl family '%s', ignoring.", family);
goto nodata;
}
}
- 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 we are in --user mode, there's no point in talking to PolicyKit or the infra to query system
+ * passwords */
+ if (arg_scope != UNIT_FILE_SYSTEM)
+ arg_ask_password = false;
+
if (arg_transport == BUS_TRANSPORT_REMOTE && arg_scope != UNIT_FILE_SYSTEM)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Cannot access user instance remotely.");
case _ACTION_INVALID:
default:
- assert_not_reached("Unknown action");
+ assert_not_reached();
}
finish:
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. */
_SD_BEGIN_DECLARATIONS;
typedef struct sd_netlink sd_netlink;
-typedef struct sd_genl_socket sd_genl_socket;
typedef struct sd_netlink_message sd_netlink_message;
typedef struct sd_netlink_slot sd_netlink_slot;
-typedef enum sd_genl_family_t {
- SD_GENL_ERROR,
- SD_GENL_DONE,
- SD_GENL_ID_CTRL,
- SD_GENL_WIREGUARD,
- SD_GENL_FOU,
- SD_GENL_L2TP,
- SD_GENL_MACSEC,
- SD_GENL_NL80211,
- SD_GENL_BATADV,
- _SD_GENL_FAMILY_MAX,
- _SD_GENL_FAMILY_INVALID = -EINVAL,
- _SD_ENUM_FORCE_S64(GENL_FAMILY)
-} sd_genl_family_t;
-
/* callback */
-
typedef int (*sd_netlink_message_handler_t)(sd_netlink *nl, sd_netlink_message *m, void *userdata);
typedef _sd_destroy_t sd_netlink_destroy_t;
/* bus */
-int sd_netlink_new_from_netlink(sd_netlink **nl, int fd);
+int sd_netlink_new_from_fd(sd_netlink **nl, int fd);
int sd_netlink_open(sd_netlink **nl);
int sd_netlink_open_fd(sd_netlink **nl, int fd);
int sd_netlink_inc_rcvbuf(sd_netlink *nl, const size_t size);
int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority);
int sd_netlink_detach_event(sd_netlink *nl);
+/* message */
int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data);
int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned short type, char * const *data);
int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type);
int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type);
int sd_netlink_message_cancel_array(sd_netlink_message *m);
-int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl);
+int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl);
sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m);
int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len);
int sd_rtnl_message_route_get_type(sd_netlink_message *m, unsigned char *type);
-int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nhmsg_type, int nh_family, unsigned char nh_protocol);
+int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int nh_family, unsigned char nh_protocol);
int sd_rtnl_message_nexthop_set_flags(sd_netlink_message *m, uint8_t flags);
int sd_rtnl_message_nexthop_get_family(sd_netlink_message *m, uint8_t *family);
int sd_rtnl_message_nexthop_get_protocol(sd_netlink_message *m, uint8_t *protocol);
-int sd_rtnl_message_new_neigh(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index, int nda_family);
+int sd_rtnl_message_new_neigh(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type, int index, int nda_family);
int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags);
int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state);
int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family);
int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex);
/* nfnl */
-int sd_nfnl_socket_open(sd_netlink **nl);
+int sd_nfnl_socket_open(sd_netlink **ret);
int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret);
int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret);
int sd_nfnl_nft_message_del_table(sd_netlink *nfnl, sd_netlink_message **ret,
int family, const char *table);
int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, uint16_t nl_flags);
+ int family, const char *table);
int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret,
int family, const char *table, const char *chain,
const char *type, uint8_t hook, int prio);
int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m);
/* genl */
-int sd_genl_socket_open(sd_netlink **nl);
-int sd_genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint8_t cmd, sd_netlink_message **ret);
-int sd_genl_message_get_family(sd_netlink *nl, sd_netlink_message *m, sd_genl_family_t *ret);
+int sd_genl_socket_open(sd_netlink **ret);
+int sd_genl_message_new(sd_netlink *genl, const char *family_name, uint8_t cmd, sd_netlink_message **ret);
+int sd_genl_message_get_family_name(sd_netlink *genl, sd_netlink_message *m, const char **ret);
+int sd_genl_message_get_command(sd_netlink *genl, sd_netlink_message *m, uint8_t *ret);
+int sd_genl_add_match(sd_netlink *nl, sd_netlink_slot **ret_slot, const char *family_name,
+ const char *multicast_group_name, uint8_t command,
+ sd_netlink_message_handler_t callback,
+ sd_netlink_destroy_t destroy_callback,
+ void *userdata, const char *description);
/* slot */
-sd_netlink_slot *sd_netlink_slot_ref(sd_netlink_slot *nl);
-sd_netlink_slot *sd_netlink_slot_unref(sd_netlink_slot *nl);
+sd_netlink_slot *sd_netlink_slot_ref(sd_netlink_slot *slot);
+sd_netlink_slot *sd_netlink_slot_unref(sd_netlink_slot *slot);
sd_netlink *sd_netlink_slot_get_netlink(sd_netlink_slot *slot);
void *sd_netlink_slot_get_userdata(sd_netlink_slot *slot);
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)
[['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;
}
#include <sys/prctl.h>
#include <sys/types.h>
+#include "sd-event.h"
+
#include "capability-util.h"
#include "cpu-set-util.h"
+#include "dropin.h"
#include "errno-list.h"
+#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "missing_prctl.h"
#include "mkdir.h"
#include "path-util.h"
+#include "process-util.h"
#include "rm-rf.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#include "service.h"
+#include "signal-util.h"
+#include "static-destruct.h"
#include "stat-util.h"
#include "tests.h"
#include "unit.h"
#include "util.h"
#include "virt.h"
+static char *user_runtime_unit_dir = NULL;
static bool can_unshare;
+STATIC_DESTRUCTOR_REGISTER(user_runtime_unit_dir, freep);
+
typedef void (*test_function_t)(Manager *m);
static int cld_dumped_to_killed(int code) {
exec_status_dump(&service->main_exec_status, stdout, "\t");
if (cld_dumped_to_killed(service->main_exec_status.code) != cld_dumped_to_killed(code_expected)) {
- log_error("%s:%u:%s %s: exit code %d, expected %d",
- file, line, func,
- unit->id,
+ log_error("%s:%u:%s %s: can_unshare=%s: exit code %d, expected %d",
+ file, line, func, unit->id, yes_no(can_unshare),
service->main_exec_status.code, code_expected);
abort();
}
if (service->main_exec_status.status != status_expected) {
- log_error("%s:%u:%s: %s: exit status %d, expected %d",
- file, line, func, unit->id,
+ log_error("%s:%u:%s: %s: can_unshare=%s: exit status %d, expected %d",
+ file, line, func, unit->id, yes_no(can_unshare),
service->main_exec_status.status, status_expected);
abort();
}
service = SERVICE(unit);
if (service->result != result_expected) {
- log_error("%s:%u:%s: %s: service end result %s, expected %s",
- file, line, func,
- unit->id,
+ log_error("%s:%u:%s: %s: can_unshare=%s: service end result %s, expected %s",
+ file, line, func, unit->id, yes_no(can_unshare),
service_result_to_string(service->result),
service_result_to_string(result_expected));
abort();
test(m, "exec-inaccessiblepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
+static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ char **result = userdata;
+ char buf[4096];
+ ssize_t l;
+
+ assert(s);
+ assert(fd >= 0);
+
+ l = read(fd, buf, sizeof(buf) - 1);
+ if (l < 0) {
+ if (errno == EAGAIN)
+ goto reenable;
+
+ return 0;
+ }
+ if (l == 0)
+ return 0;
+
+ buf[l] = '\0';
+ if (result)
+ assert_se(strextend(result, buf));
+ else
+ log_error("ldd: %s", buf);
+
+reenable:
+ /* Re-enable the event source if we did not encounter EOF */
+ assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0);
+ return 0;
+}
+
+static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ pid_t *pid = userdata;
+
+ assert(pid);
+
+ (void) kill(*pid, SIGKILL);
+
+ return 1;
+}
+
+static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ int ret = -EIO;
+
+ assert(si);
+
+ if (si->si_code == CLD_EXITED)
+ ret = si->si_status;
+
+ sd_event_exit(sd_event_source_get_event(s), ret);
+ return 1;
+}
+
+static int find_libraries(const char *exec, char ***ret) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *sigchld_source = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *stdout_source = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *stderr_source = NULL;
+ _cleanup_close_pair_ int outpipe[2] = {-1, -1}, errpipe[2] = {-1, -1};
+ _cleanup_strv_free_ char **libraries = NULL;
+ _cleanup_free_ char *result = NULL;
+ pid_t pid;
+ int r;
+
+ assert(exec);
+ assert(ret);
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
+
+ assert_se(pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) == 0);
+ assert_se(pipe2(errpipe, O_NONBLOCK|O_CLOEXEC) == 0);
+
+ r = safe_fork("(spawn-ldd)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ assert_se(r >= 0);
+ if (r == 0) {
+ if (rearrange_stdio(-1, outpipe[1], errpipe[1]) < 0)
+ _exit(EXIT_FAILURE);
+
+ (void) close_all_fds(NULL, 0);
+
+ execlp("ldd", "ldd", exec, NULL);
+ _exit(EXIT_FAILURE);
+ }
+
+ outpipe[1] = safe_close(outpipe[1]);
+ errpipe[1] = safe_close(errpipe[1]);
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_event_add_time_relative(e, NULL, CLOCK_MONOTONIC,
+ 10 * USEC_PER_SEC, USEC_PER_SEC, on_spawn_timeout, &pid) >= 0);
+ assert_se(sd_event_add_io(e, &stdout_source, outpipe[0], EPOLLIN, on_spawn_io, &result) >= 0);
+ assert_se(sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT) >= 0);
+ assert_se(sd_event_add_io(e, &stderr_source, errpipe[0], EPOLLIN, on_spawn_io, NULL) >= 0);
+ assert_se(sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT) >= 0);
+ assert_se(sd_event_add_child(e, &sigchld_source, pid, WEXITED, on_spawn_sigchld, NULL) >= 0);
+ /* SIGCHLD should be processed after IO is complete */
+ assert_se(sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ _cleanup_strv_free_ char **v = NULL;
+ assert_se(strv_split_newlines_full(&v, result, 0) >= 0);
+
+ char **q;
+ STRV_FOREACH(q, v) {
+ _cleanup_free_ char *word = NULL;
+ const char *p = *q;
+
+ r = extract_first_word(&p, &word, NULL, 0);
+ assert_se(r >= 0);
+ if (r == 0)
+ continue;
+
+ if (path_is_absolute(word)) {
+ assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
+ continue;
+ }
+
+ word = mfree(word);
+ r = extract_first_word(&p, &word, NULL, 0);
+ assert_se(r >= 0);
+ if (r == 0)
+ continue;
+
+ if (!streq_ptr(word, "=>"))
+ continue;
+
+ word = mfree(word);
+ r = extract_first_word(&p, &word, NULL, 0);
+ assert_se(r >= 0);
+ if (r == 0)
+ continue;
+
+ if (path_is_absolute(word)) {
+ assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
+ continue;
+ }
+ }
+
+ *ret = TAKE_PTR(libraries);
+ return 0;
+}
+
+static void test_exec_mount_apivfs(Manager *m) {
+ _cleanup_free_ char *fullpath_touch = NULL, *fullpath_test = NULL, *data = NULL;
+ _cleanup_strv_free_ char **libraries = NULL, **libraries_test = NULL;
+ int r;
+
+ assert(user_runtime_unit_dir);
+
+ r = find_executable("touch", &fullpath_touch);
+ if (r < 0) {
+ log_notice_errno(r, "Skipping %s, could not find 'touch' command: %m", __func__);
+ return;
+ }
+ r = find_executable("test", &fullpath_test);
+ if (r < 0) {
+ log_notice_errno(r, "Skipping %s, could not find 'test' command: %m", __func__);
+ return;
+ }
+
+ assert_se(find_libraries(fullpath_touch, &libraries) >= 0);
+ assert_se(find_libraries(fullpath_test, &libraries_test) >= 0);
+ assert_se(strv_extend_strv(&libraries, libraries_test, true) >= 0);
+
+ assert_se(strextend(&data, "[Service]\n"));
+ assert_se(strextend(&data, "ExecStart=", fullpath_touch, " /aaa\n"));
+ assert_se(strextend(&data, "ExecStart=", fullpath_test, " -f /aaa\n"));
+ assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n"));
+ assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n"));
+
+ char **p;
+ STRV_FOREACH(p, libraries)
+ assert_se(strextend(&data, "BindReadOnlyPaths=", *p, "\n"));
+
+ assert_se(write_drop_in(user_runtime_unit_dir, "exec-mount-apivfs-no.service", 10, "bind-mount", data) >= 0);
+
+ assert_se(mkdir_p("/tmp/test-exec-mount-apivfs-no/root", 0755) >= 0);
+
+ test(m, "exec-mount-apivfs-no.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+
+ (void) rm_rf("/tmp/test-exec-mount-apivfs-no/root", REMOVE_ROOT|REMOVE_PHYSICAL);
+}
+
static void test_exec_noexecpaths(Manager *m) {
test(m, "exec-noexecpaths-simple.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
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))
entry(test_exec_ignoresigpipe),
entry(test_exec_inaccessiblepaths),
entry(test_exec_ioschedulingclass),
+ entry(test_exec_mount_apivfs),
entry(test_exec_noexecpaths),
entry(test_exec_oomscoreadjust),
entry(test_exec_passenvironment),
if (r == -ENOMEDIUM)
return log_tests_skipped("cgroupfs not available");
- _cleanup_free_ char *unit_dir = NULL;
+ _cleanup_free_ char *unit_dir = NULL, *unit_paths = NULL;
assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
- assert_se(set_unit_path(unit_dir) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
+ assert_se(user_runtime_unit_dir = path_join(runtime_dir, "systemd/user"));
+ assert_se(unit_paths = strjoin(unit_dir, ":", user_runtime_unit_dir));
+ assert_se(set_unit_path(unit_paths) >= 0);
/* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
* cases, otherwise (and if they are present in the environment),
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_TIMESPEC) control;
union sockaddr_union server_addr;
struct msghdr msghdr = {
.msg_iov = &iov,
switch (cmsg->cmsg_type) {
case SCM_TIMESTAMPNS:
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct timespec)));
+
recv_time = (struct timespec *) CMSG_DATA(cmsg);
break;
}
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;
%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)
.tx_flow_control = -1,
.autoneg_flow_control = -1,
.txqueuelen = UINT32_MAX,
+ .coalesce.use_adaptive_rx_coalesce = -1,
+ .coalesce.use_adaptive_tx_coalesce = -1,
};
for (i = 0; i < ELEMENTSOF(link->features); i++)
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]);
(
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
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+set -e
+
+TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/17433"
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+do_test "$@"
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=
install_dir : testdata_dir)
install_subdir('testsuite-52.units',
install_dir : testdata_dir)
+ install_subdir('testsuite-63.units',
+ install_dir : testdata_dir)
testsuite08_dir = testdata_dir + '/testsuite-08.units'
install_data('testsuite-08.units/-.mount',
--- /dev/null
+[Unit]
+Description=Test for find_executable() with MountAPIVFS=no
+
+[Service]
+Type=oneshot
+
+MountAPIVFS=false
+PrivateDevices=false
+PrivateMounts=true
+PrivateTmp=false
+PrivateUsers=false
+ProtectControlGroups=false
+ProtectKernelModules=false
+ProtectKernelTunables=false
+RootDirectory=/tmp/test-exec-mount-apivfs-no/root
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
head
ionice
ip
+ ldd
ln
loadkeys
login
# 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
Family=ipv6
Blackhole=yes
+[NextHop]
+Id=8
+Gateway=fe80::222:4dff:ff:ff:ff:ff
+
[NextHop]
Gateway=192.168.5.2
[Route]
Gateway=2001:1234:5:8fff:ff:ff:ff:ff
+[Route]
+Destination=2001:1234:5:afff:ff:ff:ff:ff/128
+Gateway=fe80::222:4dff:ff:ff:ff:ff
+
[Route]
Destination=149.10.124.64
Scope=link
--- /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
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCPv4]
+# DenyList= will be ignored
+AllowList=192.168.5.0/24 192.168.6.0/24
+DenyList=192.168.5.0/24
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
IPv6AcceptRA=yes
[IPv6AcceptRA]
-# PrefixDenyList= and RouteDenyList= will be ignored.
-PrefixAllowList=2001:db8:0:1:: 2001:db8:0:1::
-PrefixDenyList=2001:db8:0:1:: 2001:db8:0:1::
-RouteAllowList=2001:db0:fff:: 2001:db0:fff::
-RouteDenyList=2001:db0:fff:: 2001:db0:fff::
+# PrefixDenyList= will be ignored.
+PrefixAllowList=2001:db8:0:1:: 2001:db8:0:1:: 2001:db8:0:1::/64
+PrefixDenyList=2001:db8:0:1::/64 2001:db8:0:1:: 2001:db8:0:3::/64
+RouteDenyList=2001:db1:fff::/64 2001:db1:fff:: 2001:db2:fff::/64
+UseDomains=yes
DHCP=no
IPv6SendRA=yes
+[IPv6SendRA]
+UplinkInterface=dummy98
+
[IPv6Prefix]
Prefix=2001:db8:0:1::/64
Prefix=2001:db8:0:2::/64
Assign=yes
+[IPv6Prefix]
+Prefix=2001:db8:0:3::/64
+
[IPv6RoutePrefix]
Route=2001:db0:fff::/64
LifetimeSec=1000
[IPv6RoutePrefix]
Route=2001:db1:fff::/64
LifetimeSec=1000
+
+[IPv6RoutePrefix]
+Route=2001:db2:fff::/64
+LifetimeSec=1000
--- /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',
f.write('[MACVTAP]\nMode=' + mode)
start_networkd()
- self.wait_online(['macvtap99:degraded', 'test1:degraded'])
+ self.wait_online(['macvtap99:degraded',
+ 'test1:carrier' if mode == 'passthru' else 'test1:degraded'])
output = check_output('ip -d link show macvtap99')
print(output)
f.write('[MACVLAN]\nMode=' + mode)
start_networkd()
- self.wait_online(['macvlan99:degraded', 'test1:degraded'])
+ self.wait_online(['macvlan99:degraded',
+ 'test1:carrier' if mode == 'passthru' else '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 + ' ')
+
+ 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:carrier' if mode == 'passthru' else 'test1:degraded'])
output = check_output('ip -d link show test1')
print(output)
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',
print(output)
self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
self.assertIn('2001:1234:5:8f63::1 proto kernel', output)
+ self.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output)
print('### ip -6 route show default')
output = check_output('ip -6 route show default')
self.assertIn('id 3 dev veth99', output)
self.assertIn('id 4 dev veth99', output)
self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
+ self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
output = check_output('ip nexthop list dev dummy98')
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 = [
'25-veth.netdev',
'25-vrf.netdev',
'25-vrf.network',
+ 'dhcp-client-allow-list.network',
'dhcp-client-anonymize.network',
'dhcp-client-decline.network',
'dhcp-client-gateway-ipv4.network',
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')
print(output)
self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
+ def test_dhcp_client_allow_list(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-decline.network', 'dhcp-client-allow-list.network')
+
+ start_networkd()
+ self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ output = check_output('ip -4 address show dev veth99 scope global dynamic')
+ print(output)
+ 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.assertIn('inet6 2001:db8:0:1:', output)
self.assertNotIn('inet6 2001:db8:0:2:', output)
+ self.assertNotIn('inet6 2001:db8:0:3:', output)
output = check_output('ip -6 route show dev veth-peer')
print(output)
self.assertIn('2001:db8:0:1::/64 proto ra', output)
self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
+ self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
self.assertIn('2001:db0:fff::/64 via ', output)
self.assertNotIn('2001:db1:fff::/64 via ', output)
+ self.assertNotIn('2001:db2:fff::/64 via ', output)
output = check_output('ip address show dev veth99')
print(output)
self.assertNotIn('inet6 2001:db8:0:1:', output)
self.assertIn('inet6 2001:db8:0:2:', output)
+ self.assertNotIn('inet6 2001:db8:0:3:', 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']
[Unit]
Requires=test10.socket
ConditionPathExistsGlob=/tmp/nonexistent
+# Make sure we hit the socket trigger limit in the test and not the service start limit.
+StartLimitInterval=1000
+StartLimitBurst=1000
[Service]
ExecStart=true
--- /dev/null
+[Path]
+PathExists=/tmp/test63
--- /dev/null
+[Unit]
+ConditionPathExists=!/tmp/nonexistent
+
+[Service]
+ExecStart=true
[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
--- /dev/null
+[Unit]
+Description=TEST-63-ISSUE-17433
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+Type=oneshot
+ExecStart=rm -f /tmp/nonexistent
+ExecStart=systemctl start test63.path
+ExecStart=touch /tmp/test63
+# Make sure systemd has sufficient time to hit the start limit for test63.service.
+ExecStart=sleep 2
+ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = failed'
+ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = start-limit-hit'
+ExecStart=sh -x -c 'test "$(systemctl show test63.path -P ActiveState)" = failed'
+ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = unit-start-limit-hit'
+ExecStart=sh -x -c 'echo OK >/testok'
--- /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', '',
--- /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