From: Lennart Poettering Date: Thu, 21 Dec 2023 21:47:54 +0000 (+0100) Subject: Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps X-Git-Tag: v256-rc1~1426 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=66f3da245ba2b2d8061a79d296fbe016d19e8cb7;hp=f611721fa27a5ab739eb6781c43658680efa4830;p=thirdparty%2Fsystemd.git Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps fstab-generator: disable default deps if x-systemd.{wanted,required}-by= is used --- diff --git a/NEWS b/NEWS index a42c8f514b1..b1e7779b602 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,15 @@ systemd System and Service Manager -CHANGES WITH 255 in spe: +CHANGES WITH 256 in spe: Announcements of Future Feature Removals and Incompatible Changes: + * Previously, systemd-networkd did not explicitly remove any bridge VLAN + IDs assigned on bridge master and ports. Since v256, if a .network + file for an interface has at least one valid settings in [BridgeVLAN] + section, then all assigned VLAN IDs on the interface that are not + configured in the .network file are removed. + CHANGES WITH 255: Announcements of Future Feature Removals and Incompatible Changes: diff --git a/TODO b/TODO index c75f69e650e..4d2fd411dca 100644 --- a/TODO +++ b/TODO @@ -132,6 +132,21 @@ Deprecations and removals: Features: +* add a new ExecStart= flag that inserts the configured user's shell as first + word in the comand line. (maybe use character '.'). Usecase: tool such as + uid0 can use that to spawn the target user's default shell. + +* varlink: figure out how to do docs for our varlink interfaces. Idea: install + interface files augmented with docs in /usr/share/ somewhere. And have + functionality in varlinkctl to merge interface info extracted from binaries + with interface info on disk. And store the doc strings only in the latter. + +* introduce mntid_t, and make it 64bit, as apparently the kernel switched to + 64bit mount ids + +* use udev rule networkd ownership property to take ownership of network + interfaces nspawn creates + * add a kernel cmdline switch (and cred?) for marking a system to be "headless", in which case we never open /dev/console for reading, only for writing. This would then mean: systemd-firstboot would process creds but not @@ -920,10 +935,6 @@ Features: file system paths to enable on start. • make systemd-fstab-generator look for a system credential encoding root= or usr= - • systemd-homed: when initializing, look for a credential - systemd.homed.register or so with JSON user records to automatically - register if not registered yet. Use case: deploy a system, and add an - account one can directly log into. • in gpt-auto-generator: check partition uuids against such uuids supplied via sd-stub credentials. That way, we can support parallel OS installations with pre-built kernels. @@ -2248,11 +2259,6 @@ Features: - 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? - - 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 - maybe make all *.home files owned by `systemd-home` user or so, so that we diff --git a/docs/CREDENTIALS.md b/docs/CREDENTIALS.md index f508c84f4c6..153a42be547 100644 --- a/docs/CREDENTIALS.md +++ b/docs/CREDENTIALS.md @@ -455,7 +455,7 @@ qemu-system-x86_64 \ -device scsi-hd,drive=hd,bootindex=1 \ -device vhost-vsock-pci,id=vhost-vsock-pci0,guest-cid=42 \ -smbios type=11,value=io.systemd.credential:vmm.notify_socket=vsock:2:1234 \ - -smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=$(echo "f~ /root/.ssh/authorized_keys 700 root root - $(ssh-add -L | base64 -w 0)" | base64 -w 0) + -smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=$(echo "f~ /root/.ssh/authorized_keys 600 root root - $(ssh-add -L | base64 -w 0)" | base64 -w 0) ``` A process on the host can listen for the notification, for example: diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 5e15b2ba1d6..819d5367738 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -249,6 +249,14 @@ All tools: devices sysfs path are actually backed by sysfs. Relaxing this verification is useful for testing purposes. +`udevadm` and `systemd-hwdb`: + +* `SYSTEMD_HWDB_UPDATE_BYPASS=` — If set to "1", execution of hwdb updates is skipped + when `udevadm hwdb --update` or `systemd-hwdb update` are invoked. This can + be useful if either of these tools are invoked unconditionally as a child + process by another tool, such as package managers running either of these + tools in a postinstall script. + `nss-systemd`: * `$SYSTEMD_NSS_BYPASS_SYNTHETIC=1` — if set, `nss-systemd` won't synthesize diff --git a/hwdb.d/60-evdev.hwdb b/hwdb.d/60-evdev.hwdb index a9ce4e2862d..9a90420b5f1 100644 --- a/hwdb.d/60-evdev.hwdb +++ b/hwdb.d/60-evdev.hwdb @@ -237,6 +237,15 @@ evdev:name:SYNA3602:00 0911:5288 Touchpad:dmi:*svnBANGHO:pnCLOUDPRO:* EVDEV_ABS_35=52:1747:17 EVDEV_ABS_36=45:954:14 +######################################### +# Bosto +######################################### + +# Bosto BT-12HD series +evdev:input:b0003v0ED1p7821* + EVDEV_ABS_00=::2271 + EVDEV_ABS_01=::5080 + ######################################### # Dell ######################################### diff --git a/hwdb.d/60-sensor.hwdb b/hwdb.d/60-sensor.hwdb index 5d09b88b13e..1086940b782 100644 --- a/hwdb.d/60-sensor.hwdb +++ b/hwdb.d/60-sensor.hwdb @@ -392,6 +392,9 @@ sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC4:* sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC5:* sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC7:* sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC8:* +sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CB2:* +sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:skuOCB4:* +sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CB3:* ACCEL_LOCATION=base # Dell Venue 8 Pro 3845 diff --git a/man/binfmt.d.xml b/man/binfmt.d.xml index ab56460d3e9..7420b5ee68a 100644 --- a/man/binfmt.d.xml +++ b/man/binfmt.d.xml @@ -23,9 +23,11 @@ - /etc/binfmt.d/*.conf - /run/binfmt.d/*.conf - /usr/lib/binfmt.d/*.conf + + /etc/binfmt.d/*.conf + /run/binfmt.d/*.conf + /usr/lib/binfmt.d/*.conf + diff --git a/man/coredump.conf.xml b/man/coredump.conf.xml index 61014d38235..aa73f62ea8f 100644 --- a/man/coredump.conf.xml +++ b/man/coredump.conf.xml @@ -22,10 +22,12 @@ - /etc/systemd/coredump.conf - /etc/systemd/coredump.conf.d/*.conf - /run/systemd/coredump.conf.d/*.conf - /usr/lib/systemd/coredump.conf.d/*.conf + + /etc/systemd/coredump.conf + /etc/systemd/coredump.conf.d/*.conf + /run/systemd/coredump.conf.d/*.conf + /usr/lib/systemd/coredump.conf.d/*.conf + diff --git a/man/crypttab.xml b/man/crypttab.xml index eb742be0582..c35d782c8dd 100644 --- a/man/crypttab.xml +++ b/man/crypttab.xml @@ -104,10 +104,14 @@ see above and below. The key may be acquired via a PKCS#11 compatible hardware security token or - smartcard. In this case an encrypted key is stored on disk/removable media, acquired via - AF_UNIX, or stored in the LUKS2 JSON token metadata header. The encrypted key is - then decrypted by the PKCS#11 token with an RSA key stored on it, and then used to unlock the encrypted - volume. Use the option described below to use this mechanism. + smartcard. In this case a saved key used in unlock process is stored on disk/removable media, acquired via + AF_UNIX, or stored in the LUKS2 JSON token metadata header. For RSA, the saved key + is an encrypted volume key. The encrypted volume key is then decrypted by the PKCS#11 token with an RSA + private key stored on it, and used to unlock the encrypted volume. For elliptic-curve (EC) cryptography, + the saved key is the public key generated in enrollment process. The public key is then used to derive + a shared secret with a private key stored in the PKCS#11 token. The derived shared secret is then used + to unlock the volume. Use the option described below to use this mechanism. + Similarly, the key may be acquired via a FIDO2 compatible hardware security token (which must implement the "hmac-secret" extension). In this case a key generated randomly during @@ -643,7 +647,7 @@ Takes either the special value auto or an RFC7512 PKCS#11 URI pointing to a private RSA key + url="https://tools.ietf.org/html/rfc7512">RFC7512 PKCS#11 URI pointing to a private key which is used to decrypt the encrypted key specified in the third column of the line. This is useful for unlocking encrypted volumes through PKCS#11 compatible security tokens or smartcards. See below for an example how to set up this mechanism for unlocking a LUKS2 volume with a YubiKey security @@ -653,16 +657,16 @@ security token metadata in its LUKS2 JSON token section. In this mode the URI and the encrypted key are automatically read from the LUKS2 JSON token header. Use systemd-cryptenroll1 - as simple tool for enrolling PKCS#11 security tokens or smartcards in a way compatible with + as a simple tool for enrolling PKCS#11 security tokens or smartcards in a way compatible with auto. In this mode the third column of the line should remain empty (that is, specified as -). - The specified URI can refer directly to a private RSA key stored on a token or alternatively - just to a slot or token, in which case a search for a suitable private RSA key will be performed. In - this case if multiple suitable objects are found the token is refused. The encrypted key configured - in the third column of the line is passed as is (i.e. in binary form, unprocessed) to RSA - decryption. The resulting decrypted key is then Base64 encoded before it is used to unlock the LUKS - volume. + The specified URI can refer directly to a private key stored on a token or alternatively + just to a slot or token, in which case a search for a suitable private key will be performed. In + this case if multiple suitable objects are found the token is refused. The keyfile configured + in the third column of the line is used as is (i.e. in binary form, unprocessed). The resulting + decrypted key (for RSA) or derived shared secret (for ECC) is then Base64 encoded before it is used + to unlock the LUKS volume. Use systemd-cryptenroll --pkcs11-token-uri=list to list all suitable PKCS#11 security tokens currently plugged in, along with their URIs. @@ -969,8 +973,8 @@ external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchac Yubikey-based PKCS#11 Volume Unlocking Example The PKCS#11 logic allows hooking up any compatible security token that is capable of storing RSA - decryption keys for unlocking an encrypted volume. Here's an example how to set up a Yubikey security - token for this purpose on a LUKS2 volume, using ykmap1 from the yubikey-manager project to initialize the token and systemd-cryptenroll1 diff --git a/man/dnssec-trust-anchors.d.xml b/man/dnssec-trust-anchors.d.xml index 39b9515c452..391f93b5b77 100644 --- a/man/dnssec-trust-anchors.d.xml +++ b/man/dnssec-trust-anchors.d.xml @@ -23,12 +23,14 @@ - /etc/dnssec-trust-anchors.d/*.positive - /run/dnssec-trust-anchors.d/*.positive - /usr/lib/dnssec-trust-anchors.d/*.positive - /etc/dnssec-trust-anchors.d/*.negative - /run/dnssec-trust-anchors.d/*.negative - /usr/lib/dnssec-trust-anchors.d/*.negative + + /etc/dnssec-trust-anchors.d/*.positive + /run/dnssec-trust-anchors.d/*.positive + /usr/lib/dnssec-trust-anchors.d/*.positive + /etc/dnssec-trust-anchors.d/*.negative + /run/dnssec-trust-anchors.d/*.negative + /usr/lib/dnssec-trust-anchors.d/*.negative + diff --git a/man/environment.d.xml b/man/environment.d.xml index fc03405a940..f0c63da77de 100644 --- a/man/environment.d.xml +++ b/man/environment.d.xml @@ -26,11 +26,13 @@ - ~/.config/environment.d/*.conf - /etc/environment.d/*.conf - /run/environment.d/*.conf - /usr/lib/environment.d/*.conf - /etc/environment + + ~/.config/environment.d/*.conf + /etc/environment.d/*.conf + /run/environment.d/*.conf + /usr/lib/environment.d/*.conf + /etc/environment + diff --git a/man/homectl.xml b/man/homectl.xml index 7fc7d5f0122..7e26c941799 100644 --- a/man/homectl.xml +++ b/man/homectl.xml @@ -18,6 +18,7 @@ homectl + systemd-homed-firstboot.service Create, remove, change or inspect home directories @@ -1138,6 +1139,59 @@ + + + firstboot + + This command is supposed to be invoked during the initial boot of the system. It + checks whether any regular home area exists so far, and if not queries the user interactively on the + console for user name and password and creates one. Alternatively, if one or more service credentials + whose name starts with home.create. are passed to the command (containing a user + record in JSON format) these users are automatically created at boot. + + This command is invoked by the systemd-homed-firstboot.service service + unit. + + + + + + + + Credentials + + When invoked with the firstboot command, homectl supports the + service credentials logic as implemented by + ImportCredential=/LoadCredential=/SetCredential= + (see systemd.exec1 for + details). The following credentials are used when passed in: + + + + home.create.* + + If one or more credentials whose names begin with home.create., + followed by a valid UNIX username are passed, a new home area is created, one for each specified user + record. + + + + + + + + Kernel Command Line + + + + systemd.firstboot= + + This boolean will disable the effect of homectl firstboot + command. It's primarily interpreted by + systemd-firstboot1. + + + diff --git a/man/homed.conf.xml b/man/homed.conf.xml index acc5f5f1761..4a075511b7b 100644 --- a/man/homed.conf.xml +++ b/man/homed.conf.xml @@ -22,10 +22,12 @@ - /etc/systemd/homed.conf - /etc/systemd/homed.conf.d/*.conf - /run/systemd/homed.conf.d/*.conf - /usr/lib/systemd/homed.conf.d/*.conf + + /etc/systemd/homed.conf + /etc/systemd/homed.conf.d/*.conf + /run/systemd/homed.conf.d/*.conf + /usr/lib/systemd/homed.conf.d/*.conf + diff --git a/man/journal-remote.conf.xml b/man/journal-remote.conf.xml index a5a5b56ec34..7d75f0e79f5 100644 --- a/man/journal-remote.conf.xml +++ b/man/journal-remote.conf.xml @@ -27,10 +27,12 @@ - /etc/systemd/journal-remote.conf - /etc/systemd/journal-remote.conf.d/*.conf - /run/systemd/journal-remote.conf.d/*.conf - /usr/lib/systemd/journal-remote.conf.d/*.conf + + /etc/systemd/journal-remote.conf + /etc/systemd/journal-remote.conf.d/*.conf + /run/systemd/journal-remote.conf.d/*.conf + /usr/lib/systemd/journal-remote.conf.d/*.conf + diff --git a/man/journald.conf.xml b/man/journald.conf.xml index e150d04dcff..634cec4b52d 100644 --- a/man/journald.conf.xml +++ b/man/journald.conf.xml @@ -23,14 +23,16 @@ - /etc/systemd/journald.conf - /etc/systemd/journald.conf.d/*.conf - /run/systemd/journald.conf.d/*.conf - /usr/lib/systemd/journald.conf.d/*.conf - /etc/systemd/journald@NAMESPACE.conf - /etc/systemd/journald@NAMESPACE.conf.d/*.conf - /run/systemd/journald@NAMESPACE.conf.d/*.conf - /usr/lib/systemd/journald@NAMESPACE.conf.d/*.conf + + /etc/systemd/journald.conf + /etc/systemd/journald.conf.d/*.conf + /run/systemd/journald.conf.d/*.conf + /usr/lib/systemd/journald.conf.d/*.conf + /etc/systemd/journald@NAMESPACE.conf + /etc/systemd/journald@NAMESPACE.conf.d/*.conf + /run/systemd/journald@NAMESPACE.conf.d/*.conf + /usr/lib/systemd/journald@NAMESPACE.conf.d/*.conf + diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 6ac20ad2f4f..7a7b2b7deb4 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -594,6 +594,8 @@ Takes a boolean argument, defaults to on. If off, systemd-firstboot.service8 + and + systemd-homed-firstboot.service1 will not query the user for basic system settings, even if the system boots up for the first time and the relevant settings are not initialized yet. Not to be confused with systemd.condition-first-boot= (see below), which overrides the result of the diff --git a/man/logind.conf.xml b/man/logind.conf.xml index 2b5c11b9166..e1d65821bfa 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -25,10 +25,12 @@ - /etc/systemd/logind.conf - /etc/systemd/logind.conf.d/*.conf - /run/systemd/logind.conf.d/*.conf - /usr/lib/systemd/logind.conf.d/*.conf + + /etc/systemd/logind.conf + /etc/systemd/logind.conf.d/*.conf + /run/systemd/logind.conf.d/*.conf + /usr/lib/systemd/logind.conf.d/*.conf + diff --git a/man/modules-load.d.xml b/man/modules-load.d.xml index cd0c00687a5..1293dd5249a 100644 --- a/man/modules-load.d.xml +++ b/man/modules-load.d.xml @@ -22,9 +22,11 @@ - /etc/modules-load.d/*.conf - /run/modules-load.d/*.conf - /usr/lib/modules-load.d/*.conf + + /etc/modules-load.d/*.conf + /run/modules-load.d/*.conf + /usr/lib/modules-load.d/*.conf + diff --git a/man/networkctl.xml b/man/networkctl.xml index 3a2dc09eccb..1a03e9e11d4 100644 --- a/man/networkctl.xml +++ b/man/networkctl.xml @@ -461,6 +461,40 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR) + + + + mask + FILE… + + Mask network configuration files, which include .network, + .netdev, and .link files. A symlink of the given name will + be created under /etc/ or /run/, depending on + whether is specified, that points to /dev/null. + If a non-empty config file with the specified name exists under the target directory or a directory + with higher priority (e.g. is used while an existing config resides + in /etc/), the operation is aborted. + + This command honors in the same way as edit. + + + + + + + + unmask + FILE… + + Unmask network configuration files, i.e. reverting the effect of mask. + Note that this command operates regardless of the scope of the directory, i.e. + is of no effect. + + This command honors in the same way as edit + and mask. + + + @@ -534,11 +568,11 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR) - When used with edit, + When used with edit, mask, or unmask, systemd-networkd.service8 or systemd-udevd.service8 - will not be reloaded after the editing finishes. + will not be reloaded after the operation finishes. @@ -547,8 +581,8 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR) - When used with edit, edit the file under /run/ - instead of /etc/. + When used with edit or mask, + operate on the file under /run/ instead of /etc/. diff --git a/man/networkd.conf.xml b/man/networkd.conf.xml index 018bde0fbfa..27cab25d88f 100644 --- a/man/networkd.conf.xml +++ b/man/networkd.conf.xml @@ -27,9 +27,11 @@ - /etc/systemd/networkd.conf - /etc/systemd/networkd.conf.d/*.conf - /usr/lib/systemd/networkd.conf.d/*.conf + + /etc/systemd/networkd.conf + /etc/systemd/networkd.conf.d/*.conf + /usr/lib/systemd/networkd.conf.d/*.conf + @@ -90,6 +92,17 @@ + + ManageForeignNextHops= + A boolean. When true, systemd-networkd will remove nexthops + that are not configured in .network files (except for routes with protocol + kernel). When false, it will + not remove any foreign nexthops, keeping them even if they are not configured in a .network file. + Defaults to yes. + + + + RouteTable= Defines the route table name. Takes a whitespace-separated list of the pairs of diff --git a/man/oomd.conf.xml b/man/oomd.conf.xml index 4fc7abd8950..e8f9b58658e 100644 --- a/man/oomd.conf.xml +++ b/man/oomd.conf.xml @@ -22,9 +22,11 @@ - /etc/systemd/oomd.conf - /etc/systemd/oomd.conf.d/*.conf - /usr/lib/systemd/oomd.conf.d/*.conf + + /etc/systemd/oomd.conf + /etc/systemd/oomd.conf.d/*.conf + /usr/lib/systemd/oomd.conf.d/*.conf + diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 014a5cc8833..ced9bea7db0 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -1356,7 +1356,7 @@ node /org/freedesktop/systemd1 { ResetFailed() resets the "failed" state of all units. ListUnits() returns an array of all currently loaded units. Note that - units may be known by multiple names at the same name, and hence there might be more unit names loaded + units may be known by multiple names at the same time, and hence there might be more unit names loaded than actual units behind them. The array consists of structures with the following elements: The primary unit name as string diff --git a/man/os-release.xml b/man/os-release.xml index f2e0f3ecb7b..79de2451bf3 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -22,10 +22,12 @@ - /etc/os-release - /usr/lib/os-release - /etc/initrd-release - /usr/lib/extension-release.d/extension-release.IMAGE + + /etc/os-release + /usr/lib/os-release + /etc/initrd-release + /usr/lib/extension-release.d/extension-release.IMAGE + diff --git a/man/repart.d.xml b/man/repart.d.xml index 79908a08054..ea8cd9e4e49 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -22,10 +22,11 @@ - /etc/repart.d/*.conf -/run/repart.d/*.conf -/usr/lib/repart.d/*.conf - + + /etc/repart.d/*.conf + /run/repart.d/*.conf + /usr/lib/repart.d/*.conf + diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index d153865c6e3..194a614244e 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -25,10 +25,12 @@ - /etc/systemd/resolved.conf - /etc/systemd/resolved.conf.d/*.conf - /run/systemd/resolved.conf.d/*.conf - /usr/lib/systemd/resolved.conf.d/*.conf + + /etc/systemd/resolved.conf + /etc/systemd/resolved.conf.d/*.conf + /run/systemd/resolved.conf.d/*.conf + /usr/lib/systemd/resolved.conf.d/*.conf + diff --git a/man/rules/meson.build b/man/rules/meson.build index 5dc3e08896d..622921f8d63 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -18,7 +18,7 @@ manpages = [ 'ENABLE_RESOLVE'], ['environment.d', '5', [], 'ENABLE_ENVIRONMENT_D'], ['file-hierarchy', '7', [], ''], - ['homectl', '1', [], 'ENABLE_HOMED'], + ['homectl', '1', ['systemd-homed-firstboot.service'], 'ENABLE_HOMED'], ['homed.conf', '5', ['homed.conf.d'], 'ENABLE_HOMED'], ['hostname', '5', [], ''], ['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'], @@ -1257,6 +1257,7 @@ manpages = [ ''], ['udev_new', '3', ['udev_ref', 'udev_unref'], ''], ['udevadm', '8', [], ''], + ['uid0', '1', [], ''], ['ukify', '1', [], 'ENABLE_UKIFY'], ['user@.service', '5', diff --git a/man/sd-bus.xml b/man/sd-bus.xml index 4c9c00986a2..9e67b0b4672 100644 --- a/man/sd-bus.xml +++ b/man/sd-bus.xml @@ -41,142 +41,143 @@ for more information about D-Bus IPC. See - sd_bus_add_match3, -sd_bus_add_object3, -sd_bus_add_object_manager3, -sd_bus_add_object_vtable3, -sd_bus_add_fallback3, -sd_bus_add_fallback_vtable3, -sd_bus_add_filter3, -sd_bus_add_node_enumerator3, -sd_bus_attach_event3, -sd_bus_call3, -sd_bus_call_async3, -sd_bus_call_method3, -sd_bus_call_method_async3, -sd_bus_can_send3, -sd_bus_close3, -sd_bus_creds_get_pid3, -sd_bus_creds_new_from_pid3, -sd_bus_default3, -sd_bus_emit_interfaces_added3, -sd_bus_emit_interfaces_added_strv3, -sd_bus_emit_interfaces_removed3, -sd_bus_emit_interfaces_removed_strv3, -sd_bus_emit_object_added3, -sd_bus_emit_object_removed3, -sd_bus_emit_properties_changed3, -sd_bus_emit_properties_changed_strv3, -sd_bus_emit_signal3, -sd_bus_emit_signalv3, -sd_bus_emit_signal_to3, -sd_bus_emit_signal_tov3, -sd-bus-errors3, -sd_bus_error3, -sd_bus_error_add_map3, -sd_bus_get_address3, -sd_bus_get_allow_interactive_authorization3, -sd_bus_get_bus_id3, -sd_bus_get_creds_mask3, -sd_bus_get_current_handler3, -sd_bus_get_current_message3, -sd_bus_get_current_slot3, -sd_bus_get_current_userdata3, -sd_bus_get_exit_on_disconnect3, -sd_bus_get_fd3, -sd_bus_get_method_call_timeout3, -sd_bus_get_n_queued_read3, -sd_bus_get_name_creds3, -sd_bus_get_name_machine_id3, -sd_bus_get_owner_creds3, -sd_bus_get_property3, -sd_bus_get_property_string3, -sd_bus_get_property_strv3, -sd_bus_get_property_trivial3, -sd_bus_get_scope3, -sd_bus_get_tid3, -sd_bus_get_unique_name3, -sd_bus_interface_name_is_valid3, -sd_bus_is_bus_client3, -sd_bus_is_monitor3, -sd_bus_is_server3, -sd_bus_list_names3, -sd_bus_message_append3, -sd_bus_message_append_array3, -sd_bus_message_append_basic3, -sd_bus_message_append_string_memfd3, -sd_bus_message_append_strv3, -sd_bus_message_at_end3, -sd_bus_message_close_container3, -sd_bus_message_copy3, -sd_bus_message_dump3, -sd_bus_message_enter_container3, -sd_bus_message_exit_container3, -sd_bus_message_get_allow_interactive_authorization3, -sd_bus_message_get_cookie3, -sd_bus_message_get_creds3, -sd_bus_message_get_errno3, -sd_bus_message_get_error3, -sd_bus_message_get_monotonic_usec3, -sd_bus_message_get_sender3, -sd_bus_message_get_signature3, -sd_bus_message_get_type3, -sd_bus_message_new3, -sd_bus_message_new_method_call3, -sd_bus_message_new_method_error3, -sd_bus_message_new_signal3, -sd_bus_message_new_signal_to3, -sd_bus_message_open_container3, -sd_bus_message_peek_type3, -sd_bus_message_read3, -sd_bus_message_read_array3, -sd_bus_message_read_basic3, -sd_bus_message_read_strv3, -sd_bus_message_rewind3, -sd_bus_message_seal3, -sd_bus_message_send3, -sd_bus_message_set_allow_interactive_authorization3, -sd_bus_message_set_destination3, -sd_bus_message_set_expect_reply3, -sd_bus_message_set_sender3, -sd_bus_message_skip3, -sd_bus_message_verify_type3, -sd_bus_negotiate_fds3, -sd_bus_new3, -sd_bus_path_encode3, -sd_bus_process3, -sd_bus_query_sender_creds3, -sd_bus_query_sender_privilege3, -sd_bus_reply_method_error3, -sd_bus_reply_method_return3, -sd_bus_request_name3, -sd_bus_send3, -sd_bus_send_to3, -sd_bus_set_address3, -sd_bus_set_allow_interactive_authorization3, -sd_bus_set_bus_client3, -sd_bus_set_close_on_exit3, -sd_bus_set_connected_signal3, -sd_bus_set_description3, -sd_bus_set_exit_on_disconnect3, -sd_bus_set_method_call_timeout3, -sd_bus_set_monitor3, -sd_bus_set_property3, -sd_bus_set_propertyv3, -sd_bus_set_sender3, -sd_bus_set_server3, -sd_bus_set_watch_bind3 -sd_bus_slot_get_current_handler3, -sd_bus_slot_get_current_message3, -sd_bus_slot_get_current_userdata3, -sd_bus_slot_set_description3, -sd_bus_slot_set_destroy_callback3, -sd_bus_slot_set_floating3, -sd_bus_slot_set_userdata3, -sd_bus_start3, -sd_bus_track_add_name3, -sd_bus_track_new3 - + + sd_bus_add_match3, + sd_bus_add_object3, + sd_bus_add_object_manager3, + sd_bus_add_object_vtable3, + sd_bus_add_fallback3, + sd_bus_add_fallback_vtable3, + sd_bus_add_filter3, + sd_bus_add_node_enumerator3, + sd_bus_attach_event3, + sd_bus_call3, + sd_bus_call_async3, + sd_bus_call_method3, + sd_bus_call_method_async3, + sd_bus_can_send3, + sd_bus_close3, + sd_bus_creds_get_pid3, + sd_bus_creds_new_from_pid3, + sd_bus_default3, + sd_bus_emit_interfaces_added3, + sd_bus_emit_interfaces_added_strv3, + sd_bus_emit_interfaces_removed3, + sd_bus_emit_interfaces_removed_strv3, + sd_bus_emit_object_added3, + sd_bus_emit_object_removed3, + sd_bus_emit_properties_changed3, + sd_bus_emit_properties_changed_strv3, + sd_bus_emit_signal3, + sd_bus_emit_signalv3, + sd_bus_emit_signal_to3, + sd_bus_emit_signal_tov3, + sd-bus-errors3, + sd_bus_error3, + sd_bus_error_add_map3, + sd_bus_get_address3, + sd_bus_get_allow_interactive_authorization3, + sd_bus_get_bus_id3, + sd_bus_get_creds_mask3, + sd_bus_get_current_handler3, + sd_bus_get_current_message3, + sd_bus_get_current_slot3, + sd_bus_get_current_userdata3, + sd_bus_get_exit_on_disconnect3, + sd_bus_get_fd3, + sd_bus_get_method_call_timeout3, + sd_bus_get_n_queued_read3, + sd_bus_get_name_creds3, + sd_bus_get_name_machine_id3, + sd_bus_get_owner_creds3, + sd_bus_get_property3, + sd_bus_get_property_string3, + sd_bus_get_property_strv3, + sd_bus_get_property_trivial3, + sd_bus_get_scope3, + sd_bus_get_tid3, + sd_bus_get_unique_name3, + sd_bus_interface_name_is_valid3, + sd_bus_is_bus_client3, + sd_bus_is_monitor3, + sd_bus_is_server3, + sd_bus_list_names3, + sd_bus_message_append3, + sd_bus_message_append_array3, + sd_bus_message_append_basic3, + sd_bus_message_append_string_memfd3, + sd_bus_message_append_strv3, + sd_bus_message_at_end3, + sd_bus_message_close_container3, + sd_bus_message_copy3, + sd_bus_message_dump3, + sd_bus_message_enter_container3, + sd_bus_message_exit_container3, + sd_bus_message_get_allow_interactive_authorization3, + sd_bus_message_get_cookie3, + sd_bus_message_get_creds3, + sd_bus_message_get_errno3, + sd_bus_message_get_error3, + sd_bus_message_get_monotonic_usec3, + sd_bus_message_get_sender3, + sd_bus_message_get_signature3, + sd_bus_message_get_type3, + sd_bus_message_new3, + sd_bus_message_new_method_call3, + sd_bus_message_new_method_error3, + sd_bus_message_new_signal3, + sd_bus_message_new_signal_to3, + sd_bus_message_open_container3, + sd_bus_message_peek_type3, + sd_bus_message_read3, + sd_bus_message_read_array3, + sd_bus_message_read_basic3, + sd_bus_message_read_strv3, + sd_bus_message_rewind3, + sd_bus_message_seal3, + sd_bus_message_send3, + sd_bus_message_set_allow_interactive_authorization3, + sd_bus_message_set_destination3, + sd_bus_message_set_expect_reply3, + sd_bus_message_set_sender3, + sd_bus_message_skip3, + sd_bus_message_verify_type3, + sd_bus_negotiate_fds3, + sd_bus_new3, + sd_bus_path_encode3, + sd_bus_process3, + sd_bus_query_sender_creds3, + sd_bus_query_sender_privilege3, + sd_bus_reply_method_error3, + sd_bus_reply_method_return3, + sd_bus_request_name3, + sd_bus_send3, + sd_bus_send_to3, + sd_bus_set_address3, + sd_bus_set_allow_interactive_authorization3, + sd_bus_set_bus_client3, + sd_bus_set_close_on_exit3, + sd_bus_set_connected_signal3, + sd_bus_set_description3, + sd_bus_set_exit_on_disconnect3, + sd_bus_set_method_call_timeout3, + sd_bus_set_monitor3, + sd_bus_set_property3, + sd_bus_set_propertyv3, + sd_bus_set_sender3, + sd_bus_set_server3, + sd_bus_set_watch_bind3 + sd_bus_slot_get_current_handler3, + sd_bus_slot_get_current_message3, + sd_bus_slot_get_current_userdata3, + sd_bus_slot_set_description3, + sd_bus_slot_set_destroy_callback3, + sd_bus_slot_set_floating3, + sd_bus_slot_set_userdata3, + sd_bus_start3, + sd_bus_track_add_name3, + sd_bus_track_new3 + for more information about the functions available. diff --git a/man/sd-device.xml b/man/sd-device.xml index 4950781f84b..a22db76e04c 100644 --- a/man/sd-device.xml +++ b/man/sd-device.xml @@ -44,9 +44,10 @@ libudev.h. See - sd_device_get_syspath3, -sd_device_ref3 - + + sd_device_get_syspath3, + sd_device_ref3 + for more information about the functions available. diff --git a/man/smbios-type-11.xml b/man/smbios-type-11.xml index ea7cf4c085c..18d17bd9356 100644 --- a/man/smbios-type-11.xml +++ b/man/smbios-type-11.xml @@ -37,7 +37,7 @@ - Core OS Command Line Arguments + Strings The following strings are supported: @@ -52,7 +52,7 @@ System and Service Credentials for details. - + diff --git a/man/sysctl.d.xml b/man/sysctl.d.xml index 4d810e6c0c4..116ae9b3142 100644 --- a/man/sysctl.d.xml +++ b/man/sysctl.d.xml @@ -21,9 +21,11 @@ - /etc/sysctl.d/*.conf - /run/sysctl.d/*.conf - /usr/lib/sysctl.d/*.conf + + /etc/sysctl.d/*.conf + /run/sysctl.d/*.conf + /usr/lib/sysctl.d/*.conf + key.name.under.proc.sys = some value key/name/under/proc/sys = some value diff --git a/man/systemctl.xml b/man/systemctl.xml index 21447e8ed9a..1882cd756ca 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1855,7 +1855,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err for more details. The patterns are matched against the primary names of units currently in memory, and patterns which do not match anything are silently skipped. For example: - # systemctl stop sshd@*.service + # systemctl stop "sshd@*.service" will stop all sshd@.service instances. Note that alias names of units, and units that aren't in memory are not considered for glob expansion. @@ -2607,9 +2607,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - When used with the reboot command, indicate to the system's firmware to - reboot into the firmware setup interface. Note that this functionality is not available on all - systems. + When used with the reboot, poweroff, or + halt command, indicate to the system's firmware to reboot into the firmware + setup interface for the next boot. Note that this functionality is not available on all systems. + @@ -2619,10 +2620,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - When used with the reboot command, indicate to the system's boot loader to - show the boot loader menu on the following boot. Takes a time value as parameter — indicating the - menu timeout. Pass zero in order to disable the menu timeout. Note that not all boot loaders - support this functionality. + When used with the reboot, poweroff, or + halt command, indicate to the system's boot loader to show the boot loader menu + on the following boot. Takes a time value as parameter — indicating the menu timeout. Pass zero + in order to disable the menu timeout. Note that not all boot loaders support this functionality. + @@ -2632,10 +2634,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - When used with the reboot command, indicate to the system's boot loader to - boot into a specific boot loader entry on the following boot. Takes a boot loader entry identifier - as argument, or help in order to list available entries. Note that not all boot - loaders support this functionality. + When used with the reboot, poweroff, or + halt command, indicate to the system's boot loader to boot into a specific + boot loader entry on the following boot. Takes a boot loader entry identifier as argument, + or help in order to list available entries. Note that not all boot loaders + support this functionality. diff --git a/man/systemd-ask-password-console.service.xml b/man/systemd-ask-password-console.service.xml index 03b7317cd35..90ce8cfbccd 100644 --- a/man/systemd-ask-password-console.service.xml +++ b/man/systemd-ask-password-console.service.xml @@ -25,10 +25,12 @@ - systemd-ask-password-console.service - systemd-ask-password-console.path - systemd-ask-password-wall.service - systemd-ask-password-wall.path + + systemd-ask-password-console.service + systemd-ask-password-console.path + systemd-ask-password-wall.service + systemd-ask-password-wall.path + diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index 8fd885cb264..40e07ce24d9 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -36,8 +36,8 @@ supports tokens and credentials of the following kind to be enrolled: - PKCS#11 security tokens and smartcards that may carry an RSA key pair (e.g. various - YubiKeys) + PKCS#11 security tokens and smartcards that may carry an RSA or EC key pair (e.g. + various YubiKeys) FIDO2 security tokens that implement the hmac-secret extension (most FIDO2 keys, including YubiKeys) @@ -317,9 +317,16 @@ smartcard URI referring to the token. Alternatively the special value auto may be specified, in order to automatically determine the URI of a currently plugged in security token (of which there must be exactly one). The special value list may be used to - enumerate all suitable PKCS#11 tokens currently plugged in. The security token must contain an RSA - key pair which is used to encrypt the randomly generated key that is used to unlock the LUKS2 - volume. The encrypted key is then stored in the LUKS2 JSON token header area. + enumerate all suitable PKCS#11 tokens currently plugged in. + + The PKCS#11 token must contain an RSA or EC key pair which will be used to unlock a LUKS2 volume. + For RSA, a randomly generated volume key is encrypted with a public key in the token, and stored in + the LUKS2 JSON token header area. To unlock a volume, the stored encrypted volume key will be decrypted + with a private key in the token. For ECC, ECDH algorithm is used: we generate a pair of EC keys in + the same EC group, then derive a shared secret using the generated private key and the public key + in the token. The derived shared secret is used as a volume key. The generated public key is + stored in the LUKS2 JSON token header area. The generated private key is erased. To unlock a volume, + we derive the shared secret with the stored public key and a private key in the token. In order to unlock a LUKS2 volume with an enrolled PKCS#11 security token, specify the option in the respective /etc/crypttab line: diff --git a/man/systemd-fsck@.service.xml b/man/systemd-fsck@.service.xml index 78c661bfad8..ae3574e2280 100644 --- a/man/systemd-fsck@.service.xml +++ b/man/systemd-fsck@.service.xml @@ -24,10 +24,12 @@ - systemd-fsck@.service - systemd-fsck-root.service - systemd-fsck-usr.service - /usr/lib/systemd/systemd-fsck + + systemd-fsck@.service + systemd-fsck-root.service + systemd-fsck-usr.service + /usr/lib/systemd/systemd-fsck + diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml index 31435b28658..48d1afcaf87 100644 --- a/man/systemd-journald.service.xml +++ b/man/systemd-journald.service.xml @@ -29,14 +29,16 @@ - systemd-journald.service - systemd-journald.socket - systemd-journald-dev-log.socket - systemd-journald-audit.socket - systemd-journald@.service - systemd-journald@.socket - systemd-journald-varlink@.socket - /usr/lib/systemd/systemd-journald + + systemd-journald.service + systemd-journald.socket + systemd-journald-dev-log.socket + systemd-journald-audit.socket + systemd-journald@.service + systemd-journald@.socket + systemd-journald-varlink@.socket + /usr/lib/systemd/systemd-journald + diff --git a/man/systemd-run.xml b/man/systemd-run.xml index 5be9823c373..e6cf2a3bcb0 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -176,11 +176,14 @@ Make the new .service or .scope unit part - of the inherited slice. This option can be combined with . - - An inherited slice is located within systemd-run slice. Example: if - systemd-run slice is foo.slice, and the - argument is bar, the unit will be placed under the + of the slice the systemd-run itself has been invoked in. This option may be + combined with , in which case the slice specified via + is placed within the slice the systemd-run command is + invoked in. + + Example: consider systemd-run being invoked in the slice + foo.slice, and the argument is + bar. The unit will then be placed under foo-bar.slice. @@ -485,6 +488,31 @@ + + + + By default, if the specified command fails the invoked unit will be marked failed + (though possibly still unloaded, see , above), and this is reported in the + logs. If this switch is specified this is suppressed and any non-success exit status/code of the + command is treated as success. + + + + + + + + Change the terminal background color to the specified ANSI color as long as the + session lasts. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as + 40, 41, …, 47, 48;2;…, + 48;5;…. See ANSI + Escape Code (Wikipedia) for details. + + + + + @@ -677,7 +705,8 @@ $ systemd-run --user --wait -p SuccessExitStatus=SIGUSR1 --expand-environment=no systemd.resource-control5, systemd.timer5, systemd-mount1, - machinectl1 + machinectl1, + uid01 diff --git a/man/systemd-sleep.conf.xml b/man/systemd-sleep.conf.xml index ee13ce8703f..9afa8b28bb3 100644 --- a/man/systemd-sleep.conf.xml +++ b/man/systemd-sleep.conf.xml @@ -22,10 +22,12 @@ - /etc/systemd/sleep.conf - /etc/systemd/sleep.conf.d/*.conf - /run/systemd/sleep.conf.d/*.conf - /usr/lib/systemd/sleep.conf.d/*.conf + + /etc/systemd/sleep.conf + /etc/systemd/sleep.conf.d/*.conf + /run/systemd/sleep.conf.d/*.conf + /usr/lib/systemd/sleep.conf.d/*.conf + diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml index 6e853336c22..385f1b74db1 100644 --- a/man/systemd-stub.xml +++ b/man/systemd-stub.xml @@ -25,14 +25,16 @@ - /usr/lib/systemd/boot/efi/linuxx64.efi.stub - /usr/lib/systemd/boot/efi/linuxia32.efi.stub - /usr/lib/systemd/boot/efi/linuxaa64.efi.stub - ESP/.../foo.efi.extra.d/*.addon.efi - ESP/.../foo.efi.extra.d/*.cred - ESP/.../foo.efi.extra.d/*.raw - ESP/loader/addons/*.addon.efi - ESP/loader/credentials/*.cred + + /usr/lib/systemd/boot/efi/linuxx64.efi.stub + /usr/lib/systemd/boot/efi/linuxia32.efi.stub + /usr/lib/systemd/boot/efi/linuxaa64.efi.stub + ESP/.../foo.efi.extra.d/*.addon.efi + ESP/.../foo.efi.extra.d/*.cred + ESP/.../foo.efi.extra.d/*.raw + ESP/loader/addons/*.addon.efi + ESP/loader/credentials/*.cred + diff --git a/man/systemd-suspend.service.xml b/man/systemd-suspend.service.xml index 02daecf33ba..d15a3da21e2 100644 --- a/man/systemd-suspend.service.xml +++ b/man/systemd-suspend.service.xml @@ -26,11 +26,13 @@ - systemd-suspend.service - systemd-hibernate.service - systemd-hybrid-sleep.service - systemd-suspend-then-hibernate.service - /usr/lib/systemd/system-sleep + + systemd-suspend.service + systemd-hibernate.service + systemd-hybrid-sleep.service + systemd-suspend-then-hibernate.service + /usr/lib/systemd/system-sleep + diff --git a/man/systemd-sysext.xml b/man/systemd-sysext.xml index 76076931d5f..6180d1c4f57 100644 --- a/man/systemd-sysext.xml +++ b/man/systemd-sysext.xml @@ -31,7 +31,7 @@ COMMAND - systemd-sysext.service + systemd-sysext.service systemd-confext @@ -39,8 +39,7 @@ COMMAND - systemd-confext.service - + systemd-confext.service diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index 95e1e2951c8..28ac3639883 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -35,16 +35,22 @@ System units: -systemd-tmpfiles-setup.service -systemd-tmpfiles-setup-dev-early.service -systemd-tmpfiles-setup-dev.service -systemd-tmpfiles-clean.service -systemd-tmpfiles-clean.timer + + systemd-tmpfiles-setup.service + systemd-tmpfiles-setup-dev-early.service + systemd-tmpfiles-setup-dev.service + systemd-tmpfiles-clean.service + systemd-tmpfiles-clean.timer + + User units: -systemd-tmpfiles-setup.service -systemd-tmpfiles-clean.service -systemd-tmpfiles-clean.timer + + systemd-tmpfiles-setup.service + systemd-tmpfiles-clean.service + systemd-tmpfiles-clean.timer + + diff --git a/man/systemd.environment-generator.xml b/man/systemd.environment-generator.xml index 856f6a641b1..151c6ace43c 100644 --- a/man/systemd.environment-generator.xml +++ b/man/systemd.environment-generator.xml @@ -31,19 +31,19 @@ &USER_ENV_GENERATOR_DIR;/some-generator - - /run/systemd/system-environment-generators/* -/etc/systemd/system-environment-generators/* -/usr/local/lib/systemd/system-environment-generators/* -&SYSTEM_ENV_GENERATOR_DIR;/* - - - - /run/systemd/user-environment-generators/* -/etc/systemd/user-environment-generators/* -/usr/local/lib/systemd/user-environment-generators/* -&USER_ENV_GENERATOR_DIR;/* - + + /run/systemd/system-environment-generators/* + /etc/systemd/system-environment-generators/* + /usr/local/lib/systemd/system-environment-generators/* + &SYSTEM_ENV_GENERATOR_DIR;/* + + + + /run/systemd/user-environment-generators/* + /etc/systemd/user-environment-generators/* + /usr/local/lib/systemd/user-environment-generators/* + &USER_ENV_GENERATOR_DIR;/* + diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 525303c6ebf..37fa6e4f3c1 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -703,12 +703,15 @@ SetLoginEnvironment= - Takes a boolean parameter that controls whether to set $HOME, - $LOGNAME, and $SHELL environment variables. If unset, this is - controlled by whether User= is set. If true, they will always be set for system services, - i.e. even when the default user root is used. If false, the mentioned variables are not set - by systemd, no matter whether User= is used or not. This option normally has no effect - on user services, since these variables are typically inherited from user manager's own environment anyway. + Takes a boolean parameter that controls whether to set the $HOME, + $LOGNAME, and $SHELL environment variables. If not set, this + defaults to true if User=, DynamicUser= or + PAMName= are set, false otherwise. If set to true, the variables will always be + set for system services, i.e. even when the default user root is used. If set to + false, the mentioned variables are not set by the service manager, no matter whether + User=, DynamicUser=, or PAMName= are used or + not. This option normally has no effect on services of the per-user service manager, since in that + case these variables are typically inherited from user manager's own environment anyway. diff --git a/man/systemd.generator.xml b/man/systemd.generator.xml index b216ef96d0e..06fe7ec9d9e 100644 --- a/man/systemd.generator.xml +++ b/man/systemd.generator.xml @@ -30,19 +30,19 @@ late-dir - - /run/systemd/system-generators/* -/etc/systemd/system-generators/* -/usr/local/lib/systemd/system-generators/* -&SYSTEM_GENERATOR_DIR;/* - - - - /run/systemd/user-generators/* -/etc/systemd/user-generators/* -/usr/local/lib/systemd/user-generators/* -&USER_GENERATOR_DIR;/* - + + /run/systemd/system-generators/* + /etc/systemd/system-generators/* + /usr/local/lib/systemd/system-generators/* + &SYSTEM_GENERATOR_DIR;/* + + + + /run/systemd/user-generators/* + /etc/systemd/user-generators/* + /usr/local/lib/systemd/user-generators/* + &USER_GENERATOR_DIR;/* + diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml index e9c00c935dc..e2e8a0919a3 100644 --- a/man/systemd.net-naming-scheme.xml +++ b/man/systemd.net-naming-scheme.xml @@ -531,6 +531,47 @@ particular version of systemd). + + Limiting the use of specific sysfs attributes + + When creating names for network cards, some naming schemes use data from sysfs populated + by the kernel. This means that although a specific naming scheme in udev is picked, + the network card's name can still change when a new kernel version adds a new sysfs attribute. + For example if kernel starts setting the phys_port_name, udev will append the + "nphys_port_name" suffix to the device name. + + + + ID_NET_NAME_ALLOW=BOOL + + This udev property sets a fallback policy for reading a sysfs attribute. + If set to 0 udev will not read any sysfs attribute by default, unless it is + explicitly allowlisted, see below. If set to 1 udev can use any sysfs attribute + unless it is explicitly forbidden. The default value is 1. + + + + + + + ID_NET_NAME_ALLOW_sysfsattr=BOOL + + This udev property explicitly states if udev shall use the specified + sysfsattr, when composing the device name. + + + + + + + With these options, users can set an allowlist or denylist for sysfs attributes. To create + an allowlist, the user needs to set ID_NET_NAME_ALLOW=0 for the device and then list + the allowed attributes with the + ID_NET_NAME_ALLOW_sysfsattr=1 + options. In case of a denylist, the user needs to provide the list of denied attributes with + the ID_NET_NAME_ALLOW_sysfsattr=0 options. + + Examples @@ -617,6 +658,36 @@ ID_NET_NAME_PATH=enp0s29u1u2 ID_NET_NAME_MAC=enx026d3c00000a ID_NET_NAME_PATH=encf5f0 + + + Set an allowlist for reading sysfs attributes for network card naming + + /etc/udev/hwdb.d/50-net-naming-allowlist.hwdb +net:naming:drvirtio_net:* + ID_NET_NAME_ALLOW=0 + ID_NET_NAME_ALLOW_ACPI_INDEX=1 + ID_NET_NAME_ALLOW_ADDR_ASSIGN_TYPE=1 + ID_NET_NAME_ALLOW_ADDRESS=1 + ID_NET_NAME_ALLOW_ARI_ENABLED=1 + ID_NET_NAME_ALLOW_DEV_PORT=1 + ID_NET_NAME_ALLOW_FUNCTION_ID=1 + ID_NET_NAME_ALLOW_IFLINK=1 + ID_NET_NAME_ALLOW_INDEX=1 + ID_NET_NAME_ALLOW_LABEL=1 + ID_NET_NAME_ALLOW_PHYS_PORT_NAME=1 + ID_NET_NAME_ALLOW_TYPE=1 + + + + Set a denylist so that specified sysfs attribute are ignored + + /etc/udev/hwdb.d/50-net-naming-denylist.hwdb +net:naming:drvirtio_net:* + ID_NET_NAME_ALLOW=1 + ID_NET_NAME_ALLOW_DEV_PORT=0 + ID_NET_NAME_ALLOW_PHYS_PORT_NAME=0 + + diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 3436a32b115..7dc447d0a7f 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1715,8 +1715,10 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix Id= - The id of the next hop. Takes an integer in the range 1…4294967295. If unspecified, - then automatically chosen by kernel. + The id of the next hop. Takes an integer in the range 1…4294967295. + This is mandatory if ManageForeignNextHops=no is specified in + networkd.conf5. + Otherwise, if unspecified, an unused ID will be automatically picked. @@ -3976,9 +3978,9 @@ ServerAddress=192.168.0.1/24 - + - + [IPv6Prefix] Section Options One or more [IPv6Prefix] sections contain the IPv6 prefixes that are announced via Router Advertisements. See RFC 4861 for further @@ -4053,9 +4055,9 @@ ServerAddress=192.168.0.1/24 - + - + [IPv6RoutePrefix] Section Options One or more [IPv6RoutePrefix] sections contain the IPv6 prefix routes that are announced via Router Advertisements. See @@ -4085,9 +4087,9 @@ ServerAddress=192.168.0.1/24 - + - + [IPv6PREF64Prefix] Section Options One or more [IPv6PREF64Prefix] sections contain the IPv6 PREF64 (or NAT64) prefixes that are announced via Router Advertisements. See RFC 8781 for further @@ -4114,480 +4116,486 @@ ServerAddress=192.168.0.1/24 - + - + [Bridge] Section Options - The [Bridge] section accepts the following keys: - - - UnicastFlood= - - Takes a boolean. Controls whether the bridge should flood - traffic for which an FDB entry is missing and the destination - is unknown through this port. When unset, the kernel's default will be used. - - - - - - - MulticastFlood= - - Takes a boolean. Controls whether the bridge should flood - traffic for which an MDB entry is missing and the destination - is unknown through this port. When unset, the kernel's default will be used. - - - - - - - MulticastToUnicast= - - Takes a boolean. Multicast to unicast works on top of the multicast snooping feature of - the bridge. Which means unicast copies are only delivered to hosts which are interested in it. - When unset, the kernel's default will be used. - - - - - - - NeighborSuppression= - - Takes a boolean. Configures whether ARP and ND neighbor suppression is enabled for - this port. When unset, the kernel's default will be used. - - - - - - - Learning= - - Takes a boolean. Configures whether MAC address learning is enabled for - this port. When unset, the kernel's default will be used. - - - - - - - HairPin= - - Takes a boolean. Configures whether traffic may be sent back out of the port on which it - was received. When this flag is false, then the bridge will not forward traffic back out of the - receiving port. When unset, the kernel's default will be used. - - - - - - Isolated= - - Takes a boolean. Configures whether this port is isolated or not. Within a bridge, - isolated ports can only communicate with non-isolated ports. When set to true, this port can only - communicate with other ports whose Isolated setting is false. When set to false, this port - can communicate with any other ports. When unset, the kernel's default will be used. - - - - - - UseBPDU= - - Takes a boolean. Configures whether STP Bridge Protocol Data Units will be - processed by the bridge port. When unset, the kernel's default will be used. - - - - - - FastLeave= - - Takes a boolean. This flag allows the bridge to immediately stop multicast - traffic on a port that receives an IGMP Leave message. It is only used with - IGMP snooping if enabled on the bridge. When unset, the kernel's default will be used. - - - - - - AllowPortToBeRoot= - - Takes a boolean. Configures whether a given port is allowed to - become a root port. Only used when STP is enabled on the bridge. - When unset, the kernel's default will be used. - - - - - - ProxyARP= - - Takes a boolean. Configures whether proxy ARP to be enabled on this port. - When unset, the kernel's default will be used. - - - - - - ProxyARPWiFi= - - Takes a boolean. Configures whether proxy ARP to be enabled on this port - which meets extended requirements by IEEE 802.11 and Hotspot 2.0 specifications. - When unset, the kernel's default will be used. - - - - - - MulticastRouter= - - Configures this port for having multicast routers attached. A port with a multicast - router will receive all multicast traffic. Takes one of no - to disable multicast routers on this port, query to let the system detect - the presence of routers, permanent to permanently enable multicast traffic - forwarding on this port, or temporary to enable multicast routers temporarily - on this port, not depending on incoming queries. When unset, the kernel's default will be used. - - - - - - Cost= - - Sets the "cost" of sending packets of this interface. - Each port in a bridge may have a different speed and the cost - is used to decide which link to use. Faster interfaces - should have lower costs. It is an integer value between 1 and - 65535. - - - - - - Priority= - - Sets the "priority" of sending packets on this interface. - Each port in a bridge may have a different priority which is used - to decide which link to use. Lower value means higher priority. - It is an integer value between 0 to 63. Networkd does not set any - default, meaning the kernel default value of 32 is used. - - - - - + The [Bridge] section accepts the following keys: + + + + UnicastFlood= + + Takes a boolean. Controls whether the bridge should flood + traffic for which an FDB entry is missing and the destination + is unknown through this port. When unset, the kernel's default will be used. + + + + + + + MulticastFlood= + + Takes a boolean. Controls whether the bridge should flood + traffic for which an MDB entry is missing and the destination + is unknown through this port. When unset, the kernel's default will be used. + + + + + + + MulticastToUnicast= + + Takes a boolean. Multicast to unicast works on top of the multicast snooping feature of + the bridge. Which means unicast copies are only delivered to hosts which are interested in it. + When unset, the kernel's default will be used. + + + + + + + NeighborSuppression= + + Takes a boolean. Configures whether ARP and ND neighbor suppression is enabled for + this port. When unset, the kernel's default will be used. + + + + + + + Learning= + + Takes a boolean. Configures whether MAC address learning is enabled for + this port. When unset, the kernel's default will be used. + + + + + + + HairPin= + + Takes a boolean. Configures whether traffic may be sent back out of the port on which it + was received. When this flag is false, then the bridge will not forward traffic back out of the + receiving port. When unset, the kernel's default will be used. + + + + + + Isolated= + + Takes a boolean. Configures whether this port is isolated or not. Within a bridge, + isolated ports can only communicate with non-isolated ports. When set to true, this port can only + communicate with other ports whose Isolated setting is false. When set to false, this port + can communicate with any other ports. When unset, the kernel's default will be used. + + + + + + UseBPDU= + + Takes a boolean. Configures whether STP Bridge Protocol Data Units will be + processed by the bridge port. When unset, the kernel's default will be used. + + + + + + FastLeave= + + Takes a boolean. This flag allows the bridge to immediately stop multicast + traffic on a port that receives an IGMP Leave message. It is only used with + IGMP snooping if enabled on the bridge. When unset, the kernel's default will be used. + + + + + + AllowPortToBeRoot= + + Takes a boolean. Configures whether a given port is allowed to + become a root port. Only used when STP is enabled on the bridge. + When unset, the kernel's default will be used. + + + + + + ProxyARP= + + Takes a boolean. Configures whether proxy ARP to be enabled on this port. + When unset, the kernel's default will be used. + + + + + + ProxyARPWiFi= + + Takes a boolean. Configures whether proxy ARP to be enabled on this port + which meets extended requirements by IEEE 802.11 and Hotspot 2.0 specifications. + When unset, the kernel's default will be used. + + + + + + MulticastRouter= + + Configures this port for having multicast routers attached. A port with a multicast + router will receive all multicast traffic. Takes one of no + to disable multicast routers on this port, query to let the system detect + the presence of routers, permanent to permanently enable multicast traffic + forwarding on this port, or temporary to enable multicast routers temporarily + on this port, not depending on incoming queries. When unset, the kernel's default will be used. + + + + + + Cost= + + Sets the "cost" of sending packets of this interface. + Each port in a bridge may have a different speed and the cost + is used to decide which link to use. Faster interfaces + should have lower costs. It is an integer value between 1 and + 65535. + + + + + + Priority= + + Sets the "priority" of sending packets on this interface. + Each port in a bridge may have a different priority which is used + to decide which link to use. Lower value means higher priority. + It is an integer value between 0 to 63. Networkd does not set any + default, meaning the kernel default value of 32 is used. + + + + + + [BridgeFDB] Section Options - The [BridgeFDB] section manages the forwarding database table of a port and accepts the following - keys. Specify several [BridgeFDB] sections to configure several static MAC table entries. - - - MACAddress= - - As in the [Network] section. This key is mandatory. + The [BridgeFDB] section manages the forwarding database table of a port and accepts the following + keys. Specify several [BridgeFDB] sections to configure several static MAC table entries. + + + + MACAddress= + + As in the [Network] section. This key is mandatory. - - - - Destination= - - Takes an IP address of the destination VXLAN tunnel endpoint. + + + + Destination= + + Takes an IP address of the destination VXLAN tunnel endpoint. - - - - VLANId= - - The VLAN ID for the new static MAC table entry. If - omitted, no VLAN ID information is appended to the new static MAC - table entry. - - - - - - VNI= - - The VXLAN Network Identifier (or VXLAN Segment ID) to use to connect to - the remote VXLAN tunnel endpoint. Takes a number in the range 1…16777215. - Defaults to unset. - - - - - - AssociatedWith= - - Specifies where the address is associated with. Takes one of use, - self, master or router. - use means the address is in use. User space can use this option to - indicate to the kernel that the fdb entry is in use. self means - the address is associated with the port drivers fdb. Usually hardware. master - means the address is associated with master devices fdb. router means - the destination address is associated with a router. Note that it's valid if the referenced - device is a VXLAN type device and has route shortcircuit enabled. Defaults to self. + + + + VLANId= + + The VLAN ID for the new static MAC table entry. If + omitted, no VLAN ID information is appended to the new static MAC + table entry. + + + + + + VNI= + + The VXLAN Network Identifier (or VXLAN Segment ID) to use to connect to + the remote VXLAN tunnel endpoint. Takes a number in the range 1…16777215. + Defaults to unset. - - - - OutgoingInterface= - - Specifies the name or index of the outgoing interface for the VXLAN device driver to - reach the remote VXLAN tunnel endpoint. Defaults to unset. - - - - - + + + + AssociatedWith= + + Specifies where the address is associated with. Takes one of use, + self, master or router. + use means the address is in use. User space can use this option to + indicate to the kernel that the fdb entry is in use. self means + the address is associated with the port drivers fdb. Usually hardware. master + means the address is associated with master devices fdb. router means + the destination address is associated with a router. Note that it's valid if the referenced + device is a VXLAN type device and has route shortcircuit enabled. Defaults to self. + + + + + + OutgoingInterface= + + Specifies the name or index of the outgoing interface for the VXLAN device driver to + reach the remote VXLAN tunnel endpoint. Defaults to unset. + + + + + + [BridgeMDB] Section Options - The [BridgeMDB] section manages the multicast membership entries forwarding database table of a port and accepts the following - keys. Specify several [BridgeMDB] sections to configure several permanent multicast membership entries. + The [BridgeMDB] section manages the multicast membership entries forwarding database table of a port and accepts the following + keys. Specify several [BridgeMDB] sections to configure several permanent multicast membership entries. - - - MulticastGroupAddress= - - Specifies the IPv4 or IPv6 multicast group address to add. This setting is mandatory. + + + MulticastGroupAddress= + + Specifies the IPv4 or IPv6 multicast group address to add. This setting is mandatory. - - - - VLANId= - - The VLAN ID for the new entry. Valid ranges are 0 (no VLAN) to 4094. Optional, defaults to 0. + + + + VLANId= + + The VLAN ID for the new entry. Valid ranges are 0 (no VLAN) to 4094. Optional, defaults to 0. - - - + + + [LLDP] Section Options - The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the following - keys: - - - MUDURL= - - When configured, the specified Manufacturer Usage Descriptions (MUD) URL will be sent in - LLDP packets. The syntax and semantics are the same as for MUDURL= in the - [DHCPv4] section described above. - - The MUD URLs received via LLDP packets are saved and can be read using the - sd_lldp_neighbor_get_mud_url() function. - - - - - + The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the following + keys: + + + + MUDURL= + + When configured, the specified Manufacturer Usage Descriptions (MUD) URL will be sent in + LLDP packets. The syntax and semantics are the same as for MUDURL= in the + [DHCPv4] section described above. + + The MUD URLs received via LLDP packets are saved and can be read using the + sd_lldp_neighbor_get_mud_url() function. + + + + + [CAN] Section Options - The [CAN] section manages the Controller Area Network (CAN bus) and accepts the - following keys: - - - BitRate= - - The bitrate of CAN device in bits per second. The usual SI prefixes (K, M) with the base of 1000 can - be used here. Takes a number in the range 1…4294967295. - - - - - - SamplePoint= - - Optional sample point in percent with one decimal (e.g. 75%, - 87.5%) or permille (e.g. 875‰). This will be ignored when - BitRate= is unspecified. + The [CAN] section manages the Controller Area Network (CAN bus) and accepts the + following keys: + + + BitRate= + + The bitrate of CAN device in bits per second. The usual SI prefixes (K, M) with the base of 1000 can + be used here. Takes a number in the range 1…4294967295. + + + + + + SamplePoint= + + Optional sample point in percent with one decimal (e.g. 75%, + 87.5%) or permille (e.g. 875‰). This will be ignored when + BitRate= is unspecified. - - - - TimeQuantaNSec= - PropagationSegment= - PhaseBufferSegment1= - PhaseBufferSegment2= - SyncJumpWidth= - - Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the - synchronization jump width, which allow one to define the CAN bit-timing in a hardware - independent format as proposed by the Bosch CAN 2.0 Specification. - TimeQuantaNSec= takes a timespan in nanoseconds. - PropagationSegment=, PhaseBufferSegment1=, - PhaseBufferSegment2=, and SyncJumpWidth= take number - of time quantum specified in TimeQuantaNSec= and must be an unsigned - integer in the range 0…4294967295. These settings except for - SyncJumpWidth= will be ignored when BitRate= is - specified. - - - - - - DataBitRate= - DataSamplePoint= - - The bitrate and sample point for the data phase, if CAN-FD is used. These settings are - analogous to the BitRate= and SamplePoint= keys. - - - - - - DataTimeQuantaNSec= - DataPropagationSegment= - DataPhaseBufferSegment1= - DataPhaseBufferSegment2= - DataSyncJumpWidth= - - 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 TimeQuantaNSec= or related settings. - - - - - - FDMode= - - Takes a boolean. When yes, 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 DataBitRate= and DataSamplePoint= keys, or - DataTimeQuanta= and related settings. + + + + TimeQuantaNSec= + PropagationSegment= + PhaseBufferSegment1= + PhaseBufferSegment2= + SyncJumpWidth= + + Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the + synchronization jump width, which allow one to define the CAN bit-timing in a hardware + independent format as proposed by the Bosch CAN 2.0 Specification. + TimeQuantaNSec= takes a timespan in nanoseconds. + PropagationSegment=, PhaseBufferSegment1=, + PhaseBufferSegment2=, and SyncJumpWidth= take number + of time quantum specified in TimeQuantaNSec= and must be an unsigned + integer in the range 0…4294967295. These settings except for + SyncJumpWidth= will be ignored when BitRate= is + specified. + + + + + + DataBitRate= + DataSamplePoint= + + The bitrate and sample point for the data phase, if CAN-FD is used. These settings are + analogous to the BitRate= and SamplePoint= keys. - - - - FDNonISO= - - Takes a boolean. When yes, non-ISO CAN-FD mode is enabled for the - interface. When unset, the kernel's default will be used. + + + + DataTimeQuantaNSec= + DataPropagationSegment= + DataPhaseBufferSegment1= + DataPhaseBufferSegment2= + DataSyncJumpWidth= + + 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 TimeQuantaNSec= or related settings. + + + + + + FDMode= + + Takes a boolean. When yes, 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 DataBitRate= and DataSamplePoint= keys, or + DataTimeQuanta= and related settings. + + + + + + FDNonISO= + + Takes a boolean. When yes, non-ISO CAN-FD mode is enabled for the + interface. When unset, the kernel's default will be used. + + + + + + RestartSec= + + Automatic restart delay time. If set to a non-zero value, a restart of the CAN controller will be + triggered automatically in case of a bus-off condition after the specified delay time. Subsecond delays can + be specified using decimals (e.g. 0.1s) or a ms or + us postfix. Using infinity or 0 will turn the + automatic restart off. By default automatic restart is disabled. + + + + + + Termination= + + Takes a boolean or a termination resistor value in ohm in the range 0…65535. When + yes, the termination resistor is set to 120 ohm. When + no or 0 is set, the termination resistor is disabled. + When unset, the kernel's default will be used. - - - - RestartSec= - - Automatic restart delay time. If set to a non-zero value, a restart of the CAN controller will be - triggered automatically in case of a bus-off condition after the specified delay time. Subsecond delays can - be specified using decimals (e.g. 0.1s) or a ms or - us postfix. Using infinity or 0 will turn the - automatic restart off. By default automatic restart is disabled. - - - - - - Termination= - - Takes a boolean or a termination resistor value in ohm in the range 0…65535. When - yes, the termination resistor is set to 120 ohm. When - no or 0 is set, the termination resistor is disabled. - When unset, the kernel's default will be used. - - - - - - TripleSampling= - - Takes a boolean. When yes, three samples (instead of one) are used to determine - the value of a received bit by majority rule. When unset, the kernel's default will be used. + + + + TripleSampling= + + Takes a boolean. When yes, three samples (instead of one) are used to determine + the value of a received bit by majority rule. When unset, the kernel's default will be used. - - - - BusErrorReporting= - - Takes a boolean. When yes, reporting of CAN bus errors is activated - (those include single bit, frame format, and bit stuffing errors, unable to send dominant bit, - unable to send recessive bit, bus overload, active error announcement, error occurred on - transmission). When unset, the kernel's default will be used. Note: in case of a CAN bus with a - single CAN device, sending a CAN frame may result in a huge number of CAN bus errors. + + + + BusErrorReporting= + + Takes a boolean. When yes, reporting of CAN bus errors is activated + (those include single bit, frame format, and bit stuffing errors, unable to send dominant bit, + unable to send recessive bit, bus overload, active error announcement, error occurred on + transmission). When unset, the kernel's default will be used. Note: in case of a CAN bus with a + single CAN device, sending a CAN frame may result in a huge number of CAN bus errors. - - - - ListenOnly= - - Takes a boolean. When yes, listen-only mode is enabled. When the - interface is in listen-only mode, the interface neither transmit CAN frames nor send ACK - bit. Listen-only mode is important to debug CAN networks without interfering with the - communication or acknowledge the CAN frame. When unset, the kernel's default will be used. - + + + + ListenOnly= + + Takes a boolean. When yes, listen-only mode is enabled. When the + interface is in listen-only mode, the interface neither transmit CAN frames nor send ACK + bit. Listen-only mode is important to debug CAN networks without interfering with the + communication or acknowledge the CAN frame. When unset, the kernel's default will be used. + - - - - Loopback= - - Takes a boolean. When yes, 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. + + + + Loopback= + + Takes a boolean. When yes, 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. - - - - OneShot= - - Takes a boolean. When yes, one-shot mode is enabled. When unset, - the kernel's default will be used. + + + + OneShot= + + Takes a boolean. When yes, one-shot mode is enabled. When unset, + the kernel's default will be used. - - - - PresumeAck= - - Takes a boolean. When yes, the interface will ignore missing CAN - ACKs. When unset, the kernel's default will be used. + + + + PresumeAck= + + Takes a boolean. When yes, the interface will ignore missing CAN + ACKs. When unset, the kernel's default will be used. - - - - ClassicDataLengthCode= - - Takes a boolean. When yes, the interface will handle the 4bit data - length code (DLC). When unset, the kernel's default will be used. + + + + ClassicDataLengthCode= + + Takes a boolean. When yes, the interface will handle the 4bit data + length code (DLC). When unset, the kernel's default will be used. - - - + + + [IPoIB] Section Options - The [IPoIB] section manages the IP over Infiniband and accepts the following keys: - - - - + The [IPoIB] section manages the IP over Infiniband and accepts the following keys: + + + + + @@ -4601,7 +4609,7 @@ ServerAddress=192.168.0.1/24 Specifies the parent Queueing Discipline (qdisc). Takes one of clsact or ingress. This is mandatory. - + @@ -5857,47 +5865,54 @@ ServerAddress=192.168.0.1/24 [BridgeVLAN] Section Options - The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts the - following keys. Specify several [BridgeVLAN] sections to configure several VLAN entries. The - VLANFiltering= option has to be enabled, see the [Bridge] section in - systemd.netdev5. - - - - VLAN= - - The VLAN ID allowed on the port. This can be either a single ID or a range M-N. Takes an - integer in the range 1…4094. This setting can be specified multiple times. If an empty string is - assigned, then the all previous assignments are cleared. - - - - - - EgressUntagged= - - The VLAN ID specified here will be used to untag frames on egress. Configuring - EgressUntagged= implicates the use of VLAN= above and will - enable the VLAN ID for ingress as well. This can be either a single ID or a range M-N. This - setting can be specified multiple times. If an empty string is assigned, then the all previous - assignments are cleared. - - - - - - PVID= - - The port VLAN ID specified here is assigned to all untagged frames at ingress. Takes an - VLAN ID or negative boolean value (e.g. no). When false, the currently - assigned port VLAN ID will be dropped. Configuring PVID= implicates the use of - VLAN= setting in the above and will enable the VLAN ID for ingress as well. - Defaults to unset, and will keep the assigned port VLAN ID if exists. - - - - - + + The [BridgeVLAN] section manages the VLAN ID configurations of a bridge master or port, and accepts the + following keys. To make the settings in this section take an effect, + VLANFiltering= option has to be enabled on the bridge master, see the [Bridge] + section in + systemd.netdev5. + If at least one valid settings specified in this section in a .network file for an interface, all + assigned VLAN IDs on the interface that are not configured in the .network file will be removed. If + VLAN IDs on an interface need to be managed by other tools, then the settings in this section cannot + be used in the matching .network file. + + + + + VLAN= + + The VLAN ID allowed on the port. This can be either a single ID or a range M-N. Takes an + integer in the range 1…4094. This setting can be specified multiple times. If an empty string is + assigned, then the all previous assignments are cleared. + + + + + + EgressUntagged= + + The VLAN ID specified here will be used to untag frames on egress. Configuring + EgressUntagged= implicates the use of VLAN= above and will + enable the VLAN ID for ingress as well. This can be either a single ID or a range M-N. This + setting can be specified multiple times. If an empty string is assigned, then the all previous + assignments are cleared. + + + + + + PVID= + + The port VLAN ID specified here is assigned to all untagged frames at ingress. Takes an + VLAN ID or negative boolean value (e.g. no). When false, the currently + assigned port VLAN ID will be dropped. Configuring PVID= implicates the use of + VLAN= setting in the above and will enable the VLAN ID for ingress as well. + Defaults to unset, and will keep the assigned port VLAN ID if exists. + + + + + diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index 79806199e57..786031c7557 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -24,9 +24,11 @@ - /etc/systemd/nspawn/machine.nspawn - /run/systemd/nspawn/machine.nspawn - /var/lib/machines/machine.nspawn + + /etc/systemd/nspawn/machine.nspawn + /run/systemd/nspawn/machine.nspawn + /var/lib/machines/machine.nspawn + diff --git a/man/systemd.pcrlock.xml b/man/systemd.pcrlock.xml index 5687db50218..1dcba3fe532 100644 --- a/man/systemd.pcrlock.xml +++ b/man/systemd.pcrlock.xml @@ -21,17 +21,18 @@ - -/etc/pcrlock.d/*.pcrlock -/etc/pcrlock.d/*.pcrlock.d/*.pcrlock -/run/pcrlock.d/*.pcrlock -/run/pcrlock.d/*.pcrlock.d/*.pcrlock -/var/lib/pcrlock.d/*.pcrlock -/var/lib/pcrlock.d/*.pcrlock.d/*.pcrlock -/usr/local/pcrlock.d/*.pcrlock -/usr/local/pcrlock.d/*.pcrlock.d/*.pcrlock -/usr/lib/pcrlock.d/*.pcrlock -/usr/lib/pcrlock.d/*.pcrlock.d/*.pcrlock + + /etc/pcrlock.d/*.pcrlock + /etc/pcrlock.d/*.pcrlock.d/*.pcrlock + /run/pcrlock.d/*.pcrlock + /run/pcrlock.d/*.pcrlock.d/*.pcrlock + /var/lib/pcrlock.d/*.pcrlock + /var/lib/pcrlock.d/*.pcrlock.d/*.pcrlock + /usr/local/pcrlock.d/*.pcrlock + /usr/local/pcrlock.d/*.pcrlock.d/*.pcrlock + /usr/lib/pcrlock.d/*.pcrlock + /usr/lib/pcrlock.d/*.pcrlock.d/*.pcrlock + diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml index 5d46a88c3e9..26e090ea2cc 100644 --- a/man/systemd.preset.xml +++ b/man/systemd.preset.xml @@ -21,12 +21,14 @@ - /etc/systemd/system-preset/*.preset - /run/systemd/system-preset/*.preset - /usr/lib/systemd/system-preset/*.preset - /etc/systemd/user-preset/*.preset - /run/systemd/user-preset/*.preset - /usr/lib/systemd/user-preset/*.preset + + /etc/systemd/system-preset/*.preset + /run/systemd/system-preset/*.preset + /usr/lib/systemd/system-preset/*.preset + /etc/systemd/user-preset/*.preset + /run/systemd/user-preset/*.preset + /usr/lib/systemd/user-preset/*.preset + diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml index f7f0df18aab..2a2d03b29fd 100644 --- a/man/systemd.system-credentials.xml +++ b/man/systemd.system-credentials.xml @@ -270,6 +270,16 @@ + + + home.create.* + + Creates a home area for the specified user with the user record data passed in. For details see + homectl1. + + + + diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 7fed74a2273..c21c8b82219 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -40,37 +40,41 @@ System Unit Search Path - /etc/systemd/system.control/* -/run/systemd/system.control/* -/run/systemd/transient/* -/run/systemd/generator.early/* -/etc/systemd/system/* -/etc/systemd/system.attached/* -/run/systemd/system/* -/run/systemd/system.attached/* -/run/systemd/generator/* -… -/usr/lib/systemd/system/* -/run/systemd/generator.late/* + + /etc/systemd/system.control/* + /run/systemd/system.control/* + /run/systemd/transient/* + /run/systemd/generator.early/* + /etc/systemd/system/* + /etc/systemd/system.attached/* + /run/systemd/system/* + /run/systemd/system.attached/* + /run/systemd/generator/* + … + /usr/lib/systemd/system/* + /run/systemd/generator.late/* + User Unit Search Path - ~/.config/systemd/user.control/* -$XDG_RUNTIME_DIR/systemd/user.control/* -$XDG_RUNTIME_DIR/systemd/transient/* -$XDG_RUNTIME_DIR/systemd/generator.early/* -~/.config/systemd/user/* -$XDG_CONFIG_DIRS/systemd/user/* -/etc/systemd/user/* -$XDG_RUNTIME_DIR/systemd/user/* -/run/systemd/user/* -$XDG_RUNTIME_DIR/systemd/generator/* -$XDG_DATA_HOME/systemd/user/* -$XDG_DATA_DIRS/systemd/user/* -… -/usr/lib/systemd/user/* -$XDG_RUNTIME_DIR/systemd/generator.late/* + + ~/.config/systemd/user.control/* + $XDG_RUNTIME_DIR/systemd/user.control/* + $XDG_RUNTIME_DIR/systemd/transient/* + $XDG_RUNTIME_DIR/systemd/generator.early/* + ~/.config/systemd/user/* + $XDG_CONFIG_DIRS/systemd/user/* + /etc/systemd/user/* + $XDG_RUNTIME_DIR/systemd/user/* + /run/systemd/user/* + $XDG_RUNTIME_DIR/systemd/generator/* + $XDG_DATA_HOME/systemd/user/* + $XDG_DATA_DIRS/systemd/user/* + … + /usr/lib/systemd/user/* + $XDG_RUNTIME_DIR/systemd/generator.late/* + diff --git a/man/sysupdate.d.xml b/man/sysupdate.d.xml index 00b4ac887d9..b5c89811f5c 100644 --- a/man/sysupdate.d.xml +++ b/man/sysupdate.d.xml @@ -22,10 +22,11 @@ - /etc/sysupdate.d/*.conf -/run/sysupdate.d/*.conf -/usr/lib/sysupdate.d/*.conf - + + /etc/sysupdate.d/*.conf + /run/sysupdate.d/*.conf + /usr/lib/sysupdate.d/*.conf + diff --git a/man/sysusers.d.xml b/man/sysusers.d.xml index e7cd2855d93..5c126741a0a 100644 --- a/man/sysusers.d.xml +++ b/man/sysusers.d.xml @@ -22,9 +22,11 @@ - /etc/sysusers.d/*.conf - /run/sysusers.d/*.conf - /usr/lib/sysusers.d/*.conf + + /etc/sysusers.d/*.conf + /run/sysusers.d/*.conf + /usr/lib/sysusers.d/*.conf + #Type Name ID GECOS Home directory Shell diff --git a/man/timesyncd.conf.xml b/man/timesyncd.conf.xml index e804f5fb841..b2000383760 100644 --- a/man/timesyncd.conf.xml +++ b/man/timesyncd.conf.xml @@ -22,10 +22,12 @@ - /etc/systemd/timesyncd.conf - /etc/systemd/timesyncd.conf.d/*.conf - /run/systemd/timesyncd.conf.d/*.conf - /usr/lib/systemd/timesyncd.conf.d/*.conf + + /etc/systemd/timesyncd.conf + /etc/systemd/timesyncd.conf.d/*.conf + /run/systemd/timesyncd.conf.d/*.conf + /usr/lib/systemd/timesyncd.conf.d/*.conf + diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 971b7e6a9e9..1800de903aa 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -21,22 +21,23 @@ tmpfiles.d - Configuration for creation, deletion and cleaning of - volatile and temporary files + Configuration for creation, deletion, and cleaning of files and directories - /etc/tmpfiles.d/*.conf -/run/tmpfiles.d/*.conf -/usr/lib/tmpfiles.d/*.conf - - - ~/.config/user-tmpfiles.d/*.conf -$XDG_RUNTIME_DIR/user-tmpfiles.d/*.conf -~/.local/share/user-tmpfiles.d/*.conf -… -/usr/share/user-tmpfiles.d/*.conf - + + /etc/tmpfiles.d/*.conf + /run/tmpfiles.d/*.conf + /usr/lib/tmpfiles.d/*.conf + + + + ~/.config/user-tmpfiles.d/*.conf + $XDG_RUNTIME_DIR/user-tmpfiles.d/*.conf + ~/.local/share/user-tmpfiles.d/*.conf + … + /usr/share/user-tmpfiles.d/*.conf + #Type Path Mode User Group Age Argument f /file/to/create mode user group - content diff --git a/man/uid0.xml b/man/uid0.xml new file mode 100644 index 00000000000..0d36ca77a4f --- /dev/null +++ b/man/uid0.xml @@ -0,0 +1,232 @@ + + + + + + + + uid0 + systemd + + + + uid0 + 1 + + + + uid0 + Elevate privileges + + + + + uid0 + OPTIONS + COMMAND + + + + + Description + + uid0 may be used to temporarily and interactively acquire elavated or different + privileges. It serves a similar purpose as sudo8, but + operates differently in a couple of key areas: + + + No execution or security context credentials are inherited from the caller into the + invoked commands, as they are invoked from a fresh, isolated service forked off the service + manager. + + Authentication takes place via polkit, thus isolating the + authentication prompt from the terminal (if possible). + + An independent pseudo-tty is allocated for the invoked command, detaching its lifecycle and + isolating it for security. + + No SetUID/SetGID file access bit functionality is used for the implementation. + + + Altogether this should provide a safer and more robust alternative to the sudo + mechanism, in particular in OS environments where SetUID/SetGID support is not available (for example by + setting the NoNewPrivileges= variable in + systemd-system.conf5). + + Any session invoked via uid0 will run through the + systemd-uid0 PAM stack. + + Note that uid0 is implemented as an alternative multi-call invocation of + systemd-run1. + + + + Options + + The following options are understood: + + + + + + Do not query the user for authentication for privileged operations. + + + + + + + + Use this unit name instead of an automatically generated one. + + + + + + + + Sets a property on the service unit that is created. This option takes an assignment + in the same format as + systemctl1's + set-property command. + + + + + + + + + Provide a description for the service unit that is invoked. If not specified, + the command itself will be used as a description. See Description= in + systemd.unit5. + + + + + + + + + Make the new .service unit part of the specified slice, instead + of user.slice. + + + + + + + + + Make the new .service unit part of the slice the + uid0 itself has been invoked in. This option may be combined with + , in which case the slice specified via is placed + within the slice the uid0 command is invoked in. + + Example: consider uid0 being invoked in the slice + foo.slice, and the argument is + bar. The unit will then be placed under + foo-bar.slice. + + + + + + + + + + + + + Switches to the specified user/group instead of root. + + + + + + + + + Runs the invoked session with the specified nice level. + + + + + + + + + + Runs the invoked session with the specified working directory. If not specified + defaults to the client's current working directory if switching to the root user, or the target + user's home directory otherwise. + + + + + + + + + Runs the invoked session with the specified environment variable set. This parameter + may be used more than once to set multiple variables. When = and + VALUE are omitted, the value of the variable with the same name in the + invoking environment will be used. + + + + + + + + + Change the terminal background color to the specified ANSI color as long as the + session lasts. If not specified, the background will be tinted in a reddish tone when operating as + root, and in a yellowish tone when operating under another UID, as reminder of the changed + privileges. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as + 40, 41, …, 47, 48;2;…, + 48;5;…. See ANSI + Escape Code (Wikipedia) for details. Set to an empty string to disable. + + Example: --background=44 for a blue background. + + + + + + + + + + + All command line arguments after the first non-option argument become part of the command line of + the launched process. If no command line is specified an interactive shell is invoked. The shell to + invoke may be controlled via and currently defaults to the + originating user's shell (i.e. not the target user's!) if operating locally, or + /bin/sh when operating with . + + + + Exit status + + On success, 0 is returned. If uid0 failed to start the session or the specified command fails, a + non-zero return value will be returned. + + + + See Also + + systemd1, + systemd-run1, + sudo8, + machinectl1 + + + + diff --git a/man/ukify.xml b/man/ukify.xml index 9b7e20997ac..1865c1bfed7 100644 --- a/man/ukify.xml +++ b/man/ukify.xml @@ -140,6 +140,12 @@ Also see the description of / and . + + Other tools that may be useful for inspect UKIs: + llvm-objdump1 + and pe-inspect. + + diff --git a/meson.build b/meson.build index 06033b8b627..5ebc0a51e91 100644 --- a/meson.build +++ b/meson.build @@ -698,7 +698,7 @@ if run_command(ln, '--relative', '--help', check : false).returncode() != 0 error('ln does not support --relative (added in coreutils 8.16)') endif -############################################################ +##################################################################### gperf = find_program('gperf') @@ -724,7 +724,7 @@ message('gperf len type is @0@'.format(gperf_len_type)) conf.set('GPERF_LEN_TYPE', gperf_len_type, description : 'The type of gperf "len" parameter') -############################################################ +##################################################################### if not cc.has_header('sys/capability.h') error('POSIX caps headers not found') @@ -744,7 +744,7 @@ foreach header : ['crypt.h', cc.has_header(header)) endforeach -############################################################ +##################################################################### fallback_hostname = get_option('fallback-hostname') if fallback_hostname == '' or fallback_hostname[0] == '.' or fallback_hostname[0] == '-' @@ -1659,13 +1659,13 @@ conf.set10('ENABLE_TIMEDATECTL', get_option('timedated') or get_option('timesync conf.set10('SYSTEMD_SLOW_TESTS_DEFAULT', slow_tests) -############################################################ +##################################################################### pymod = import('python') python = pymod.find_installation('python3', required : true, modules : ['jinja2']) python_39 = python.language_version().version_compare('>=3.9') -############################################################ +##################################################################### if conf.get('BPF_FRAMEWORK') == 1 bpf_clang_flags = [ @@ -1814,7 +1814,7 @@ endif want_ukify = get_option('ukify').require(python_39, error_message : 'Python >= 3.9 required').allowed() conf.set10('ENABLE_UKIFY', want_ukify) -############################################################ +##################################################################### check_version_history_py = find_program('tools/check-version-history.py') elf2efi_py = find_program('tools/elf2efi.py') @@ -1832,7 +1832,7 @@ update_man_rules_py = find_program('tools/update-man-rules.py') update_syscall_tables_sh = find_program('tools/update-syscall-tables.sh') xml_helper_py = find_program('tools/xml_helper.py') -############################################################ +##################################################################### version_tag = get_option('version-tag') version_h = vcs_tag( @@ -1849,13 +1849,13 @@ if shared_lib_tag == '' shared_lib_tag = meson.project_version() endif -############################################################ +##################################################################### if get_option('b_coverage') userspace_c_args += ['-include', 'src/basic/coverage.h'] endif -############################################################ +##################################################################### config_h = configure_file( output : 'config.h', @@ -1873,7 +1873,7 @@ userspace = declare_dependency( man_page_depends = [] -############################################################ +##################################################################### simple_tests = [] libsystemd_tests = [] @@ -2006,7 +2006,7 @@ install_libudev_static = static_library( c_args : static_libudev_pic ? [] : ['-fno-PIC'], pic : static_libudev_pic) -############################################################ +##################################################################### runtest_env = custom_target( 'systemd-runtest.env', @@ -2027,7 +2027,7 @@ if '-O2' in c_args and '-flto=auto' in c_args test_cflags += cc.first_supported_argument('-Wno-maybe-uninitialized') endif -############################################################ +##################################################################### executable_template = { 'include_directories' : includes, @@ -2123,7 +2123,7 @@ module_additional_kwargs = { 'dependencies' : userspace, } -############################################################ +##################################################################### # systemd-analyze requires 'libcore' subdir('src/core') @@ -2241,7 +2241,7 @@ subdir('mime') alias_target('devel', libsystemd_pc, libudev_pc, systemd_pc, udev_pc) -############################################################ +##################################################################### foreach test : simple_tests executables += test_template + { 'sources' : [test] } @@ -2378,7 +2378,7 @@ endforeach alias_target('fuzzers', fuzzer_exes) -############################################################ +##################################################################### test_dlopen = executables_by_name.get('test-dlopen') @@ -2442,7 +2442,7 @@ foreach dict : modules endif endforeach -############################################################ +##################################################################### ukify = custom_target( 'ukify', @@ -2461,12 +2461,12 @@ if want_ukify libexecdir / 'ukify')) endif -############################################################ +##################################################################### subdir('rules.d') subdir('test') -############################################################ +##################################################################### subdir('docs/sysvinit') subdir('docs/var-log') @@ -2508,7 +2508,7 @@ install_subdir('LICENSES', install_emptydir(systemdstatedir) -############################################################ +##################################################################### # Ensure that changes to the docs/ directory do not break the # basic Github pages build. But only run it in developer mode, @@ -2524,7 +2524,7 @@ if get_option('mode') == 'developer' and want_tests != 'false' and jekyll.found( '--destination', project_build_root / '_site']) endif -############################################################ +##################################################################### check_help = find_program('tools/check-help.sh') check_version = find_program('tools/check-version.sh') @@ -2593,7 +2593,7 @@ foreach tuple : fuzz_sanitizers endif endforeach -############################################################ +##################################################################### if git.found() all_files = run_command( @@ -2650,7 +2650,7 @@ if git.found() 'HEAD']) endif -############################################################ +##################################################################### check_api_docs_sh = find_program('tools/check-api-docs.sh') run_target( @@ -2670,7 +2670,7 @@ if not meson.is_cross_build() command : [export_dbus_interfaces_py, '@OUTPUT@', dbus_programs]) endif -############################################################ +##################################################################### alt_time_epoch = run_command('date', '-Is', '-u', '-d', '@@0@'.format(time_epoch), check : true).stdout().strip() diff --git a/mkosi.conf b/mkosi.conf index 9961407a043..b02c24cdd90 100644 --- a/mkosi.conf +++ b/mkosi.conf @@ -4,7 +4,7 @@ Images=system [Output] -OutputDirectory=mkosi.output +@OutputDirectory=mkosi.output BuildDirectory=mkosi.builddir CacheDirectory=mkosi.cache @@ -18,8 +18,6 @@ Environment=ASAN_OPTIONS=verify_asan_link_order=false @Incremental=yes @QemuMem=2G @RuntimeSize=8G -# Make sure we don't trigger systemd-firstboot prompting for the root password. -Credentials=passwd.plaintext-password.root= KernelCommandLineExtra=systemd.crash_shell systemd.log_level=debug systemd.log_ratelimit_kmsg=0 @@ -37,3 +35,4 @@ KernelCommandLineExtra=systemd.crash_shell selinux=0 enforcing=0 systemd.early_core_pattern=/core + systemd.firstboot=no diff --git a/mkosi.images/base/mkosi.build.chroot b/mkosi.images/base/mkosi.build.chroot index f26098cedf5..9524fdb103a 100755 --- a/mkosi.images/base/mkosi.build.chroot +++ b/mkosi.images/base/mkosi.build.chroot @@ -44,7 +44,7 @@ EOF fi if [ ! -f "$BUILDDIR"/build.ninja ]; then - sysvinit_path=$(realpath /etc/init.d) + [[ -d /etc/rc.d/init.d ]] && sysvinit_path="/etc/rc.d/init.d" || sysvinit_path="/etc/init.d" if [ "$ID" = "centos" ] && [ "$VERSION" = "8" ]; then UKIFY="disabled" @@ -70,7 +70,7 @@ if [ ! -f "$BUILDDIR"/build.ninja ]; then -D tests=unsafe -D slow-tests="${SLOW_TESTS:-false}" -D create-log-dirs=false - -D pamconfdir=no + -D pamconfdir=/usr/lib/pam.d/ -D utmp=true -D hibernate=true -D ldconfig=true diff --git a/mkosi.images/base/mkosi.conf b/mkosi.images/base/mkosi.conf index 6c6d0457752..be8586ce3b9 100644 --- a/mkosi.images/base/mkosi.conf +++ b/mkosi.images/base/mkosi.conf @@ -15,10 +15,11 @@ Packages= BuildPackages= acl - diffutils - gawk binutils clang + diffutils + gawk + gdb gettext git gperf @@ -30,5 +31,6 @@ BuildPackages= pkgconf rsync sed + strace tar zstd diff --git a/mkosi.images/system/mkosi.conf b/mkosi.images/system/mkosi.conf index 7612f221cc5..6948f8eb7ce 100644 --- a/mkosi.images/system/mkosi.conf +++ b/mkosi.images/system/mkosi.conf @@ -33,6 +33,7 @@ Packages= strace systemd tmux + tar tree udev util-linux diff --git a/mkosi.images/system/mkosi.conf.d/10-centos-fedora.conf b/mkosi.images/system/mkosi.conf.d/10-centos-fedora.conf index 145c79bf63d..67d46432d40 100644 --- a/mkosi.images/system/mkosi.conf.d/10-centos-fedora.conf +++ b/mkosi.images/system/mkosi.conf.d/10-centos-fedora.conf @@ -14,6 +14,7 @@ Packages= integritysetup iproute iproute-tc + kernel-core libcap-ng-utils netcat openssh-server diff --git a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf b/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf index af4862d4b14..146e03a8955 100644 --- a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf +++ b/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf @@ -2,3 +2,7 @@ [Match] Distribution=centos + +[Content] +Packages= + kernel-modules # For squashfs support diff --git a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos8.conf b/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos8.conf deleted file mode 100644 index 30643e72b36..00000000000 --- a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos8.conf +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later - -[Match] -Release=8 - -[Content] -Packages= - kernel-core-4.18.0-521.el8 - kernel-modules-4.18.0-521.el8 # For squashfs support diff --git a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos9.conf b/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos9.conf deleted file mode 100644 index a21739f2300..00000000000 --- a/mkosi.images/system/mkosi.conf.d/10-centos/mkosi.conf.d/10-centos9.conf +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later - -[Match] -Release=9 - -[Content] -Packages= - kernel-core - kernel-modules diff --git a/mkosi.images/system/mkosi.conf.d/10-fedora.conf b/mkosi.images/system/mkosi.conf.d/10-fedora.conf index 281e9464f62..5863f03b19c 100644 --- a/mkosi.images/system/mkosi.conf.d/10-fedora.conf +++ b/mkosi.images/system/mkosi.conf.d/10-fedora.conf @@ -8,4 +8,4 @@ Packages= btrfs-progs compsize f2fs-tools - kernel-core + glibc-langpack-en diff --git a/mkosi.images/system/mkosi.postinst.chroot b/mkosi.images/system/mkosi.postinst.chroot index e0728de2798..0fec067ebb2 100755 --- a/mkosi.images/system/mkosi.postinst.chroot +++ b/mkosi.images/system/mkosi.postinst.chroot @@ -83,3 +83,12 @@ if [ "$ID" = "centos" ] && [ "$VERSION" = "8" ]; then alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1 alternatives --set python3 /usr/bin/python3.9 fi + +mkdir -p /usr/lib/sysusers.d +cat >/usr/lib/sysusers.d/testuser.conf </usr/lib/tmpfiles.d/testuser.conf <\n" -"Language-Team: Turkish \n" +"Language-Team: Turkish \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.18.2\n" +"X-Generator: Weblate 5.2.1\n" #: src/core/org.freedesktop.systemd1.policy.in:22 msgid "Send passphrase back to system" @@ -120,11 +121,14 @@ msgstr "Bir kullanıcının ev alanının parolasını değiştirmek kimlik doğ #, c-format msgid "Home of user %s is currently absent, please plug in the necessary storage device or backing file system." msgstr "" +"%s kullanıcısının ev dizini şu anda mevcut değil, lütfen gerekli depolama " +"aygıtını veya içeren dosya sistemini bağlayın." #: src/home/pam_systemd_home.c:292 #, c-format msgid "Too frequent login attempts for user %s, try again later." msgstr "" +"%s kullanıcısı için çok sık oturum açma denemesi, daha sonra tekrar deneyin." #: src/home/pam_systemd_home.c:304 msgid "Password: " @@ -134,116 +138,135 @@ msgstr "Parola: " #, c-format msgid "Password incorrect or not sufficient for authentication of user %s." msgstr "" +"Parola yanlış veya %s kullanıcısının kimlik doğrulaması için yeterli değil." #: src/home/pam_systemd_home.c:307 msgid "Sorry, try again: " -msgstr "" +msgstr "Üzgünüm, tekrar deneyin: " #: src/home/pam_systemd_home.c:329 msgid "Recovery key: " -msgstr "" +msgstr "Kurtarma anahtarı: " #: src/home/pam_systemd_home.c:331 #, c-format msgid "Password/recovery key incorrect or not sufficient for authentication of user %s." msgstr "" +"Parola/kurtarma anahtarı yanlış veya %s kullanıcısının kimlik doğrulaması " +"için yeterli değil." #: src/home/pam_systemd_home.c:332 msgid "Sorry, reenter recovery key: " -msgstr "" +msgstr "Üzgünüm, kurtarma anahtarını yeniden girin: " #: src/home/pam_systemd_home.c:352 #, c-format msgid "Security token of user %s not inserted." -msgstr "" +msgstr "%s kullanıcısının güvenlik belirteci girilmedi." #: src/home/pam_systemd_home.c:353 src/home/pam_systemd_home.c:356 msgid "Try again with password: " -msgstr "" +msgstr "Parola ile tekrar deneyin: " #: src/home/pam_systemd_home.c:355 #, c-format msgid "Password incorrect or not sufficient, and configured security token of user %s not inserted." msgstr "" +"Parola yanlış veya yeterli değil ve %s kullanıcısının yapılandırılan " +"güvenlik belirteci girilmedi." #: src/home/pam_systemd_home.c:376 msgid "Security token PIN: " -msgstr "" +msgstr "Güvenlik belirteci PIN kodu: " #: src/home/pam_systemd_home.c:393 #, c-format msgid "Please authenticate physically on security token of user %s." msgstr "" +"Lütfen %s kullanıcısının güvenlik belirteci ile fiziksel olarak kimlik " +"doğrulaması yapın." #: src/home/pam_systemd_home.c:404 #, c-format msgid "Please confirm presence on security token of user %s." -msgstr "" +msgstr "Lütfen %s kullanıcısının güvenlik belirtecinin varlığını doğrulayın." #: src/home/pam_systemd_home.c:415 #, c-format msgid "Please verify user on security token of user %s." msgstr "" +"Lütfen %s kullanıcısının güvenlik belirtecindeki kullanıcıyı doğrulayın." #: src/home/pam_systemd_home.c:424 msgid "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)" msgstr "" +"Güvenlik belirteci PIN kodu kilitli, lütfen önce kilidini açın. (Ä°pucu: " +"Çıkarma ve yeniden takma yeterli olabilir.)" #: src/home/pam_systemd_home.c:432 #, c-format msgid "Security token PIN incorrect for user %s." -msgstr "" +msgstr "Güvenlik belirteci PIN kodu %s kullanıcısı için yanlış." #: src/home/pam_systemd_home.c:433 src/home/pam_systemd_home.c:452 #: src/home/pam_systemd_home.c:471 msgid "Sorry, retry security token PIN: " -msgstr "" +msgstr "Üzgünüm, güvenlik belirteci PIN kodunu yeniden deneyin: " #: src/home/pam_systemd_home.c:451 #, c-format msgid "Security token PIN of user %s incorrect (only a few tries left!)" msgstr "" +"%s kullanıcısının güvenlik belirteci PIN kodu yanlış (sadece birkaç deneme " +"kaldı!)" #: src/home/pam_systemd_home.c:470 #, c-format msgid "Security token PIN of user %s incorrect (only one try left!)" msgstr "" +"%s kullanıcısının güvenlik belirteci PIN kodu yanlış (sadece bir deneme " +"kaldı!)" #: src/home/pam_systemd_home.c:616 #, c-format msgid "Home of user %s is currently not active, please log in locally first." msgstr "" +"%s kullanıcısının ev dizini şu anda etkin değil, lütfen önce yerel olarak " +"oturum açın." #: src/home/pam_systemd_home.c:618 #, c-format msgid "Home of user %s is currently locked, please unlock locally first." msgstr "" +"%s kullanıcısının ev dizini şu anda kilitli, lütfen önce yerel olarak " +"kilidini açın." #: src/home/pam_systemd_home.c:645 #, c-format msgid "Too many unsuccessful login attempts for user %s, refusing." msgstr "" +"%s kullanıcısı için çok fazla başarısız oturum açma denemesi, reddediliyor." #: src/home/pam_systemd_home.c:868 msgid "User record is blocked, prohibiting access." -msgstr "" +msgstr "Kullanıcı kaydı engellendi, erişim engelleniyor." #: src/home/pam_systemd_home.c:872 msgid "User record is not valid yet, prohibiting access." -msgstr "" +msgstr "Kullanıcı kaydı henüz geçerli değil, erişim engelleniyor." #: src/home/pam_systemd_home.c:876 msgid "User record is not valid anymore, prohibiting access." -msgstr "" +msgstr "Kullanıcı kaydı artık geçerli değil, erişim engelleniyor." #: src/home/pam_systemd_home.c:881 src/home/pam_systemd_home.c:932 msgid "User record not valid, prohibiting access." -msgstr "" +msgstr "Kullanıcı kaydı geçerli değil, erişim engelleniyor." #: src/home/pam_systemd_home.c:893 #, c-format msgid "Too many logins, try again in %s." -msgstr "" +msgstr "Çok fazla oturum açıldı, %s içinde tekrar deneyin." #: src/home/pam_systemd_home.c:904 msgid "Password change required." diff --git a/rules.d/50-udev-default.rules.in b/rules.d/50-udev-default.rules.in index 10234fd9e0b..bb241534e43 100644 --- a/rules.d/50-udev-default.rules.in +++ b/rules.d/50-udev-default.rules.in @@ -32,10 +32,7 @@ ACTION!="add", GOTO="default_end" SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666" SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666" -SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620" -SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620" -SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620" -SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620" +SUBSYSTEM=="tty", KERNEL=="tty[0-9]*|hvc[0-9]*|sclp_line[0-9]*|ttysclp[0-9]*|3270/tty[0-9]*", GROUP="tty", MODE="0620" SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty" KERNEL=="tty[A-Z]*[0-9]|ttymxc[0-9]*|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout" diff --git a/rules.d/75-net-description.rules b/rules.d/75-net-description.rules index 7e62f8b26b7..5ba70a65450 100644 --- a/rules.d/75-net-description.rules +++ b/rules.d/75-net-description.rules @@ -3,6 +3,8 @@ ACTION=="remove", GOTO="net_end" SUBSYSTEM!="net", GOTO="net_end" +IMPORT{builtin}="hwdb 'net:naming:dr$env{ID_NET_DRIVER}:'" + IMPORT{builtin}="net_id" SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" diff --git a/src/analyze/analyze-verify-util.c b/src/analyze/analyze-verify-util.c index 6fbd6fa54c3..bc61ebe2b17 100644 --- a/src/analyze/analyze-verify-util.c +++ b/src/analyze/analyze-verify-util.c @@ -152,7 +152,7 @@ int verify_set_unit_path(char **filenames) { * Treat explicit empty path to mean that nothing should be appended. */ old = getenv("SYSTEMD_UNIT_PATH"); if (!streq_ptr(old, "") && - !strextend_with_separator(&joined, ":", old ?: "")) + !strextend_with_separator(&joined, ":", strempty(old))) return -ENOMEM; assert_se(set_unit_path(joined) >= 0); @@ -201,19 +201,23 @@ static int verify_executables(Unit *u, const char *root) { assert(u); - ExecCommand *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; - RET_GATHER(r, verify_executable(u, exec, root)); + if (u->type == UNIT_MOUNT) + FOREACH_ARRAY(i, MOUNT(u)->exec_command, ELEMENTSOF(MOUNT(u)->exec_command)) + RET_GATHER(r, verify_executable(u, i, root)); if (u->type == UNIT_SERVICE) FOREACH_ARRAY(i, SERVICE(u)->exec_command, ELEMENTSOF(SERVICE(u)->exec_command)) - RET_GATHER(r, verify_executable(u, *i, root)); + LIST_FOREACH(command, j, *i) + RET_GATHER(r, verify_executable(u, j, root)); if (u->type == UNIT_SOCKET) FOREACH_ARRAY(i, SOCKET(u)->exec_command, ELEMENTSOF(SOCKET(u)->exec_command)) - RET_GATHER(r, verify_executable(u, *i, root)); + LIST_FOREACH(command, j, *i) + RET_GATHER(r, verify_executable(u, j, root)); + + if (u->type == UNIT_SWAP) + FOREACH_ARRAY(i, SWAP(u)->exec_command, ELEMENTSOF(SWAP(u)->exec_command)) + RET_GATHER(r, verify_executable(u, i, root)); return r; } diff --git a/src/basic/build.c b/src/basic/build.c index c587adad7b0..8fb32ab9b6e 100644 --- a/src/basic/build.c +++ b/src/basic/build.c @@ -138,6 +138,12 @@ const char* const systemd_features = " -LIBCRYPTSETUP" #endif +#if HAVE_LIBCRYPTSETUP_PLUGINS + " +LIBCRYPTSETUP_PLUGINS" +#else + " -LIBCRYPTSETUP_PLUGINS" +#endif + #if HAVE_LIBFDISK " +LIBFDISK" #else diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 18b16ecc0e5..68b15846292 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -2141,15 +2141,14 @@ int cg_kernel_controllers(Set **ret) { _cleanup_free_ char *controller = NULL; int enabled = 0; - errno = 0; if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { + if (ferror(f)) + return -errno; + if (feof(f)) break; - if (ferror(f)) - return errno_or_else(EIO); - return -EBADMSG; } diff --git a/src/basic/env-util.c b/src/basic/env-util.c index d3bf73385fb..7ac47732ba8 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -458,6 +458,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) { return strv_env_replace_consume(l, p); } +int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) { + int r; + + assert(l); + assert(key); + + if (!env_name_is_valid(key)) + return -EINVAL; + + if (!valuef) { + strv_env_unset(*l, key); + return 0; + } + + _cleanup_free_ char *value = NULL; + va_list ap; + va_start(ap, valuef); + r = vasprintf(&value, valuef, ap); + va_end(ap); + if (r < 0) + return -ENOMEM; + + char *p = strjoin(key, "=", value); + if (!p) + return -ENOMEM; + + return strv_env_replace_consume(l, p); +} + int _strv_env_assign_many(char ***l, ...) { va_list ap; int r; diff --git a/src/basic/env-util.h b/src/basic/env-util.h index f7fb1e90823..8e77cc71d6b 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -49,6 +49,7 @@ 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); +int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4); int _strv_env_assign_many(char ***l, ...) _sentinel_; #define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL) diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c index 0a6a54f8f13..12c256a474e 100644 --- a/src/basic/ether-addr-util.c +++ b/src/basic/ether-addr-util.c @@ -270,3 +270,11 @@ int parse_ether_addr(const char *s, struct ether_addr *ret) { *ret = a.ether; return 0; } + +void ether_addr_mark_random(struct ether_addr *addr) { + assert(addr); + + /* see eth_random_addr in the kernel */ + addr->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ + addr->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ +} diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h index 83ed77d6345..187e4ef5830 100644 --- a/src/basic/ether-addr-util.h +++ b/src/basic/ether-addr-util.h @@ -113,3 +113,5 @@ static inline bool ether_addr_is_global(const struct ether_addr *addr) { extern const struct hash_ops ether_addr_hash_ops; extern const struct hash_ops ether_addr_hash_ops_free; + +void ether_addr_mark_random(struct ether_addr *addr); diff --git a/src/basic/iovec-util.c b/src/basic/iovec-util.c index 991889a14eb..64569452832 100644 --- a/src/basic/iovec-util.c +++ b/src/basic/iovec-util.c @@ -62,8 +62,10 @@ char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const ch return x; } -void iovec_array_free(struct iovec *iovec, size_t n) { - FOREACH_ARRAY(i, iovec, n) +void iovec_array_free(struct iovec *iovec, size_t n_iovec) { + assert(iovec || n_iovec == 0); + + FOREACH_ARRAY(i, iovec, n_iovec) free(i->iov_base); free(iovec); diff --git a/src/basic/iovec-util.h b/src/basic/iovec-util.h index 39feabd4260..3f5cdd02e10 100644 --- a/src/basic/iovec-util.h +++ b/src/basic/iovec-util.h @@ -41,4 +41,4 @@ static inline bool iovec_is_set(const struct iovec *iovec) { char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value); char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value); -void iovec_array_free(struct iovec *iovec, size_t n); +void iovec_array_free(struct iovec *iovec, size_t n_iovec); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index dc868c9b8e9..0430e33e40d 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -123,8 +123,7 @@ int parse_ifindex(const char *s) { } int parse_mtu(int family, const char *s, uint32_t *ret) { - uint64_t u; - size_t m; + uint64_t u, m; int r; r = parse_size(s, 1024, &u); @@ -134,10 +133,16 @@ int parse_mtu(int family, const char *s, uint32_t *ret) { if (u > UINT32_MAX) return -ERANGE; - if (family == AF_INET6) + switch (family) { + case AF_INET: + m = IPV4_MIN_MTU; /* This is 68 */ + break; + case AF_INET6: m = IPV6_MIN_MTU; /* This is 1280 */ - else - m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */ + break; + default: + m = 0; + } if (u < m) return -ERANGE; diff --git a/src/basic/pidref.c b/src/basic/pidref.c index 69b5cad5759..d3821c1f544 100644 --- a/src/basic/pidref.c +++ b/src/basic/pidref.c @@ -106,6 +106,38 @@ int pidref_set_pidfd_consume(PidRef *pidref, int fd) { return r; } +int pidref_set_parent(PidRef *ret) { + _cleanup_(pidref_done) PidRef parent = PIDREF_NULL; + pid_t ppid; + int r; + + assert(ret); + + /* Acquires a pidref to our parent process. Deals with the fact that parent processes might exit, and + * we get reparented to other processes, with our old parent's PID already being recycled. */ + + ppid = getppid(); + for (;;) { + r = pidref_set_pid(&parent, ppid); + if (r < 0) + return r; + + if (parent.fd < 0) /* If pidfds are not available, then we are done */ + break; + + pid_t now_ppid = getppid(); + if (now_ppid == ppid) /* If our ppid is still the same, then we are done */ + break; + + /* Otherwise let's try again with the new ppid */ + ppid = now_ppid; + pidref_done(&parent); + } + + *ret = TAKE_PIDREF(parent); + return 0; +} + void pidref_done(PidRef *pidref) { assert(pidref); diff --git a/src/basic/pidref.h b/src/basic/pidref.h index dada069357d..a01d4cc85ba 100644 --- a/src/basic/pidref.h +++ b/src/basic/pidref.h @@ -38,7 +38,7 @@ int pidref_set_pidstr(PidRef *pidref, const char *pid); int pidref_set_pidfd(PidRef *pidref, int fd); int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/ int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */ - +int pidref_set_parent(PidRef *ret); static inline int pidref_set_self(PidRef *pidref) { return pidref_set_pid(pidref, 0); } diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index c1f0b2b974c..a9f7b87f289 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -6,11 +6,14 @@ #include "errno-util.h" #include "extract-word.h" #include "fd-util.h" +#include "fileio.h" #include "format-util.h" #include "macro.h" #include "missing_resource.h" +#include "process-util.h" #include "rlimit-util.h" #include "string-table.h" +#include "strv.h" #include "time-util.h" int setrlimit_closest(int resource, const struct rlimit *rlim) { @@ -426,3 +429,116 @@ int rlimit_nofile_safe(void) { return 1; } + +int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret) { + + static const char * const prefix_table[_RLIMIT_MAX] = { + [RLIMIT_CPU] = "Max cpu time", + [RLIMIT_FSIZE] = "Max file size", + [RLIMIT_DATA] = "Max data size", + [RLIMIT_STACK] = "Max stack size", + [RLIMIT_CORE] = "Max core file size", + [RLIMIT_RSS] = "Max resident set", + [RLIMIT_NPROC] = "Max processes", + [RLIMIT_NOFILE] = "Max open files", + [RLIMIT_MEMLOCK] = "Max locked memory", + [RLIMIT_AS] = "Max address space", + [RLIMIT_LOCKS] = "Max file locks", + [RLIMIT_SIGPENDING] = "Max pending signals", + [RLIMIT_MSGQUEUE] = "Max msgqueue size", + [RLIMIT_NICE] = "Max nice priority", + [RLIMIT_RTPRIO] = "Max realtime priority", + [RLIMIT_RTTIME] = "Max realtime timeout", + }; + + int r; + + assert(resource >= 0); + assert(resource < _RLIMIT_MAX); + assert(pid >= 0); + assert(ret); + + if (pid == 0 || pid == getpid_cached()) + return RET_NERRNO(getrlimit(resource, ret)); + + r = RET_NERRNO(prlimit(pid, resource, /* new_limit= */ NULL, ret)); + if (!ERRNO_IS_NEG_PRIVILEGE(r)) + return r; + + /* We don't have access? Then try to go via /proc/$PID/limits. Weirdly that's world readable in + * contrast to querying the data via prlimit() */ + + const char *p = procfs_file_alloca(pid, "limits"); + _cleanup_free_ char *limits = NULL; + + r = read_full_virtual_file(p, &limits, NULL); + if (r < 0) + return -EPERM; /* propagate original permission error if we can't access the limits file */ + + _cleanup_strv_free_ char **l = NULL; + l = strv_split(limits, "\n"); + if (!l) + return -ENOMEM; + + STRV_FOREACH(i, strv_skip(l, 1)) { + _cleanup_free_ char *soft = NULL, *hard = NULL; + uint64_t sv, hv; + const char *e; + + e = startswith(*i, prefix_table[resource]); + if (!e) + continue; + + if (*e != ' ') + continue; + + e += strspn(e, WHITESPACE); + + size_t n; + n = strcspn(e, WHITESPACE); + if (n == 0) + continue; + + soft = strndup(e, n); + if (!soft) + return -ENOMEM; + + e += n; + if (*e != ' ') + continue; + + e += strspn(e, WHITESPACE); + n = strcspn(e, WHITESPACE); + if (n == 0) + continue; + + hard = strndup(e, n); + if (!hard) + return -ENOMEM; + + if (streq(soft, "unlimited")) + sv = RLIM_INFINITY; + else { + r = safe_atou64(soft, &sv); + if (r < 0) + return r; + } + + if (streq(hard, "unlimited")) + hv = RLIM_INFINITY; + else { + r = safe_atou64(hard, &hv); + if (r < 0) + return r; + } + + *ret = (struct rlimit) { + .rlim_cur = sv, + .rlim_max = hv, + }; + + return 0; + } + + return -ENOTRECOVERABLE; +} diff --git a/src/basic/rlimit-util.h b/src/basic/rlimit-util.h index 202c3fdc449..afc1a1f2622 100644 --- a/src/basic/rlimit-util.h +++ b/src/basic/rlimit-util.h @@ -25,3 +25,5 @@ void rlimit_free_all(struct rlimit **rl); int rlimit_nofile_bump(int limit); int rlimit_nofile_safe(void); + +int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret); diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c index 5d9484626d8..f8f4e509ad2 100644 --- a/src/basic/signal-util.c +++ b/src/basic/signal-util.c @@ -120,39 +120,39 @@ int sigprocmask_many(int how, sigset_t *old, ...) { } static const char *const static_signal_table[] = { - [SIGHUP] = "HUP", - [SIGINT] = "INT", - [SIGQUIT] = "QUIT", - [SIGILL] = "ILL", - [SIGTRAP] = "TRAP", - [SIGABRT] = "ABRT", - [SIGBUS] = "BUS", - [SIGFPE] = "FPE", - [SIGKILL] = "KILL", - [SIGUSR1] = "USR1", - [SIGSEGV] = "SEGV", - [SIGUSR2] = "USR2", - [SIGPIPE] = "PIPE", - [SIGALRM] = "ALRM", - [SIGTERM] = "TERM", + [SIGHUP] = "HUP", + [SIGINT] = "INT", + [SIGQUIT] = "QUIT", + [SIGILL] = "ILL", + [SIGTRAP] = "TRAP", + [SIGABRT] = "ABRT", + [SIGBUS] = "BUS", + [SIGFPE] = "FPE", + [SIGKILL] = "KILL", + [SIGUSR1] = "USR1", + [SIGSEGV] = "SEGV", + [SIGUSR2] = "USR2", + [SIGPIPE] = "PIPE", + [SIGALRM] = "ALRM", + [SIGTERM] = "TERM", #ifdef SIGSTKFLT [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */ #endif - [SIGCHLD] = "CHLD", - [SIGCONT] = "CONT", - [SIGSTOP] = "STOP", - [SIGTSTP] = "TSTP", - [SIGTTIN] = "TTIN", - [SIGTTOU] = "TTOU", - [SIGURG] = "URG", - [SIGXCPU] = "XCPU", - [SIGXFSZ] = "XFSZ", + [SIGCHLD] = "CHLD", + [SIGCONT] = "CONT", + [SIGSTOP] = "STOP", + [SIGTSTP] = "TSTP", + [SIGTTIN] = "TTIN", + [SIGTTOU] = "TTOU", + [SIGURG] = "URG", + [SIGXCPU] = "XCPU", + [SIGXFSZ] = "XFSZ", [SIGVTALRM] = "VTALRM", - [SIGPROF] = "PROF", - [SIGWINCH] = "WINCH", - [SIGIO] = "IO", - [SIGPWR] = "PWR", - [SIGSYS] = "SYS" + [SIGPROF] = "PROF", + [SIGWINCH] = "WINCH", + [SIGIO] = "IO", + [SIGPWR] = "PWR", + [SIGSYS] = "SYS" }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int); diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h index 0b3e845bf46..72147a99040 100644 --- a/src/basic/siphash24.h +++ b/src/basic/siphash24.h @@ -25,12 +25,12 @@ void siphash24_compress(const void *in, size_t inlen, struct siphash *state); static inline void siphash24_compress_boolean(bool in, struct siphash *state) { uint8_t i = in; - siphash24_compress(&i, sizeof i, state); } static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) { - siphash24_compress(&in, sizeof in, state); + uint64_t u = htole64(in); + siphash24_compress(&u, sizeof u, state); } static inline void siphash24_compress_safe(const void *in, size_t inlen, struct siphash *state) { diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index beb64d8e6c7..4f28d16b5e4 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -872,13 +872,11 @@ bool address_label_valid(const char *p) { int getpeercred(int fd, struct ucred *ucred) { socklen_t n = sizeof(struct ucred); struct ucred u; - int r; assert(fd >= 0); assert(ucred); - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n); - if (r < 0) + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n) < 0) return -errno; if (n != sizeof(struct ucred)) @@ -907,8 +905,10 @@ int getpeersec(int fd, char **ret) { if (!s) return -ENOMEM; - if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) + if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) { + s[n] = 0; break; + } if (errno != ERANGE) return -errno; diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 3355b749cc0..b740b49775f 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -27,6 +27,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hexdecoct.h" #include "inotify-util.h" #include "io-util.h" #include "log.h" @@ -1551,3 +1552,218 @@ int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column) return 0; } + +void termios_disable_echo(struct termios *termios) { + assert(termios); + + termios->c_lflag &= ~(ICANON|ECHO); + termios->c_cc[VMIN] = 1; + termios->c_cc[VTIME] = 0; +} + +typedef enum BackgroundColorState { + BACKGROUND_TEXT, + BACKGROUND_ESCAPE, + BACKGROUND_BRACKET, + BACKGROUND_FIRST_ONE, + BACKGROUND_SECOND_ONE, + BACKGROUND_SEMICOLON, + BACKGROUND_R, + BACKGROUND_G, + BACKGROUND_B, + BACKGROUND_RED, + BACKGROUND_GREEN, + BACKGROUND_BLUE, +} BackgroundColorState; + +typedef struct BackgroundColorContext { + BackgroundColorState state; + uint32_t red, green, blue; + unsigned red_bits, green_bits, blue_bits; +} BackgroundColorContext; + +static int scan_background_color_response( + BackgroundColorContext *context, + const char *buf, + size_t size) { + + assert(context); + assert(buf || size == 0); + + for (size_t i = 0; i < size; i++) { + char c = buf[i]; + + switch (context->state) { + + case BACKGROUND_TEXT: + context->state = c == '\x1B' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT; + break; + + case BACKGROUND_ESCAPE: + context->state = c == ']' ? BACKGROUND_BRACKET : BACKGROUND_TEXT; + break; + + case BACKGROUND_BRACKET: + context->state = c == '1' ? BACKGROUND_FIRST_ONE : BACKGROUND_TEXT; + break; + + case BACKGROUND_FIRST_ONE: + context->state = c == '1' ? BACKGROUND_SECOND_ONE : BACKGROUND_TEXT; + break; + + case BACKGROUND_SECOND_ONE: + context->state = c == ';' ? BACKGROUND_SEMICOLON : BACKGROUND_TEXT; + break; + + case BACKGROUND_SEMICOLON: + context->state = c == 'r' ? BACKGROUND_R : BACKGROUND_TEXT; + break; + + case BACKGROUND_R: + context->state = c == 'g' ? BACKGROUND_G : BACKGROUND_TEXT; + break; + + case BACKGROUND_G: + context->state = c == 'b' ? BACKGROUND_B : BACKGROUND_TEXT; + break; + + case BACKGROUND_B: + context->state = c == ':' ? BACKGROUND_RED : BACKGROUND_TEXT; + break; + + case BACKGROUND_RED: + if (c == '/') + context->state = context->red_bits > 0 ? BACKGROUND_GREEN : BACKGROUND_TEXT; + else { + int d = unhexchar(c); + if (d < 0 || context->red_bits >= sizeof(context->red)*8) + context->state = BACKGROUND_TEXT; + else { + context->red = (context->red << 4) | d; + context->red_bits += 4; + } + } + break; + + case BACKGROUND_GREEN: + if (c == '/') + context->state = context->green_bits > 0 ? BACKGROUND_BLUE : BACKGROUND_TEXT; + else { + int d = unhexchar(c); + if (d < 0 || context->green_bits >= sizeof(context->green)*8) + context->state = BACKGROUND_TEXT; + else { + context->green = (context->green << 4) | d; + context->green_bits += 4; + } + } + break; + + case BACKGROUND_BLUE: + if (c == '\x07') { + if (context->blue_bits > 0) + return 1; /* success! */ + + context->state = BACKGROUND_TEXT; + } else { + int d = unhexchar(c); + if (d < 0 || context->blue_bits >= sizeof(context->blue)*8) + context->state = BACKGROUND_TEXT; + else { + context->blue = (context->blue << 4) | d; + context->blue_bits += 4; + } + } + break; + } + + /* Reset any colors we might have picked up */ + if (context->state == BACKGROUND_TEXT) { + /* reset color */ + context->red = context->green = context->blue = 0; + context->red_bits = context->green_bits = context->blue_bits = 0; + } + } + + return 0; /* all good, but not enough data yet */ +} + +int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue) { + int r; + + assert(ret_red); + assert(ret_green); + assert(ret_blue); + + if (!colors_enabled()) + return -EOPNOTSUPP; + + if (isatty(STDOUT_FILENO) < 1 || isatty(STDIN_FILENO) < 1) + return -EOPNOTSUPP; + + if (streq_ptr(getenv("TERM"), "linux")) { + /* Linux console is black */ + *ret_red = *ret_green = *ret_blue = 0.0; + return 0; + } + + struct termios old_termios; + if (tcgetattr(STDIN_FILENO, &old_termios) < 0) + return -errno; + + struct termios new_termios = old_termios; + termios_disable_echo(&new_termios); + + if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_termios) < 0) + return -errno; + + r = loop_write(STDOUT_FILENO, "\x1B]11;?\x07", SIZE_MAX); + if (r < 0) + goto finish; + + usec_t end = usec_add(now(CLOCK_MONOTONIC), 100 * USEC_PER_MSEC); + char buf[256]; + size_t buf_full = 0; + BackgroundColorContext context = {}; + + for (;;) { + usec_t n = now(CLOCK_MONOTONIC); + + if (n >= end) { + r = -EOPNOTSUPP; + goto finish; + } + + r = fd_wait_for_event(STDIN_FILENO, POLLIN, usec_sub_unsigned(end, n)); + if (r < 0) + goto finish; + + ssize_t l; + l = read(STDIN_FILENO, buf, sizeof(buf) - buf_full); + if (l < 0) { + r = -errno; + goto finish; + } + + buf_full += l; + assert(buf_full <= sizeof(buf)); + + r = scan_background_color_response(&context, buf, buf_full); + if (r < 0) + goto finish; + if (r > 0) { + assert(context.red_bits > 0); + *ret_red = (double) context.red / ((UINT64_C(1) << context.red_bits) - 1); + assert(context.green_bits > 0); + *ret_green = (double) context.green / ((UINT64_C(1) << context.green_bits) - 1); + assert(context.blue_bits > 0); + *ret_blue = (double) context.blue / ((UINT64_C(1) << context.blue_bits) - 1); + r = 0; + goto finish; + } + } + +finish: + (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_termios); + return r; +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index cae42887c44..1ec057c2dd5 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "macro.h" #include "time-util.h" @@ -168,7 +169,6 @@ bool underline_enabled(void); bool dev_console_colors_enabled(void); static inline bool colors_enabled(void) { - /* Returns true if colors are considered supported on our stdout. */ return get_color_mode() != COLOR_OFF; } @@ -286,3 +286,7 @@ static inline const char* ansi_highlight_green_red(bool b) { /* This assumes there is a 'tty' group */ #define TTY_MODE 0620 + +void termios_disable_echo(struct termios *termios); + +int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue); diff --git a/src/basic/time-util.h b/src/basic/time-util.h index ed4c1aabd48..29373477f48 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -219,6 +219,9 @@ static inline int usleep_safe(usec_t usec) { * ⚠️ Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not * CLOCK_MONOTONIC! */ + if (usec == 0) + return 0; + // FIXME: use RET_NERRNO() macro here. Currently, this header cannot include errno-util.h. return clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL) < 0 ? -errno : 0; } diff --git a/src/basic/uid-range.c b/src/basic/uid-range.c index 84635992761..d933d9fa5c1 100644 --- a/src/basic/uid-range.c +++ b/src/basic/uid-range.c @@ -180,6 +180,30 @@ bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) { return false; } +int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) { + uid_t uid_base, uid_shift, uid_range; + int r; + + assert(f); + assert(ret_base); + assert(ret_shift); + assert(ret_range); + + errno = 0; + r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range); + if (r == EOF) + return errno_or_else(ENOMSG); + assert(r >= 0); + if (r != 3) + return -EBADMSG; + + *ret_base = uid_base; + *ret_shift = uid_shift; + *ret_range = uid_range; + + return 0; +} + int uid_range_load_userns(UidRange **ret, const char *path) { _cleanup_(uid_range_freep) UidRange *range = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -212,18 +236,12 @@ int uid_range_load_userns(UidRange **ret, const char *path) { for (;;) { uid_t uid_base, uid_shift, uid_range; - int k; - - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range); - if (k == EOF) { - if (ferror(f)) - return errno_or_else(EIO); + r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range); + if (r == -ENOMSG) break; - } - if (k != 3) - return -EBADMSG; + if (r < 0) + return r; r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false); if (r < 0) diff --git a/src/basic/uid-range.h b/src/basic/uid-range.h index 461a5117373..bfe78926698 100644 --- a/src/basic/uid-range.h +++ b/src/basic/uid-range.h @@ -31,4 +31,6 @@ static inline bool uid_range_contains(const UidRange *range, uid_t uid) { return uid_range_covers(range, uid, 1); } +int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range); + int uid_range_load_userns(UidRange **ret, const char *path); diff --git a/src/basic/virt.c b/src/basic/virt.c index a0b6fbcd658..09aebabcd5e 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -21,6 +21,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "uid-range.h" #include "virt.h" enum { @@ -814,7 +815,7 @@ Virtualization detect_virtualization(void) { static int userns_has_mapping(const char *name) { _cleanup_fclose_ FILE *f = NULL; - uid_t a, b, c; + uid_t base, shift, range; int r; f = fopen(name, "re"); @@ -823,26 +824,22 @@ static int userns_has_mapping(const char *name) { return errno == ENOENT ? false : -errno; } - errno = 0; - r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &a, &b, &c); - if (r == EOF) { - if (ferror(f)) - return log_debug_errno(errno_or_else(EIO), "Failed to read %s: %m", name); - - log_debug("%s is empty, we're in an uninitialized user namespace", name); + r = uid_map_read_one(f, &base, &shift, &range); + if (r == -ENOMSG) { + log_debug("%s is empty, we're in an uninitialized user namespace.", name); return true; } - if (r != 3) - return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse %s: %m", name); + if (r < 0) + return log_debug_errno(r, "Failed to read %s: %m", name); - if (a == 0 && b == 0 && c == UINT32_MAX) { + if (base == 0 && shift == 0 && range == UINT32_MAX) { /* The kernel calls mappings_overlap() and does not allow overlaps */ log_debug("%s has a full 1:1 mapping", name); return false; } /* Anything else implies that we are in a user namespace */ - log_debug("Mapping found in %s, we're in a user namespace", name); + log_debug("Mapping found in %s, we're in a user namespace.", name); return true; } diff --git a/src/boot/bootctl-install.c b/src/boot/bootctl-install.c index bacbbb29390..d1bd3c681c7 100644 --- a/src/boot/bootctl-install.c +++ b/src/boot/bootctl-install.c @@ -318,6 +318,46 @@ static int create_subdirs(const char *root, const char * const *subdirs) { return 0; } +static int update_efi_boot_binaries(const char *esp_path, const char *source_path) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *p = NULL; + int r, ret = 0; + + r = chase_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path); + + FOREACH_DIRENT(de, d, break) { + _cleanup_close_ int fd = -EBADF; + _cleanup_free_ char *v = NULL; + + if (!endswith_no_case(de->d_name, ".efi")) + continue; + + fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name); + + r = get_file_version(fd, &v); + if (r == -ESRCH) + continue; /* No version information */ + if (r < 0) + return r; + if (startswith(v, "systemd-boot ")) { + _cleanup_free_ char *dest_path = NULL; + + dest_path = path_join(p, de->d_name); + if (!dest_path) + return log_oom(); + + RET_GATHER(ret, copy_file_with_version_check(source_path, dest_path, /* force = */ false)); + } + } + + return ret; +} static int copy_one_file(const char *esp_path, const char *name, bool force) { char *root = IN_SET(arg_install_source, ARG_INSTALL_SOURCE_AUTO, ARG_INSTALL_SOURCE_IMAGE) ? arg_root : NULL; @@ -371,9 +411,12 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) { if (r < 0) return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", v, esp_path); - r = copy_file_with_version_check(source_path, default_dest_path, force); - if (r < 0 && ret == 0) - ret = r; + RET_GATHER(ret, copy_file_with_version_check(source_path, default_dest_path, force)); + + /* If we were installed under any other name in /EFI/BOOT, make sure we update those binaries + * as well. */ + if (!force) + RET_GATHER(ret, update_efi_boot_binaries(esp_path, source_path)); } return ret; @@ -845,9 +888,6 @@ static int remove_boot_efi(const char *esp_path) { if (!endswith_no_case(de->d_name, ".efi")) continue; - if (!startswith_no_case(de->d_name, "boot")) - continue; - fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC); if (fd < 0) return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name); diff --git a/src/boot/bootctl-status.c b/src/boot/bootctl-status.c index 16b2eaed072..c8041c4bd1f 100644 --- a/src/boot/bootctl-status.c +++ b/src/boot/bootctl-status.c @@ -187,7 +187,6 @@ static int status_variables(void) { static int enumerate_binaries( const char *esp_path, const char *path, - const char *prefix, char **previous, bool *is_first) { @@ -213,9 +212,6 @@ static int enumerate_binaries( if (!endswith_no_case(de->d_name, ".efi")) continue; - if (prefix && !startswith_no_case(de->d_name, prefix)) - continue; - filename = path_join(p, de->d_name); if (!filename) return log_oom(); @@ -272,11 +268,11 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) { printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition)); printf("\n"); - r = enumerate_binaries(esp_path, "EFI/systemd", NULL, &last, &is_first); + r = enumerate_binaries(esp_path, "EFI/systemd", &last, &is_first); if (r < 0) goto fail; - k = enumerate_binaries(esp_path, "EFI/BOOT", "boot", &last, &is_first); + k = enumerate_binaries(esp_path, "EFI/BOOT", &last, &is_first); if (k < 0) { r = k; goto fail; diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 4237e694c01..4c0b3ddf9c2 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -2188,7 +2188,7 @@ int bus_cgroup_set_property( c->restrict_network_interfaces_is_allow_list = is_allow_list; STRV_FOREACH(s, l) { - if (!ifname_valid(*s)) { + if (!ifname_valid_full(*s, IFNAME_VALID_ALTERNATIVE)) { log_full(LOG_WARNING, "Invalid interface name, ignoring: %s", *s); continue; } diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 4daa1cefd33..2c6dce0a088 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -67,6 +67,7 @@ static BUS_DEFINE_PROPERTY_GET(property_get_cpu_sched_policy, "i", ExecContext, static BUS_DEFINE_PROPERTY_GET(property_get_cpu_sched_priority, "i", ExecContext, exec_context_get_cpu_sched_priority); static BUS_DEFINE_PROPERTY_GET(property_get_coredump_filter, "t", ExecContext, exec_context_get_coredump_filter); static BUS_DEFINE_PROPERTY_GET(property_get_timer_slack_nsec, "t", ExecContext, exec_context_get_timer_slack_nsec); +static BUS_DEFINE_PROPERTY_GET(property_get_set_login_environment, "b", ExecContext, exec_context_get_set_login_environment); static int property_get_environment_files( sd_bus *bus, @@ -1038,7 +1039,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SetLoginEnvironment", "b", bus_property_get_tristate, offsetof(ExecContext, set_login_environment), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SetLoginEnvironment", "b", property_get_set_login_environment, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SetCredential", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 41f4ee399ef..77cf6f003d7 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -166,9 +166,7 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_ r = bus_verify_manage_units_async_full( u, is_image ? "mount-image" : "bind-mount", - CAP_SYS_ADMIN, N_("Authentication is required to mount on '$(unit)'."), - true, message, error); if (r < 0) diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 48b7e10ea56..8b4983dcb5f 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -408,9 +408,7 @@ int bus_unit_method_start_generic( r = bus_verify_manage_units_async_full( u, verb, - CAP_SYS_ADMIN, polkit_message_for_job[job_type], - true, message, error); if (r < 0) @@ -491,9 +489,7 @@ int bus_unit_method_enqueue_job(sd_bus_message *message, void *userdata, sd_bus_ r = bus_verify_manage_units_async_full( u, jtype, - CAP_SYS_ADMIN, polkit_message_for_job[type], - true, message, error); if (r < 0) @@ -549,9 +545,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error * r = bus_verify_manage_units_async_full( u, "kill", - CAP_KILL, N_("Authentication is required to send a UNIX signal to the processes of '$(unit)'."), - true, message, error); if (r < 0) @@ -579,9 +573,7 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus r = bus_verify_manage_units_async_full( u, "reset-failed", - CAP_SYS_ADMIN, N_("Authentication is required to reset the \"failed\" state of '$(unit)'."), - true, message, error); if (r < 0) @@ -611,9 +603,7 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b r = bus_verify_manage_units_async_full( u, "set-property", - CAP_SYS_ADMIN, N_("Authentication is required to set properties on '$(unit)'."), - true, message, error); if (r < 0) @@ -641,9 +631,7 @@ int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *e r = bus_verify_manage_units_async_full( u, "ref", - CAP_SYS_ADMIN, - NULL, - false, + /* polkit_message= */ NULL, message, error); if (r < 0) @@ -712,9 +700,7 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error r = bus_verify_manage_units_async_full( u, "clean", - CAP_DAC_OVERRIDE, N_("Authentication is required to delete files and directories associated with '$(unit)'."), - true, message, error); if (r < 0) @@ -760,9 +746,7 @@ static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userda r = bus_verify_manage_units_async_full( u, perm, - CAP_SYS_ADMIN, N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."), - true, message, error); if (r < 0) diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c index d680a642686..822a17e49ff 100644 --- a/src/core/dbus-util.c +++ b/src/core/dbus-util.c @@ -151,9 +151,7 @@ int bus_set_transient_usec_internal( int bus_verify_manage_units_async_full( Unit *u, const char *verb, - int capability, const char *polkit_message, - bool interactive, sd_bus_message *call, sd_bus_error *error) { @@ -171,11 +169,8 @@ int bus_verify_manage_units_async_full( return bus_verify_polkit_async( call, - capability, "org.freedesktop.systemd1.manage-units", details, - interactive, - UID_INVALID, &u->manager->polkit_registry, error); } diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h index 9464b25516d..ee944c166ce 100644 --- a/src/core/dbus-util.h +++ b/src/core/dbus-util.h @@ -249,7 +249,7 @@ static inline int bus_set_transient_usec(Unit *u, const char *name, usec_t *p, s static inline int bus_set_transient_usec_fix_0(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) { return bus_set_transient_usec_internal(u, name, p, true, message, flags, error); } -int bus_verify_manage_units_async_full(Unit *u, const char *verb, int capability, const char *polkit_message, bool interactive, sd_bus_message *call, sd_bus_error *error); +int bus_verify_manage_units_async_full(Unit *u, const char *verb, const char *polkit_message, sd_bus_message *call, sd_bus_error *error); int bus_read_mount_options(sd_bus_message *message, sd_bus_error *error, MountOptions **ret_options, char **ret_format_str, const char *separator); diff --git a/src/core/dbus.c b/src/core/dbus.c index ba2cec4d771..f7d4a970962 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -1189,22 +1189,46 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) { } int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", NULL, false, UID_INVALID, &m->polkit_registry, error); + return bus_verify_polkit_async( + call, + "org.freedesktop.systemd1.manage-units", + /* details= */ NULL, + &m->polkit_registry, + error); } int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", NULL, false, UID_INVALID, &m->polkit_registry, error); + return bus_verify_polkit_async( + call, + "org.freedesktop.systemd1.manage-unit-files", + /* details= */ NULL, + &m->polkit_registry, + error); } int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", NULL, false, UID_INVALID, &m->polkit_registry, error); + return bus_verify_polkit_async( + call, + "org.freedesktop.systemd1.reload-daemon", + /* details= */ NULL, + &m->polkit_registry, error); } int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", NULL, false, UID_INVALID, &m->polkit_registry, error); + return bus_verify_polkit_async( + call, + "org.freedesktop.systemd1.set-environment", + /* details= */ NULL, + &m->polkit_registry, + error); } int bus_verify_bypass_dump_ratelimit_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.bypass-dump-ratelimit", NULL, false, UID_INVALID, &m->polkit_registry, error); + return bus_verify_polkit_async( + call, + "org.freedesktop.systemd1.bypass-dump-ratelimit", + /* details= */ NULL, + &m->polkit_registry, + error); } uint64_t manager_bus_n_queued_write(Manager *m) { diff --git a/src/core/device.c b/src/core/device.c index d07adb2d2ae..6b2d7c3e247 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -1291,6 +1291,7 @@ const UnitVTable device_vtable = { .status_message_formats = { .starting_stopping = { [0] = "Expecting device %s...", + [1] = "Waiting for device %s to disappear...", }, .finished_start_job = { [JOB_DONE] = "Found device %s.", diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index 7ce26582969..61f66b6914a 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -1883,7 +1883,7 @@ static int build_environment( "Failed to determine user credentials for root: %m"); } - bool set_user_login_env = c->set_login_environment >= 0 ? c->set_login_environment : (c->user || c->dynamic_user); + bool set_user_login_env = exec_context_get_set_login_environment(c); if (username) { x = strjoin("USER=", username); diff --git a/src/core/execute.c b/src/core/execute.c index 1e48e88ee8c..7b661d70c9f 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -161,9 +161,10 @@ void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) * systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd * that will be closed automatically, and operate on it for convenience. */ lock_fd = lock_dev_console(); - if (lock_fd < 0) - return (void) log_debug_errno(lock_fd, - "Failed to lock /dev/console: %m"); + if (ERRNO_IS_NEG_PRIVILEGE(lock_fd)) + log_debug_errno(lock_fd, "No privileges to lock /dev/console, proceeding without: %m"); + else if (lock_fd < 0) + return (void) log_debug_errno(lock_fd, "Failed to lock /dev/console: %m"); if (context->tty_vhangup) (void) terminal_vhangup_fd(fd); @@ -1453,7 +1454,7 @@ void exec_context_revert_tty(ExecContext *c) { assert(c); /* First, reset the TTY (possibly kicking everybody else from the TTY) */ - exec_context_tty_reset(c, NULL); + exec_context_tty_reset(c, /* parameters= */ NULL); /* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path * configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed @@ -1465,7 +1466,7 @@ void exec_context_revert_tty(ExecContext *c) { if (!path) return; - fd = open(path, O_PATH|O_CLOEXEC); + fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */ if (fd < 0) return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m", @@ -1484,7 +1485,7 @@ void exec_context_revert_tty(ExecContext *c) { r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID); if (r < 0) - log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s, ignoring: %m", path); + log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s to " UID_FMT ":" GID_FMT ", ignoring: %m", path, (uid_t) 0, (gid_t) TTY_GID); } int exec_context_get_clean_directories( @@ -1660,6 +1661,15 @@ uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) { return (uint64_t) MAX(r, 0); } +bool exec_context_get_set_login_environment(const ExecContext *c) { + assert(c); + + if (c->set_login_environment >= 0) + return c->set_login_environment; + + return c->user || c->dynamic_user || c->pam_name; +} + char** exec_context_get_syscall_filter(const ExecContext *c) { _cleanup_strv_free_ char **l = NULL; @@ -1954,8 +1964,7 @@ static char *destroy_tree(char *path) { } void exec_shared_runtime_done(ExecSharedRuntime *rt) { - if (!rt) - return; + assert(rt); if (rt->manager) (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id); @@ -1968,8 +1977,10 @@ void exec_shared_runtime_done(ExecSharedRuntime *rt) { } static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) { - exec_shared_runtime_done(rt); + if (!rt) + return NULL; + exec_shared_runtime_done(rt); return mfree(rt); } @@ -2093,15 +2104,13 @@ static int exec_shared_runtime_make( return r; } - if (exec_needs_network_namespace(c)) { + if (exec_needs_network_namespace(c)) if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0) return -errno; - } - if (exec_needs_ipc_namespace(c)) { + if (exec_needs_ipc_namespace(c)) if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0) return -errno; - } r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret); if (r < 0) diff --git a/src/core/execute.h b/src/core/execute.h index 5a6927aa027..e3708e1b014 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -527,6 +527,7 @@ int exec_context_get_nice(const ExecContext *c); int exec_context_get_cpu_sched_policy(const ExecContext *c); int exec_context_get_cpu_sched_priority(const ExecContext *c); uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c); +bool exec_context_get_set_login_environment(const ExecContext *c); char** exec_context_get_syscall_filter(const ExecContext *c); char** exec_context_get_syscall_archs(const ExecContext *c); char** exec_context_get_syscall_log(const ExecContext *c); diff --git a/src/core/job.c b/src/core/job.c index e7d1f65dbcc..e78c2a70db6 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -833,13 +833,12 @@ static int job_perform_on_unit(Job **j) { Manager *m; JobType t; Unit *u; + bool wait_only; int r; - /* While we execute this operation the job might go away (for - * example: because it finishes immediately or is replaced by - * a new, conflicting job.) To make sure we don't access a - * freed job later on we store the id here, so that we can - * verify the job is still valid. */ + /* While we execute this operation the job might go away (for example: because it finishes immediately + * or is replaced by a new, conflicting job). To make sure we don't access a freed job later on we + * store the id here, so that we can verify the job is still valid. */ assert(j); assert(*j); @@ -853,6 +852,7 @@ static int job_perform_on_unit(Job **j) { switch (t) { case JOB_START: r = unit_start(u, a); + wait_only = r == -EBADR; /* If the unit type does not support starting, then simply wait. */ break; case JOB_RESTART: @@ -860,24 +860,28 @@ static int job_perform_on_unit(Job **j) { _fallthrough_; case JOB_STOP: r = unit_stop(u); + wait_only = r == -EBADR; /* If the unit type does not support stopping, then simply wait. */ break; case JOB_RELOAD: r = unit_reload(u); + wait_only = false; /* A clear error is generated if reload is not supported. */ break; default: assert_not_reached(); } - /* Log if the job still exists and the start/stop/reload function actually did something. Note that this means - * for units for which there's no 'activating' phase (i.e. because we transition directly from 'inactive' to - * 'active') we'll possibly skip the "Starting..." message. */ + /* Log if the job still exists and the start/stop/reload function actually did something or we're + * only waiting for unit status change (common for device units). The latter ensures that job start + * messages for device units are correctly shown. Note that if the job disappears too quickly, e.g. + * for units for which there's no 'activating' phase (i.e. because we transition directly from + * 'inactive' to 'active'), we'll possibly skip the "Starting..." message. */ *j = manager_get_job(m, id); - if (*j && r > 0) + if (*j && (r > 0 || wait_only)) job_emit_start_message(u, id, t); - return r; + return wait_only ? 0 : r; } int job_run_and_invalidate(Job *j) { @@ -919,13 +923,6 @@ int job_run_and_invalidate(Job *j) { case JOB_START: case JOB_STOP: case JOB_RESTART: - r = job_perform_on_unit(&j); - - /* If the unit type does not support starting/stopping, then simply wait. */ - if (r == -EBADR) - r = 0; - break; - case JOB_RELOAD: r = job_perform_on_unit(&j); break; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 05843662c75..b424ef06207 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -6096,7 +6096,7 @@ int config_parse_restrict_network_interfaces( break; } - if (!ifname_valid(word)) { + if (!ifname_valid_full(word, IFNAME_VALID_ALTERNATIVE)) { log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", word); continue; } diff --git a/src/core/manager.c b/src/core/manager.c index 6ca643e6932..c07f537b9f2 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1804,7 +1804,7 @@ static void manager_distribute_fds(Manager *m, FDSet *fds) { HASHMAP_FOREACH(u, m->units) { - if (fdset_size(fds) <= 0) + if (fdset_isempty(fds)) break; if (!UNIT_VTABLE(u)->distribute_fds) @@ -2745,7 +2745,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t if (!found) log_warning("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid); - if (fdset_size(fds) > 0) + if (!fdset_isempty(fds)) log_warning("Got extra auxiliary fds with notification message, closing them."); return 0; @@ -3810,7 +3810,7 @@ void manager_check_finished(Manager *m) { manager_check_basic_target(m); - if (hashmap_size(m->jobs) > 0) { + if (!hashmap_isempty(m->jobs)) { if (m->jobs_in_progress_event_source) /* Ignore any failure, this is only for feedback */ (void) sd_event_source_set_time(m->jobs_in_progress_event_source, @@ -4545,7 +4545,7 @@ ManagerState manager_state(Manager *m) { } /* Are there any failed units? If so, we are in degraded mode */ - if (set_size(m->failed_units) > 0) + if (!set_isempty(m->failed_units)) return MANAGER_DEGRADED; return MANAGER_RUNNING; diff --git a/src/core/unit.c b/src/core/unit.c index b407387fc13..0772a67001c 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -67,14 +67,16 @@ #endif /* Thresholds for logging at INFO level about resource consumption */ -#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC) -#define MENTIONWORTHY_IO_BYTES (1024 * 1024ULL) -#define MENTIONWORTHY_IP_BYTES (0ULL) +#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC) +#define MENTIONWORTHY_MEMORY_BYTES (64 * U64_MB) +#define MENTIONWORTHY_IO_BYTES (1 * U64_MB) +#define MENTIONWORTHY_IP_BYTES UINT64_C(0) -/* Thresholds for logging at INFO level about resource consumption */ -#define NOTICEWORTHY_CPU_NSEC (10*60 * NSEC_PER_SEC) /* 10 minutes */ -#define NOTICEWORTHY_IO_BYTES (10 * 1024 * 1024ULL) /* 10 MB */ -#define NOTICEWORTHY_IP_BYTES (128 * 1024 * 1024ULL) /* 128 MB */ +/* Thresholds for logging at NOTICE level about resource consumption */ +#define NOTICEWORTHY_CPU_NSEC (10 * NSEC_PER_MINUTE) +#define NOTICEWORTHY_MEMORY_BYTES (512 * U64_MB) +#define NOTICEWORTHY_IO_BYTES (10 * U64_MB) +#define NOTICEWORTHY_IP_BYTES (128 * U64_MB) const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -2339,273 +2341,167 @@ static int raise_level(int log_level, bool condition_info, bool condition_notice } static int unit_log_resources(Unit *u) { - struct iovec iovec[1 + 2 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4]; - bool any_traffic = false, have_ip_accounting = false, any_io = false, have_io_accounting = false; - _cleanup_free_ char *igress = NULL, *egress = NULL, *rr = NULL, *wr = NULL; - int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */ - size_t n_message_parts = 0, n_iovec = 0; - char* message_parts[1 + 2 + 2 + 2 + 1], *t; - nsec_t nsec = NSEC_INFINITY; - uint64_t memory_peak = UINT64_MAX, memory_swap_peak = UINT64_MAX; - int r; - const char* const ip_fields[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = { - [CGROUP_IP_INGRESS_BYTES] = "IP_METRIC_INGRESS_BYTES", - [CGROUP_IP_INGRESS_PACKETS] = "IP_METRIC_INGRESS_PACKETS", - [CGROUP_IP_EGRESS_BYTES] = "IP_METRIC_EGRESS_BYTES", - [CGROUP_IP_EGRESS_PACKETS] = "IP_METRIC_EGRESS_PACKETS", - }; - const char* const io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { - [CGROUP_IO_READ_BYTES] = "IO_METRIC_READ_BYTES", - [CGROUP_IO_WRITE_BYTES] = "IO_METRIC_WRITE_BYTES", - [CGROUP_IO_READ_OPERATIONS] = "IO_METRIC_READ_OPERATIONS", - [CGROUP_IO_WRITE_OPERATIONS] = "IO_METRIC_WRITE_OPERATIONS", + + static const struct { + const char *journal_field; + const char *message_suffix; + } memory_fields[_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1] = { + [CGROUP_MEMORY_PEAK] = { "MEMORY_PEAK", "memory peak" }, + [CGROUP_MEMORY_SWAP_PEAK] = { "MEMORY_SWAP_PEAK", "memory swap peak" }, + }, ip_fields[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IP_INGRESS_BYTES] = { "IP_METRIC_INGRESS_BYTES", "incoming IP traffic" }, + [CGROUP_IP_EGRESS_BYTES] = { "IP_METRIC_EGRESS_BYTES", "outgoing IP traffic" }, + [CGROUP_IP_INGRESS_PACKETS] = { "IP_METRIC_INGRESS_PACKETS", NULL }, + [CGROUP_IP_EGRESS_PACKETS] = { "IP_METRIC_EGRESS_PACKETS", NULL }, + }, io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = { + [CGROUP_IO_READ_BYTES] = { "IO_METRIC_READ_BYTES", "read from disk" }, + [CGROUP_IO_WRITE_BYTES] = { "IO_METRIC_WRITE_BYTES", "written to disk" }, + [CGROUP_IO_READ_OPERATIONS] = { "IO_METRIC_READ_OPERATIONS", NULL }, + [CGROUP_IO_WRITE_OPERATIONS] = { "IO_METRIC_WRITE_OPERATIONS", NULL }, }; + struct iovec *iovec = NULL; + size_t n_iovec = 0; + _cleanup_free_ char *message = NULL, *t = NULL; + nsec_t cpu_nsec = NSEC_INFINITY; + int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */ + assert(u); + CLEANUP_ARRAY(iovec, n_iovec, iovec_array_free); + + iovec = new(struct iovec, 1 + (_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1) + + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4); + if (!iovec) + return log_oom(); + /* Invoked whenever a unit enters failed or dead state. Logs information about consumed resources if resource * accounting was enabled for a unit. It does this in two ways: a friendly human readable string with reduced * information and the complete data in structured fields. */ - (void) unit_get_cpu_usage(u, &nsec); - if (nsec != NSEC_INFINITY) { + (void) unit_get_cpu_usage(u, &cpu_nsec); + if (cpu_nsec != NSEC_INFINITY) { /* Format the CPU time for inclusion in the structured log message */ - if (asprintf(&t, "CPU_USAGE_NSEC=%" PRIu64, nsec) < 0) { - r = log_oom(); - goto finish; - } - iovec[n_iovec++] = IOVEC_MAKE_STRING(t); + if (asprintf(&t, "CPU_USAGE_NSEC=%" PRIu64, cpu_nsec) < 0) + return log_oom(); + iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t)); /* Format the CPU time for inclusion in the human language message string */ - t = strjoin("consumed ", FORMAT_TIMESPAN(nsec / NSEC_PER_USEC, USEC_PER_MSEC), " CPU time"); - if (!t) { - r = log_oom(); - goto finish; - } - - message_parts[n_message_parts++] = t; + if (strextendf_with_separator(&message, ", ", + "Consumed %s CPU time", + FORMAT_TIMESPAN(cpu_nsec / NSEC_PER_USEC, USEC_PER_MSEC)) < 0) + return log_oom(); log_level = raise_level(log_level, - nsec > MENTIONWORTHY_CPU_NSEC, - nsec > NOTICEWORTHY_CPU_NSEC); + cpu_nsec > MENTIONWORTHY_CPU_NSEC, + cpu_nsec > NOTICEWORTHY_CPU_NSEC); } - (void) unit_get_memory_accounting(u, CGROUP_MEMORY_PEAK, &memory_peak); - if (memory_peak != UINT64_MAX) { - /* Format peak memory for inclusion in the structured log message */ - if (asprintf(&t, "MEMORY_PEAK=%" PRIu64, memory_peak) < 0) { - r = log_oom(); - goto finish; - } - iovec[n_iovec++] = IOVEC_MAKE_STRING(t); + for (CGroupMemoryAccountingMetric metric = 0; metric <= _CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST; metric++) { + uint64_t v = UINT64_MAX; - /* Format peak memory for inclusion in the human language message string */ - t = strjoin(FORMAT_BYTES(memory_peak), " memory peak"); - if (!t) { - r = log_oom(); - goto finish; - } - message_parts[n_message_parts++] = t; - } + assert(memory_fields[metric].journal_field); + assert(memory_fields[metric].message_suffix); - (void) unit_get_memory_accounting(u, CGROUP_MEMORY_SWAP_PEAK, &memory_swap_peak); - if (memory_swap_peak != UINT64_MAX) { - /* Format peak swap memory for inclusion in the structured log message */ - if (asprintf(&t, "MEMORY_SWAP_PEAK=%" PRIu64, memory_swap_peak) < 0) { - r = log_oom(); - goto finish; - } - iovec[n_iovec++] = IOVEC_MAKE_STRING(t); + (void) unit_get_memory_accounting(u, metric, &v); + if (v == UINT64_MAX) + continue; - /* Format peak swap memory for inclusion in the human language message string */ - t = strjoin(FORMAT_BYTES(memory_swap_peak), " memory swap peak"); - if (!t) { - r = log_oom(); - goto finish; - } - message_parts[n_message_parts++] = t; + if (asprintf(&t, "%s=%" PRIu64, memory_fields[metric].journal_field, v) < 0) + return log_oom(); + iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t)); + + if (strextendf_with_separator(&message, ", ", "%s %s", + FORMAT_BYTES(v), memory_fields[metric].message_suffix) < 0) + return log_oom(); + + log_level = raise_level(log_level, + v > MENTIONWORTHY_MEMORY_BYTES, + v > NOTICEWORTHY_MEMORY_BYTES); } for (CGroupIOAccountingMetric k = 0; k < _CGROUP_IO_ACCOUNTING_METRIC_MAX; k++) { uint64_t value = UINT64_MAX; - assert(io_fields[k]); + assert(io_fields[k].journal_field); (void) unit_get_io_accounting(u, k, k > 0, &value); if (value == UINT64_MAX) continue; - have_io_accounting = true; - if (value > 0) - any_io = true; - /* Format IO accounting data for inclusion in the structured log message */ - if (asprintf(&t, "%s=%" PRIu64, io_fields[k], value) < 0) { - r = log_oom(); - goto finish; - } - iovec[n_iovec++] = IOVEC_MAKE_STRING(t); + if (asprintf(&t, "%s=%" PRIu64, io_fields[k].journal_field, value) < 0) + return log_oom(); + iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t)); /* Format the IO accounting data for inclusion in the human language message string, but only * for the bytes counters (and not for the operations counters) */ - if (k == CGROUP_IO_READ_BYTES) { - assert(!rr); - rr = strjoin("read ", strna(FORMAT_BYTES(value)), " from disk"); - if (!rr) { - r = log_oom(); - goto finish; - } - } else if (k == CGROUP_IO_WRITE_BYTES) { - assert(!wr); - wr = strjoin("written ", strna(FORMAT_BYTES(value)), " to disk"); - if (!wr) { - r = log_oom(); - goto finish; - } - } + if (io_fields[k].message_suffix) { + if (strextendf_with_separator(&message, ", ", "%s %s", + FORMAT_BYTES(value), io_fields[k].message_suffix) < 0) + return log_oom(); - if (IN_SET(k, CGROUP_IO_READ_BYTES, CGROUP_IO_WRITE_BYTES)) log_level = raise_level(log_level, value > MENTIONWORTHY_IO_BYTES, value > NOTICEWORTHY_IO_BYTES); - } - - if (have_io_accounting) { - if (any_io) { - if (rr) - message_parts[n_message_parts++] = TAKE_PTR(rr); - if (wr) - message_parts[n_message_parts++] = TAKE_PTR(wr); - - } else { - char *k; - - k = strdup("no IO"); - if (!k) { - r = log_oom(); - goto finish; - } - - message_parts[n_message_parts++] = k; } } for (CGroupIPAccountingMetric m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) { uint64_t value = UINT64_MAX; - assert(ip_fields[m]); + assert(ip_fields[m].journal_field); (void) unit_get_ip_accounting(u, m, &value); if (value == UINT64_MAX) continue; - have_ip_accounting = true; - if (value > 0) - any_traffic = true; - /* Format IP accounting data for inclusion in the structured log message */ - if (asprintf(&t, "%s=%" PRIu64, ip_fields[m], value) < 0) { - r = log_oom(); - goto finish; - } - iovec[n_iovec++] = IOVEC_MAKE_STRING(t); - - /* Format the IP accounting data for inclusion in the human language message string, but only for the - * bytes counters (and not for the packets counters) */ - if (m == CGROUP_IP_INGRESS_BYTES) { - assert(!igress); - igress = strjoin("received ", strna(FORMAT_BYTES(value)), " IP traffic"); - if (!igress) { - r = log_oom(); - goto finish; - } - } else if (m == CGROUP_IP_EGRESS_BYTES) { - assert(!egress); - egress = strjoin("sent ", strna(FORMAT_BYTES(value)), " IP traffic"); - if (!egress) { - r = log_oom(); - goto finish; - } - } + if (asprintf(&t, "%s=%" PRIu64, ip_fields[m].journal_field, value) < 0) + return log_oom(); + iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t)); + + /* Format the IP accounting data for inclusion in the human language message string, but only + * for the bytes counters (and not for the packets counters) */ + if (ip_fields[m].message_suffix) { + if (strextendf_with_separator(&message, ", ", "%s %s", + FORMAT_BYTES(value), ip_fields[m].message_suffix) < 0) + return log_oom(); - if (IN_SET(m, CGROUP_IP_INGRESS_BYTES, CGROUP_IP_EGRESS_BYTES)) log_level = raise_level(log_level, value > MENTIONWORTHY_IP_BYTES, value > NOTICEWORTHY_IP_BYTES); - } - - /* This check is here because it is the earliest point following all possible log_level assignments. If - * log_level is assigned anywhere after this point, move this check. */ - if (!unit_log_level_test(u, log_level)) { - r = 0; - goto finish; - } - - if (have_ip_accounting) { - if (any_traffic) { - if (igress) - message_parts[n_message_parts++] = TAKE_PTR(igress); - if (egress) - message_parts[n_message_parts++] = TAKE_PTR(egress); - - } else { - char *k; - - k = strdup("no IP traffic"); - if (!k) { - r = log_oom(); - goto finish; - } - - message_parts[n_message_parts++] = k; } } + /* This check is here because it is the earliest point following all possible log_level assignments. + * (If log_level is assigned anywhere after this point, move this check.) */ + if (!unit_log_level_test(u, log_level)) + return 0; + /* Is there any accounting data available at all? */ if (n_iovec == 0) { - r = 0; - goto finish; - } - - if (n_message_parts == 0) - t = strjoina("MESSAGE=", u->id, ": Completed."); - else { - _cleanup_free_ char *joined = NULL; - - message_parts[n_message_parts] = NULL; - - joined = strv_join(message_parts, ", "); - if (!joined) { - r = log_oom(); - goto finish; - } - - joined[0] = ascii_toupper(joined[0]); - t = strjoina("MESSAGE=", u->id, ": ", joined, "."); + assert(!message); + return 0; } - /* The following four fields we allocate on the stack or are static strings, we hence don't want to free them, - * and hence don't increase n_iovec for them */ - iovec[n_iovec] = IOVEC_MAKE_STRING(t); - iovec[n_iovec + 1] = IOVEC_MAKE_STRING("MESSAGE_ID=" SD_MESSAGE_UNIT_RESOURCES_STR); - - t = strjoina(u->manager->unit_log_field, u->id); - iovec[n_iovec + 2] = IOVEC_MAKE_STRING(t); - - t = strjoina(u->manager->invocation_log_field, u->invocation_id_string); - iovec[n_iovec + 3] = IOVEC_MAKE_STRING(t); + t = strjoin("MESSAGE=", u->id, ": ", message ?: "Completed", "."); + if (!t) + return log_oom(); + iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t)); - log_unit_struct_iovec(u, log_level, iovec, n_iovec + 4); - r = 0; + if (!set_iovec_string_field(iovec, &n_iovec, "MESSAGE_ID=", SD_MESSAGE_UNIT_RESOURCES_STR)) + return log_oom(); -finish: - free_many_charp(message_parts, n_message_parts); + if (!set_iovec_string_field(iovec, &n_iovec, u->manager->unit_log_field, u->id)) + return log_oom(); - for (size_t i = 0; i < n_iovec; i++) - free(iovec[i].iov_base); + if (!set_iovec_string_field(iovec, &n_iovec, u->manager->invocation_log_field, u->invocation_id_string)) + return log_oom(); - return r; + log_unit_struct_iovec(u, log_level, iovec, n_iovec); + return 0; } static void unit_update_on_console(Unit *u) { diff --git a/src/creds/creds.c b/src/creds/creds.c index 10d117118fb..c9bf2e1e36c 100644 --- a/src/creds/creds.c +++ b/src/creds/creds.c @@ -24,6 +24,9 @@ #include "terminal-util.h" #include "tpm2-pcr.h" #include "tpm2-util.h" +#include "user-util.h" +#include "varlink.h" +#include "varlink-io.systemd.Credentials.h" #include "verbs.h" typedef enum TranscodeMode { @@ -54,6 +57,7 @@ static usec_t arg_timestamp = USEC_INFINITY; static usec_t arg_not_after = USEC_INFINITY; static bool arg_pretty = false; static bool arg_quiet = false; +static bool arg_varlink = false; STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep); @@ -933,6 +937,11 @@ static int parse_argv(int argc, char *argv[]) { if (arg_tpm2_public_key_pcr_mask == UINT32_MAX) arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_BOOT; + r = varlink_invocation(VARLINK_ALLOW_ACCEPT); + if (r < 0) + return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m"); + arg_varlink = r; + return 1; } @@ -952,6 +961,150 @@ static int creds_main(int argc, char *argv[]) { return dispatch_verb(argc, argv, verbs, NULL); } +typedef struct MethodEncryptParameters { + const char *name; + const char *text; + struct iovec data; + uint64_t timestamp; + uint64_t not_after; +} MethodEncryptParameters; + +static void method_encrypt_parameters_done(MethodEncryptParameters *p) { + assert(p); + + iovec_done_erase(&p->data); +} + +static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { + { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodEncryptParameters, name), 0 }, + { "text", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodEncryptParameters, text), 0 }, + { "data", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodEncryptParameters, data), 0 }, + { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodEncryptParameters, timestamp), 0 }, + { "notAfter", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodEncryptParameters, not_after), 0 }, + {} + }; + _cleanup_(method_encrypt_parameters_done) MethodEncryptParameters p = { + .timestamp = UINT64_MAX, + .not_after = UINT64_MAX, + }; + _cleanup_(iovec_done) struct iovec output = {}; + int r; + + assert(link); + + json_variant_sensitive(parameters); + + r = varlink_dispatch(link, parameters, dispatch_table, &p); + if (r != 0) + return r; + + if (p.name && !credential_name_valid(p.name)) + return varlink_error_invalid_parameter_name(link, "name"); + /* Specifying both or neither the text string and the binary data is not allowed */ + if (!!p.text == !!p.data.iov_base) + return varlink_error_invalid_parameter_name(link, "data"); + if (p.timestamp == UINT64_MAX) + p.timestamp = now(CLOCK_REALTIME); + if (p.not_after != UINT64_MAX && p.not_after < p.timestamp) + return varlink_error_invalid_parameter_name(link, "notAfter"); + + r = encrypt_credential_and_warn( + arg_with_key, + p.name, + p.timestamp, + p.not_after, + arg_tpm2_device, + arg_tpm2_pcr_mask, + arg_tpm2_public_key, + arg_tpm2_public_key_pcr_mask, + p.text ?: p.data.iov_base, p.text ? strlen(p.text) : p.data.iov_len, + &output.iov_base, &output.iov_len); + if (r < 0) + return r; + + _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL; + + r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("blob", &output))); + if (r < 0) + return r; + + /* Let's also mark the (theoretically encrypted) reply as sensitive, in case the NULL encryption scheme was used. */ + json_variant_sensitive(reply); + + return varlink_reply(link, reply); +} + +typedef struct MethodDecryptParameters { + const char *name; + struct iovec blob; + uint64_t timestamp; +} MethodDecryptParameters; + +static void method_decrypt_parameters_done(MethodDecryptParameters *p) { + assert(p); + + iovec_done_erase(&p->blob); +} + +static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { + { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodDecryptParameters, name), 0 }, + { "blob", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodDecryptParameters, blob), 0 }, + { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodDecryptParameters, timestamp), 0 }, + {} + }; + _cleanup_(method_decrypt_parameters_done) MethodDecryptParameters p = { + .timestamp = UINT64_MAX, + }; + _cleanup_(iovec_done_erase) struct iovec output = {}; + int r; + + assert(link); + + /* Let's also mark the (theoretically encrypted) input as sensitive, in case the NULL encryption scheme was used. */ + json_variant_sensitive(parameters); + + r = varlink_dispatch(link, parameters, dispatch_table, &p); + if (r != 0) + return r; + + if (p.name && !credential_name_valid(p.name)) + return varlink_error_invalid_parameter_name(link, "name"); + if (!p.blob.iov_base) + return varlink_error_invalid_parameter_name(link, "blob"); + if (p.timestamp == UINT64_MAX) + p.timestamp = now(CLOCK_REALTIME); + + r = decrypt_credential_and_warn( + p.name, + p.timestamp, + arg_tpm2_device, + arg_tpm2_signature, + p.blob.iov_base, p.blob.iov_len, + &output.iov_base, &output.iov_len); + if (r == -EBADMSG) + return varlink_error(link, "io.systemd.Credentials.BadFormat", NULL); + if (r == -EREMOTE) + return varlink_error(link, "io.systemd.Credentials.NameMismatch", NULL); + if (r == -ESTALE) + return varlink_error(link, "io.systemd.Credentials.TimeMismatch", NULL); + if (r < 0) + return r; + + _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL; + + r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("data", &output))); + if (r < 0) + return r; + + json_variant_sensitive(reply); + + return varlink_reply(link, reply); +} + static int run(int argc, char *argv[]) { int r; @@ -961,6 +1114,33 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; + if (arg_varlink) { + _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL; + + /* Invocation as Varlink service */ + + r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_INHERIT_USERDATA); + if (r < 0) + return log_error_errno(r, "Failed to allocate Varlink server: %m"); + + r = varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_Credentials); + if (r < 0) + return log_error_errno(r, "Failed to add Varlink interface: %m"); + + r = varlink_server_bind_method_many( + varlink_server, + "io.systemd.Credentials.Encrypt", vl_method_encrypt, + "io.systemd.Credentials.Decrypt", vl_method_decrypt); + if (r < 0) + return log_error_errno(r, "Failed to bind Varlink methods: %m"); + + r = varlink_server_loop_auto(varlink_server); + if (r < 0) + return log_error_errno(r, "Failed to run Varlink event loop: %m"); + + return 0; + } + return creds_main(argc, argv); } diff --git a/src/cryptenroll/cryptenroll-pkcs11.c b/src/cryptenroll/cryptenroll-pkcs11.c index 54b6b862428..7d6112e4027 100644 --- a/src/cryptenroll/cryptenroll-pkcs11.c +++ b/src/cryptenroll/cryptenroll-pkcs11.c @@ -6,7 +6,6 @@ #include "memory-util.h" #include "openssl-util.h" #include "pkcs11-util.h" -#include "random-util.h" int enroll_pkcs11( struct crypt_device *cd, @@ -18,12 +17,11 @@ int enroll_pkcs11( _cleanup_(erase_and_freep) char *base64_encoded = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ char *keyslot_as_string = NULL; - size_t decrypted_key_size, encrypted_key_size; - _cleanup_free_ void *encrypted_key = NULL; + size_t decrypted_key_size, saved_key_size; + _cleanup_free_ void *saved_key = NULL; _cleanup_(X509_freep) X509 *cert = NULL; ssize_t base64_encoded_size; const char *node; - EVP_PKEY *pkey; int keyslot, r; assert_se(cd); @@ -37,27 +35,9 @@ int enroll_pkcs11( if (r < 0) return r; - pkey = X509_get0_pubkey(cert); - if (!pkey) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate."); - - r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size); - if (r < 0) - return log_error_errno(r, "Failed to determine RSA public key size."); - - log_debug("Generating %zu bytes random key.", decrypted_key_size); - - decrypted_key = malloc(decrypted_key_size); - if (!decrypted_key) - return log_oom(); - - r = crypto_random_bytes(decrypted_key, decrypted_key_size); - if (r < 0) - return log_error_errno(r, "Failed to generate random key: %m"); - - r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size); + r = x509_generate_volume_keys(cert, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size); if (r < 0) - return log_error_errno(r, "Failed to encrypt key: %m"); + return log_error_errno(r, "Failed to generate volume keys: %m"); /* Let's base64 encode the key to use, for compat with homed (and it's easier to type it in by * keyboard, if that might ever end up being necessary.) */ @@ -87,7 +67,7 @@ int enroll_pkcs11( JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-pkcs11")), JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))), JSON_BUILD_PAIR("pkcs11-uri", JSON_BUILD_STRING(uri)), - JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)))); + JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(saved_key, saved_key_size)))); if (r < 0) return log_error_errno(r, "Failed to prepare PKCS#11 JSON token object: %m"); diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index dc753b461cd..f9f47cf7aac 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -827,16 +827,15 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) pager_open(arg_pager_flags); - if (arg_json_format_flags & JSON_FORMAT_OFF) - printf(" Name: %s%s%s\n", ansi_highlight(), bn, ansi_normal()); + if (arg_json_format_flags & JSON_FORMAT_OFF) { + printf(" Name: %s%s%s\n", + ansi_highlight(), bn, ansi_normal()); - if (ioctl(d->fd, BLKGETSIZE64, &size) < 0) - log_debug_errno(errno, "Failed to query size of loopback device: %m"); - else if (arg_json_format_flags & JSON_FORMAT_OFF) - printf(" Size: %s\n", FORMAT_BYTES(size)); + printf(" Size: %s\n", + FORMAT_BYTES(m->image_size)); - if (arg_json_format_flags & JSON_FORMAT_OFF) { - printf(" Sec. Size: %" PRIu32 "\n", m->sector_size); + printf(" Sec. Size: %" PRIu32 "\n", + m->sector_size); printf(" Arch.: %s\n", strna(architecture_to_string(dissected_image_architecture(m)))); @@ -960,7 +959,12 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { return log_oom(); table_set_ersatz_string(t, TABLE_ERSATZ_DASH); - (void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100); + (void) table_set_align_percent(t, table_get_cell(t, 0, 9), 100); + + /* Hide the device path if this is a loopback device that is not relinquished, since that means the + * device node is not going to be useful the instant our command exits */ + if ((!d || d->created) && (arg_json_format_flags & JSON_FORMAT_OFF)) + table_hide_column_from_display(t, 8); for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) { DissectedPartition *p = m->partitions + i; diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 17d344e9803..5dd7bdb1e7a 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -1622,7 +1622,7 @@ static int reload_vconsole(sd_bus **bus) { if (r < 0) return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, false, NULL); + r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL); if (r < 0) return log_error_errno(r, "Failed to wait for systemd-vconsole-setup.service/restart: %m"); return 0; @@ -1655,8 +1655,8 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m"); if (r > 0 && !enabled) { - log_debug("Found systemd.firstboot=no kernel command line argument, terminating."); - return 0; /* disabled */ + log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts."); + arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false; } } diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index 000ed69667d..729209fc89d 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -177,7 +177,7 @@ static int process_progress(int fd, FILE* console) { else if (feof(f)) r = 0; else - r = log_warning_errno(SYNTHETIC_ERRNO(errno), "Failed to parse progress pipe data"); + r = log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse progress pipe data."); break; } diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index 797330dd97d..041649a03bc 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -158,6 +158,10 @@ __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \ }) +#define U64_KB UINT64_C(1024) +#define U64_MB (UINT64_C(1024) * U64_KB) +#define U64_GB (UINT64_C(1024) * U64_MB) + #undef MAX #define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) #define __MAX(aq, a, bq, b) \ diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index c1c21a2c13d..01684283942 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -54,26 +54,30 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; } -static int process_resume(const char *device) { +static int process_resume(const HibernateInfo *info) { _cleanup_free_ char *device_unit = NULL; int r; - assert(device); + assert(info); - r = unit_name_from_path(device, ".device", &device_unit); + r = unit_name_from_path(info->device, ".device", &device_unit); if (r < 0) - return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", device); + return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", info->device); - r = write_drop_in(arg_dest, device_unit, 40, "device-timeout", - "# Automatically generated by systemd-hibernate-resume-generator\n\n" - "[Unit]\n" - "JobTimeoutSec=infinity\n"); + /* If hibernate info is acquired from EFI variable, don't wait forever by default. Otherwise, if + * swap device is not present and HibernateLocation was not correctly cleared, we end up blocking + * the boot process infinitely. */ + r = write_drop_in_format(arg_dest, device_unit, 40, "device-timeout", + "# Automatically generated by systemd-hibernate-resume-generator\n\n" + "[Unit]\n" + "JobTimeoutSec=%s\n", + info->cmdline ? "infinity" : "2min"); if (r < 0) log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m"); r = generator_write_timeouts(arg_dest, - device, - device, + info->device, + info->device, arg_resume_options ?: arg_root_options, NULL); if (r < 0) @@ -120,7 +124,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) if (r < 0) return r; - return process_resume(info.device); + return process_resume(&info); } DEFINE_MAIN_GENERATOR_FUNCTION(run); diff --git a/src/home/homectl-pkcs11.c b/src/home/homectl-pkcs11.c index 2539af06311..6ae291ed932 100644 --- a/src/home/homectl-pkcs11.c +++ b/src/home/homectl-pkcs11.c @@ -8,7 +8,6 @@ #include "memory-util.h" #include "openssl-util.h" #include "pkcs11-util.h" -#include "random-util.h" #include "strv.h" static int add_pkcs11_encrypted_key( @@ -158,11 +157,10 @@ static int acquire_pkcs11_certificate( } int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) { - _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL; + _cleanup_(erase_and_freep) void *decrypted_key = NULL, *saved_key = NULL; _cleanup_(erase_and_freep) char *pin = NULL; - size_t decrypted_key_size, encrypted_key_size; + size_t decrypted_key_size, saved_key_size; _cleanup_(X509_freep) X509 *cert = NULL; - EVP_PKEY *pkey; int r; assert(v); @@ -171,27 +169,9 @@ int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) { if (r < 0) return r; - pkey = X509_get0_pubkey(cert); - if (!pkey) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate."); - - r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size); - if (r < 0) - return log_error_errno(r, "Failed to extract RSA key size from X509 certificate."); - - log_debug("Generating %zu bytes random key.", decrypted_key_size); - - decrypted_key = malloc(decrypted_key_size); - if (!decrypted_key) - return log_oom(); - - r = crypto_random_bytes(decrypted_key, decrypted_key_size); - if (r < 0) - return log_error_errno(r, "Failed to generate random key: %m"); - - r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size); + r = x509_generate_volume_keys(cert, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size); if (r < 0) - return log_error_errno(r, "Failed to encrypt key: %m"); + return log_error_errno(r, "Failed to generate volume keys: %m"); /* Add the token URI to the public part of the record. */ r = add_pkcs11_token_uri(v, uri); @@ -202,7 +182,7 @@ int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) { r = add_pkcs11_encrypted_key( v, uri, - encrypted_key, encrypted_key_size, + saved_key, saved_key_size, decrypted_key, decrypted_key_size); if (r < 0) return r; diff --git a/src/home/homectl.c b/src/home/homectl.c index a6951c8562a..f2fe90c75b0 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -12,6 +12,7 @@ #include "cap-list.h" #include "capability-util.h" #include "cgroup-util.h" +#include "creds-util.h" #include "dns-domain.h" #include "env-util.h" #include "fd-util.h" @@ -35,7 +36,9 @@ #include "percent-util.h" #include "pkcs11-util.h" #include "pretty-print.h" +#include "proc-cmdline.h" #include "process-util.h" +#include "recurse-dir.h" #include "rlimit-util.h" #include "spawn-polkit-agent.h" #include "terminal-util.h" @@ -45,6 +48,7 @@ #include "user-record-show.h" #include "user-record-util.h" #include "user-util.h" +#include "userdb.h" #include "verbs.h" static PagerFlags arg_pager_flags = 0; @@ -80,6 +84,7 @@ static enum { } arg_export_format = EXPORT_FORMAT_FULL; static uint64_t arg_capability_bounding_set = UINT64_MAX; static uint64_t arg_capability_ambient_set = UINT64_MAX; +static bool arg_prompt_new_user = false; STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, json_variant_unrefp); STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, json_variant_unrefp); @@ -1092,7 +1097,7 @@ static int add_disposition(JsonVariant **v) { return 1; } -static int acquire_new_home_record(UserRecord **ret) { +static int acquire_new_home_record(JsonVariant *input, UserRecord **ret) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(user_record_unrefp) UserRecord *hr = NULL; int r; @@ -1102,12 +1107,16 @@ static int acquire_new_home_record(UserRecord **ret) { if (arg_identity) { unsigned line, column; + if (input) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Two identity records specified, refusing."); + r = json_parse_file( streq(arg_identity, "-") ? stdin : NULL, streq(arg_identity, "-") ? "" : arg_identity, JSON_PARSE_SENSITIVE, &v, &line, &column); if (r < 0) return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column); - } + } else + v = json_variant_ref(input); r = apply_identity_changes(&v); if (r < 0) @@ -1146,7 +1155,18 @@ static int acquire_new_home_record(UserRecord **ret) { if (!hr) return log_oom(); - r = user_record_load(hr, v, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG|USER_RECORD_PERMISSIVE); + r = user_record_load( + hr, + v, + USER_RECORD_REQUIRE_REGULAR| + USER_RECORD_ALLOW_SECRET| + USER_RECORD_ALLOW_PRIVILEGED| + USER_RECORD_ALLOW_PER_MACHINE| + USER_RECORD_STRIP_BINDING| + USER_RECORD_STRIP_STATUS| + USER_RECORD_STRIP_SIGNATURE| + USER_RECORD_LOG| + USER_RECORD_PERMISSIVE); if (r < 0) return r; @@ -1247,7 +1267,7 @@ static int acquire_new_password( } } -static int create_home(int argc, char *argv[], void *userdata) { +static int create_home_common(JsonVariant *input) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(user_record_unrefp) UserRecord *hr = NULL; int r; @@ -1258,36 +1278,7 @@ static int create_home(int argc, char *argv[], void *userdata) { (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - if (argc >= 2) { - /* If a username was specified, use it */ - - if (valid_user_group_name(argv[1], 0)) - r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]); - else { - _cleanup_free_ char *un = NULL, *rr = NULL; - - /* Before we consider the user name invalid, let's check if we can split it? */ - r = split_user_name_realm(argv[1], &un, &rr); - if (r < 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]); - - if (rr) { - r = json_variant_set_field_string(&arg_identity_extra, "realm", rr); - if (r < 0) - return log_error_errno(r, "Failed to set realm field: %m"); - } - - r = json_variant_set_field_string(&arg_identity_extra, "userName", un); - } - if (r < 0) - return log_error_errno(r, "Failed to set userName field: %m"); - } else { - /* If neither a username nor an identity have been specified we cannot operate. */ - if (!arg_identity) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required."); - } - - r = acquire_new_home_record(&hr); + r = acquire_new_home_record(input, &hr); if (r < 0) return r; @@ -1374,6 +1365,41 @@ static int create_home(int argc, char *argv[], void *userdata) { return 0; } +static int create_home(int argc, char *argv[], void *userdata) { + int r; + + if (argc >= 2) { + /* If a username was specified, use it */ + + if (valid_user_group_name(argv[1], 0)) + r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]); + else { + _cleanup_free_ char *un = NULL, *rr = NULL; + + /* Before we consider the user name invalid, let's check if we can split it? */ + r = split_user_name_realm(argv[1], &un, &rr); + if (r < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]); + + if (rr) { + r = json_variant_set_field_string(&arg_identity_extra, "realm", rr); + if (r < 0) + return log_error_errno(r, "Failed to set realm field: %m"); + } + + r = json_variant_set_field_string(&arg_identity_extra, "userName", un); + } + if (r < 0) + return log_error_errno(r, "Failed to set userName field: %m"); + } else { + /* If neither a username nor an identity have been specified we cannot operate. */ + if (!arg_identity) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required."); + } + + return create_home_common(/* input= */ NULL); +} + static int remove_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r, ret = 0; @@ -2131,6 +2157,190 @@ static int rebalance(int argc, char *argv[], void *userdata) { return 0; } +static int create_from_credentials(void) { + _cleanup_close_ int fd = -EBADF; + int ret = 0, n_created = 0, r; + + fd = open_credentials_dir(); + if (IN_SET(fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */ + return 0; + if (fd < 0) + return log_error_errno(fd, "Failed to open credentials directory: %m"); + + _cleanup_free_ DirectoryEntries *des = NULL; + r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des); + if (r < 0) + return log_error_errno(r, "Failed to enumerate credentials: %m"); + + FOREACH_ARRAY(i, des->entries, des->n_entries) { + _cleanup_(json_variant_unrefp) JsonVariant *identity = NULL; + struct dirent *de = *i; + const char *e; + + if (de->d_type != DT_REG) + continue; + + e = startswith(de->d_name, "home.create."); + if (!e) + continue; + + if (!valid_user_group_name(e, 0)) { + log_notice("Skipping over credential with name that is not a suitable user name: %s", de->d_name); + continue; + } + + r = json_parse_file_at( + /* f= */ NULL, + fd, + de->d_name, + /* flags= */ 0, + &identity, + /* ret_line= */ NULL, + /* ret_column= */ NULL); + if (r < 0) { + log_warning_errno(r, "Failed to parse user record in credential '%s', ignoring: %m", de->d_name); + continue; + } + + JsonVariant *un; + un = json_variant_by_key(identity, "userName"); + if (un) { + if (!json_variant_is_string(un)) { + log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de->d_name); + continue; + } + + if (!streq(json_variant_string(un), e)) { + log_warning("User record from credential '%s' contains 'userName' field (%s) that doesn't match credential name (%s), ignoring.", de->d_name, json_variant_string(un), e); + continue; + } + } else { + r = json_variant_set_field_string(&identity, "userName", e); + if (r < 0) + return log_warning_errno(r, "Failed to set userName field: %m"); + } + + log_notice("Processing user '%s' from credentials.", e); + + r = create_home_common(identity); + if (r >= 0) + n_created++; + + RET_GATHER(ret, r); + } + + return ret < 0 ? ret : n_created; +} + +static int has_regular_user(void) { + _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; + int r; + + r = userdb_all(USERDB_SUPPRESS_SHADOW, &iterator); + if (r < 0) + return log_error_errno(r, "Failed to create user enumerator: %m"); + + for (;;) { + _cleanup_(user_record_unrefp) UserRecord *ur = NULL; + + r = userdb_iterator_get(iterator, &ur); + if (r == -ESRCH) + break; + if (r < 0) + return log_error_errno(r, "Failed to enumerate users: %m"); + + if (user_record_disposition(ur) == USER_REGULAR) + return true; + } + + return false; +} + +static int create_interactively(void) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ char *username = NULL; + int r; + + if (!arg_prompt_new_user) { + log_debug("Prompting for user creation was not requested."); + return 0; + } + + r = acquire_bus(&bus); + if (r < 0) + return r; + + (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); + + (void) reset_terminal_fd(STDIN_FILENO, /* switch_to_text= */ false); + + for (;;) { + username = mfree(username); + + r = ask_string(&username, + "%s Please enter user name to create (empty to skip): ", + special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET)); + if (r < 0) + return log_error_errno(r, "Failed to query user for username: %m"); + + if (isempty(username)) { + log_info("No data entered, skipping."); + return 0; + } + + if (!valid_user_group_name(username, /* flags= */ 0)) { + log_notice("Specified user name is not a valid UNIX user name, try again: %s", username); + continue; + } + + r = userdb_by_name(username, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL); + if (r == -ESRCH) + break; + if (r < 0) + return log_error_errno(r, "Failed to check if specified user '%s' already exists: %m", username); + + log_notice("Specified user '%s' exists already, try again.", username); + } + + r = json_variant_set_field_string(&arg_identity_extra, "userName", username); + if (r < 0) + return log_error_errno(r, "Failed to set userName field: %m"); + + return create_home_common(/* input= */ NULL); +} + +static int verb_firstboot(int argc, char *argv[], void *userdata) { + int r; + + /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot + * tool. */ + + bool enabled; + r = proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled); + if (r < 0) + return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m"); + if (r > 0 && !enabled) { + log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts."); + arg_prompt_new_user = false; + } + + r = create_from_credentials(); + if (r < 0) + return r; + if (r > 0) /* Already created users from credentials */ + return 0; + + r = has_regular_user(); + if (r < 0) + return r; + if (r > 0) { + log_info("Regular user already present in user database, skipping user creation."); + return 0; + } + + return create_interactively(); +} + static int drop_from_identity(const char *field) { int r; @@ -2187,6 +2397,7 @@ static int help(int argc, char *argv[], void *userdata) { " deactivate-all Deactivate all active home areas\n" " rebalance Rebalance free space between home areas\n" " with USER [COMMAND…] Run shell or command with access to a home area\n" + " firstboot Run first-boot home area creation wizard\n" "\n%4$sOptions:%5$s\n" " -h --help Show this help\n" " --version Show package version\n" @@ -2205,6 +2416,8 @@ static int help(int argc, char *argv[], void *userdata) { " -E When specified once equals -j --export-format=\n" " stripped, when specified twice equals\n" " -j --export-format=minimal\n" + " --prompt-new-user firstboot: Query user interactively for user\n" + " to create\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" @@ -2412,6 +2625,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_FIDO2_CRED_ALG, ARG_CAPABILITY_BOUNDING_SET, ARG_CAPABILITY_AMBIENT_SET, + ARG_PROMPT_NEW_USER, }; static const struct option options[] = { @@ -2504,6 +2718,7 @@ static int parse_argv(int argc, char *argv[]) { { "rebalance-weight", required_argument, NULL, ARG_REBALANCE_WEIGHT }, { "capability-bounding-set", required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET }, { "capability-ambient-set", required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET }, + { "prompt-new-user", no_argument, NULL, ARG_PROMPT_NEW_USER }, {} }; @@ -3788,6 +4003,10 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_PROMPT_NEW_USER: + arg_prompt_new_user = true; + break; + case '?': return -EINVAL; @@ -3854,6 +4073,7 @@ static int run(int argc, char *argv[]) { { "lock-all", VERB_ANY, 1, 0, lock_all_homes }, { "deactivate-all", VERB_ANY, 1, 0, deactivate_all_homes }, { "rebalance", VERB_ANY, 1, 0, rebalance }, + { "firstboot", VERB_ANY, 1, 0, verb_firstboot }, {} }; diff --git a/src/home/homed-home-bus.c b/src/home/homed-home-bus.c index a47f4d8a844..413a706f4c3 100644 --- a/src/home/homed-home-bus.c +++ b/src/home/homed-home-bus.c @@ -203,11 +203,8 @@ int bus_home_method_unregister( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.home1.remove-home", - NULL, - true, - UID_INVALID, + /* details= */ NULL, &h->manager->polkit_registry, error); if (r < 0) @@ -243,11 +240,8 @@ int bus_home_method_realize( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.home1.create-home", - NULL, - true, - UID_INVALID, + /* details= */ NULL, &h->manager->polkit_registry, error); if (r < 0) @@ -283,11 +277,8 @@ int bus_home_method_remove( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.home1.remove-home", - NULL, - true, - UID_INVALID, + /* details= */ NULL, &h->manager->polkit_registry, error); if (r < 0) @@ -354,12 +345,11 @@ int bus_home_method_authenticate( if (r < 0) return r; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_SYS_ADMIN, "org.freedesktop.home1.authenticate-home", - NULL, - true, + /* details= */ NULL, + /* interactive= */ false, h->uid, &h->manager->polkit_registry, error); @@ -395,11 +385,8 @@ int bus_home_method_update_record(Home *h, sd_bus_message *message, UserRecord * r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.home1.update-home", - NULL, - true, - UID_INVALID, + /* details= */ NULL, &h->manager->polkit_registry, error); if (r < 0) @@ -461,11 +448,8 @@ int bus_home_method_resize( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.home1.resize-home", - NULL, - true, - UID_INVALID, + /* details= */ NULL, &h->manager->polkit_registry, error); if (r < 0) @@ -506,12 +490,11 @@ int bus_home_method_change_password( if (r < 0) return r; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_SYS_ADMIN, "org.freedesktop.home1.passwd-home", - NULL, - true, + /* details= */ NULL, + /* interactive= */ false, h->uid, &h->manager->polkit_registry, error); diff --git a/src/home/homed-manager-bus.c b/src/home/homed-manager-bus.c index 7cf543922f9..b5dffb2c695 100644 --- a/src/home/homed-manager-bus.c +++ b/src/home/homed-manager-bus.c @@ -396,11 +396,8 @@ static int method_register_home( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.home1.create-home", - NULL, - true, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -443,11 +440,8 @@ static int method_create_home( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.home1.create-home", - NULL, - true, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 5bd78a03ed4..57016fb14b0 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -199,7 +199,7 @@ static int block_get_size_by_fd(int fd, uint64_t *ret) { if (!S_ISBLK(st.st_mode)) return -ENOTBLK; - return RET_NERRNO(ioctl(fd, BLKGETSIZE64, ret)); + return blockdev_get_device_size(fd, ret); } static int block_get_size_by_path(const char *path, uint64_t *ret) { @@ -1295,9 +1295,6 @@ int home_setup_luks( if (!IN_SET(errno, ENOTTY, EINVAL)) return log_error_errno(errno, "Failed to get block device metrics of %s: %m", n); - if (ioctl(setup->loop->fd, BLKGETSIZE64, &size) < 0) - return log_error_errno(r, "Failed to read block device size of %s: %m", n); - if (fstat(setup->loop->fd, &st) < 0) return log_error_errno(r, "Failed to stat block device %s: %m", n); assert(S_ISBLK(st.st_mode)); @@ -1329,6 +1326,8 @@ int home_setup_luks( offset *= 512U; } + + size = setup->loop->device_size; } else { #if HAVE_VALGRIND_MEMCHECK_H VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); @@ -2227,8 +2226,9 @@ int home_create_luks( if (flock(setup->image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */ return log_error_errno(errno, "Failed to lock block device %s: %m", ip); - if (ioctl(setup->image_fd, BLKGETSIZE64, &block_device_size) < 0) - return log_error_errno(errno, "Failed to read block device size: %m"); + r = blockdev_get_device_size(setup->image_fd, &block_device_size); + if (r < 0) + return log_error_errno(r, "Failed to read block device size: %m"); if (h->disk_size == UINT64_MAX) { @@ -3183,8 +3183,9 @@ int home_resize_luks( } else log_info("Operating on whole block device %s.", ip); - if (ioctl(image_fd, BLKGETSIZE64, &old_image_size) < 0) - return log_error_errno(errno, "Failed to determine size of original block device: %m"); + r = blockdev_get_device_size(image_fd, &old_image_size); + if (r < 0) + return log_error_errno(r, "Failed to determine size of original block device: %m"); if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */ return log_error_errno(errno, "Failed to lock block device %s: %m", ip); diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index fc7a97fb99b..893eb4cc0f1 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -1054,13 +1054,12 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * context_read_etc_hostname(c); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_ADMIN, "org.freedesktop.hostname1.set-hostname", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) @@ -1101,13 +1100,12 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_ if (name && !hostname_is_valid(name, 0)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_ADMIN, "org.freedesktop.hostname1.set-static-hostname", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) @@ -1177,17 +1175,15 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name); } - /* Since the pretty hostname should always be changed at the - * same time as the static one, use the same policy action for - * both... */ + /* Since the pretty hostname should always be changed at the same time as the static one, use the + * same policy action for both... */ - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_ADMIN, prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) @@ -1259,13 +1255,12 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err if (r < 0) return r; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_ADMIN, "org.freedesktop.hostname1.get-product-uuid", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) @@ -1306,11 +1301,8 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_ r = bus_verify_polkit_async( m, - CAP_SYS_ADMIN, "org.freedesktop.hostname1.get-hardware-serial", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &c->polkit_registry, error); if (r < 0) @@ -1350,11 +1342,8 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro r = bus_verify_polkit_async( m, - CAP_SYS_ADMIN, "org.freedesktop.hostname1.get-description", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &c->polkit_registry, error); if (r == 0) diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c index 4287b1f0660..861dac0fd15 100644 --- a/src/hwdb/hwdb.c +++ b/src/hwdb/hwdb.c @@ -22,6 +22,9 @@ static int verb_query(int argc, char *argv[], void *userdata) { } static int verb_update(int argc, char *argv[], void *userdata) { + if (hwdb_bypass()) + return 0; + return hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, false); } diff --git a/src/import/importd.c b/src/import/importd.c index e1a1ddc2ee1..e9bbbb628da 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -704,11 +704,8 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_ r = bus_verify_polkit_async( msg, - CAP_SYS_ADMIN, "org.freedesktop.import1.import", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -775,11 +772,8 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e r = bus_verify_polkit_async( msg, - CAP_SYS_ADMIN, "org.freedesktop.import1.import", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -843,11 +837,8 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_ r = bus_verify_polkit_async( msg, - CAP_SYS_ADMIN, "org.freedesktop.import1.export", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -916,11 +907,8 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er r = bus_verify_polkit_async( msg, - CAP_SYS_ADMIN, "org.freedesktop.import1.pull", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -1036,11 +1024,8 @@ static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *erro r = bus_verify_polkit_async( msg, - CAP_SYS_ADMIN, "org.freedesktop.import1.pull", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &t->manager->polkit_registry, error); if (r < 0) @@ -1065,11 +1050,8 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er r = bus_verify_polkit_async( msg, - CAP_SYS_ADMIN, "org.freedesktop.import1.pull", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index de1534befd2..fe68ec4310b 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -1088,9 +1088,9 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--since= must be before --until=."); - if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1) + if (!!arg_cursor + !!arg_after_cursor + !!arg_cursor_file + !!arg_since_set > 1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Please specify only one of --since=, --cursor=, and --after-cursor=."); + "Please specify only one of --since=, --cursor=, --cursor-file=, and --after-cursor=."); if (arg_follow && arg_reverse) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index f5f6ec590ff..78401f9857c 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -650,8 +650,8 @@ void client_context_flush_all(Server *s) { client_context_flush_regular(s); - assert(prioq_size(s->client_contexts_lru) == 0); - assert(hashmap_size(s->client_contexts) == 0); + assert(prioq_isempty(s->client_contexts_lru)); + assert(hashmap_isempty(s->client_contexts)); s->client_contexts_lru = prioq_free(s->client_contexts_lru); s->client_contexts = hashmap_free(s->client_contexts); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index d1f8485661e..476da317fe7 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -2633,7 +2633,7 @@ int server_init(Server *s, const char *namespace) { /* Try to restore streams, but don't bother if this fails */ (void) server_restore_streams(s, fds); - if (fdset_size(fds) > 0) { + if (!fdset_isempty(fds)) { log_warning("%u unknown file descriptors passed, closing.", fdset_size(fds)); fds = fdset_free(fds); } diff --git a/src/kernel-install/60-ukify.install.in b/src/kernel-install/60-ukify.install.in index 978f1795444..e10dc0f8699 100755 --- a/src/kernel-install/60-ukify.install.in +++ b/src/kernel-install/60-ukify.install.in @@ -21,8 +21,8 @@ import argparse import os -import runpy import shlex +import types from shutil import which from pathlib import Path from typing import Optional @@ -209,22 +209,20 @@ def initrd_list(opts) -> list[Path]: return [*microcode, *opts.initrd, *initrd] -def call_ukify(opts): - # Punish me harder. - # We want this: - # ukify = importlib.machinery.SourceFileLoader('ukify', UKIFY).load_module() - # but it throws a DeprecationWarning. - # https://stackoverflow.com/questions/67631/how-can-i-import-a-module-dynamically-given-the-full-path - # https://github.com/python/cpython/issues/65635 - # offer "explanations", but to actually load a python file without a .py extension, - # the "solution" is 4+ incomprehensible lines. - # The solution with runpy gives a dictionary, which isn't great, but will do. - ukify = runpy.run_path(UKIFY, run_name='ukify') +def load_module(name: str, path: str) -> types.ModuleType: + module = types.ModuleType(name) + text = open(path).read() + exec(compile(text, path, 'exec'), module.__dict__) + return module + + +def call_ukify(opts) -> None: + ukify = load_module('ukify', UKIFY) # Create "empty" namespace. We want to override just a few settings, so it # doesn't make sense to configure everything. We pretend to parse an empty # argument set to prepopulate the namespace with the defaults. - opts2 = ukify['create_parser']().parse_args(['build']) + opts2 = ukify.create_parser().parse_args(['build']) opts2.config = uki_conf_location() opts2.uname = opts.kernel_version @@ -240,12 +238,10 @@ def call_ukify(opts): if BOOT_STUB: opts2.stub = BOOT_STUB - # opts2.summary = True - - ukify['apply_config'](opts2) - ukify['finalize_options'](opts2) - ukify['check_inputs'](opts2) - ukify['make_uki'](opts2) + ukify.apply_config(opts2) + ukify.finalize_options(opts2) + ukify.check_inputs(opts2) + ukify.make_uki(opts2) log(f'{opts2.output} has been created') diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c index f9c59a1007c..6f6fa2d943a 100644 --- a/src/libsystemd/sd-bus/bus-track.c +++ b/src/libsystemd/sd-bus/bus-track.c @@ -69,7 +69,7 @@ static void bus_track_add_to_queue(sd_bus_track *track) { return; /* still referenced? */ - if (hashmap_size(track->names) > 0) + if (!hashmap_isempty(track->names)) return; /* Nothing to call? */ diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c index 13801becc90..d18ce88a256 100644 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ b/src/libsystemd/sd-bus/test-bus-creds.c @@ -12,7 +12,7 @@ int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); - if (cg_unified() == -ENOMEDIUM) + if (IN_SET(cg_unified(), -ENOMEDIUM, -ENOENT)) return log_tests_skipped("/sys/fs/cgroup/ not available"); r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL); diff --git a/src/libsystemd/sd-device/test-sd-device-monitor.c b/src/libsystemd/sd-device/test-sd-device-monitor.c index e124e0021c3..4f80c4e5794 100644 --- a/src/libsystemd/sd-device/test-sd-device-monitor.c +++ b/src/libsystemd/sd-device/test-sd-device-monitor.c @@ -10,6 +10,7 @@ #include "device-private.h" #include "device-util.h" #include "macro.h" +#include "mountpoint-util.h" #include "path-util.h" #include "stat-util.h" #include "string-util.h" @@ -298,6 +299,9 @@ int main(int argc, char *argv[]) { if (getuid() != 0) return log_tests_skipped("not root"); + if (path_is_mount_point("/sys", NULL, 0) <= 0) + return log_tests_skipped("/sys is not mounted"); + if (path_is_read_only_fs("/sys") > 0) return log_tests_skipped("Running in container"); diff --git a/src/libsystemd/sd-device/test-sd-device-thread.c b/src/libsystemd/sd-device/test-sd-device-thread.c index c99d179b335..539dabd7a6b 100644 --- a/src/libsystemd/sd-device/test-sd-device-thread.c +++ b/src/libsystemd/sd-device/test-sd-device-thread.c @@ -8,6 +8,7 @@ #include "sd-device.h" #include "device-util.h" +#include "tests.h" #define handle_error_errno(error, msg) \ ({ \ @@ -30,6 +31,8 @@ int main(int argc, char *argv[]) { int r; r = sd_device_new_from_syspath(&loopback, "/sys/class/net/lo"); + if (r == -ENODEV) + return log_tests_skipped("Loopback device not found"); if (r < 0) return handle_error_errno(r, "Failed to create loopback device object"); diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index bce99b55186..f64f6013cf7 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -11,6 +11,7 @@ #include "errno-util.h" #include "fd-util.h" #include "hashmap.h" +#include "mountpoint-util.h" #include "nulstr-util.h" #include "path-util.h" #include "rm-rf.h" @@ -675,4 +676,11 @@ TEST(devname_from_devnum) { } } -DEFINE_TEST_MAIN(LOG_INFO); +static int intro(void) { + if (path_is_mount_point("/sys", NULL, 0) <= 0) + return log_tests_skipped("/sys is not mounted"); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/libsystemd/sd-journal/catalog.c b/src/libsystemd/sd-journal/catalog.c index 4c5562ba488..832b678bf33 100644 --- a/src/libsystemd/sd-journal/catalog.c +++ b/src/libsystemd/sd-journal/catalog.c @@ -452,11 +452,12 @@ int catalog_update(const char* database, const char* root, const char* const* di return log_error_errno(r, "Failed to import file '%s': %m", *f); } - if (ordered_hashmap_size(h) <= 0) { + if (ordered_hashmap_isempty(h)) { log_info("No items in catalog."); return 0; - } else - log_debug("Found %u items in catalog.", ordered_hashmap_size(h)); + } + + log_debug("Found %u items in catalog.", ordered_hashmap_size(h)); items = new(CatalogItem, ordered_hashmap_size(h)); if (!items) diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index 2812eb386a2..cef59c8944e 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -47,10 +47,6 @@ #define DEFAULT_COMPRESS_THRESHOLD (512ULL) #define MIN_COMPRESS_THRESHOLD (8ULL) -#define U64_KB UINT64_C(1024) -#define U64_MB (UINT64_C(1024) * U64_KB) -#define U64_GB (UINT64_C(1024) * U64_MB) - /* This is the minimum journal file size */ #define JOURNAL_FILE_SIZE_MIN (512 * U64_KB) /* 512 KiB */ #define JOURNAL_COMPACT_SIZE_MAX ((uint64_t) UINT32_MAX) /* 4 GiB */ diff --git a/src/libsystemd/sd-journal/sd-journal.c b/src/libsystemd/sd-journal/sd-journal.c index 6b9ff0a4ed8..f2a0c666703 100644 --- a/src/libsystemd/sd-journal/sd-journal.c +++ b/src/libsystemd/sd-journal/sd-journal.c @@ -932,8 +932,8 @@ static int real_journal_next(sd_journal *j, direction_t direction) { if (r < 0) return r; - for (unsigned i = 0; i < n_files; i++) { - JournalFile *f = (JournalFile *)files[i]; + FOREACH_ARRAY(_f, files, n_files) { + JournalFile *f = (JournalFile*) *_f; bool found; r = next_beyond_location(j, f, direction); diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c index 819f86f7446..313e5e339da 100644 --- a/src/libsystemd/sd-login/test-login.c +++ b/src/libsystemd/sd-login/test-login.c @@ -10,6 +10,7 @@ #include "format-util.h" #include "log.h" #include "missing_syscall.h" +#include "mountpoint-util.h" #include "process-util.h" #include "string-util.h" #include "strv.h" @@ -327,6 +328,9 @@ TEST(monitor) { } static int intro(void) { + if (IN_SET(cg_unified(), -ENOENT, -ENOMEDIUM)) + return log_tests_skipped("cgroupfs is not mounted"); + log_info("/* Information printed is from the live system */"); return EXIT_SUCCESS; } diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c index 008e8022b1d..09116b14380 100644 --- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c +++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c @@ -336,7 +336,7 @@ int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret, return r; if (nlmsg_type == RTM_NEWNEXTHOP) - (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; nhm = NLMSG_DATA((*ret)->hdr); diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index b6730b71dc1..b347ee63395 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -176,7 +176,7 @@ static int dispatch_rqueue(sd_netlink *nl, sd_netlink_message **ret) { assert(nl); assert(ret); - if (ordered_set_size(nl->rqueue) <= 0) { + if (ordered_set_isempty(nl->rqueue)) { /* Try to read a new message */ r = socket_read_message(nl); if (r == -ENOBUFS) /* FIXME: ignore buffer overruns for now */ @@ -443,7 +443,7 @@ int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) { assert_return(nl, -EINVAL); assert_return(!netlink_pid_changed(nl), -ECHILD); - if (ordered_set_size(nl->rqueue) > 0) + if (!ordered_set_isempty(nl->rqueue)) return 0; r = netlink_poll(nl, false, timeout_usec); @@ -623,7 +623,7 @@ int sd_netlink_get_events(sd_netlink *nl) { assert_return(nl, -EINVAL); assert_return(!netlink_pid_changed(nl), -ECHILD); - return ordered_set_size(nl->rqueue) == 0 ? POLLIN : 0; + return ordered_set_isempty(nl->rqueue) ? POLLIN : 0; } int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) { @@ -633,7 +633,7 @@ int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) { assert_return(timeout_usec, -EINVAL); assert_return(!netlink_pid_changed(nl), -ECHILD); - if (ordered_set_size(nl->rqueue) > 0) { + if (!ordered_set_isempty(nl->rqueue)) { *timeout_usec = 0; return 1; } diff --git a/src/libudev/test-udev-device-thread.c b/src/libudev/test-udev-device-thread.c index c082fdca468..fdf08188631 100644 --- a/src/libudev/test-udev-device-thread.c +++ b/src/libudev/test-udev-device-thread.c @@ -6,6 +6,7 @@ #include #include "libudev.h" +#include "tests.h" #define handle_error_errno(error, msg) \ ({ \ @@ -29,8 +30,12 @@ int main(int argc, char *argv[]) { int r; loopback = udev_device_new_from_syspath(NULL, "/sys/class/net/lo"); - if (!loopback) + if (!loopback) { + if (errno == ENODEV) + return log_tests_skipped_errno(errno, "Loopback device not found"); + return handle_error_errno(errno, "Failed to create loopback device object"); + } entry = udev_device_get_properties_list_entry(loopback); udev_list_entry_foreach(e, entry) diff --git a/src/locale/localed.c b/src/locale/localed.c index 5d96237fae6..8ce8c0d08fc 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -281,13 +281,12 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er return sd_bus_reply_method_return(m, NULL); } - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_ADMIN, "org.freedesktop.locale1.set-locale", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) @@ -386,13 +385,12 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro if (vc_context_equal(&c->vc, &in) && !x_needs_update) return sd_bus_reply_method_return(m, NULL); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) @@ -506,13 +504,12 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err if (x11_context_equal(&c->x11_from_vc, &in) && x11_context_equal(&c->x11_from_xorg, &in) && !convert) return sd_bus_reply_method_return(m, NULL); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 34992b56810..898c6f752b6 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -236,7 +236,6 @@ int manager_get_seat_from_creds( static int return_test_polkit( sd_bus_message *message, - int capability, const char *action, const char **details, uid_t good_user, @@ -246,7 +245,7 @@ static int return_test_polkit( bool challenge; int r; - r = bus_test_polkit(message, capability, action, details, good_user, &challenge, e); + r = bus_test_polkit(message, action, details, good_user, &challenge, e); if (r < 0) return r; @@ -1245,11 +1244,8 @@ static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_ r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.login1.lock-sessions", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -1397,14 +1393,13 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu if (!pw) return errno_or_else(ENOENT); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_SYS_ADMIN, uid == auth_uid ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -1565,13 +1560,12 @@ static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_ } else if (!seat_name_is_valid(seat)) /* Note that a seat does not have to exist yet for this operation to succeed */ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat name %s is not valid", seat); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_SYS_ADMIN, "org.freedesktop.login1.attach-device", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -1596,13 +1590,12 @@ static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_ if (r < 0) return r; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_SYS_ADMIN, "org.freedesktop.login1.flush-devices", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -1938,13 +1931,12 @@ static int verify_shutdown_creds( interactive = flags & SD_LOGIND_INTERACTIVE; if (multiple_sessions) { - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_SYS_BOOT, a->polkit_action_multiple_sessions, - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -1959,12 +1951,12 @@ static int verify_shutdown_creds( return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access denied to root due to active block inhibitor"); - r = bus_verify_polkit_async(message, - CAP_SYS_BOOT, + r = bus_verify_polkit_async_full( + message, a->polkit_action_ignore_inhibit, - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -1974,12 +1966,12 @@ static int verify_shutdown_creds( } if (!multiple_sessions && !blocked) { - r = bus_verify_polkit_async(message, - CAP_SYS_BOOT, + r = bus_verify_polkit_async_full( + message, a->polkit_action, - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -2529,11 +2521,8 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd r = bus_verify_polkit_async( message, - CAP_SYS_BOOT, a->polkit_action, - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -2640,7 +2629,13 @@ static int method_can_shutdown_or_sleep( } if (multiple_sessions) { - r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_multiple_sessions, NULL, UID_INVALID, &challenge, error); + r = bus_test_polkit( + message, + a->polkit_action_multiple_sessions, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + &challenge, + error); if (r < 0) return r; @@ -2653,7 +2648,13 @@ static int method_can_shutdown_or_sleep( } if (blocked) { - r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_ignore_inhibit, NULL, UID_INVALID, &challenge, error); + r = bus_test_polkit( + message, + a->polkit_action_ignore_inhibit, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + &challenge, + error); if (r < 0) return r; @@ -2671,7 +2672,13 @@ static int method_can_shutdown_or_sleep( /* If neither inhibit nor multiple sessions * apply then just check the normal policy */ - r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action, NULL, UID_INVALID, &challenge, error); + r = bus_test_polkit( + message, + a->polkit_action, + /* details= */ NULL, + /* good_user= */ UID_INVALID, + &challenge, + error); if (r < 0) return r; @@ -2779,14 +2786,12 @@ static int method_set_reboot_parameter( return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot parameter not supported in containers, refusing."); - r = bus_verify_polkit_async(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-parameter", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.login1.set-reboot-parameter", + /* details= */ NULL, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -2817,10 +2822,9 @@ static int method_can_reboot_parameter( return return_test_polkit( message, - CAP_SYS_ADMIN, "org.freedesktop.login1.set-reboot-parameter", - NULL, - UID_INVALID, + /* details= */ NULL, + /* good_user= */ UID_INVALID, error); } @@ -2898,14 +2902,12 @@ static int method_set_reboot_to_firmware_setup( /* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to on */ use_efi = false; - r = bus_verify_polkit_async(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-firmware-setup", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.login1.set-reboot-to-firmware-setup", + /* details= */ NULL, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -2962,10 +2964,9 @@ static int method_can_reboot_to_firmware_setup( return return_test_polkit( message, - CAP_SYS_ADMIN, "org.freedesktop.login1.set-reboot-to-firmware-setup", - NULL, - UID_INVALID, + /* details= */ NULL, + /* good_user= */ UID_INVALID, error); } @@ -3062,14 +3063,12 @@ static int method_set_reboot_to_boot_loader_menu( /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to on */ use_efi = false; - r = bus_verify_polkit_async(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-boot-loader-menu", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.login1.set-reboot-to-boot-loader-menu", + /* details= */ NULL, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -3137,10 +3136,9 @@ static int method_can_reboot_to_boot_loader_menu( return return_test_polkit( message, - CAP_SYS_ADMIN, "org.freedesktop.login1.set-reboot-to-boot-loader-menu", - NULL, - UID_INVALID, + /* details= */ NULL, + /* good_user= */ UID_INVALID, error); } @@ -3261,14 +3259,12 @@ static int method_set_reboot_to_boot_loader_entry( /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to on */ use_efi = false; - r = bus_verify_polkit_async(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-boot-loader-entry", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.login1.set-reboot-to-boot-loader-entry", + /* details= */ NULL, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -3329,10 +3325,9 @@ static int method_can_reboot_to_boot_loader_entry( return return_test_polkit( message, - CAP_SYS_ADMIN, "org.freedesktop.login1.set-reboot-to-boot-loader-entry", - NULL, - UID_INVALID, + /* details= */ NULL, + /* good_user= */ UID_INVALID, error); } @@ -3403,14 +3398,12 @@ static int method_set_wall_message( m->enable_wall_messages == enable_wall_messages) goto done; - r = bus_verify_polkit_async(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-wall-message", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.login1.set-wall-message", + /* details= */ NULL, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -3470,7 +3463,6 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error r = bus_verify_polkit_async( message, - CAP_SYS_BOOT, w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") : w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") : w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" : @@ -3479,9 +3471,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error w == INHIBIT_HANDLE_REBOOT_KEY ? "org.freedesktop.login1.inhibit-handle-reboot-key" : w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" : "org.freedesktop.login1.inhibit-handle-lid-switch", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) diff --git a/src/login/logind-polkit.c b/src/login/logind-polkit.c index e4efd646167..2f1523ae04b 100644 --- a/src/login/logind-polkit.c +++ b/src/login/logind-polkit.c @@ -9,11 +9,8 @@ int check_polkit_chvt(sd_bus_message *message, Manager *manager, sd_bus_error *e #if ENABLE_POLKIT return bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.login1.chvt", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &manager->polkit_registry, error); #else diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c index 877b9c1af14..0a395c65095 100644 --- a/src/login/logind-seat-dbus.c +++ b/src/login/logind-seat-dbus.c @@ -134,11 +134,8 @@ int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er r = bus_verify_polkit_async( message, - CAP_KILL, "org.freedesktop.login1.manage", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &s->manager->polkit_registry, error); if (r < 0) diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index a136ae418ce..9348ccc4dd0 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -158,12 +158,11 @@ int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus assert(message); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_KILL, "org.freedesktop.login1.manage", - NULL, - false, + /* details= */ NULL, + /* interactive= */ false, s->user->user_record->uid, &s->manager->polkit_registry, error); @@ -204,12 +203,11 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro assert(message); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_SYS_ADMIN, "org.freedesktop.login1.lock-sessions", - NULL, - false, + /* details= */ NULL, + /* interactive= */ false, s->user->user_record->uid, &s->manager->polkit_registry, error); @@ -309,12 +307,11 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_KILL, "org.freedesktop.login1.manage", - NULL, - false, + /* details= */ NULL, + /* interactive= */ false, s->user->user_record->uid, &s->manager->polkit_registry, error); diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index 88649b2f4be..0763a5b03f3 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -192,12 +192,11 @@ int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er assert(message); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_KILL, "org.freedesktop.login1.manage", - NULL, - false, + /* details= */ NULL, + /* interactive= */ false, u->user_record->uid, &u->manager->polkit_registry, error); @@ -220,12 +219,11 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error * assert(message); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, - CAP_KILL, "org.freedesktop.login1.manage", - NULL, - false, + /* details= */ NULL, + /* interactive= */ false, u->user_record->uid, &u->manager->polkit_registry, error); diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index aa4525ddbda..69039de2e6c 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -50,11 +50,8 @@ int bus_image_method_remove( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", details, - false, - UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -121,11 +118,8 @@ int bus_image_method_rename( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", details, - false, - UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -173,11 +167,8 @@ int bus_image_method_clone( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", details, - false, - UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -240,11 +231,8 @@ int bus_image_method_mark_read_only( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", details, - false, - UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -285,11 +273,8 @@ int bus_image_method_set_limit( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", details, - false, - UID_INVALID, &m->polkit_registry, error); if (r < 0) diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 4620f32d627..6c2c2232fe3 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -73,11 +73,8 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu r = bus_verify_polkit_async( message, - CAP_KILL, "org.freedesktop.machine1.manage-machines", details, - false, - UID_INVALID, &m->manager->polkit_registry, error); if (r < 0) @@ -106,11 +103,8 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus r = bus_verify_polkit_async( message, - CAP_KILL, "org.freedesktop.machine1.manage-machines", details, - false, - UID_INVALID, &m->manager->polkit_registry, error); if (r < 0) @@ -157,11 +151,8 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro r = bus_verify_polkit_async( message, - CAP_KILL, "org.freedesktop.machine1.manage-machines", details, - false, - UID_INVALID, &m->manager->polkit_registry, error); if (r < 0) @@ -449,11 +440,8 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", details, - false, - UID_INVALID, &m->manager->polkit_registry, error); if (r < 0) @@ -541,11 +529,8 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", details, - false, - UID_INVALID, &m->manager->polkit_registry, error); if (r < 0) @@ -656,11 +641,8 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", details, - false, - UID_INVALID, &m->manager->polkit_registry, error); if (r < 0) @@ -861,11 +843,8 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", details, - false, - UID_INVALID, &m->manager->polkit_registry, error); if (r < 0) @@ -949,11 +928,8 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", details, - false, - UID_INVALID, &m->manager->polkit_registry, error); if (r < 0) @@ -1070,11 +1046,8 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", details, - false, - UID_INVALID, &m->manager->polkit_registry, error); if (r < 0) diff --git a/src/machine/machine.c b/src/machine/machine.c index 44ff5c190bb..af8a88f26ca 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -32,6 +32,7 @@ #include "string-table.h" #include "terminal-util.h" #include "tmpfile-util.h" +#include "uid-range.h" #include "unit-name.h" #include "user-util.h" @@ -658,7 +659,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) { uid_t uid_base, uid_shift, uid_range; gid_t gid_base, gid_shift, gid_range; _cleanup_fclose_ FILE *f = NULL; - int k, r; + int r; assert(m); assert(ret); @@ -690,14 +691,9 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) { } /* Read the first line. There's at least one. */ - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range); - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EBADMSG; - } + r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range); + if (r < 0) + return r; /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */ if (uid_base != 0) @@ -722,13 +718,12 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) { /* Read the first line. There's at least one. */ errno = 0; - k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range); - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - + r = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range); + if (r == EOF) + return errno_or_else(ENOMSG); + assert(r >= 0); + if (r != 3) return -EBADMSG; - } /* If there's more than one line, then we don't support this file. */ r = safe_fgetc(f, NULL); @@ -757,6 +752,7 @@ static int machine_owns_uid_internal( _cleanup_fclose_ FILE *f = NULL; const char *p; + int r; /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */ assert_cc(sizeof(uid_t) == sizeof(gid_t)); @@ -778,18 +774,12 @@ static int machine_owns_uid_internal( for (;;) { uid_t uid_base, uid_shift, uid_range, converted; - int k; - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); - if (k < 0 && feof(f)) + r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range); + if (r == -ENOMSG) break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } + if (r < 0) + return r; /* The private user namespace is disabled, ignoring. */ if (uid_shift == 0) @@ -831,6 +821,7 @@ static int machine_translate_uid_internal( _cleanup_fclose_ FILE *f = NULL; const char *p; + int r; /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */ assert_cc(sizeof(uid_t) == sizeof(gid_t)); @@ -850,18 +841,12 @@ static int machine_translate_uid_internal( for (;;) { uid_t uid_base, uid_shift, uid_range, converted; - int k; - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); - if (k < 0 && feof(f)) + r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range); + if (r == -ENOMSG) break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } + if (r < 0) + return r; if (uid < uid_base || uid >= uid_base + uid_range) continue; @@ -872,6 +857,7 @@ static int machine_translate_uid_internal( if (ret_host_uid) *ret_host_uid = converted; + return 0; } diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 9fec047385b..6b108dc0094 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -720,11 +720,8 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", details, - false, - UID_INVALID, &m->polkit_registry, error); if (r < 0) @@ -855,11 +852,8 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", details, - false, - UID_INVALID, &m->polkit_registry, error); if (r < 0) diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index f626f07af63..2c276ef22a4 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -665,7 +665,7 @@ static int start_transient_mount( if (r < 0) return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL); + r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, NULL); if (r < 0) return r; } @@ -774,7 +774,7 @@ static int start_transient_automount( if (r < 0) return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL); + r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, NULL); if (r < 0) return r; } @@ -936,7 +936,7 @@ static int stop_mount( if (r < 0) return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL); + r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, NULL); if (r < 0) return r; } diff --git a/src/network/meson.build b/src/network/meson.build index 5c05eba0955..3d692abf443 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -109,7 +109,10 @@ systemd_networkd_wait_online_sources = files( 'wait-online/wait-online.c', ) -networkctl_sources = files('networkctl.c') +networkctl_sources = files( + 'networkctl.c', + 'networkctl-config-file.c' +) network_generator_sources = files( 'generator/main.c', diff --git a/src/network/networkctl-config-file.c b/src/network/networkctl-config-file.c new file mode 100644 index 00000000000..670f1c2fd7b --- /dev/null +++ b/src/network/networkctl-config-file.c @@ -0,0 +1,628 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "sd-daemon.h" +#include "sd-device.h" +#include "sd-netlink.h" +#include "sd-network.h" + +#include "bus-error.h" +#include "bus-locator.h" +#include "bus-util.h" +#include "bus-wait-for-jobs.h" +#include "conf-files.h" +#include "edit-util.h" +#include "mkdir-label.h" +#include "netlink-util.h" +#include "networkctl.h" +#include "networkctl-config-file.h" +#include "pager.h" +#include "path-lookup.h" +#include "path-util.h" +#include "pretty-print.h" +#include "selinux-util.h" +#include "strv.h" +#include "virt.h" + +typedef enum ReloadFlags { + RELOAD_NETWORKD = 1 << 0, + RELOAD_UDEVD = 1 << 1, +} ReloadFlags; + +static int get_config_files_by_name( + const char *name, + bool allow_masked, + char **ret_path, + char ***ret_dropins) { + + _cleanup_free_ char *path = NULL; + int r; + + assert(name); + assert(ret_path); + + STRV_FOREACH(i, NETWORK_DIRS) { + _cleanup_free_ char *p = NULL; + + p = path_join(*i, name); + if (!p) + return -ENOMEM; + + r = RET_NERRNO(access(p, F_OK)); + if (r >= 0) { + if (!allow_masked) { + r = null_or_empty_path(p); + if (r < 0) + return log_debug_errno(r, + "Failed to check if network config '%s' is masked: %m", + name); + if (r > 0) + return -ERFKILL; + } + + path = TAKE_PTR(p); + break; + } + + if (r != -ENOENT) + log_debug_errno(r, "Failed to determine whether '%s' exists, ignoring: %m", p); + } + + if (!path) + return -ENOENT; + + if (ret_dropins) { + _cleanup_free_ char *dropin_dirname = NULL; + + dropin_dirname = strjoin(name, ".d"); + if (!dropin_dirname) + return -ENOMEM; + + r = conf_files_list_dropins(ret_dropins, dropin_dirname, /* root = */ NULL, NETWORK_DIRS); + if (r < 0) + return r; + } + + *ret_path = TAKE_PTR(path); + + return 0; +} + +static int get_dropin_by_name( + const char *name, + char * const *dropins, + char **ret) { + + assert(name); + assert(ret); + + STRV_FOREACH(i, dropins) + if (path_equal_filename(*i, name)) { + _cleanup_free_ char *d = NULL; + + d = strdup(*i); + if (!d) + return -ENOMEM; + + *ret = TAKE_PTR(d); + return 1; + } + + *ret = NULL; + return 0; +} + +static int get_network_files_by_link( + sd_netlink **rtnl, + const char *link, + char **ret_path, + char ***ret_dropins) { + + _cleanup_strv_free_ char **dropins = NULL; + _cleanup_free_ char *path = NULL; + int r, ifindex; + + assert(rtnl); + assert(link); + assert(ret_path); + assert(ret_dropins); + + ifindex = rtnl_resolve_interface_or_warn(rtnl, link); + if (ifindex < 0) + return ifindex; + + r = sd_network_link_get_network_file(ifindex, &path); + if (r == -ENODATA) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "Link '%s' has no associated network file.", link); + if (r < 0) + return log_error_errno(r, "Failed to get network file for link '%s': %m", link); + + r = sd_network_link_get_network_file_dropins(ifindex, &dropins); + if (r < 0 && r != -ENODATA) + return log_error_errno(r, "Failed to get network drop-ins for link '%s': %m", link); + + *ret_path = TAKE_PTR(path); + *ret_dropins = TAKE_PTR(dropins); + + return 0; +} + +static int get_link_files_by_link(const char *link, char **ret_path, char ***ret_dropins) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + _cleanup_strv_free_ char **dropins_split = NULL; + _cleanup_free_ char *p = NULL; + const char *path, *dropins; + int r; + + assert(link); + assert(ret_path); + assert(ret_dropins); + + r = sd_device_new_from_ifname(&device, link); + if (r < 0) + return log_error_errno(r, "Failed to create sd-device object for link '%s': %m", link); + + r = sd_device_get_property_value(device, "ID_NET_LINK_FILE", &path); + if (r == -ENOENT) + return log_error_errno(r, "Link '%s' has no associated link file.", link); + if (r < 0) + return log_error_errno(r, "Failed to get link file for link '%s': %m", link); + + r = sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &dropins); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to get link drop-ins for link '%s': %m", link); + if (r >= 0) { + r = strv_split_full(&dropins_split, dropins, ":", EXTRACT_CUNESCAPE); + if (r < 0) + return log_error_errno(r, "Failed to parse link drop-ins for link '%s': %m", link); + } + + p = strdup(path); + if (!p) + return log_oom(); + + *ret_path = TAKE_PTR(p); + *ret_dropins = TAKE_PTR(dropins_split); + + return 0; +} + +static int get_config_files_by_link_config( + const char *link_config, + sd_netlink **rtnl, + char **ret_path, + char ***ret_dropins, + ReloadFlags *ret_reload) { + + _cleanup_strv_free_ char **dropins = NULL, **link_config_split = NULL; + _cleanup_free_ char *path = NULL; + const char *ifname, *type; + ReloadFlags reload; + size_t n; + int r; + + assert(link_config); + assert(rtnl); + assert(ret_path); + assert(ret_dropins); + + link_config_split = strv_split(link_config, ":"); + if (!link_config_split) + return log_oom(); + + n = strv_length(link_config_split); + if (n == 0 || isempty(link_config_split[0])) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No link name is given."); + if (n > 2) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid link config '%s'.", link_config); + + ifname = link_config_split[0]; + type = n == 2 ? link_config_split[1] : "network"; + + if (streq(type, "network")) { + if (!networkd_is_running()) + return log_error_errno(SYNTHETIC_ERRNO(ESRCH), + "Cannot get network file for link if systemd-networkd is not running."); + + r = get_network_files_by_link(rtnl, ifname, &path, &dropins); + if (r < 0) + return r; + + reload = RELOAD_NETWORKD; + } else if (streq(type, "link")) { + r = get_link_files_by_link(ifname, &path, &dropins); + if (r < 0) + return r; + + reload = RELOAD_UDEVD; + } else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid config type '%s' for link '%s'.", type, ifname); + + *ret_path = TAKE_PTR(path); + *ret_dropins = TAKE_PTR(dropins); + + if (ret_reload) + *ret_reload = reload; + + return 0; +} + +static int add_config_to_edit( + EditFileContext *context, + const char *path, + char * const *dropins) { + + _cleanup_free_ char *new_path = NULL, *dropin_path = NULL, *old_dropin = NULL; + _cleanup_strv_free_ char **comment_paths = NULL; + int r; + + assert(context); + assert(path); + + /* If we're supposed to edit main config file in /run/, but a config with the same name is present + * under /etc/, we bail out since the one in /etc/ always overrides that in /run/. */ + if (arg_runtime && !arg_drop_in && path_startswith(path, "/etc")) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "Cannot edit runtime config file: overridden by %s", path); + + if (path_startswith(path, "/usr") || arg_runtime != !!path_startswith(path, "/run")) { + _cleanup_free_ char *name = NULL; + + r = path_extract_filename(path, &name); + if (r < 0) + return log_error_errno(r, "Failed to extract filename from '%s': %m", path); + + new_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], name); + if (!new_path) + return log_oom(); + } + + if (!arg_drop_in) + return edit_files_add(context, new_path ?: path, path, NULL); + + bool need_new_dropin; + + r = get_dropin_by_name(arg_drop_in, dropins, &old_dropin); + if (r < 0) + return log_error_errno(r, "Failed to acquire drop-in '%s': %m", arg_drop_in); + if (r > 0) { + /* See the explanation above */ + if (arg_runtime && path_startswith(old_dropin, "/etc")) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "Cannot edit runtime config file: overridden by %s", old_dropin); + + need_new_dropin = path_startswith(old_dropin, "/usr") || arg_runtime != !!path_startswith(old_dropin, "/run"); + } else + need_new_dropin = true; + + if (!need_new_dropin) + /* An existing drop-in is found in the correct scope. Let's edit it directly. */ + dropin_path = TAKE_PTR(old_dropin); + else { + /* No drop-in was found or an existing drop-in is in a different scope. Let's create a new + * drop-in file. */ + dropin_path = strjoin(new_path ?: path, ".d/", arg_drop_in); + if (!dropin_path) + return log_oom(); + } + + comment_paths = strv_new(path); + if (!comment_paths) + return log_oom(); + + r = strv_extend_strv(&comment_paths, dropins, /* filter_duplicates = */ false); + if (r < 0) + return log_oom(); + + return edit_files_add(context, dropin_path, old_dropin, comment_paths); +} + +static int udevd_reload(sd_bus *bus) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; + const char *job_path; + int r; + + assert(bus); + + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); + + r = bus_call_method(bus, + bus_systemd_mgr, + "ReloadUnit", + &error, + &reply, + "ss", + "systemd-udevd.service", + "replace"); + if (r < 0) + return log_error_errno(r, "Failed to reload systemd-udevd: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "o", &job_path); + if (r < 0) + return bus_log_parse_error(r); + + r = bus_wait_for_jobs_one(w, job_path, /* flags = */ 0, NULL); + if (r == -ENOEXEC) { + log_debug("systemd-udevd is not running, skipping reload."); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to reload systemd-udevd: %m"); + + return 1; +} + +static int reload_daemons(ReloadFlags flags) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r, ret = 1; + + if (arg_no_reload) + return 0; + + if (flags == 0) + return 0; + + if (!sd_booted() || running_in_chroot() > 0) { + log_debug("System is not booted with systemd or is running in chroot, skipping reload."); + return 0; + } + + r = sd_bus_open_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); + + if (FLAGS_SET(flags, RELOAD_UDEVD)) + RET_GATHER(ret, udevd_reload(bus)); + + if (FLAGS_SET(flags, RELOAD_NETWORKD)) { + if (networkd_is_running()) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL); + if (r < 0) + RET_GATHER(ret, log_error_errno(r, "Failed to reload systemd-networkd: %s", bus_error_message(&error, r))); + } else + log_debug("systemd-networkd is not running, skipping reload."); + } + + return ret; +} + +int verb_edit(int argc, char *argv[], void *userdata) { + _cleanup_(edit_file_context_done) EditFileContext context = { + .marker_start = DROPIN_MARKER_START, + .marker_end = DROPIN_MARKER_END, + .remove_parent = !!arg_drop_in, + }; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + ReloadFlags reload = 0; + int r; + + if (!on_tty()) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit network config files if not on a tty."); + + r = mac_selinux_init(); + if (r < 0) + return r; + + STRV_FOREACH(name, strv_skip(argv, 1)) { + _cleanup_strv_free_ char **dropins = NULL; + _cleanup_free_ char *path = NULL; + const char *link_config; + + link_config = startswith(*name, "@"); + if (link_config) { + ReloadFlags flags; + + r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, &flags); + if (r < 0) + return r; + + reload |= flags; + + r = add_config_to_edit(&context, path, dropins); + if (r < 0) + return r; + + continue; + } + + if (ENDSWITH_SET(*name, ".network", ".netdev")) + reload |= RELOAD_NETWORKD; + else if (endswith(*name, ".link")) + reload |= RELOAD_UDEVD; + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name); + + r = get_config_files_by_name(*name, /* allow_masked = */ false, &path, &dropins); + if (r == -ERFKILL) + return log_error_errno(r, "Network config '%s' is masked.", *name); + if (r == -ENOENT) { + if (arg_drop_in) + return log_error_errno(r, "Cannot find network config '%s'.", *name); + + log_debug("No existing network config '%s' found, creating a new file.", *name); + + path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name); + if (!path) + return log_oom(); + + r = edit_files_add(&context, path, NULL, NULL); + if (r < 0) + return r; + continue; + } + if (r < 0) + return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name); + + r = add_config_to_edit(&context, path, dropins); + if (r < 0) + return r; + } + + r = do_edit_files_and_install(&context); + if (r < 0) + return r; + + return reload_daemons(reload); +} + +int verb_cat(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int r, ret = 0; + + pager_open(arg_pager_flags); + + bool first = true; + STRV_FOREACH(name, strv_skip(argv, 1)) { + _cleanup_strv_free_ char **dropins = NULL; + _cleanup_free_ char *path = NULL; + const char *link_config; + + link_config = startswith(*name, "@"); + if (link_config) { + r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, /* ret_reload = */ NULL); + if (r < 0) + return RET_GATHER(ret, r); + } else { + r = get_config_files_by_name(*name, /* allow_masked = */ false, &path, &dropins); + if (r == -ENOENT) { + RET_GATHER(ret, log_error_errno(r, "Cannot find network config file '%s'.", *name)); + continue; + } + if (r == -ERFKILL) { + RET_GATHER(ret, log_debug_errno(r, "Network config '%s' is masked, ignoring.", *name)); + continue; + } + if (r < 0) { + log_error_errno(r, "Failed to get the path of network config '%s': %m", *name); + return RET_GATHER(ret, r); + } + } + + if (!first) + putchar('\n'); + + r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS); + if (r < 0) + return RET_GATHER(ret, r); + + first = false; + } + + return ret; +} + +int verb_mask(int argc, char *argv[], void *userdata) { + ReloadFlags flags = 0; + int r; + + r = mac_selinux_init(); + if (r < 0) + return r; + + STRV_FOREACH(name, strv_skip(argv, 1)) { + _cleanup_free_ char *config_path = NULL, *symlink_path = NULL; + ReloadFlags reload; + + /* We update the real 'flags' at last, since the operation can be skipped. */ + if (ENDSWITH_SET(*name, ".network", ".netdev")) + reload = RELOAD_NETWORKD; + else if (endswith(*name, ".link")) + reload = RELOAD_UDEVD; + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name); + + r = get_config_files_by_name(*name, /* allow_masked = */ true, &config_path, /* ret_dropins = */ NULL); + if (r == -ENOENT) + log_warning("No existing network config '%s' found, proceeding anyway.", *name); + else if (r < 0) + return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name); + else if (!path_startswith(config_path, "/usr")) { + r = null_or_empty_path(config_path); + if (r < 0) + return log_error_errno(r, + "Failed to check if '%s' is masked: %m", config_path); + if (r > 0) { + log_debug("%s is already masked, skipping.", config_path); + continue; + } + + /* At this point, we have found a config under mutable dir (/run/ or /etc/), + * so masking through /run/ (--runtime) is not possible. If it's under /etc/, + * then it doesn't work without --runtime either. */ + if (arg_runtime || path_startswith(config_path, "/etc")) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "Cannot mask network config %s: %s exists", + *name, config_path); + } + + symlink_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name); + if (!symlink_path) + return log_oom(); + + (void) mkdir_parents_label(symlink_path, 0755); + + if (symlink("/dev/null", symlink_path) < 0) + return log_error_errno(errno, + "Failed to create symlink '%s' to /dev/null: %m", symlink_path); + + flags |= reload; + log_info("Successfully created symlink '%s' to /dev/null.", symlink_path); + } + + return reload_daemons(flags); +} + +int verb_unmask(int argc, char *argv[], void *userdata) { + ReloadFlags flags = 0; + int r; + + STRV_FOREACH(name, strv_skip(argv, 1)) { + _cleanup_free_ char *path = NULL; + ReloadFlags reload; + + if (ENDSWITH_SET(*name, ".network", ".netdev")) + reload = RELOAD_NETWORKD; + else if (endswith(*name, ".link")) + reload = RELOAD_UDEVD; + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name); + + r = get_config_files_by_name(*name, /* allow_masked = */ true, &path, /* ret_dropins = */ NULL); + if (r == -ENOENT) { + log_debug_errno(r, "Network configuration '%s' doesn't exist, skipping.", *name); + continue; + } + if (r < 0) + return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name); + + r = null_or_empty_path(path); + if (r < 0) + return log_error_errno(r, "Failed to check if '%s' is masked: %m", path); + if (r == 0) + continue; + + if (path_startswith(path, "/usr")) + return log_error_errno(r, "Cannot unmask network config under /usr/: %s", path); + + if (unlink(path) < 0) { + if (errno == ENOENT) + continue; + + return log_error_errno(errno, "Failed to remove '%s': %m", path); + } + + flags |= reload; + log_info("Successfully removed masked network config '%s'.", path); + } + + return reload_daemons(flags); +} diff --git a/src/network/networkctl-config-file.h b/src/network/networkctl-config-file.h new file mode 100644 index 00000000000..38210a8093b --- /dev/null +++ b/src/network/networkctl-config-file.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_edit(int argc, char *argv[], void *userdata); +int verb_cat(int argc, char *argv[], void *userdata); + +int verb_mask(int argc, char *argv[], void *userdata); +int verb_unmask(int argc, char *argv[], void *userdata); diff --git a/src/network/networkctl.c b/src/network/networkctl.c index f8ee3931720..cf9d17c8b2f 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -26,10 +26,7 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-locator.h" -#include "bus-wait-for-jobs.h" -#include "conf-files.h" #include "device-util.h" -#include "edit-util.h" #include "escape.h" #include "ether-addr-util.h" #include "ethtool-util.h" @@ -51,6 +48,8 @@ #include "netlink-util.h" #include "network-internal.h" #include "network-util.h" +#include "networkctl.h" +#include "networkctl-config-file.h" #include "pager.h" #include "parse-argument.h" #include "parse-util.h" @@ -72,7 +71,6 @@ #include "udev-util.h" #include "unit-def.h" #include "verbs.h" -#include "virt.h" #include "wifi-util.h" /* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */ @@ -81,16 +79,16 @@ /* use 128 kB for receive socket kernel queue, we shouldn't need more here */ #define RCVBUF_SIZE (128*1024) -static PagerFlags arg_pager_flags = 0; -static bool arg_legend = true; -static bool arg_no_reload = false; -static bool arg_all = false; -static bool arg_stats = false; -static bool arg_full = false; -static bool arg_runtime = false; -static unsigned arg_lines = 10; -static char *arg_drop_in = NULL; -static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; +PagerFlags arg_pager_flags = 0; +bool arg_legend = true; +bool arg_no_reload = false; +bool arg_all = false; +bool arg_stats = false; +bool arg_full = false; +bool arg_runtime = false; +unsigned arg_lines = 10; +char *arg_drop_in = NULL; +JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; STATIC_DESTRUCTOR_REGISTER(arg_drop_in, freep); @@ -122,7 +120,7 @@ static int check_netns_match(sd_bus *bus) { return 0; } -static bool networkd_is_running(void) { +bool networkd_is_running(void) { static int cached = -1; int r; @@ -141,7 +139,7 @@ static bool networkd_is_running(void) { return cached; } -static int acquire_bus(sd_bus **ret) { +int acquire_bus(sd_bus **ret) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; @@ -2855,471 +2853,6 @@ static int verb_reconfigure(int argc, char *argv[], void *userdata) { return 0; } -typedef enum ReloadFlags { - RELOAD_NETWORKD = 1 << 0, - RELOAD_UDEVD = 1 << 1, -} ReloadFlags; - -static int get_config_files_by_name(const char *name, char **ret_path, char ***ret_dropins) { - _cleanup_free_ char *path = NULL; - int r; - - assert(name); - assert(ret_path); - - STRV_FOREACH(i, NETWORK_DIRS) { - _cleanup_free_ char *p = NULL; - - p = path_join(*i, name); - if (!p) - return -ENOMEM; - - r = RET_NERRNO(access(p, F_OK)); - if (r >= 0) { - path = TAKE_PTR(p); - break; - } - - if (r != -ENOENT) - log_debug_errno(r, "Failed to determine whether '%s' exists, ignoring: %m", p); - } - - if (!path) - return -ENOENT; - - if (ret_dropins) { - _cleanup_free_ char *dropin_dirname = NULL; - - dropin_dirname = strjoin(name, ".d"); - if (!dropin_dirname) - return -ENOMEM; - - r = conf_files_list_dropins(ret_dropins, dropin_dirname, /* root = */ NULL, NETWORK_DIRS); - if (r < 0) - return r; - } - - *ret_path = TAKE_PTR(path); - - return 0; -} - -static int get_dropin_by_name( - const char *name, - char * const *dropins, - char **ret) { - - assert(name); - assert(ret); - - STRV_FOREACH(i, dropins) - if (path_equal_filename(*i, name)) { - _cleanup_free_ char *d = NULL; - - d = strdup(*i); - if (!d) - return -ENOMEM; - - *ret = TAKE_PTR(d); - return 1; - } - - *ret = NULL; - return 0; -} - -static int get_network_files_by_link( - sd_netlink **rtnl, - const char *link, - char **ret_path, - char ***ret_dropins) { - - _cleanup_strv_free_ char **dropins = NULL; - _cleanup_free_ char *path = NULL; - int r, ifindex; - - assert(rtnl); - assert(link); - assert(ret_path); - assert(ret_dropins); - - ifindex = rtnl_resolve_interface_or_warn(rtnl, link); - if (ifindex < 0) - return ifindex; - - r = sd_network_link_get_network_file(ifindex, &path); - if (r == -ENODATA) - return log_error_errno(SYNTHETIC_ERRNO(ENOENT), - "Link '%s' has no associated network file.", link); - if (r < 0) - return log_error_errno(r, "Failed to get network file for link '%s': %m", link); - - r = sd_network_link_get_network_file_dropins(ifindex, &dropins); - if (r < 0 && r != -ENODATA) - return log_error_errno(r, "Failed to get network drop-ins for link '%s': %m", link); - - *ret_path = TAKE_PTR(path); - *ret_dropins = TAKE_PTR(dropins); - - return 0; -} - -static int get_link_files_by_link(const char *link, char **ret_path, char ***ret_dropins) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - _cleanup_strv_free_ char **dropins_split = NULL; - _cleanup_free_ char *p = NULL; - const char *path, *dropins; - int r; - - assert(link); - assert(ret_path); - assert(ret_dropins); - - r = sd_device_new_from_ifname(&device, link); - if (r < 0) - return log_error_errno(r, "Failed to create sd-device object for link '%s': %m", link); - - r = sd_device_get_property_value(device, "ID_NET_LINK_FILE", &path); - if (r == -ENOENT) - return log_error_errno(r, "Link '%s' has no associated link file.", link); - if (r < 0) - return log_error_errno(r, "Failed to get link file for link '%s': %m", link); - - r = sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &dropins); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "Failed to get link drop-ins for link '%s': %m", link); - if (r >= 0) { - r = strv_split_full(&dropins_split, dropins, ":", EXTRACT_CUNESCAPE); - if (r < 0) - return log_error_errno(r, "Failed to parse link drop-ins for link '%s': %m", link); - } - - p = strdup(path); - if (!p) - return log_oom(); - - *ret_path = TAKE_PTR(p); - *ret_dropins = TAKE_PTR(dropins_split); - - return 0; -} - -static int get_config_files_by_link_config( - const char *link_config, - sd_netlink **rtnl, - char **ret_path, - char ***ret_dropins, - ReloadFlags *ret_reload) { - - _cleanup_strv_free_ char **dropins = NULL, **link_config_split = NULL; - _cleanup_free_ char *path = NULL; - const char *ifname, *type; - ReloadFlags reload; - size_t n; - int r; - - assert(link_config); - assert(rtnl); - assert(ret_path); - assert(ret_dropins); - - link_config_split = strv_split(link_config, ":"); - if (!link_config_split) - return log_oom(); - - n = strv_length(link_config_split); - if (n == 0 || isempty(link_config_split[0])) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No link name is given."); - if (n > 2) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid link config '%s'.", link_config); - - ifname = link_config_split[0]; - type = n == 2 ? link_config_split[1] : "network"; - - if (streq(type, "network")) { - if (!networkd_is_running()) - return log_error_errno(SYNTHETIC_ERRNO(ESRCH), - "Cannot get network file for link if systemd-networkd is not running."); - - r = get_network_files_by_link(rtnl, ifname, &path, &dropins); - if (r < 0) - return r; - - reload = RELOAD_NETWORKD; - } else if (streq(type, "link")) { - r = get_link_files_by_link(ifname, &path, &dropins); - if (r < 0) - return r; - - reload = RELOAD_UDEVD; - } else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Invalid config type '%s' for link '%s'.", type, ifname); - - *ret_path = TAKE_PTR(path); - *ret_dropins = TAKE_PTR(dropins); - - if (ret_reload) - *ret_reload = reload; - - return 0; -} - -static int add_config_to_edit( - EditFileContext *context, - const char *path, - char * const *dropins) { - - _cleanup_free_ char *new_path = NULL, *dropin_path = NULL, *old_dropin = NULL; - _cleanup_strv_free_ char **comment_paths = NULL; - int r; - - assert(context); - assert(path); - - /* If we're supposed to edit main config file in /run/, but a config with the same name is present - * under /etc/, we bail out since the one in /etc/ always overrides that in /run/. */ - if (arg_runtime && !arg_drop_in && path_startswith(path, "/etc")) - return log_error_errno(SYNTHETIC_ERRNO(EEXIST), - "Cannot edit runtime config file: overridden by %s", path); - - if (path_startswith(path, "/usr") || arg_runtime != !!path_startswith(path, "/run")) { - _cleanup_free_ char *name = NULL; - - r = path_extract_filename(path, &name); - if (r < 0) - return log_error_errno(r, "Failed to extract filename from '%s': %m", path); - - new_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], name); - if (!new_path) - return log_oom(); - } - - if (!arg_drop_in) - return edit_files_add(context, new_path ?: path, path, NULL); - - bool need_new_dropin; - - r = get_dropin_by_name(arg_drop_in, dropins, &old_dropin); - if (r < 0) - return log_error_errno(r, "Failed to acquire drop-in '%s': %m", arg_drop_in); - if (r > 0) { - /* See the explanation above */ - if (arg_runtime && path_startswith(old_dropin, "/etc")) - return log_error_errno(SYNTHETIC_ERRNO(EEXIST), - "Cannot edit runtime config file: overridden by %s", old_dropin); - - need_new_dropin = path_startswith(old_dropin, "/usr") || arg_runtime != !!path_startswith(old_dropin, "/run"); - } else - need_new_dropin = true; - - if (!need_new_dropin) - /* An existing drop-in is found in the correct scope. Let's edit it directly. */ - dropin_path = TAKE_PTR(old_dropin); - else { - /* No drop-in was found or an existing drop-in is in a different scope. Let's create a new - * drop-in file. */ - dropin_path = strjoin(new_path ?: path, ".d/", arg_drop_in); - if (!dropin_path) - return log_oom(); - } - - comment_paths = strv_new(path); - if (!comment_paths) - return log_oom(); - - r = strv_extend_strv(&comment_paths, dropins, /* filter_duplicates = */ false); - if (r < 0) - return log_oom(); - - return edit_files_add(context, dropin_path, old_dropin, comment_paths); -} - -static int udevd_reload(sd_bus *bus) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; - const char *job_path; - int r; - - assert(bus); - - r = bus_wait_for_jobs_new(bus, &w); - if (r < 0) - return log_error_errno(r, "Could not watch jobs: %m"); - - r = bus_call_method(bus, - bus_systemd_mgr, - "ReloadUnit", - &error, - &reply, - "ss", - "systemd-udevd.service", - "replace"); - if (r < 0) - return log_error_errno(r, "Failed to reload systemd-udevd: %s", bus_error_message(&error, r)); - - r = sd_bus_message_read(reply, "o", &job_path); - if (r < 0) - return bus_log_parse_error(r); - - r = bus_wait_for_jobs_one(w, job_path, /* quiet = */ true, NULL); - if (r == -ENOEXEC) { - log_debug("systemd-udevd is not running, skipping reload."); - return 0; - } - if (r < 0) - return log_error_errno(r, "Failed to reload systemd-udevd: %m"); - - return 1; -} - -static int verb_edit(int argc, char *argv[], void *userdata) { - _cleanup_(edit_file_context_done) EditFileContext context = { - .marker_start = DROPIN_MARKER_START, - .marker_end = DROPIN_MARKER_END, - .remove_parent = !!arg_drop_in, - }; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - ReloadFlags reload = 0; - int r; - - if (!on_tty()) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit network config files if not on a tty."); - - r = mac_selinux_init(); - if (r < 0) - return r; - - STRV_FOREACH(name, strv_skip(argv, 1)) { - _cleanup_strv_free_ char **dropins = NULL; - _cleanup_free_ char *path = NULL; - const char *link_config; - - link_config = startswith(*name, "@"); - if (link_config) { - ReloadFlags flags; - - r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, &flags); - if (r < 0) - return r; - - reload |= flags; - - r = add_config_to_edit(&context, path, dropins); - if (r < 0) - return r; - - continue; - } - - if (ENDSWITH_SET(*name, ".network", ".netdev")) - reload |= RELOAD_NETWORKD; - else if (endswith(*name, ".link")) - reload |= RELOAD_UDEVD; - else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name); - - r = get_config_files_by_name(*name, &path, &dropins); - if (r == -ENOENT) { - if (arg_drop_in) - return log_error_errno(r, "Cannot find network config '%s'.", *name); - - log_debug("No existing network config '%s' found, creating a new file.", *name); - - path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name); - if (!path) - return log_oom(); - - r = edit_files_add(&context, path, NULL, NULL); - if (r < 0) - return r; - continue; - } - if (r < 0) - return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name); - - r = add_config_to_edit(&context, path, dropins); - if (r < 0) - return r; - } - - r = do_edit_files_and_install(&context); - if (r < 0) - return r; - - if (arg_no_reload) - return 0; - - if (!sd_booted() || running_in_chroot() > 0) { - log_debug("System is not booted with systemd or is running in chroot, skipping reload."); - return 0; - } - - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - - r = sd_bus_open_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to connect to system bus: %m"); - - if (FLAGS_SET(reload, RELOAD_UDEVD)) { - r = udevd_reload(bus); - if (r < 0) - return r; - } - - if (FLAGS_SET(reload, RELOAD_NETWORKD)) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - if (!networkd_is_running()) { - log_debug("systemd-networkd is not running, skipping reload."); - return 0; - } - - r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to reload systemd-networkd: %s", bus_error_message(&error, r)); - } - - return 0; -} - -static int verb_cat(int argc, char *argv[], void *userdata) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - int r, ret = 0; - - pager_open(arg_pager_flags); - - STRV_FOREACH(name, strv_skip(argv, 1)) { - _cleanup_strv_free_ char **dropins = NULL; - _cleanup_free_ char *path = NULL; - const char *link_config; - - link_config = startswith(*name, "@"); - if (link_config) { - r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, /* ret_reload = */ NULL); - if (r < 0) - return RET_GATHER(ret, r); - } else { - r = get_config_files_by_name(*name, &path, &dropins); - if (r == -ENOENT) { - RET_GATHER(ret, log_error_errno(r, "Cannot find network config file '%s'.", *name)); - continue; - } - if (r < 0) { - log_error_errno(r, "Failed to get the path of network config '%s': %m", *name); - return RET_GATHER(ret, r); - } - } - - r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS); - if (r < 0) - return RET_GATHER(ret, r); - } - - return ret; -} - static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -3344,6 +2877,8 @@ static int help(void) { " reload Reload .network and .netdev files\n" " edit FILES|DEVICES... Edit network configuration files\n" " cat FILES|DEVICES... Show network configuration files\n" + " mask FILES... Mask network configuration files\n" + " unmask FILES... Unmask network configuration files\n" "\nOptions:\n" " -h --help Show this help\n" " --version Show package version\n" @@ -3500,6 +3035,8 @@ static int networkctl_main(int argc, char *argv[]) { { "reload", 1, 1, VERB_ONLINE_ONLY, verb_reload }, { "edit", 2, VERB_ANY, 0, verb_edit }, { "cat", 2, VERB_ANY, 0, verb_cat }, + { "mask", 2, VERB_ANY, 0, verb_mask }, + { "unmask", 2, VERB_ANY, 0, verb_unmask }, {} }; diff --git a/src/network/networkctl.h b/src/network/networkctl.h new file mode 100644 index 00000000000..46b44f79755 --- /dev/null +++ b/src/network/networkctl.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "sd-bus.h" + +#include "output-mode.h" +#include "pager.h" + +extern PagerFlags arg_pager_flags; +extern bool arg_legend; +extern bool arg_no_reload; +extern bool arg_all; +extern bool arg_stats; +extern bool arg_full; +extern bool arg_runtime; +extern unsigned arg_lines; +extern char *arg_drop_in; +extern JsonFormatFlags arg_json_format_flags; + +bool networkd_is_running(void); +int acquire_bus(sd_bus **ret); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 29c424026e2..e3bdc66c587 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -674,8 +674,8 @@ static void address_modify_nft_set_context(Address *address, bool add, NFTSetCon } if (r < 0) - log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, IP address %s, ignoring", - add? "add" : "delete", + log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, IP address %s, ignoring: %m", + add ? "add" : "delete", nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set, IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen)); else @@ -1480,7 +1480,7 @@ static bool address_is_ready_to_configure(Link *link, const Address *address) { } static int address_process_request(Request *req, Link *link, Address *address) { - struct Address *existing; + Address *existing; struct ifa_cacheinfo c; int r; @@ -2472,7 +2472,7 @@ int address_section_verify(Address *address) { if (address->flags != filtered_flags) { _cleanup_free_ char *str = NULL; - (void) address_flags_to_string_alloc(filtered_flags, address->family, &str); + (void) address_flags_to_string_alloc(address->flags ^ filtered_flags, address->family, &str); return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "%s: unexpected address flags \"%s\" were configured. " "Ignoring [Address] section from line %u.", diff --git a/src/network/networkd-bridge-vlan.c b/src/network/networkd-bridge-vlan.c index d84b6b34f63..3c052675217 100644 --- a/src/network/networkd-bridge-vlan.c +++ b/src/network/networkd-bridge-vlan.c @@ -27,9 +27,18 @@ static void set_bit(unsigned nr, uint32_t *addr) { addr[nr / 32] |= (UINT32_C(1) << (nr % 32)); } -static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid) { +static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid, char **str) { assert(m); assert(id < BRIDGE_VLAN_BITMAP_MAX); + + if (DEBUG_LOGGING) + (void) strextendf_with_separator(str, ",", "%u%s%s%s%s%s", id, + (untagged || is_pvid) ? "(" : "", + untagged ? "untagged" : "", + (untagged && is_pvid) ? "," : "", + is_pvid ? "pvid" : "", + (untagged || is_pvid) ? ")" : ""); + return sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO, &(struct bridge_vlan_info) { .vid = id, @@ -39,7 +48,7 @@ static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is sizeof(struct bridge_vlan_info)); } -static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged) { +static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged, char **str) { int r; assert(m); @@ -47,7 +56,10 @@ static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool u assert(end < BRIDGE_VLAN_BITMAP_MAX); if (begin == end) - return add_single(m, begin, untagged, /* is_pvid = */ false); + return add_single(m, begin, untagged, /* is_pvid = */ false, str); + + if (DEBUG_LOGGING) + (void) strextendf_with_separator(str, ",", "%u-%u%s", begin, end, untagged ? "(untagged)" : ""); r = sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO, &(struct bridge_vlan_info) { @@ -95,6 +107,7 @@ static uint16_t link_get_pvid(Link *link, bool *ret_untagged) { } static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) { + _cleanup_free_ char *str = NULL; uint16_t pvid, begin = UINT16_MAX; bool untagged, pvid_is_untagged; int r; @@ -112,14 +125,14 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) { if (begin != UINT16_MAX) { assert(begin < k); - r = add_range(m, begin, k - 1, untagged); + r = add_range(m, begin, k - 1, untagged, &str); if (r < 0) return r; begin = UINT16_MAX; } - r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true); + r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true, &str); if (r < 0) return r; @@ -131,7 +144,7 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) { if (begin != UINT16_MAX) { assert(begin < k); - r = add_range(m, begin, k - 1, untagged); + r = add_range(m, begin, k - 1, untagged, &str); if (r < 0) return r; @@ -151,7 +164,7 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) { continue; /* Tagging flag is changed from the previous bits. Finish them. */ - r = add_range(m, begin, k - 1, untagged); + r = add_range(m, begin, k - 1, untagged, &str); if (r < 0) return r; @@ -170,10 +183,13 @@ static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) { * the above loop, we run 0…4095. */ assert_cc(BRIDGE_VLAN_BITMAP_MAX > VLANID_MAX); assert(begin == UINT16_MAX); + + log_link_debug(link, "Setting Bridge VLAN IDs: %s", strna(str)); return 0; } static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) { + _cleanup_free_ char *str = NULL; uint16_t pvid, begin = UINT16_MAX; int r; @@ -192,7 +208,7 @@ static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) { if (begin != UINT16_MAX) { assert(begin < k); - r = add_range(m, begin, k - 1, /* untagged = */ false); + r = add_range(m, begin, k - 1, /* untagged = */ false, &str); if (r < 0) return r; @@ -211,6 +227,8 @@ static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) { /* No pending bit sequence. */ assert(begin == UINT16_MAX); + + log_link_debug(link, "Removing Bridge VLAN IDs: %s", strna(str)); return 0; } diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf index 8542ffa6b5d..c9e3c937f47 100644 --- a/src/network/networkd-gperf.gperf +++ b/src/network/networkd-gperf.gperf @@ -25,6 +25,7 @@ Network.SpeedMeter, config_parse_bool, Network.SpeedMeterIntervalSec, config_parse_sec, 0, offsetof(Manager, speed_meter_interval_usec) Network.ManageForeignRoutingPolicyRules, config_parse_bool, 0, offsetof(Manager, manage_foreign_rules) Network.ManageForeignRoutes, config_parse_bool, 0, offsetof(Manager, manage_foreign_routes) +Network.ManageForeignNextHops, config_parse_bool, 0, offsetof(Manager, manage_foreign_nexthops) Network.RouteTable, config_parse_route_table_names, 0, 0 Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Manager, ipv6_privacy_extensions) DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp_duid) diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index eed8d9f7879..b4defb7091c 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -185,16 +185,20 @@ static int nexthop_build_json(NextHop *n, JsonVariant **ret) { JSON_BUILD_PAIR_STRING("ConfigState", state))); } -static int nexthops_append_json(Set *nexthops, JsonVariant **v) { +static int nexthops_append_json(Manager *manager, int ifindex, JsonVariant **v) { _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; NextHop *nexthop; int r; + assert(manager); assert(v); - SET_FOREACH(nexthop, nexthops) { + HASHMAP_FOREACH(nexthop, manager->nexthops_by_id) { _cleanup_(json_variant_unrefp) JsonVariant *e = NULL; + if (nexthop->ifindex != ifindex) + continue; + r = nexthop_build_json(nexthop, &e); if (r < 0) return r; @@ -1354,7 +1358,7 @@ int link_build_json(Link *link, JsonVariant **ret) { if (r < 0) return r; - r = nexthops_append_json(link->nexthops, &v); + r = nexthops_append_json(link->manager, link->ifindex, &v); if (r < 0) return r; @@ -1417,7 +1421,7 @@ int manager_build_json(Manager *manager, JsonVariant **ret) { if (r < 0) return r; - r = nexthops_append_json(manager->nexthops, &v); + r = nexthops_append_json(manager, /* ifindex = */ 0, &v); if (r < 0) return r; diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index 58d487570a7..743957d27c6 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -100,10 +100,12 @@ int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server: %s", *i); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.set-ntp-servers", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.set-ntp-servers", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -134,10 +136,12 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.set-dns-servers", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.set-dns-servers", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) goto finalize; if (r == 0) { @@ -231,10 +235,12 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_ if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.set-domains", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.set-domains", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -266,10 +272,12 @@ int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.set-default-route", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.set-default-route", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -310,10 +318,12 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.set-llmnr", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.set-llmnr", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -354,10 +364,12 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.set-mdns", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.set-mdns", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -398,10 +410,12 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.set-dns-over-tls", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.set-dns-over-tls", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -442,10 +456,12 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.set-dnssec", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.set-dnssec", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -496,10 +512,12 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v return r; } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.set-dnssec-negative-trust-anchors", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.set-dnssec-negative-trust-anchors", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -525,10 +543,11 @@ int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_e if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.revert-ntp", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.revert-ntp", + /* details= */ NULL, + &l->manager->polkit_registry, error); if (r < 0) return r; if (r == 0) @@ -553,10 +572,12 @@ int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_e if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.revert-dns", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.revert-dns", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -580,10 +601,12 @@ int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_ "Interface %s is not managed by systemd-networkd", l->ifname); - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.forcerenew", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.forcerenew", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -607,10 +630,12 @@ int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error "Interface %s is not managed by systemd-networkd", l->ifname); - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.renew", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.renew", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -629,10 +654,12 @@ int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_ assert(message); - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.reconfigure", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.reconfigure", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index aad86eed991..47433ef1ab0 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -209,7 +209,6 @@ static Link *link_free(Link *link) { link_dns_settings_clear(link); link->routes = set_free(link->routes); - link->nexthops = set_free(link->nexthops); link->neighbors = set_free(link->neighbors); link->addresses = set_free(link->addresses); link->qdiscs = set_free(link->qdiscs); @@ -252,7 +251,9 @@ int link_get_by_index(Manager *m, int ifindex, Link **ret) { Link *link; assert(m); - assert(ifindex > 0); + + if (ifindex <= 0) + return -EINVAL; link = hashmap_get(m->links_by_index, INT_TO_PTR(ifindex)); if (!link) @@ -1049,6 +1050,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_configure_mtu(link); + if (r < 0) + return r; + if (link->iftype == ARPHRD_CAN) { /* let's shortcut things for CAN which doesn't need most of what's done below. */ r = link_request_to_set_can(link); @@ -1082,10 +1087,6 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = link_configure_mtu(link); - if (r < 0) - return r; - r = link_request_to_set_addrgen_mode(link); if (r < 0) return r; @@ -1240,10 +1241,20 @@ int link_reconfigure_impl(Link *link, bool force) { return 0; if (network) { + _cleanup_free_ char *joined = strv_join(network->dropins, ", "); + if (link->state == LINK_STATE_INITIALIZED) - log_link_info(link, "Configuring with %s.", network->filename); + log_link_info(link, "Configuring with %s%s%s%s.", + network->filename, + isempty(joined) ? "" : " (dropins: ", + joined, + isempty(joined) ? "" : ")"); else - log_link_info(link, "Reconfiguring with %s.", network->filename); + log_link_info(link, "Reconfiguring with %s%s%s%s.", + network->filename, + isempty(joined) ? "" : " (dropins: ", + joined, + isempty(joined) ? "" : ")"); } else log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO, "Unmanaging interface."); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 7458ea93bd0..cad9fa84106 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -125,7 +125,6 @@ typedef struct Link { Set *addresses; Set *neighbors; Set *routes; - Set *nexthops; Set *qdiscs; Set *tclasses; diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c index aecbc1d67c6..a8906f81c1b 100644 --- a/src/network/networkd-manager-bus.c +++ b/src/network/networkd-manager-bus.c @@ -201,10 +201,12 @@ static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_err Manager *manager = userdata; int r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.network1.reload", - NULL, true, UID_INVALID, - &manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.network1.reload", + /* details= */ NULL, + &manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index fca5d766184..b162d21aa0f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -591,6 +591,7 @@ int manager_new(Manager **ret, bool test_mode) { .online_state = _LINK_ONLINE_STATE_INVALID, .manage_foreign_routes = true, .manage_foreign_rules = true, + .manage_foreign_nexthops = true, .ethtool_fd = -EBADF, .dhcp_duid.type = DUID_TYPE_EN, .dhcp6_duid.type = DUID_TYPE_EN, @@ -648,7 +649,6 @@ Manager* manager_free(Manager *m) { * set_free() must be called after the above sd_netlink_unref(). */ m->routes = set_free(m->routes); - m->nexthops = set_free(m->nexthops); m->nexthops_by_id = hashmap_free(m->nexthops_by_id); sd_event_source_unref(m->speed_meter_event_source); @@ -867,6 +867,9 @@ static int manager_enumerate_nexthop(Manager *m) { assert(m); assert(m->rtnl); + if (!m->manage_foreign_nexthops) + return 0; + r = sd_rtnl_message_new_nexthop(m->rtnl, &req, RTM_GETNEXTHOP, 0, 0); if (r < 0) return r; diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index fbef5289d28..a9827d8a4b4 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -38,6 +38,7 @@ struct Manager { bool restarting; bool manage_foreign_routes; bool manage_foreign_rules; + bool manage_foreign_nexthops; Set *dirty_links; Set *new_wlan_ifindices; @@ -73,9 +74,6 @@ struct Manager { /* Manage nexthops by id. */ Hashmap *nexthops_by_id; - /* Manager stores nexthops without RTA_OIF attribute. */ - Set *nexthops; - /* Manager stores routes without RTA_OIF attribute. */ unsigned route_remove_messages; Set *routes; diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index eb208c183cd..b9c7875bc4d 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -648,12 +648,12 @@ int network_drop_invalid_neighbors(Network *network) { dup = set_remove(neighbors, neighbor); if (dup) { log_warning("%s: Duplicated neighbor settings for %s is specified at line %u and %u, " - "dropping the address setting specified at line %u.", + "dropping the neighbor setting specified at line %u.", dup->section->filename, IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr), neighbor->section->line, dup->section->line, dup->section->line); - /* neighbor_free() will drop the address from neighbors_by_section. */ + /* neighbor_free() will drop the neighbor from neighbors_by_section. */ neighbor_free(dup); } diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index e2ded28197b..aeb33ae8d60 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -29,18 +29,9 @@ NextHop *nexthop_free(NextHop *nexthop) { config_section_free(nexthop->section); - if (nexthop->link) { - set_remove(nexthop->link->nexthops, nexthop); - - if (nexthop->link->manager && nexthop->id > 0) - hashmap_remove(nexthop->link->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id)); - } - if (nexthop->manager) { - set_remove(nexthop->manager->nexthops, nexthop); - - if (nexthop->id > 0) - hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id)); + assert(nexthop->id > 0); + hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id)); } hashmap_free_free(nexthop->group); @@ -50,6 +41,14 @@ NextHop *nexthop_free(NextHop *nexthop) { DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop, nexthop_free); +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + nexthop_hash_ops, + void, + trivial_hash_func, + trivial_compare_func, + NextHop, + nexthop_free); + static int nexthop_new(NextHop **ret) { _cleanup_(nexthop_freep) NextHop *nexthop = NULL; @@ -106,35 +105,54 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) { assert(nexthop); + assert(state); - siphash24_compress(&nexthop->protocol, sizeof(nexthop->protocol), state); siphash24_compress(&nexthop->id, sizeof(nexthop->id), state); - siphash24_compress(&nexthop->blackhole, sizeof(nexthop->blackhole), state); - siphash24_compress(&nexthop->family, sizeof(nexthop->family), state); +} - switch (nexthop->family) { - case AF_INET: - case AF_INET6: - siphash24_compress(&nexthop->gw, FAMILY_ADDRESS_SIZE(nexthop->family), state); +static int nexthop_compare_func(const NextHop *a, const NextHop *b) { + assert(a); + assert(b); - break; - default: - /* treat any other address family as AF_UNSPEC */ - break; - } + return CMP(a->id, b->id); } -static int nexthop_compare_func(const NextHop *a, const NextHop *b) { +static int nexthop_compare_full(const NextHop *a, const NextHop *b) { int r; + assert(a); + assert(b); + + /* This compares detailed configs, except for ID and ifindex. */ + r = CMP(a->protocol, b->protocol); if (r != 0) return r; - r = CMP(a->id, b->id); + r = CMP(a->flags, b->flags); + if (r != 0) + return r; + + r = CMP(hashmap_size(a->group), hashmap_size(b->group)); if (r != 0) return r; + if (!hashmap_isempty(a->group)) { + struct nexthop_grp *ga; + + HASHMAP_FOREACH(ga, a->group) { + struct nexthop_grp *gb; + + gb = hashmap_get(b->group, UINT32_TO_PTR(ga->id)); + if (!gb) + return CMP(ga, gb); + + r = CMP(ga->weight, gb->weight); + if (r != 0) + return r; + } + } + r = CMP(a->blackhole, b->blackhole); if (r != 0) return r; @@ -143,29 +161,15 @@ static int nexthop_compare_func(const NextHop *a, const NextHop *b) { if (r != 0) return r; - if (IN_SET(a->family, AF_INET, AF_INET6)) - return memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family)); + if (IN_SET(a->family, AF_INET, AF_INET6)) { + r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family)); + if (r != 0) + return r; + } return 0; } -DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( - nexthop_hash_ops, - NextHop, - nexthop_hash_func, - nexthop_compare_func, - nexthop_free); - -static bool nexthop_equal(const NextHop *a, const NextHop *b) { - if (a == b) - return true; - - if (!a || !b) - return false; - - return nexthop_compare_func(a, b) == 0; -} - static int nexthop_dup(const NextHop *src, NextHop **ret) { _cleanup_(nexthop_freep) NextHop *dest = NULL; struct nexthop_grp *nhg; @@ -180,7 +184,6 @@ static int nexthop_dup(const NextHop *src, NextHop **ret) { /* unset all pointers */ dest->manager = NULL; - dest->link = NULL; dest->network = NULL; dest->section = NULL; dest->group = NULL; @@ -203,7 +206,12 @@ static int nexthop_dup(const NextHop *src, NextHop **ret) { return 0; } -int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) { +static bool nexthop_bound_to_link(const NextHop *nexthop) { + assert(nexthop); + return !nexthop->blackhole && hashmap_isempty(nexthop->group); +} + +int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret) { NextHop *nh; assert(manager); @@ -220,96 +228,116 @@ int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) { return 0; } -static bool nexthop_owned_by_link(const NextHop *nexthop) { - return !nexthop->blackhole && hashmap_isempty(nexthop->group); -} - -static int nexthop_get(Manager *manager, Link *link, NextHop *in, NextHop **ret) { +static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) { NextHop *nexthop; - Set *nexthops; + int ifindex; + assert(link); + assert(link->manager); assert(in); - if (nexthop_owned_by_link(in)) { - if (!link) - return -ENOENT; + if (in->id > 0) + return nexthop_get_by_id(link->manager, in->id, ret); - nexthops = link->nexthops; - } else { - if (!manager) - return -ENOENT; + ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0; - nexthops = manager->nexthops; - } + HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { + if (nexthop->ifindex != ifindex) + continue; + if (nexthop_compare_full(nexthop, in) != 0) + continue; - nexthop = set_get(nexthops, in); - if (nexthop) { if (ret) *ret = nexthop; return 0; } - if (in->id > 0) + return -ENOENT; +} + +static int nexthop_get_request_by_id(Manager *manager, uint32_t id, Request **ret) { + Request *req; + + assert(manager); + + req = ordered_set_get( + manager->request_queue, + &(Request) { + .type = REQUEST_TYPE_NEXTHOP, + .userdata = (void*) &(const NextHop) { .id = id }, + .hash_func = (hash_func_t) nexthop_hash_func, + .compare_func = (compare_func_t) nexthop_compare_func, + }); + if (!req) return -ENOENT; - /* Also find nexthop configured without ID. */ - SET_FOREACH(nexthop, nexthops) { - uint32_t id; - bool found; + if (ret) + *ret = req; + return 0; +} - id = nexthop->id; - nexthop->id = 0; - found = nexthop_equal(nexthop, in); - nexthop->id = id; +static int nexthop_get_request(Link *link, const NextHop *in, Request **ret) { + Request *req; + int ifindex; + + assert(link); + assert(link->manager); + assert(in); + + if (in->id > 0) + return nexthop_get_request_by_id(link->manager, in->id, ret); + + ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0; - if (!found) + ORDERED_SET_FOREACH(req, link->manager->request_queue) { + if (req->type != REQUEST_TYPE_NEXTHOP) + continue; + + NextHop *nexthop = ASSERT_PTR(req->userdata); + if (nexthop->ifindex != ifindex) + continue; + if (nexthop_compare_full(nexthop, in) != 0) continue; if (ret) - *ret = nexthop; + *ret = req; return 0; } return -ENOENT; } -static int nexthop_add(Manager *manager, Link *link, NextHop *nexthop) { +static int nexthop_add_new(Manager *manager, uint32_t id, NextHop **ret) { + _cleanup_(nexthop_freep) NextHop *nexthop = NULL; int r; - assert(nexthop); - assert(nexthop->id > 0); - - if (nexthop_owned_by_link(nexthop)) { - assert(link); + assert(manager); + assert(id > 0); - r = set_ensure_put(&link->nexthops, &nexthop_hash_ops, nexthop); - if (r < 0) - return r; - if (r == 0) - return -EEXIST; + r = nexthop_new(&nexthop); + if (r < 0) + return r; - nexthop->link = link; + nexthop->id = id; - manager = link->manager; - } else { - assert(manager); + r = hashmap_ensure_put(&manager->nexthops_by_id, &nexthop_hash_ops, UINT32_TO_PTR(nexthop->id), nexthop); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; - r = set_ensure_put(&manager->nexthops, &nexthop_hash_ops, nexthop); - if (r < 0) - return r; - if (r == 0) - return -EEXIST; + nexthop->manager = manager; - nexthop->manager = manager; - } + if (ret) + *ret = nexthop; - return hashmap_ensure_put(&manager->nexthops_by_id, NULL, UINT32_TO_PTR(nexthop->id), nexthop); + TAKE_PTR(nexthop); + return 0; } static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) { _cleanup_set_free_ Set *ids = NULL; Network *network; - uint32_t id; int r; assert(manager); @@ -318,6 +346,10 @@ static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) { if (nexthop->id > 0) return 0; + /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by + * nexthop_section_verify(). */ + assert(manager->manage_foreign_nexthops); + /* Find the lowest unused ID. */ ORDERED_HASHMAP_FOREACH(network, manager->networks) { @@ -333,30 +365,34 @@ static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) { } } - for (id = 1; id < UINT32_MAX; id++) { - if (manager_get_nexthop_by_id(manager, id, NULL) >= 0) + for (uint32_t id = 1; id < UINT32_MAX; id++) { + if (nexthop_get_by_id(manager, id, NULL) >= 0) + continue; + if (nexthop_get_request_by_id(manager, id, NULL) >= 0) continue; if (set_contains(ids, UINT32_TO_PTR(id))) continue; - break; + + nexthop->id = id; + return 0; } - nexthop->id = id; - return 0; + return -EBUSY; } -static void log_nexthop_debug(const NextHop *nexthop, const char *str, const Link *link) { +static void log_nexthop_debug(const NextHop *nexthop, const char *str, Manager *manager) { _cleanup_free_ char *state = NULL, *group = NULL, *flags = NULL; struct nexthop_grp *nhg; + Link *link = NULL; assert(nexthop); assert(str); - - /* link may be NULL. */ + assert(manager); if (!DEBUG_LOGGING) return; + (void) link_get_by_index(manager, nexthop->ifindex, &link); (void) network_config_state_to_string_alloc(nexthop->state, &state); (void) route_flags_to_string_alloc(nexthop->flags, &flags); @@ -390,22 +426,21 @@ static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link static int nexthop_remove(NextHop *nexthop) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; Manager *manager; - Link *link; + Link *link = NULL; + Request *req; int r; - assert(nexthop); - assert(nexthop->manager || (nexthop->link && nexthop->link->manager)); + manager = ASSERT_PTR(ASSERT_PTR(nexthop)->manager); /* link may be NULL. */ - link = nexthop->link; - manager = nexthop->manager ?: nexthop->link->manager; + (void) link_get_by_index(manager, nexthop->ifindex, &link); if (nexthop->id == 0) { log_link_debug(link, "Cannot remove nexthop without valid ID, ignoring."); return 0; } - log_nexthop_debug(nexthop, "Removing", link); + log_nexthop_debug(nexthop, "Removing", manager); r = sd_rtnl_message_new_nexthop(manager->rtnl, &m, RTM_DELNEXTHOP, AF_UNSPEC, RTPROT_UNSPEC); if (r < 0) @@ -423,6 +458,9 @@ static int nexthop_remove(NextHop *nexthop) { link_ref(link); /* link may be NULL, link_ref() is OK with that */ nexthop_enter_removing(nexthop); + if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0) + nexthop_enter_removing(req->userdata); + return 0; } @@ -431,6 +469,7 @@ static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) { int r; assert(nexthop); + assert(nexthop->id > 0); assert(IN_SET(nexthop->family, AF_UNSPEC, AF_INET, AF_INET6)); assert(link); assert(link->manager); @@ -438,17 +477,15 @@ static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) { assert(link->ifindex > 0); assert(req); - log_nexthop_debug(nexthop, "Configuring", link); + log_nexthop_debug(nexthop, "Configuring", link->manager); r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &m, RTM_NEWNEXTHOP, nexthop->family, nexthop->protocol); if (r < 0) return r; - if (nexthop->id > 0) { - r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id); - if (r < 0) - return r; - } + r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id); + if (r < 0) + return r; if (!hashmap_isempty(nexthop->group)) { _cleanup_free_ struct nexthop_grp *group = NULL; @@ -471,7 +508,9 @@ static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) { if (r < 0) return r; } else { - r = sd_netlink_message_append_u32(m, NHA_OIF, link->ifindex); + assert(nexthop->ifindex == link->ifindex); + + r = sd_netlink_message_append_u32(m, NHA_OIF, nexthop->ifindex); if (r < 0) return r; @@ -520,7 +559,9 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { if (!link_is_ready_to_configure(link, false)) return false; - if (nexthop_owned_by_link(nexthop)) { + if (nexthop_bound_to_link(nexthop)) { + assert(nexthop->ifindex == link->ifindex); + /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of * kernel. */ @@ -534,7 +575,7 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { HASHMAP_FOREACH(nhg, nexthop->group) { NextHop *g; - if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0) + if (nexthop_get_by_id(link->manager, nhg->id, &g) < 0) return false; if (!nexthop_exists(g)) @@ -556,10 +597,12 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { } static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) { + NextHop *existing; int r; assert(req); assert(link); + assert(link->manager); assert(nexthop); if (!nexthop_is_ready_to_configure(link, nexthop)) @@ -570,39 +613,49 @@ static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) { return log_link_warning_errno(link, r, "Failed to configure nexthop"); nexthop_enter_configuring(nexthop); + if (nexthop_get_by_id(link->manager, nexthop->id, &existing) >= 0) + nexthop_enter_configuring(existing); + return 1; } -static int link_request_nexthop(Link *link, NextHop *nexthop) { - NextHop *existing; +static int link_request_nexthop(Link *link, const NextHop *nexthop) { + _cleanup_(nexthop_freep) NextHop *tmp = NULL; + NextHop *existing = NULL; int r; assert(link); + assert(link->manager); assert(nexthop); assert(nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN); - if (nexthop_get(link->manager, link, nexthop, &existing) < 0) { - _cleanup_(nexthop_freep) NextHop *tmp = NULL; + if (nexthop_get_request(link, nexthop, NULL) >= 0) + return 0; /* already requested, skipping. */ - r = nexthop_dup(nexthop, &tmp); - if (r < 0) - return r; + r = nexthop_dup(nexthop, &tmp); + if (r < 0) + return r; + if (nexthop_get(link, nexthop, &existing) < 0) { r = nexthop_acquire_id(link->manager, tmp); if (r < 0) return r; + } else { + /* Copy ID */ + assert(tmp->id == 0 || tmp->id == existing->id); + tmp->id = existing->id; - r = nexthop_add(link->manager, link, tmp); - if (r < 0) - return r; + /* Copy state for logging below. */ + tmp->state = existing->state; + } - existing = TAKE_PTR(tmp); - } else - existing->source = nexthop->source; + if (nexthop_bound_to_link(tmp)) + tmp->ifindex = link->ifindex; - log_nexthop_debug(existing, "Requesting", link); + log_nexthop_debug(tmp, "Requesting", link->manager); r = link_queue_request_safe(link, REQUEST_TYPE_NEXTHOP, - existing, NULL, + tmp, + nexthop_free, nexthop_hash_func, nexthop_compare_func, nexthop_process_request, @@ -612,7 +665,11 @@ static int link_request_nexthop(Link *link, NextHop *nexthop) { if (r <= 0) return r; - nexthop_enter_requesting(existing); + nexthop_enter_requesting(tmp); + if (existing) + nexthop_enter_requesting(existing); + + TAKE_PTR(tmp); return 1; } @@ -645,14 +702,40 @@ int link_request_static_nexthops(Link *link, bool only_ipv4) { return 0; } -static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *except) { +static bool nexthop_can_update(const NextHop *assigned_nexthop, const NextHop *requested_nexthop) { + assert(assigned_nexthop); + assert(assigned_nexthop->manager); + assert(requested_nexthop); + assert(requested_nexthop->network); + + /* A group nexthop cannot be replaced with a non-group nexthop, and vice versa. + * See replace_nexthop_grp() and replace_nexthop_single() in net/ipv4/nexthop.c of the kernel. */ + if (hashmap_isempty(assigned_nexthop->group) != hashmap_isempty(requested_nexthop->group)) + return false; + + /* There are several more conditions if we can replace a group nexthop, e.g. hash threshold and + * resilience. But, currently we do not support to modify that. Let's add checks for them in the + * future when we support to configure them.*/ + + /* When a nexthop is replaced with a blackhole nexthop, and a group nexthop has multiple nexthops + * including this nexthop, then the kernel refuses to replace the existing nexthop. + * So, here, for simplicity, let's unconditionally refuse to replace a non-blackhole nexthop with + * a blackhole nexthop. See replace_nexthop() in net/ipv4/nexthop.c of the kernel. */ + if (!assigned_nexthop->blackhole && requested_nexthop->blackhole) + return false; + + return true; +} + +static void link_mark_nexthops(Link *link, bool foreign) { NextHop *nexthop; - Link *link; + Link *other; - assert(manager); + assert(link); + assert(link->manager); /* First, mark all nexthops. */ - SET_FOREACH(nexthop, manager->nexthops) { + HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { /* do not touch nexthop created by the kernel */ if (nexthop->protocol == RTPROT_KERNEL) continue; @@ -665,33 +748,46 @@ static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *ex if (!nexthop_exists(nexthop)) continue; + /* Ignore nexthops bound to other links. */ + if (nexthop->ifindex > 0 && nexthop->ifindex != link->ifindex) + continue; + nexthop_mark(nexthop); } /* Then, unmark all nexthops requested by active links. */ - HASHMAP_FOREACH(link, manager->links_by_index) { - if (link == except) + HASHMAP_FOREACH(other, link->manager->links_by_index) { + if (!foreign && other == link) continue; - if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) continue; - HASHMAP_FOREACH(nexthop, link->network->nexthops_by_section) { + HASHMAP_FOREACH(nexthop, other->network->nexthops_by_section) { NextHop *existing; - if (nexthop_get(manager, NULL, nexthop, &existing) >= 0) - nexthop_unmark(existing); + if (nexthop_get(other, nexthop, &existing) < 0) + continue; + + if (!nexthop_can_update(existing, nexthop)) + continue; + + /* Found matching static configuration. Keep the existing nexthop. */ + nexthop_unmark(existing); } } } -static int manager_drop_marked_nexthops(Manager *manager) { +int link_drop_nexthops(Link *link, bool foreign) { NextHop *nexthop; int r = 0; - assert(manager); + assert(link); + assert(link->manager); + + link_mark_nexthops(link, foreign); - SET_FOREACH(nexthop, manager->nexthops) { + HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { if (!nexthop_is_marked(nexthop)) continue; @@ -701,106 +797,80 @@ static int manager_drop_marked_nexthops(Manager *manager) { return r; } -int link_drop_foreign_nexthops(Link *link) { +void link_foreignize_nexthops(Link *link) { NextHop *nexthop; - int r = 0; assert(link); assert(link->manager); - assert(link->network); - - /* First, mark all nexthops. */ - SET_FOREACH(nexthop, link->nexthops) { - /* do not touch nexthop created by the kernel */ - if (nexthop->protocol == RTPROT_KERNEL) - continue; - - /* Do not remove nexthops we configured. */ - if (nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN) - continue; - - /* Ignore nexthops not assigned yet or already removed. */ - if (!nexthop_exists(nexthop)) - continue; - - nexthop_mark(nexthop); - } - - /* Then, unmark all nexthops requested by active links. */ - HASHMAP_FOREACH(nexthop, link->network->nexthops_by_section) { - NextHop *existing; - if (nexthop_get(NULL, link, nexthop, &existing) >= 0) - nexthop_unmark(existing); - } + link_mark_nexthops(link, /* foreign = */ false); - /* Finally, remove all marked rules. */ - SET_FOREACH(nexthop, link->nexthops) { + HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) { if (!nexthop_is_marked(nexthop)) continue; - RET_GATHER(r, nexthop_remove(nexthop)); + nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN; } +} - manager_mark_nexthops(link->manager, /* foreign = */ true, NULL); +static int nexthop_update_group(NextHop *nexthop, const struct nexthop_grp *group, size_t size) { + _cleanup_hashmap_free_free_ Hashmap *h = NULL; + size_t n_group; + int r; - return RET_GATHER(r, manager_drop_marked_nexthops(link->manager)); -} + assert(nexthop); + assert(group || size == 0); -int link_drop_managed_nexthops(Link *link) { - NextHop *nexthop; - int r = 0; + if (size == 0 || size % sizeof(struct nexthop_grp) != 0) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "rtnl: received nexthop message with invalid nexthop group size, ignoring."); - assert(link); - assert(link->manager); + if ((uintptr_t) group % alignof(struct nexthop_grp) != 0) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "rtnl: received nexthop message with invalid alignment, ignoring."); - SET_FOREACH(nexthop, link->nexthops) { - /* do not touch nexthop created by the kernel */ - if (nexthop->protocol == RTPROT_KERNEL) - continue; + n_group = size / sizeof(struct nexthop_grp); + for (size_t i = 0; i < n_group; i++) { + _cleanup_free_ struct nexthop_grp *nhg = NULL; - /* Do not touch addresses managed by kernel or other tools. */ - if (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN) + if (group[i].id == 0) { + log_debug("rtnl: received nexthop message with invalid ID in group, ignoring."); continue; + } - /* Ignore nexthops not assigned yet or already removing. */ - if (!nexthop_exists(nexthop)) + if (group[i].weight > 254) { + log_debug("rtnl: received nexthop message with invalid weight in group, ignoring."); continue; + } - RET_GATHER(r, nexthop_remove(nexthop)); - } - - manager_mark_nexthops(link->manager, /* foreign = */ false, link); - - return RET_GATHER(r, manager_drop_marked_nexthops(link->manager)); -} - -void link_foreignize_nexthops(Link *link) { - NextHop *nexthop; - - assert(link); - - SET_FOREACH(nexthop, link->nexthops) - nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN; - - manager_mark_nexthops(link->manager, /* foreign = */ false, link); + nhg = newdup(struct nexthop_grp, group + i, 1); + if (!nhg) + return log_oom(); - SET_FOREACH(nexthop, link->manager->nexthops) { - if (!nexthop_is_marked(nexthop)) + r = hashmap_ensure_put(&h, NULL, UINT32_TO_PTR(nhg->id), nhg); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_debug_errno(r, "Failed to store nexthop group, ignoring: %m"); continue; - - nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN; + } + if (r > 0) + TAKE_PTR(nhg); } + + hashmap_free_free(nexthop->group); + nexthop->group = TAKE_PTR(h); + return 0; } int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { - _cleanup_(nexthop_freep) NextHop *tmp = NULL; _cleanup_free_ void *raw_group = NULL; - NextHop *nexthop = NULL; size_t raw_group_size; - uint32_t ifindex; uint16_t type; - Link *link = NULL; + uint32_t id, ifindex; + NextHop *nexthop = NULL; + Request *req = NULL; + bool is_new = false; int r; assert(rtnl); @@ -824,163 +894,107 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, return 0; } - r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m"); + r = sd_netlink_message_read_u32(message, NHA_ID, &id); + if (r == -ENODATA) { + log_warning_errno(r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m"); return 0; - } else if (r >= 0) { - if (ifindex <= 0) { - log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex); - return 0; - } - - r = link_get_by_index(m, ifindex, &link); - if (r < 0) { - if (!m->enumerating) - log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex); - return 0; - } - } - - r = nexthop_new(&tmp); - if (r < 0) - return log_oom(); - - r = sd_rtnl_message_get_family(message, &tmp->family); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: could not get nexthop family, ignoring: %m"); + } else if (r < 0) { + log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m"); return 0; - } else if (!IN_SET(tmp->family, AF_UNSPEC, AF_INET, AF_INET6)) { - log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family); + } else if (id == 0) { + log_warning("rtnl: received nexthop message with invalid nexthop ID, ignoring: %m"); return 0; } - r = sd_rtnl_message_nexthop_get_protocol(message, &tmp->protocol); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: could not get nexthop protocol, ignoring: %m"); - return 0; - } + (void) nexthop_get_by_id(m, id, &nexthop); + (void) nexthop_get_request_by_id(m, id, &req); - r = sd_rtnl_message_nexthop_get_flags(message, &tmp->flags); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: could not get nexthop flags, ignoring: %m"); - return 0; - } + if (type == RTM_DELNEXTHOP) { + if (nexthop) { + nexthop_enter_removed(nexthop); + log_nexthop_debug(nexthop, "Forgetting removed", m); + nexthop_free(nexthop); + } else + log_nexthop_debug(&(const NextHop) { .id = id }, "Kernel removed unknown", m); + + if (req) + nexthop_enter_removed(req->userdata); - r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m"); return 0; - } else if (r >= 0) { - struct nexthop_grp *group = raw_group; - size_t n_group; + } - if (raw_group_size == 0 || raw_group_size % sizeof(struct nexthop_grp) != 0) { - log_link_warning(link, "rtnl: received nexthop message with invalid nexthop group size, ignoring."); + /* If we did not know the nexthop, then save it. */ + if (!nexthop) { + r = nexthop_add_new(m, id, &nexthop); + if (r < 0) { + log_warning_errno(r, "Failed to add received nexthop, ignoring: %m"); return 0; } - assert((uintptr_t) group % alignof(struct nexthop_grp) == 0); + is_new = true; + } - n_group = raw_group_size / sizeof(struct nexthop_grp); - for (size_t i = 0; i < n_group; i++) { - _cleanup_free_ struct nexthop_grp *nhg = NULL; + /* Also update information that cannot be obtained through netlink notification. */ + if (req && req->waiting_reply) { + NextHop *n = ASSERT_PTR(req->userdata); - if (group[i].id == 0) { - log_link_warning(link, "rtnl: received nexthop message with invalid ID in group, ignoring."); - return 0; - } - if (group[i].weight > 254) { - log_link_warning(link, "rtnl: received nexthop message with invalid weight in group, ignoring."); - return 0; - } + nexthop->source = n->source; + } - nhg = newdup(struct nexthop_grp, group + i, 1); - if (!nhg) - return log_oom(); + r = sd_rtnl_message_get_family(message, &nexthop->family); + if (r < 0) + log_debug_errno(r, "rtnl: could not get nexthop family, ignoring: %m"); - r = hashmap_ensure_put(&tmp->group, NULL, UINT32_TO_PTR(nhg->id), nhg); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to store nexthop group, ignoring: %m"); - return 0; - } - if (r > 0) - TAKE_PTR(nhg); - } - } + r = sd_rtnl_message_nexthop_get_protocol(message, &nexthop->protocol); + if (r < 0) + log_debug_errno(r, "rtnl: could not get nexthop protocol, ignoring: %m"); - if (tmp->family != AF_UNSPEC) { - r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, tmp->family, &tmp->gw); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m"); - return 0; - } + r = sd_rtnl_message_nexthop_get_flags(message, &nexthop->flags); + if (r < 0) + log_debug_errno(r, "rtnl: could not get nexthop flags, ignoring: %m"); + + r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group); + if (r == -ENODATA) + nexthop->group = hashmap_free_free(nexthop->group); + else if (r < 0) + log_debug_errno(r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m"); + else + (void) nexthop_update_group(nexthop, raw_group, raw_group_size); + + if (nexthop->family != AF_UNSPEC) { + r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw); + if (r == -ENODATA) + nexthop->gw = IN_ADDR_NULL; + else if (r < 0) + log_debug_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m"); } r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m"); - return 0; - } - tmp->blackhole = r; + if (r < 0) + log_debug_errno(r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m"); + else + nexthop->blackhole = r; - r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id); - if (r == -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m"); - return 0; - } else if (r < 0) { - log_link_warning_errno(link, r, "rtnl: could not get NHA_ID attribute, ignoring: %m"); - return 0; - } else if (tmp->id == 0) { - log_link_warning(link, "rtnl: received nexthop message with invalid nexthop ID, ignoring: %m"); - return 0; - } + r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex); + if (r == -ENODATA) + nexthop->ifindex = 0; + else if (r < 0) + log_debug_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m"); + else if (ifindex > INT32_MAX) + log_debug_errno(r, "rtnl: received invalid NHA_OIF attribute, ignoring: %m"); + else + nexthop->ifindex = (int) ifindex; /* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not * set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */ - if (!nexthop_owned_by_link(tmp)) - link = NULL; + if (!nexthop_bound_to_link(nexthop)) + nexthop->ifindex = 0; - (void) nexthop_get(m, link, tmp, &nexthop); - - switch (type) { - case RTM_NEWNEXTHOP: - if (nexthop) { - nexthop->flags = tmp->flags; - nexthop_enter_configured(nexthop); - log_nexthop_debug(tmp, "Received remembered", link); - } else { - nexthop_enter_configured(tmp); - log_nexthop_debug(tmp, "Remembering", link); - - r = nexthop_add(m, link, tmp); - if (r < 0) { - log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m"); - return 0; - } - - TAKE_PTR(tmp); - } - - break; - case RTM_DELNEXTHOP: - if (nexthop) { - nexthop_enter_removed(nexthop); - if (nexthop->state == 0) { - log_nexthop_debug(nexthop, "Forgetting", link); - nexthop_free(nexthop); - } else - log_nexthop_debug(nexthop, "Removed", link); - } else - log_nexthop_debug(tmp, "Kernel removed unknown", link); - break; - - default: - assert_not_reached(); - } + nexthop_enter_configured(nexthop); + if (req) + nexthop_enter_configured(req->userdata); + log_nexthop_debug(nexthop, is_new ? "Remembering" : "Received remembered", m); return 1; } @@ -988,6 +1002,13 @@ static int nexthop_section_verify(NextHop *nh) { if (section_is_invalid(nh->section)) return -EINVAL; + if (!nh->network->manager->manage_foreign_nexthops && nh->id == 0) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: [NextHop] section without specifying Id= is not supported " + "if ManageForeignNextHops=no is set in networkd.conf. " + "Ignoring [NextHop] section from line %u.", + nh->section->filename, nh->section->line); + if (!hashmap_isempty(nh->group)) { if (in_addr_is_set(nh->family, &nh->gw)) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h index 6f2aa6f6dc3..c1d39e6e8ea 100644 --- a/src/network/networkd-nexthop.h +++ b/src/network/networkd-nexthop.h @@ -20,13 +20,12 @@ typedef struct Network Network; typedef struct NextHop { Network *network; Manager *manager; - Link *link; ConfigSection *section; NetworkConfigSource source; NetworkConfigState state; uint8_t protocol; - + int ifindex; uint32_t id; bool blackhole; int family; @@ -40,13 +39,18 @@ NextHop *nexthop_free(NextHop *nexthop); void network_drop_invalid_nexthops(Network *network); -int link_drop_managed_nexthops(Link *link); -int link_drop_foreign_nexthops(Link *link); +int link_drop_nexthops(Link *link, bool foreign); +static inline int link_drop_foreign_nexthops(Link *link) { + return link_drop_nexthops(link, /* foreign = */ true); +} +static inline int link_drop_managed_nexthops(Link *link) { + return link_drop_nexthops(link, /* foreign = */ false); +} void link_foreignize_nexthops(Link *link); int link_request_static_nexthops(Link *link, bool only_ipv4); -int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret); +int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret); int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(NextHop, nexthop); diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 0d782dc9a7a..a88418e28e1 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -58,12 +58,14 @@ static void request_hash_func(const Request *req, struct siphash *state) { assert(req); assert(state); - siphash24_compress_boolean(req->link, state); - if (req->link) - siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state); - siphash24_compress(&req->type, sizeof(req->type), state); + if (req->type != REQUEST_TYPE_NEXTHOP) { + siphash24_compress_boolean(req->link, state); + if (req->link) + siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state); + } + siphash24_compress(&req->hash_func, sizeof(req->hash_func), state); siphash24_compress(&req->compare_func, sizeof(req->compare_func), state); @@ -77,19 +79,21 @@ static int request_compare_func(const struct Request *a, const struct Request *b assert(a); assert(b); - r = CMP(!!a->link, !!b->link); + r = CMP(a->type, b->type); if (r != 0) return r; - if (a->link) { - r = CMP(a->link->ifindex, b->link->ifindex); + if (a->type != REQUEST_TYPE_NEXTHOP) { + r = CMP(!!a->link, !!b->link); if (r != 0) return r; - } - r = CMP(a->type, b->type); - if (r != 0) - return r; + if (a->link) { + r = CMP(a->link->ifindex, b->link->ifindex); + if (r != 0) + return r; + } + } r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func)); if (r != 0) diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 7218d799fc8..c0189ae899a 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -461,7 +461,7 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes * struct nexthop_grp *nhg; NextHop *nh; - r = manager_get_nexthop_by_id(manager, route->nexthop_id, &nh); + r = nexthop_get_by_id(manager, route->nexthop_id, &nh); if (r < 0) return r; @@ -475,7 +475,7 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes * return r; route_apply_nexthop(c->routes[0], nh, UINT8_MAX); - c->links[0] = nh->link; + (void) link_get_by_index(manager, nh->ifindex, c->links); *ret = TAKE_PTR(c); return 1; @@ -489,7 +489,7 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes * HASHMAP_FOREACH(nhg, nh->group) { NextHop *h; - r = manager_get_nexthop_by_id(manager, nhg->id, &h); + r = nexthop_get_by_id(manager, nhg->id, &h); if (r < 0) return r; @@ -498,7 +498,7 @@ static int route_convert(Manager *manager, const Route *route, ConvertedRoutes * return r; route_apply_nexthop(c->routes[i], h, nhg->weight); - c->links[i] = h->link; + (void) link_get_by_index(manager, h->ifindex, c->links + i); i++; } @@ -1284,7 +1284,7 @@ static int route_is_ready_to_configure(const Route *route, Link *link) { struct nexthop_grp *nhg; NextHop *nh; - if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0) + if (nexthop_get_by_id(link->manager, route->nexthop_id, &nh) < 0) return false; if (!nexthop_exists(nh)) @@ -1293,7 +1293,7 @@ static int route_is_ready_to_configure(const Route *route, Link *link) { HASHMAP_FOREACH(nhg, nh->group) { NextHop *g; - if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0) + if (nexthop_get_by_id(link->manager, nhg->id, &g) < 0) return false; if (!nexthop_exists(g)) diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 2b37c86d235..38f90969ec4 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -563,12 +563,20 @@ static int link_is_ready_to_set_link(Link *link, Request *req) { break; } case REQUEST_TYPE_SET_LINK_MTU: { - Request req_ipoib = { - .link = link, - .type = REQUEST_TYPE_SET_LINK_IPOIB, - }; + if (ordered_set_contains(link->manager->request_queue, + &(const Request) { + .link = link, + .type = REQUEST_TYPE_SET_LINK_IPOIB, + })) + return false; - return !ordered_set_contains(link->manager->request_queue, &req_ipoib); + /* Changing FD mode may affect MTU. */ + if (ordered_set_contains(link->manager->request_queue, + &(const Request) { + .link = link, + .type = REQUEST_TYPE_SET_LINK_CAN, + })) + return false; } default: break; @@ -858,7 +866,7 @@ int link_request_to_set_master(Link *link) { int link_request_to_set_mtu(Link *link, uint32_t mtu) { const char *origin; - uint32_t min_mtu; + uint32_t min_mtu, max_mtu; Request *req; int r; @@ -886,10 +894,19 @@ int link_request_to_set_mtu(Link *link, uint32_t mtu) { mtu = min_mtu; } - if (mtu > link->max_mtu) { + max_mtu = link->max_mtu; + if (link->iftype == ARPHRD_CAN) + /* The maximum MTU may be changed when FD mode is changed. + * See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support + * MTU = 16 (CAN_MTU) => Classical CAN device + * MTU = 72 (CANFD_MTU) => CAN FD capable device + * So, even if the current maximum is 16, we should not reduce the requested value now. */ + max_mtu = MAX(max_mtu, 72u); + + if (mtu > max_mtu) { log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".", - mtu, link->max_mtu); - mtu = link->max_mtu; + mtu, max_mtu); + mtu = max_mtu; } if (link->mtu == mtu) diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c index 2ac6c3527bc..2b226b2e2a1 100644 --- a/src/network/networkd-sysctl.c +++ b/src/network/networkd-sysctl.c @@ -2,6 +2,7 @@ #include #include +#include #include "missing_network.h" #include "networkd-link.h" @@ -12,10 +13,31 @@ #include "string-table.h" #include "sysctl-util.h" -static int link_update_ipv6_sysctl(Link *link) { +static bool link_is_configured_for_family(Link *link, int family) { assert(link); + if (!link->network) + return false; + if (link->flags & IFF_LOOPBACK) + return false; + + /* CAN devices do not support IP layer. Most of the functions below are never called for CAN devices, + * but link_set_ipv6_mtu() may be called after setting interface MTU, and warn about the failure. For + * safety, let's unconditionally check if the interface is not a CAN device. */ + if (IN_SET(family, AF_INET, AF_INET6) && link->iftype == ARPHRD_CAN) + return false; + + if (family == AF_INET6 && !socket_ipv6_is_supported()) + return false; + + return true; +} + +static int link_update_ipv6_sysctl(Link *link) { + assert(link); + + if (!link_is_configured_for_family(link, AF_INET6)) return 0; if (!link_ipv6_enabled(link)) @@ -27,10 +49,7 @@ static int link_update_ipv6_sysctl(Link *link) { static int link_set_proxy_arp(Link *link) { assert(link); - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) + if (!link_is_configured_for_family(link, AF_INET)) return 0; if (link->network->proxy_arp < 0) @@ -43,13 +62,7 @@ static bool link_ip_forward_enabled(Link *link, int family) { assert(link); assert(IN_SET(family, AF_INET, AF_INET6)); - if (family == AF_INET6 && !socket_ipv6_is_supported()) - return false; - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) + if (!link_is_configured_for_family(link, family)) return false; return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6); @@ -92,10 +105,7 @@ static int link_set_ipv6_forward(Link *link) { static int link_set_ipv4_rp_filter(Link *link) { assert(link); - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) + if (!link_is_configured_for_family(link, AF_INET)) return 0; if (link->network->ipv4_rp_filter < 0) @@ -110,13 +120,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) { assert(link); assert(link->manager); - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) + if (!link_is_configured_for_family(link, AF_INET6)) return 0; val = link->network->ipv6_privacy_extensions; @@ -133,14 +137,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) { static int link_set_ipv6_accept_ra(Link *link) { assert(link); - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) + if (!link_is_configured_for_family(link, AF_INET6)) return 0; return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0"); @@ -149,14 +146,7 @@ static int link_set_ipv6_accept_ra(Link *link) { static int link_set_ipv6_dad_transmits(Link *link) { assert(link); - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) + if (!link_is_configured_for_family(link, AF_INET6)) return 0; if (link->network->ipv6_dad_transmits < 0) @@ -168,14 +158,7 @@ static int link_set_ipv6_dad_transmits(Link *link) { static int link_set_ipv6_hop_limit(Link *link) { assert(link); - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) + if (!link_is_configured_for_family(link, AF_INET6)) return 0; if (link->network->ipv6_hop_limit <= 0) @@ -189,13 +172,7 @@ static int link_set_ipv6_proxy_ndp(Link *link) { assert(link); - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) + if (!link_is_configured_for_family(link, AF_INET6)) return 0; if (link->network->ipv6_proxy_ndp >= 0) @@ -211,14 +188,7 @@ int link_set_ipv6_mtu(Link *link) { assert(link); - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) + if (!link_is_configured_for_family(link, AF_INET6)) return 0; if (link->network->ipv6_mtu == 0) @@ -237,7 +207,7 @@ int link_set_ipv6_mtu(Link *link) { static int link_set_ipv4_accept_local(Link *link) { assert(link); - if (link->flags & IFF_LOOPBACK) + if (!link_is_configured_for_family(link, AF_INET)) return 0; if (link->network->ipv4_accept_local < 0) @@ -249,7 +219,7 @@ static int link_set_ipv4_accept_local(Link *link) { static int link_set_ipv4_route_localnet(Link *link) { assert(link); - if (link->flags & IFF_LOOPBACK) + if (!link_is_configured_for_family(link, AF_INET)) return 0; if (link->network->ipv4_route_localnet < 0) @@ -258,6 +228,20 @@ static int link_set_ipv4_route_localnet(Link *link) { return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0); } +static int link_set_ipv4_promote_secondaries(Link *link) { + assert(link); + + if (!link_is_configured_for_family(link, AF_INET)) + return 0; + + /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not + * changes between leases. The kernel will remove all secondary IP addresses of an interface + * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a + * secondary IP and when the primary one expires it relies on the kernel to promote the + * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */ + return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true); +} + int link_set_sysctl(Link *link) { int r; @@ -321,12 +305,7 @@ int link_set_sysctl(Link *link) { if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m"); - /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not - * changes between leases. The kernel will remove all secondary IP addresses of an interface - * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a - * secondary IP and when the primary one expires it relies on the kernel to promote the - * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */ - r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true); + r = link_set_ipv4_promote_secondaries(link); if (r < 0) log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m"); diff --git a/src/network/networkd.conf b/src/network/networkd.conf index e5a5e889262..2994b8b70c1 100644 --- a/src/network/networkd.conf +++ b/src/network/networkd.conf @@ -21,6 +21,7 @@ #SpeedMeterIntervalSec=10sec #ManageForeignRoutingPolicyRules=yes #ManageForeignRoutes=yes +#ManageForeignNextHops=yes #RouteTable= #IPv6PrivacyExtensions=no diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c index a9d36627a86..a5002437c6f 100644 --- a/src/nspawn/nspawn-cgroup.c +++ b/src/nspawn/nspawn-cgroup.c @@ -35,6 +35,8 @@ static int chown_cgroup_path(const char *path, uid_t uid_shift) { "cgroup.stat", "cgroup.subtree_control", "cgroup.threads", + "memory.oom.group", + "memory.reclaim", "notify_on_release", "tasks") if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0) diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index c661f1d9e0b..369d8742b60 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -95,9 +95,7 @@ static int generate_mac( assert_cc(ETH_ALEN <= sizeof(result)); memcpy(mac->ether_addr_octet, &result, ETH_ALEN); - /* see eth_random_addr in the kernel */ - mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ - mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ + ether_addr_mark_random(mac); return 0; } diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index 66962d7ba95..27400aa4a21 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -368,7 +368,7 @@ int allocate_scope( if (r < 0) return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, false, NULL); + r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL); if (r < 0) return r; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 6ab604d3dcc..b4a74a1e437 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -229,8 +229,7 @@ static DeviceNode* arg_extra_nodes = NULL; static size_t arg_n_extra_nodes = 0; static char **arg_sysctl = NULL; static ConsoleMode arg_console_mode = _CONSOLE_MODE_INVALID; -static MachineCredential *arg_credentials = NULL; -static size_t arg_n_credentials = 0; +static MachineCredentialContext arg_credentials = {}; static char **arg_bind_user = NULL; static bool arg_suppress_sync = false; static char *arg_settings_filename = NULL; @@ -266,6 +265,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_syscall_deny_list, strv_freep); #if HAVE_SECCOMP STATIC_DESTRUCTOR_REGISTER(arg_seccomp, seccomp_releasep); #endif +STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done); STATIC_DESTRUCTOR_REGISTER(arg_cpu_set, cpu_set_reset); STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep); @@ -1568,7 +1568,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SET_CREDENTIAL: - r = machine_credential_set(&arg_credentials, &arg_n_credentials, optarg); + r = machine_credential_set(&arg_credentials, optarg); if (r < 0) return r; @@ -1576,7 +1576,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_LOAD_CREDENTIAL: - r = machine_credential_load(&arg_credentials, &arg_n_credentials, optarg); + r = machine_credential_load(&arg_credentials, optarg); if (r < 0) return r; @@ -2182,7 +2182,7 @@ static int copy_devnodes(const char *dest) { if (mknod(to, st.st_mode, st.st_rdev) < 0) { /* Explicitly warn the user when /dev is already populated. */ if (errno == EEXIST) - log_notice("%s/dev is pre-mounted and pre-populated. If a pre-mounted /dev is provided it needs to be an unpopulated file system.", dest); + log_notice("%s/dev/ is pre-mounted and pre-populated. If a pre-mounted /dev/ is provided it needs to be an unpopulated file system.", dest); if (errno != EPERM) return log_error_errno(errno, "mknod(%s) failed: %m", to); @@ -2367,7 +2367,7 @@ static int setup_credentials(const char *root) { const char *q; int r; - if (arg_n_credentials <= 0) + if (arg_credentials.n_credentials == 0) return 0; r = userns_mkdir(root, "/run/host", 0755, 0, 0); @@ -2383,11 +2383,11 @@ static int setup_credentials(const char *root) { if (r < 0) return r; - for (size_t i = 0; i < arg_n_credentials; i++) { + FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) { _cleanup_free_ char *j = NULL; _cleanup_close_ int fd = -EBADF; - j = path_join(q, arg_credentials[i].id); + j = path_join(q, cred->id); if (!j) return log_oom(); @@ -2395,7 +2395,7 @@ static int setup_credentials(const char *root) { if (fd < 0) return log_error_errno(errno, "Failed to create credential file %s: %m", j); - r = loop_write(fd, arg_credentials[i].data, arg_credentials[i].size); + r = loop_write(fd, cred->data, cred->size); if (r < 0) return log_error_errno(r, "Failed to write credential to file %s: %m", j); @@ -3394,7 +3394,7 @@ static int inner_child( if (asprintf(envp + n_env++, "container_uuid=%s", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0) return log_oom(); - if (fdset_size(fds) > 0) { + if (!fdset_isempty(fds)) { r = fdset_cloexec(fds, false); if (r < 0) return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors."); @@ -3406,7 +3406,7 @@ static int inner_child( if (asprintf(envp + n_env++, "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0) return log_oom(); - if (arg_n_credentials > 0) { + if (arg_credentials.n_credentials > 0) { envp[n_env] = strdup("CREDENTIALS_DIRECTORY=/run/host/credentials"); if (!envp[n_env]) return log_oom(); @@ -5286,7 +5286,7 @@ static int initialize_rlimits(void) { * don't read the other limits from PID 1 but prefer the static table above. */ }; - int rl; + int rl, r; for (rl = 0; rl < _RLIMIT_MAX; rl++) { /* Let's only fill in what the user hasn't explicitly configured anyway */ @@ -5297,8 +5297,9 @@ static int initialize_rlimits(void) { if (IN_SET(rl, RLIMIT_NPROC, RLIMIT_SIGPENDING)) { /* For these two let's read the limits off PID 1. See above for an explanation. */ - if (prlimit(1, rl, NULL, &buffer) < 0) - return log_error_errno(errno, "Failed to read resource limit RLIMIT_%s of PID 1: %m", rlimit_to_string(rl)); + r = pid_getrlimit(1, rl, &buffer); + if (r < 0) + return log_error_errno(r, "Failed to read resource limit RLIMIT_%s of PID 1: %m", rlimit_to_string(rl)); v = &buffer; } else if (rl == RLIMIT_NOFILE) { @@ -5643,7 +5644,7 @@ static int run(int argc, char *argv[]) { /* Always take an exclusive lock on our own ephemeral copy. */ r = image_path_lock(np, LOCK_EX|LOCK_NB, &tree_global_lock, &tree_local_lock); if (r < 0) { - r = log_error_errno(r, "Failed to create image lock: %m"); + log_error_errno(r, "Failed to create image lock: %m"); goto finish; } @@ -5668,11 +5669,11 @@ static int run(int argc, char *argv[]) { } else { r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); if (r == -EBUSY) { - r = log_error_errno(r, "Disk image %s is currently busy.", arg_image); + log_error_errno(r, "Disk image %s is currently busy.", arg_image); goto finish; } if (r < 0) { - r = log_error_errno(r, "Failed to create image lock: %m"); + log_error_errno(r, "Failed to create image lock: %m"); goto finish; } @@ -5857,7 +5858,6 @@ finish: expose_port_free_all(arg_expose_ports); rlimit_free_all(arg_rlimit); device_node_array_free(arg_extra_nodes, arg_n_extra_nodes); - machine_credential_free_all(arg_credentials, arg_n_credentials); if (r < 0) return r; diff --git a/src/oom/oomd-util.c b/src/oom/oomd-util.c index f9f0af2d049..6e6678c33d9 100644 --- a/src/oom/oomd-util.c +++ b/src/oom/oomd-util.c @@ -276,7 +276,7 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) { if (r < 0) log_debug_errno(r, "Failed to set user.oomd_kill on kill: %m"); - return set_size(pids_killed) != 0; + return !set_isempty(pids_killed); } typedef void (*dump_candidate_func)(const OomdCGroupContext *ctx, FILE *f, const char *prefix); diff --git a/src/partition/growfs.c b/src/partition/growfs.c index 62f3ee6744a..491040ffb08 100644 --- a/src/partition/growfs.c +++ b/src/partition/growfs.c @@ -55,8 +55,9 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_ return log_error_errno(r, "Failed to open main block device " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(main_devno)); - if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0) - return log_error_errno(errno, "Failed to query size of \"%s\" (before resize): %m", + r = blockdev_get_device_size(main_devfd, &size); + if (r < 0) + return log_error_errno(r, "Failed to query size of \"%s\" (before resize): %m", main_devpath); log_debug("%s is %"PRIu64" bytes", main_devpath, size); @@ -83,9 +84,9 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_ if (r < 0) return log_error_errno(r, "crypt_resize() of %s failed: %m", devpath); - if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0) - log_warning_errno(errno, "Failed to query size of \"%s\" (after resize): %m", - devpath); + r = blockdev_get_device_size(main_devfd, &size); + if (r < 0) + log_warning_errno(r, "Failed to query size of \"%s\" (after resize): %m", devpath); else log_debug("%s is now %"PRIu64" bytes", main_devpath, size); @@ -250,8 +251,9 @@ static int run(int argc, char *argv[]) { return log_error_errno(r, "Failed to open block device " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(devno)); - if (ioctl(devfd, BLKGETSIZE64, &size) != 0) - return log_error_errno(errno, "Failed to query size of \"%s\": %m", devpath); + r = blockdev_get_device_size(devfd, &size); + if (r < 0) + return log_error_errno(r, "Failed to query size of \"%s\": %m", devpath); log_debug("Resizing \"%s\" to %"PRIu64" bytes...", arg_target, size); diff --git a/src/partition/repart.c b/src/partition/repart.c index 3a92d1be1c1..1e9284e2e2e 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -6042,8 +6042,9 @@ static int context_open_copy_block_paths( if (S_ISREG(st.st_mode)) size = st.st_size; else if (S_ISBLK(st.st_mode)) { - if (ioctl(source_fd, BLKGETSIZE64, &size) != 0) - return log_error_errno(errno, "Failed to determine size of block device to copy from: %m"); + r = blockdev_get_device_size(source_fd, &size); + if (r < 0) + return log_error_errno(r, "Failed to determine size of block device to copy from: %m"); } else return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", opened); @@ -7365,8 +7366,9 @@ static int resize_backing_fd( assert(loop_device); - if (ioctl(*fd, BLKGETSIZE64, ¤t_size) < 0) - return log_error_errno(errno, "Failed to determine size of block device %s: %m", node); + r = blockdev_get_device_size(*fd, ¤t_size); + if (r < 0) + return log_error_errno(r, "Failed to determine size of block device %s: %m", node); } else { r = stat_verify_regular(&st); if (r < 0) diff --git a/src/pcrextend/pcrextend.c b/src/pcrextend/pcrextend.c index 12959496ded..ead353f5a69 100644 --- a/src/pcrextend/pcrextend.c +++ b/src/pcrextend/pcrextend.c @@ -276,18 +276,18 @@ static int vl_method_extend(Varlink *link, JsonVariant *parameters, VarlinkMetho return r; if (!TPM2_PCR_INDEX_VALID(p.pcr)) - return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "pcr"))); + return varlink_error_invalid_parameter_name(link, "pcr"); if (p.text) { /* Specifying both the text string and the binary data is not allowed */ if (p.data.iov_base) - return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "data"))); + return varlink_error_invalid_parameter_name(link, "data"); r = extend_now(p.pcr, p.text, strlen(p.text), _TPM2_USERSPACE_EVENT_TYPE_INVALID); } else if (p.data.iov_base) r = extend_now(p.pcr, p.data.iov_base, p.data.iov_len, _TPM2_USERSPACE_EVENT_TYPE_INVALID); else - return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "text"))); + return varlink_error_invalid_parameter_name(link, "text"); if (r < 0) return r; diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index dc48bc57e56..23bdab0befe 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -11,6 +11,7 @@ #include "blockdev-util.h" #include "build.h" #include "chase.h" +#include "color-util.h" #include "conf-files.h" #include "efi-api.h" #include "env-util.h" @@ -1932,40 +1933,6 @@ static int event_log_map_components(EventLog *el) { return event_log_validate_fully_recognized(el); } -static void hsv_to_rgb( - double h, double s, double v, - uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) { - - double c, x, m, r, g, b; - - assert(s >= 0 && s <= 100); - assert(v >= 0 && v <= 100); - assert(ret_r); - assert(ret_g); - assert(ret_b); - - c = (s / 100.0) * (v / 100.0); - x = c * (1 - fabs(fmod(h / 60.0, 2) - 1)); - m = (v / 100) - c; - - if (h >= 0 && h < 60) - r = c, g = x, b = 0.0; - else if (h >= 60 && h < 120) - r = x, g = c, b = 0.0; - else if (h >= 120 && h < 180) - r = 0.0, g = c, b = x; - else if (h >= 180 && h < 240) - r = 0.0, g = x, b = c; - else if (h >= 240 && h < 300) - r = x, g = 0.0, b = c; - else - r = c, g = 0.0, b = x; - - *ret_r = (uint8_t) ((r + m) * 255); - *ret_g = (uint8_t) ((g + m) * 255); - *ret_b = (uint8_t) ((b + m) * 255); -} - #define ANSI_TRUE_COLOR_MAX (7U + 3U + 1U + 3U + 1U + 3U + 2U) static const char *ansi_true_color(uint8_t r, uint8_t g, uint8_t b, char ret[static ANSI_TRUE_COLOR_MAX]) { @@ -4906,7 +4873,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_PCRLOCK: - if (isempty(optarg) || streq(optarg, "-")) + if (empty_or_dash(optarg)) arg_pcrlock_path = mfree(arg_pcrlock_path); else { r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_pcrlock_path); @@ -4918,7 +4885,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_POLICY: - if (isempty(optarg) || streq(optarg, "-")) + if (empty_or_dash(optarg)) arg_policy_path = mfree(arg_policy_path); else { r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_policy_path); diff --git a/src/portable/portabled-bus.c b/src/portable/portabled-bus.c index 0d5518060eb..4f239e2b125 100644 --- a/src/portable/portabled-bus.c +++ b/src/portable/portabled-bus.c @@ -320,11 +320,8 @@ static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_e r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.portable1.attach-images", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -377,11 +374,8 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.portable1.manage-images", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c index 1f61c3b8c42..63f177eb74b 100644 --- a/src/portable/portabled-image-bus.c +++ b/src/portable/portabled-image-bus.c @@ -451,11 +451,8 @@ static int bus_image_method_detach( r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, "org.freedesktop.portable1.attach-images", - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -1010,11 +1007,8 @@ int bus_image_acquire( if (mode == BUS_IMAGE_AUTHENTICATE_ALL) { r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, polkit_action, - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) @@ -1064,11 +1058,8 @@ int bus_image_acquire( if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) { r = bus_verify_polkit_async( message, - CAP_SYS_ADMIN, polkit_action, - NULL, - false, - UID_INVALID, + /* details= */ NULL, &m->polkit_registry, error); if (r < 0) diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c index da68b41a371..1af370d2393 100644 --- a/src/resolve/dns-type.c +++ b/src/resolve/dns-type.c @@ -80,7 +80,7 @@ bool dns_type_is_valid_query(uint16_t type) { DNS_TYPE_RRSIG); } -bool dns_type_is_zone_transer(uint16_t type) { +bool dns_type_is_zone_transfer(uint16_t type) { /* Zone transfers, either normal or incremental */ diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h index f0bb3be7bea..9c294f4e5fd 100644 --- a/src/resolve/dns-type.h +++ b/src/resolve/dns-type.h @@ -118,7 +118,7 @@ bool dns_type_is_obsolete(uint16_t type); bool dns_type_may_wildcard(uint16_t type); bool dns_type_apex_only(uint16_t type); bool dns_type_needs_authentication(uint16_t type); -bool dns_type_is_zone_transer(uint16_t type); +bool dns_type_is_zone_transfer(uint16_t type); int dns_type_to_af(uint16_t type); bool dns_class_is_pseudo(uint16_t class); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 1ef25acdad4..ef3f5237a9e 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -806,7 +806,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd if (!dns_type_is_valid_query(type)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified resource record type %" PRIu16 " may not be used in a query.", type); - if (dns_type_is_zone_transer(type)) + if (dns_type_is_zone_transfer(type)) return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface."); if (dns_type_is_obsolete(type)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type); @@ -1988,10 +1988,12 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata, if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_SYS_ADMIN, - "org.freedesktop.resolve1.register-service", - NULL, false, UID_INVALID, - &m->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.register-service", + /* details= */ NULL, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 78665bc93be..c5c5037cec6 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -164,8 +164,8 @@ void dns_cache_flush(DnsCache *c) { while ((key = hashmap_first_key(c->by_key))) dns_cache_remove_by_key(c, key); - assert(hashmap_size(c->by_key) == 0); - assert(prioq_size(c->by_expiry) == 0); + assert(hashmap_isempty(c->by_key)); + assert(prioq_isempty(c->by_expiry)); c->by_key = hashmap_free(c->by_key); c->by_expiry = prioq_free(c->by_expiry); @@ -186,7 +186,7 @@ static void dns_cache_make_space(DnsCache *c, unsigned add) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; DnsCacheItem *i; - if (prioq_size(c->by_expiry) <= 0) + if (prioq_isempty(c->by_expiry)) break; if (prioq_size(c->by_expiry) + add < CACHE_MAX) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 2580c2333c3..3c9b90c89b2 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -1951,7 +1951,7 @@ found_closest_encloser: } static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) { - char label[DNS_LABEL_MAX]; + char label[DNS_LABEL_MAX+1]; const char *n; int r; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index ca1eea43463..991a1be4584 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -552,7 +552,7 @@ int dns_packet_append_name( while (!dns_name_is_root(name)) { const char *z = name; - char label[DNS_LABEL_MAX]; + char label[DNS_LABEL_MAX+1]; size_t n = 0; if (allow_compression) diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 2e8b3e55801..40615ff6051 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -1557,7 +1557,7 @@ int dns_scope_add_dnssd_services(DnsScope *scope) { assert(scope); - if (hashmap_size(scope->manager->dnssd_services) == 0) + if (hashmap_isempty(scope->manager->dnssd_services)) return 0; scope->announced = false; diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index c59e3b7f495..f43e1f8b617 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -929,7 +929,7 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea return; } - if (dns_type_is_zone_transer(dns_question_first_key(p->question)->type)) { + if (dns_type_is_zone_transfer(dns_question_first_key(p->question)->type)) { log_debug("Got request for zone transfer, refusing."); dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false); return; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 696fce532a4..fe88e502e7c 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -2808,7 +2808,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * if (r == 0) continue; - return FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED); + return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED); } return true; @@ -2835,7 +2835,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * /* We found the transaction that was supposed to find the SOA RR for us. It was * successful, but found no RR for us. This means we are not at a zone cut. In this * case, we require authentication if the SOA lookup was authenticated too. */ - return FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED); + return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED); } return true; diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 1703c43d4b0..2156f4f685d 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -181,7 +181,7 @@ static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) { * trust anchor defined at all. This enables easy overriding * of negative trust anchors. */ - if (set_size(d->negative_by_name) > 0) + if (!set_isempty(d->negative_by_name)) return 0; r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops); diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index f533f972fc9..d4ede46459c 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -70,8 +70,8 @@ void dns_zone_flush(DnsZone *z) { while ((i = hashmap_first(z->by_key))) dns_zone_item_remove_and_free(z, i); - assert(hashmap_size(z->by_key) == 0); - assert(hashmap_size(z->by_name) == 0); + assert(hashmap_isempty(z->by_key)); + assert(hashmap_isempty(z->by_name)); z->by_key = hashmap_free(z->by_key); z->by_name = hashmap_free(z->by_name); diff --git a/src/resolve/resolved-dnssd-bus.c b/src/resolve/resolved-dnssd-bus.c index 0f0d4786ef1..0ae24fbf028 100644 --- a/src/resolve/resolved-dnssd-bus.c +++ b/src/resolve/resolved-dnssd-bus.c @@ -20,10 +20,14 @@ int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_ m = s->manager; - r = bus_verify_polkit_async(message, CAP_SYS_ADMIN, - "org.freedesktop.resolve1.unregister-service", - NULL, false, s->originator, - &m->polkit_registry, error); + r = bus_verify_polkit_async_full( + message, + "org.freedesktop.resolve1.unregister-service", + /* details= */ NULL, + /* interactive= */ false, + /* good_user= */ s->originator, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c index 4f8f591306c..7ca3214b064 100644 --- a/src/resolve/resolved-link-bus.c +++ b/src/resolve/resolved-link-bus.c @@ -236,10 +236,11 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.resolve1.set-dns-servers", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.set-dns-servers", + /* details= */ NULL, + &l->manager->polkit_registry, error); if (r < 0) goto finalize; if (r == 0) { @@ -368,10 +369,12 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_ if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.resolve1.set-domains", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.set-domains", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -446,10 +449,12 @@ int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.resolve1.set-default-route", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.set-default-route", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -493,10 +498,12 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.resolve1.set-llmnr", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.set-llmnr", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -541,10 +548,12 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.resolve1.set-mdns", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.set-mdns", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -589,10 +598,12 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.resolve1.set-dns-over-tls", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.set-dns-over-tls", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -637,10 +648,12 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.resolve1.set-dnssec", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.set-dnssec", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -698,10 +711,12 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v return -ENOMEM; } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -734,10 +749,12 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error if (r < 0) return r; - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.resolve1.revert", - NULL, true, UID_INVALID, - &l->manager->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.resolve1.revert", + /* details= */ NULL, + &l->manager->polkit_registry, + error); if (r < 0) return r; if (r == 0) diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index b52619e287b..0295662b5b5 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -388,7 +388,7 @@ static char* fallback_hostname(void) { static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) { _cleanup_free_ char *h = NULL, *n = NULL, *m = NULL; - char label[DNS_LABEL_MAX]; + char label[DNS_LABEL_MAX+1]; const char *p; int r; diff --git a/src/resolve/resolved-util.c b/src/resolve/resolved-util.c index 00abada426c..adcd35d6bee 100644 --- a/src/resolve/resolved-util.c +++ b/src/resolve/resolved-util.c @@ -14,7 +14,7 @@ int resolve_system_hostname(char **full_hostname, char **first_label) { #elif HAVE_LIBIDN int k; #endif - char label[DNS_LABEL_MAX]; + char label[DNS_LABEL_MAX+1]; const char *p, *decoded; int r; diff --git a/src/run/meson.build b/src/run/meson.build index 597a25abeb6..95336b86fe5 100644 --- a/src/run/meson.build +++ b/src/run/meson.build @@ -7,3 +7,17 @@ executables += [ 'sources' : files('run.c'), }, ] + +install_emptydir(bindir) + +meson.add_install_script(sh, '-c', + ln_s.format(bindir / 'systemd-run', + bindir / 'uid0')) + +custom_target( + 'systemd-uid0', + input : 'systemd-uid0.in', + output : 'systemd-uid0', + command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'], + install : pamconfdir != 'no', + install_dir : pamconfdir) diff --git a/src/run/run.c b/src/run/run.c index 88eca0fd6d1..45da75f8897 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -17,6 +17,7 @@ #include "bus-unit-util.h" #include "bus-wait-for-jobs.h" #include "calendarspec.h" +#include "color-util.h" #include "env-util.h" #include "escape.h" #include "exit-status.h" @@ -73,6 +74,9 @@ static bool arg_aggressive_gc = false; static char *arg_working_directory = NULL; static bool arg_shell = false; static char **arg_cmdline = NULL; +static char *arg_exec_path = NULL; +static bool arg_ignore_failure = false; +static char *arg_background = NULL; STATIC_DESTRUCTOR_REGISTER(arg_description, freep); STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep); @@ -82,6 +86,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_socket_property, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep); STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_background, freep); static int help(void) { _cleanup_free_ char *link = NULL; @@ -91,8 +97,8 @@ static int help(void) { if (r < 0) return log_oom(); - printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n" - "\n%sRun the specified command in a transient scope or service.%s\n\n" + printf("%1$s [OPTIONS...] COMMAND [ARGUMENTS...]\n" + "\n%5$sRun the specified command in a transient scope or service.%6$s\n\n" " -h --help Show this help\n" " --version Show package version\n" " --no-ask-password Do not prompt for password\n" @@ -104,7 +110,7 @@ static int help(void) { " -p --property=NAME=VALUE Set service or scope unit property\n" " --description=TEXT Description for unit\n" " --slice=SLICE Run in the specified slice\n" - " --slice-inherit Inherit the slice\n" + " --slice-inherit Inherit the slice from the caller\n" " --expand-environment=BOOL Control expansion of environment variables\n" " --no-block Do not wait until operation finished\n" " -r --remain-after-exit Leave service around until explicitly stopped\n" @@ -122,12 +128,14 @@ static int help(void) { " -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n" " -q --quiet Suppress information messages during runtime\n" " -G --collect Unload unit after it ran, even when failed\n" - " -S --shell Invoke a $SHELL interactively\n\n" - "Path options:\n" - " --path-property=NAME=VALUE Set path unit property\n\n" - "Socket options:\n" - " --socket-property=NAME=VALUE Set socket unit property\n\n" - "Timer options:\n" + " -S --shell Invoke a $SHELL interactively\n" + " --ignore-failure Ignore the exit status of the invoked process\n" + " --background=COLOR Set ANSI color for background\n" + "\n%3$sPath options:%4$s\n" + " --path-property=NAME=VALUE Set path unit property\n" + "\n%3$sSocket options:%4$s\n" + " --socket-property=NAME=VALUE Set socket unit property\n" + "\n%3$sTimer options:%4$s\n" " --on-active=SECONDS Run after SECONDS delay\n" " --on-boot=SECONDS Run SECONDS after machine was booted up\n" " --on-startup=SECONDS Run SECONDS after systemd activation\n" @@ -137,6 +145,40 @@ static int help(void) { " --on-timezone-change Run when the timezone changes\n" " --on-clock-change Run when the realtime clock jumps\n" " --timer-property=NAME=VALUE Set timer unit property\n" + "\nSee the %2$s for details.\n", + program_invocation_short_name, + link, + ansi_underline(), ansi_normal(), + ansi_highlight(), ansi_normal()); + + return 0; +} + +static int help_sudo_mode(void) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("uid0", "1", &link); + if (r < 0) + return log_oom(); + + printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n" + "\n%sElevate privileges interactively.%s\n\n" + " -h --help Show this help\n" + " -V --version Show package version\n" + " --no-ask-password Do not prompt for password\n" + " --machine=CONTAINER Operate on local container\n" + " --unit=UNIT Run under the specified unit name\n" + " --property=NAME=VALUE Set service or scope unit property\n" + " --description=TEXT Description for unit\n" + " --slice=SLICE Run in the specified slice\n" + " --slice-inherit Inherit the slice\n" + " -u --user=USER Run as system user\n" + " -g --group=GROUP Run as system group\n" + " --nice=NICE Nice level\n" + " -D --chdir=PATH Set working directory\n" + " --setenv=NAME[=VALUE] Set environment variable\n" + " --background=COLOR Set ANSI color for background\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -162,6 +204,18 @@ static int add_timer_property(const char *name, const char *val) { return 0; } +static char **make_login_shell_cmdline(const char *shell) { + _cleanup_free_ char *argv0 = NULL; + + assert(shell); + + argv0 = strjoin("-", shell); /* The - is how shells determine if they shall be consider login shells */ + if (!argv0) + return NULL; + + return strv_new(argv0); +} + static int parse_argv(int argc, char *argv[]) { enum { @@ -194,6 +248,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_WAIT, ARG_WORKING_DIRECTORY, ARG_SHELL, + ARG_IGNORE_FAILURE, + ARG_BACKGROUND, }; static const struct option options[] = { @@ -239,6 +295,8 @@ static int parse_argv(int argc, char *argv[]) { { "working-directory", required_argument, NULL, ARG_WORKING_DIRECTORY }, { "same-dir", no_argument, NULL, 'd' }, { "shell", no_argument, NULL, 'S' }, + { "ignore-failure", no_argument, NULL, ARG_IGNORE_FAILURE }, + { "background", no_argument, NULL, ARG_BACKGROUND }, {}, }; @@ -282,7 +340,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_DESCRIPTION: - r = free_and_strdup(&arg_description, optarg); + r = free_and_strdup_warn(&arg_description, optarg); if (r < 0) return r; break; @@ -524,6 +582,16 @@ static int parse_argv(int argc, char *argv[]) { arg_shell = true; break; + case ARG_IGNORE_FAILURE: + arg_ignore_failure = true; + break; + + case ARG_BACKGROUND: + r = free_and_strdup_warn(&arg_background, optarg); + if (r < 0) + return r; + break; + case '?': return -EINVAL; @@ -556,11 +624,8 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(r, "Failed to get current working directory: %m"); } - if (!arg_service_type) { - arg_service_type = strdup("exec"); - if (!arg_service_type) - return log_oom(); - } + if (!arg_service_type) + arg_service_type = "exec"; arg_wait = true; } @@ -654,6 +719,259 @@ static int parse_argv(int argc, char *argv[]) { return 1; } +static int parse_argv_sudo_mode(int argc, char *argv[]) { + + enum { + ARG_NO_ASK_PASSWORD = 0x100, + ARG_HOST, + ARG_MACHINE, + ARG_UNIT, + ARG_PROPERTY, + ARG_DESCRIPTION, + ARG_SLICE, + ARG_SLICE_INHERIT, + ARG_NICE, + ARG_SETENV, + ARG_BACKGROUND, + }; + + /* If invoked as "uid0" binary, let's expose a more sudo-like interface. We add various extensions + * though (but limit the extension to long options). */ + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "machine", required_argument, NULL, ARG_MACHINE }, + { "unit", required_argument, NULL, ARG_UNIT }, + { "property", required_argument, NULL, ARG_PROPERTY }, + { "description", required_argument, NULL, ARG_DESCRIPTION }, + { "slice", required_argument, NULL, ARG_SLICE }, + { "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT }, + { "user", required_argument, NULL, 'u' }, + { "group", required_argument, NULL, 'g' }, + { "nice", required_argument, NULL, ARG_NICE }, + { "chdir", required_argument, NULL, 'D' }, + { "setenv", required_argument, NULL, ARG_SETENV }, + { "background", required_argument, NULL, ARG_BACKGROUND }, + {}, + }; + + int r, c; + + assert(argc >= 0); + assert(argv); + + /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long() + * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */ + optind = 0; + while ((c = getopt_long(argc, argv, "+hVu:g:D:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + return help_sudo_mode(); + + case 'V': + return version(); + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case ARG_MACHINE: + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case ARG_UNIT: + arg_unit = optarg; + break; + + case ARG_PROPERTY: + if (strv_extend(&arg_property, optarg) < 0) + return log_oom(); + + break; + + case ARG_DESCRIPTION: + r = free_and_strdup_warn(&arg_description, optarg); + if (r < 0) + return r; + break; + + case ARG_SLICE: + arg_slice = optarg; + break; + + case ARG_SLICE_INHERIT: + arg_slice_inherit = true; + break; + + case 'u': + arg_exec_user = optarg; + break; + + case 'g': + arg_exec_group = optarg; + break; + + case ARG_NICE: + r = parse_nice(optarg, &arg_nice); + if (r < 0) + return log_error_errno(r, "Failed to parse nice value: %s", optarg); + + arg_nice_set = true; + break; + + case 'D': + r = parse_path_argument(optarg, true, &arg_working_directory); + if (r < 0) + return r; + + break; + + case ARG_SETENV: + 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; + + case ARG_BACKGROUND: + r = free_and_strdup_warn(&arg_background, optarg); + if (r < 0) + return r; + + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached(); + } + + if (!arg_working_directory) { + if (arg_exec_user) { + /* When switching to a specific user, also switch to its home directory. */ + arg_working_directory = strdup("~"); + if (!arg_working_directory) + return log_oom(); + } else { + /* When switching to root without this being specified, then stay in the current directory */ + r = safe_getcwd(&arg_working_directory); + if (r < 0) + return log_error_errno(r, "Failed to get current working directory: %m"); + } + } + + arg_service_type = "exec"; + arg_quiet = true; + arg_wait = true; + arg_aggressive_gc = true; + + arg_stdio = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT; + arg_expand_environment = false; + arg_send_sighup = true; + + _cleanup_strv_free_ char **l = NULL; + if (argc > optind) + l = strv_copy(argv + optind); + else { + const char *e; + + e = strv_env_get(arg_environment, "SHELL"); + if (e) + arg_exec_path = strdup(e); + else { + if (arg_transport == BUS_TRANSPORT_LOCAL) { + r = get_shell(&arg_exec_path); + if (r < 0) + return log_error_errno(r, "Failed to determine shell: %m"); + } else + arg_exec_path = strdup("/bin/sh"); + } + if (!arg_exec_path) + return log_oom(); + + l = make_login_shell_cmdline(arg_exec_path); + } + if (!l) + return log_oom(); + + strv_free_and_replace(arg_cmdline, l); + + if (!arg_slice) { + arg_slice = strdup("user.slice"); + if (!arg_slice) + return log_oom(); + } + + _cleanup_free_ char *un = NULL; + un = getusername_malloc(); + if (!un) + return log_oom(); + + /* Set a bunch of environment variables in a roughly sudo-compatible way */ + r = strv_env_assign(&arg_environment, "SUDO_USER", un); + if (r < 0) + return log_error_errno(r, "Failed to set $SUDO_USER environment variable: %m"); + + r = strv_env_assignf(&arg_environment, "SUDO_UID", UID_FMT, getuid()); + if (r < 0) + return log_error_errno(r, "Failed to set $SUDO_UID environment variable: %m"); + + r = strv_env_assignf(&arg_environment, "SUDO_GID", GID_FMT, getgid()); + if (r < 0) + return log_error_errno(r, "Failed to set $SUDO_GID environment variable: %m"); + + if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_UID=" UID_FMT, getuid()) < 0) + return log_oom(); + + if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_GID=" GID_FMT, getgid()) < 0) + return log_oom(); + + if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_USER=%s", un) < 0) + return log_oom(); + + if (strv_extend(&arg_property, "PAMName=systemd-uid0") < 0) + return log_oom(); + + if (!arg_background && arg_stdio == ARG_STDIO_PTY) { + double red, green, blue; + + r = get_default_background_color(&red, &green, &blue); + if (r < 0) + log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m"); + else { + double h, s, v; + + rgb_to_hsv(red, green, blue, &h, &s, &v); + + if (!arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0")) + h = 0; /* red */ + else + h = 60 /* yellow */; + + if (v > 50) /* If the background is bright, then pull down saturation */ + s = 25; + else /* otherwise pump it up */ + s = 75; + + v = MAX(30, v); /* Make sure we don't hide the color in black */ + + uint8_t r8, g8, b8; + hsv_to_rgb(h, s, v, &r8, &g8, &b8); + + if (asprintf(&arg_background, "48;2;%u;%u;%u", r8, g8, b8) < 0) + return log_oom(); + } + } + + return 1; +} + static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) { int r; @@ -902,7 +1220,7 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append(m, "s", arg_cmdline[0]); + r = sd_bus_message_append(m, "s", arg_exec_path ?: arg_cmdline[0]); if (r < 0) return bus_log_create_error(r); @@ -913,9 +1231,10 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p if (use_ex_prop) r = sd_bus_message_append_strv( m, - STRV_MAKE(arg_expand_environment > 0 ? NULL : "no-env-expand")); + STRV_MAKE(arg_expand_environment > 0 ? NULL : "no-env-expand", + arg_ignore_failure ? "ignore-failure" : NULL)); else - r = sd_bus_message_append(m, "b", false); + r = sd_bus_message_append(m, "b", arg_ignore_failure); if (r < 0) return bus_log_create_error(r); @@ -1381,7 +1700,7 @@ static int start_transient_service(sd_bus *bus) { r = bus_wait_for_jobs_one(w, object, - arg_quiet, + arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL); if (r < 0) return r; @@ -1435,6 +1754,9 @@ static int start_transient_service(sd_bus *bus) { /* Make sure to process any TTY events before we process bus events */ (void) pty_forward_set_priority(c.forward, SD_EVENT_PRIORITY_IMPORTANT); + + if (!isempty(arg_background)) + (void) pty_forward_set_background_color(c.forward, arg_background); } path = unit_dbus_path_from_name(service); @@ -1616,7 +1938,8 @@ static int start_transient_scope(sd_bus *bus) { if (r < 0) return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, arg_quiet, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL); + r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, + arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL); if (r < 0) return r; @@ -1886,7 +2209,8 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) { if (r < 0) return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, arg_quiet, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL); + r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, + arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL); if (r < 0) return r; @@ -1900,6 +2224,8 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) { } static bool shall_make_executable_absolute(void) { + if (arg_exec_path) + return false; if (strv_isempty(arg_cmdline)) return false; if (arg_transport != BUS_TRANSPORT_LOCAL) @@ -1920,7 +2246,10 @@ static int run(int argc, char* argv[]) { log_parse_environment(); log_open(); - r = parse_argv(argc, argv); + if (invoked_as(argv, "uid0")) + r = parse_argv_sudo_mode(argc, argv); + else + r = parse_argv(argc, argv); if (r <= 0) return r; diff --git a/src/run/systemd-uid0.in b/src/run/systemd-uid0.in new file mode 100644 index 00000000000..57bd5e38b92 --- /dev/null +++ b/src/run/systemd-uid0.in @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# This file is part of systemd. +# +# Used by uid0 sessions + +{% if ENABLE_HOMED %} +-account sufficient pam_systemd_home.so +{% endif %} +account required pam_unix.so + +{% if HAVE_SELINUX %} +session required pam_selinux.so close +session required pam_selinux.so open +{% endif %} +session required pam_loginuid.so +session optional pam_keyinit.so force revoke +session required pam_namespace.so +{% if ENABLE_HOMED %} +-session optional pam_systemd_home.so +{% endif %} +session optional pam_umask.so silent +session optional pam_systemd.so +session required pam_unix.so diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 0e323f4644e..6d71245549d 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -443,9 +443,7 @@ int ask_password_tty( (void) loop_write(ttyfd, ANSI_NORMAL, SIZE_MAX); new_termios = old_termios; - new_termios.c_lflag &= ~(ICANON|ECHO); - new_termios.c_cc[VMIN] = 1; - new_termios.c_cc[VTIME] = 0; + termios_disable_echo(&new_termios); r = RET_NERRNO(tcsetattr(ttyfd, TCSADRAIN, &new_termios)); if (r < 0) diff --git a/src/shared/blockdev-util.c b/src/shared/blockdev-util.c index c906aec109c..347cd787c3a 100644 --- a/src/shared/blockdev-util.c +++ b/src/shared/blockdev-util.c @@ -777,6 +777,21 @@ int blockdev_get_sector_size(int fd, uint32_t *ret) { return 0; } +int blockdev_get_device_size(int fd, uint64_t *ret) { + uint64_t sz = 0; + + assert(fd >= 0); + assert(ret); + + /* This is just a type-safe wrapper around BLKGETSIZE64 that gets us around having to include messy linux/fs.h in various clients */ + + if (ioctl(fd, BLKGETSIZE64, &sz) < 0) + return -errno; + + *ret = sz; + return 0; +} + int blockdev_get_root(int level, dev_t *ret) { _cleanup_free_ char *p = NULL; dev_t devno; diff --git a/src/shared/blockdev-util.h b/src/shared/blockdev-util.h index 954a23df3e9..28aede289a0 100644 --- a/src/shared/blockdev-util.h +++ b/src/shared/blockdev-util.h @@ -57,5 +57,6 @@ int block_device_has_partitions(sd_device *dev); int blockdev_reread_partition_table(sd_device *dev); int blockdev_get_sector_size(int fd, uint32_t *ret); +int blockdev_get_device_size(int fd, uint64_t *ret); int blockdev_get_root(int level, dev_t *ret); diff --git a/src/shared/bus-polkit.c b/src/shared/bus-polkit.c index 904b897984a..9f923372a4b 100644 --- a/src/shared/bus-polkit.c +++ b/src/shared/bus-polkit.c @@ -102,7 +102,6 @@ static int bus_message_new_polkit_auth_call( int bus_test_polkit( sd_bus_message *call, - int capability, const char *action, const char **details, uid_t good_user, @@ -120,7 +119,7 @@ int bus_test_polkit( if (r != 0) return r; - r = sd_bus_query_sender_privilege(call, capability); + r = sd_bus_query_sender_privilege(call, -1); if (r < 0) return r; if (r > 0) @@ -465,12 +464,11 @@ static int async_polkit_query_check_action( * <- async_polkit_defer(q) */ -int bus_verify_polkit_async( +int bus_verify_polkit_async_full( sd_bus_message *call, - int capability, const char *action, const char **details, - bool interactive, + bool interactive, /* Use only for legacy method calls that have a separate "allow_interactive_authentication" field */ uid_t good_user, Hashmap **registry, sd_bus_error *ret_error) { @@ -499,7 +497,7 @@ int bus_verify_polkit_async( } #endif - r = sd_bus_query_sender_privilege(call, capability); + r = sd_bus_query_sender_privilege(call, -1); if (r < 0) return r; if (r > 0) diff --git a/src/shared/bus-polkit.h b/src/shared/bus-polkit.h index e2a3b7eef66..d82ac4679c6 100644 --- a/src/shared/bus-polkit.h +++ b/src/shared/bus-polkit.h @@ -4,8 +4,13 @@ #include "sd-bus.h" #include "hashmap.h" +#include "user-util.h" -int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e); +int bus_test_polkit(sd_bus_message *call, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e); + +int bus_verify_polkit_async_full(sd_bus_message *call, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error); +static inline int bus_verify_polkit_async(sd_bus_message *call, const char *action, const char **details, Hashmap **registry, sd_bus_error *ret_error) { + return bus_verify_polkit_async_full(call, action, details, false, UID_INVALID, registry, ret_error); +} -int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error); Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry); diff --git a/src/shared/bus-wait-for-jobs.c b/src/shared/bus-wait-for-jobs.c index 969c62979f0..b3280c15dfe 100644 --- a/src/shared/bus-wait-for-jobs.c +++ b/src/shared/bus-wait-for-jobs.c @@ -227,12 +227,12 @@ finish: service_shell_quoted ?: ""); } -static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { +static int check_wait_response(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) { assert(d); assert(d->name); assert(d->result); - if (!quiet) { + if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_ERROR)) { if (streq(d->result, "canceled")) log_error("Job for %s canceled.", strna(d->name)); else if (streq(d->result, "timeout")) @@ -279,14 +279,21 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* return -EOPNOTSUPP; else if (streq(d->result, "once")) return -ESTALE; - else if (STR_IN_SET(d->result, "done", "skipped")) + else if (streq(d->result, "done")) { + if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS)) + log_info("Job for %s finished.", strna(d->name)); return 0; + } else if (streq(d->result, "skipped")) { + if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS)) + log_info("Job for %s was skipped.", strna(d->name)); + return 0; + } return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Unexpected job result, assuming server side newer than us: %s", d->result); } -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { +int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) { int r = 0; assert(d); @@ -299,7 +306,7 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_ar return log_error_errno(q, "Failed to wait for response: %m"); if (d->name && d->result) { - q = check_wait_response(d, quiet, extra_args); + q = check_wait_response(d, flags, extra_args); /* Return the first error as it is most likely to be * meaningful. */ if (q < 0 && r == 0) @@ -322,12 +329,12 @@ int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { return set_put_strdup(&d->jobs, path); } -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet, const char* const* extra_args) { +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, WaitJobsFlags flags, const char* const* extra_args) { int r; r = bus_wait_for_jobs_add(d, path); if (r < 0) return log_oom(); - return bus_wait_for_jobs(d, quiet, extra_args); + return bus_wait_for_jobs(d, flags, extra_args); } diff --git a/src/shared/bus-wait-for-jobs.h b/src/shared/bus-wait-for-jobs.h index 5acf8b9241d..4d56c079ab3 100644 --- a/src/shared/bus-wait-for-jobs.h +++ b/src/shared/bus-wait-for-jobs.h @@ -5,12 +5,17 @@ #include "macro.h" +typedef enum WaitJobsFlags { + BUS_WAIT_JOBS_LOG_ERROR = 1 << 0, + BUS_WAIT_JOBS_LOG_SUCCESS = 1 << 1, +} WaitJobsFlags; + typedef struct BusWaitForJobs BusWaitForJobs; int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d); int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args); -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet, const char* const* extra_args); +int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args); +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, WaitJobsFlags flags, const char* const* extra_args); DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); diff --git a/src/shared/cgroup-setup.c b/src/shared/cgroup-setup.c index 811f129f6cd..934a16eaf38 100644 --- a/src/shared/cgroup-setup.c +++ b/src/shared/cgroup-setup.c @@ -421,6 +421,8 @@ int cg_set_access( { "cgroup.procs", true }, { "cgroup.subtree_control", true }, { "cgroup.threads", false }, + { "memory.oom.group", false }, + { "memory.reclaim", false }, {}, }; diff --git a/src/shared/color-util.c b/src/shared/color-util.c new file mode 100644 index 00000000000..776445ecfc5 --- /dev/null +++ b/src/shared/color-util.c @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "color-util.h" +#include "macro.h" + +void rgb_to_hsv(double r, double g, double b, + double *ret_h, double *ret_s, double *ret_v) { + + assert(r >= 0 && r <= 1); + assert(g >= 0 && g <= 1); + assert(b >= 0 && b <= 1); + assert(ret_h); + assert(ret_s); + assert(ret_v); + + double max_color = fmax(r, fmax(g, b)); + double min_color = fmin(r, fmin(g, b)); + double delta = max_color - min_color; + + *ret_v = max_color * 100.0; + + if (max_color > 0) + *ret_s = delta / max_color * 100.0; + else { + *ret_s = 0; + *ret_h = NAN; + return; + } + + if (delta > 0) { + if (r >= max_color) + *ret_h = 60 * fmod((g - b) / delta, 6); + else if (g >= max_color) + *ret_h = 60 * (((b - r) / delta) + 2); + else if (b >= max_color) + *ret_h = 60 * (((r - g) / delta) + 4); + + *ret_h = fmod(*ret_h, 360); + } else + *ret_h = NAN; +} + +void hsv_to_rgb(double h, double s, double v, + uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) { + + double c, x, m, r, g, b; + + assert(s >= 0 && s <= 100); + assert(v >= 0 && v <= 100); + assert(ret_r); + assert(ret_g); + assert(ret_b); + + h = fmod(h, 360); + c = (s / 100.0) * (v / 100.0); + x = c * (1 - fabs(fmod(h / 60.0, 2) - 1)); + m = (v / 100) - c; + + if (h >= 0 && h < 60) + r = c, g = x, b = 0.0; + else if (h >= 60 && h < 120) + r = x, g = c, b = 0.0; + else if (h >= 120 && h < 180) + r = 0.0, g = c, b = x; + else if (h >= 180 && h < 240) + r = 0.0, g = x, b = c; + else if (h >= 240 && h < 300) + r = x, g = 0.0, b = c; + else + r = c, g = 0.0, b = x; + + *ret_r = (uint8_t) ((r + m) * 255); + *ret_g = (uint8_t) ((g + m) * 255); + *ret_b = (uint8_t) ((b + m) * 255); +} diff --git a/src/shared/color-util.h b/src/shared/color-util.h new file mode 100644 index 00000000000..a4ae9eba09f --- /dev/null +++ b/src/shared/color-util.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +void rgb_to_hsv(double r, double g, double b, + double *ret_h, double *ret_s, double *ret_v); + +void hsv_to_rgb( + double h, double s, double v, + uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b); diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 7cc88895953..0026da5b483 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -100,6 +100,17 @@ int get_encrypted_credentials_dir(const char **ret) { return get_credentials_dir_internal("ENCRYPTED_CREDENTIALS_DIRECTORY", ret); } +int open_credentials_dir(void) { + const char *d; + int r; + + r = get_credentials_dir(&d); + if (r < 0) + return r; + + return RET_NERRNO(open(d, O_CLOEXEC|O_DIRECTORY)); +} + int read_credential(const char *name, void **ret, size_t *ret_size) { _cleanup_free_ char *fn = NULL; const char *d; diff --git a/src/shared/creds-util.h b/src/shared/creds-util.h index 5e39a6a022f..36ca0fb6100 100644 --- a/src/shared/creds-util.h +++ b/src/shared/creds-util.h @@ -31,6 +31,8 @@ bool credential_glob_valid(const char *s); int get_credentials_dir(const char **ret); int get_encrypted_credentials_dir(const char **ret); +int open_credentials_dir(void); + /* Where creds have been passed to the system */ #define SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@system" #define ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@encrypted" diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 348880cac88..b3f4c9ab766 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -13,6 +13,7 @@ #include #include "alloc-util.h" +#include "blockdev-util.h" #include "btrfs-util.h" #include "chase.h" #include "chattr-util.h" @@ -446,8 +447,9 @@ static int image_make( read_only = true; } - if (ioctl(block_fd, BLKGETSIZE64, &size) < 0) - log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename); + r = blockdev_get_device_size(block_fd, &size); + if (r < 0) + log_debug_errno(r, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename); block_fd = safe_close(block_fd); } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 2687eafaf6b..98690367a15 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1547,6 +1547,7 @@ int dissect_image_file( #if HAVE_BLKID _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; _cleanup_close_ int fd = -EBADF; + struct stat st; int r; assert(path); @@ -1555,7 +1556,10 @@ int dissect_image_file( if (fd < 0) return -errno; - r = fd_verify_regular(fd); + if (fstat(fd, &st) < 0) + return -errno; + + r = stat_verify_regular(&st); if (r < 0) return r; @@ -1563,6 +1567,8 @@ int dissect_image_file( if (r < 0) return r; + m->image_size = st.st_size; + r = probe_sector_size(fd, &m->sector_size); if (r < 0) return r; @@ -1759,8 +1765,9 @@ static int fs_grow(const char *node_path, int mount_fd, const char *mount_path) if (node_fd < 0) return log_debug_errno(errno, "Failed to open node device %s: %m", node_path); - if (ioctl(node_fd, BLKGETSIZE64, &size) != 0) - return log_debug_errno(errno, "Failed to get block device size of %s: %m", node_path); + r = blockdev_get_device_size(node_fd, &size); + if (r < 0) + return log_debug_errno(r, "Failed to get block device size of %s: %m", node_path); if (mount_fd < 0) { assert(mount_path); @@ -2179,7 +2186,7 @@ int dissected_image_mount( if (r > 0) ok = true; } - if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT)) { + if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT) && m->image_name) { r = extension_has_forbidden_content(where); if (r < 0) return r; @@ -3390,10 +3397,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ assert(m); 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] = -EBADF; - continue; - } + assert(paths[n_meta_initialized]); if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) { r = -errno; @@ -3435,14 +3439,16 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ for (unsigned k = 0; k < _META_MAX; k++) { _cleanup_close_ int fd = -ENOENT; - if (!paths[k]) - continue; + assert(paths[k]); fds[2*k] = safe_close(fds[2*k]); switch (k) { case META_SYSEXT_RELEASE: + if (!m->image_name) + goto next; + /* 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 @@ -3463,6 +3469,9 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ break; case META_CONFEXT_RELEASE: + if (!m->image_name) + goto next; + /* As above */ r = open_extension_release( t, @@ -3498,7 +3507,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ if (r < 0) goto inner_fail; - continue; + goto next; } default: @@ -3511,14 +3520,14 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ 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]); - continue; + goto next; } r = copy_bytes(fd, fds[2*k+1], UINT64_MAX, 0); if (r < 0) goto inner_fail; + next: fds[2*k+1] = safe_close(fds[2*k+1]); } @@ -3535,8 +3544,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ for (unsigned k = 0; k < _META_MAX; k++) { _cleanup_fclose_ FILE *f = NULL; - if (!paths[k]) - continue; + assert(paths[k]); fds[2*k+1] = safe_close(fds[2*k+1]); @@ -3628,18 +3636,25 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ r = wait_for_terminate_and_check("(sd-dissect)", child, 0); child = 0; if (r < 0) - return r; + goto finish; n = read(error_pipe[0], &v, sizeof(v)); - if (n < 0) - return -errno; - if (n == sizeof(v)) - return v; /* propagate error sent to us from child */ - if (n != 0) - return -EIO; - - if (r != EXIT_SUCCESS) - return -EPROTO; + if (n < 0) { + r = -errno; + goto finish; + } + if (n == sizeof(v)) { + r = v; /* propagate error sent to us from child */ + goto finish; + } + if (n != 0) { + r = -EIO; + goto finish; + } + if (r != EXIT_SUCCESS) { + r = -EPROTO; + goto finish; + } free_and_replace(m->hostname, hostname); m->machine_id = machine_id; @@ -3690,6 +3705,7 @@ int dissect_loop_device( return r; m->loop = loop_device_ref(loop); + m->image_size = m->loop->device_size; m->sector_size = m->loop->sector_size; r = dissect_image(m, loop->fd, loop->node, verity, mount_options, image_policy, flags); diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 979fd384fe6..ed02049ed0c 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -102,10 +102,12 @@ struct DissectedImage { DecryptedImage *decrypted_image; uint32_t sector_size; + uint64_t image_size; - /* Meta information extracted from /etc/os-release and similar */ char *image_name; sd_id128_t image_uuid; + + /* Meta information extracted from /etc/os-release and similar */ char *hostname; sd_id128_t machine_id; char **machine_info; diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index b41c9b06ca3..909b4cdcc93 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -410,7 +410,7 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r goto finish; for (;;) { - char label[DNS_LABEL_MAX]; + char label[DNS_LABEL_MAX+1]; r = dns_label_unescape(&p, label, sizeof label, flags); if (r < 0) @@ -507,7 +507,7 @@ int dns_name_compare_func(const char *a, const char *b) { y = b + strlen(b); for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; + char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; if (x == NULL && y == NULL) return 0; @@ -543,7 +543,7 @@ int dns_name_equal(const char *x, const char *y) { assert(y); for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; + char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; r = dns_label_unescape(&x, la, sizeof la, 0); if (r < 0) @@ -574,7 +574,7 @@ int dns_name_endswith(const char *name, const char *suffix) { s = suffix; for (;;) { - char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; + char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1]; r = dns_label_unescape(&n, ln, sizeof ln, 0); if (r < 0) @@ -612,7 +612,7 @@ int dns_name_startswith(const char *name, const char *prefix) { p = prefix; for (;;) { - char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX]; + char ln[DNS_LABEL_MAX+1], lp[DNS_LABEL_MAX+1]; r = dns_label_unescape(&p, lp, sizeof lp, 0); if (r < 0) @@ -644,7 +644,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char s = old_suffix; for (;;) { - char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; + char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1]; if (!saved_before) saved_before = n; @@ -929,7 +929,7 @@ bool dns_srv_type_is_valid(const char *name) { return false; for (;;) { - char label[DNS_LABEL_MAX]; + char label[DNS_LABEL_MAX+1]; /* This more or less implements RFC 6335, Section 5.1 */ @@ -1227,7 +1227,7 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) { return m; for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; + char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; const char *x, *y; if (k >= n || k >= m) { @@ -1328,7 +1328,7 @@ int dns_name_apply_idna(const char *name, char **ret) { assert(ret); for (;;) { - char label[DNS_LABEL_MAX]; + char label[DNS_LABEL_MAX+1]; r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index 9b8c7f73bcd..db87084a4da 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -27,9 +27,33 @@ typedef enum VerifyESPFlags { VERIFY_ESP_SEARCHING = 1 << 0, /* Downgrade various "not found" logs to debug level */ VERIFY_ESP_UNPRIVILEGED_MODE = 1 << 1, /* Call into udev rather than blkid */ - VERIFY_ESP_RELAX_CHECKS = 1 << 2, /* Do not validate ESP partition */ + VERIFY_ESP_SKIP_FSTYPE_CHECK = 1 << 2, /* Skip filesystem check */ + VERIFY_ESP_SKIP_DEVICE_CHECK = 1 << 3, /* Skip device node check */ } VerifyESPFlags; +static VerifyESPFlags verify_esp_flags_init(int unprivileged_mode, const char *env_name_for_relaxing) { + VerifyESPFlags flags = 0; + int r; + + assert(env_name_for_relaxing); + + if (unprivileged_mode < 0) + unprivileged_mode = geteuid() != 0; + if (unprivileged_mode) + flags |= VERIFY_ESP_UNPRIVILEGED_MODE; + + r = getenv_bool(env_name_for_relaxing); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $%s environment variable, assuming false.", env_name_for_relaxing); + else if (r > 0) + flags |= VERIFY_ESP_SKIP_FSTYPE_CHECK | VERIFY_ESP_SKIP_DEVICE_CHECK; + + if (detect_container() > 0) + flags |= VERIFY_ESP_SKIP_DEVICE_CHECK; + + return flags; +} + static int verify_esp_blkid( dev_t devid, VerifyESPFlags flags, @@ -326,8 +350,8 @@ static int verify_esp( dev_t *ret_devid, VerifyESPFlags flags) { - bool relax_checks, searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING), - unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE); + bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING), + unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE); _cleanup_free_ char *p = NULL; _cleanup_close_ int pfd = -EBADF; dev_t devid = 0; @@ -343,10 +367,6 @@ static int verify_esp( * -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing */ - relax_checks = - getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0 || - FLAGS_SET(flags, VERIFY_ESP_RELAX_CHECKS); - /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any * issues. Let's also, silence the error messages. */ @@ -356,7 +376,7 @@ static int verify_esp( (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR, r, "Failed to open parent directory of \"%s\": %m", path); - if (!relax_checks) { + if (!FLAGS_SET(flags, VERIFY_ESP_SKIP_FSTYPE_CHECK)) { _cleanup_free_ char *f = NULL; struct statfs sfs; @@ -383,19 +403,20 @@ static int verify_esp( "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); } - relax_checks = - relax_checks || - detect_container() > 0; - - r = verify_fsroot_dir(pfd, p, flags, relax_checks ? NULL : &devid); + r = verify_fsroot_dir(pfd, p, flags, FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK) ? NULL : &devid); if (r < 0) return r; /* In a container we don't have access to block devices, skip this part of the verification, we trust * the container manager set everything up correctly on its own. */ - if (relax_checks) + if (FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK)) goto finish; + if (devnum_is_zero(devid)) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Could not determine backing block device of directory \"%s\" (btrfs RAID?).", p); + /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell), @@ -454,15 +475,7 @@ int find_esp_and_warn_at( assert(rfd >= 0 || rfd == AT_FDCWD); - if (unprivileged_mode < 0) - unprivileged_mode = geteuid() != 0; - flags = unprivileged_mode > 0 ? VERIFY_ESP_UNPRIVILEGED_MODE : 0; - - r = dir_fd_is_root_or_cwd(rfd); - if (r < 0) - return log_error_errno(r, "Failed to check if directory file descriptor is root: %m"); - if (r == 0) - flags |= VERIFY_ESP_RELAX_CHECKS; + flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_ESP_CHECKS"); if (path) return verify_esp(rfd, path, ret_path, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags); @@ -742,8 +755,7 @@ static int verify_xbootldr( _cleanup_free_ char *p = NULL; _cleanup_close_ int pfd = -EBADF; bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING), - unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE), - relax_checks; + unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE); dev_t devid = 0; int r; @@ -756,17 +768,22 @@ static int verify_xbootldr( (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR, r, "Failed to open parent directory of \"%s\": %m", path); - relax_checks = - getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0 || - detect_container() > 0; - - r = verify_fsroot_dir(pfd, p, flags, relax_checks ? NULL : &devid); + r = verify_fsroot_dir(pfd, p, flags, FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK) ? NULL : &devid); if (r < 0) return r; - if (relax_checks) + if (FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK)) goto finish; + if (devnum_is_zero(devid)) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Could not determine backing block device of directory \"%s\" (btrfs RAID?).%s", + p, + searching ? "" : + "\nHint: set $SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes environment variable " + "to bypass this and further verifications for the directory."); + if (unprivileged_mode) r = verify_xbootldr_udev(devid, flags, ret_uuid); else @@ -800,17 +817,14 @@ int find_xbootldr_and_warn_at( sd_id128_t *ret_uuid, dev_t *ret_devid) { - VerifyESPFlags flags = 0; + VerifyESPFlags flags; int r; /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */ assert(rfd >= 0 || rfd == AT_FDCWD); - if (unprivileged_mode < 0) - unprivileged_mode = geteuid() != 0; - if (unprivileged_mode) - flags |= VERIFY_ESP_UNPRIVILEGED_MODE; + flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_XBOOTLDR_CHECKS"); if (path) return verify_xbootldr(rfd, path, flags, ret_path, ret_uuid, ret_devid); diff --git a/src/shared/hwdb-util.c b/src/shared/hwdb-util.c index f67e917b533..648bfc50265 100644 --- a/src/shared/hwdb-util.c +++ b/src/shared/hwdb-util.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "conf-files.h" +#include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -710,3 +711,16 @@ bool hwdb_should_reload(sd_hwdb *hwdb) { return true; return false; } + +int hwdb_bypass(void) { + int r; + + r = getenv_bool("SYSTEMD_HWDB_UPDATE_BYPASS"); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_HWDB_UPDATE_BYPASS, assuming no."); + if (r <= 0) + return false; + + log_debug("$SYSTEMD_HWDB_UPDATE_BYPASS is enabled, skipping execution."); + return true; +} diff --git a/src/shared/hwdb-util.h b/src/shared/hwdb-util.h index cb93690ee8d..00610b18d49 100644 --- a/src/shared/hwdb-util.h +++ b/src/shared/hwdb-util.h @@ -8,3 +8,4 @@ bool hwdb_should_reload(sd_hwdb *hwdb); int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat); int hwdb_query(const char *modalias, const char *root); +int hwdb_bypass(void); diff --git a/src/shared/install.c b/src/shared/install.c index 97707e50b40..265407f080e 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -747,7 +747,7 @@ static int remove_marked_symlinks( assert(config_path); assert(lp); - if (set_size(remove_symlinks_to) <= 0) + if (set_isempty(remove_symlinks_to)) return 0; fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC); @@ -2271,13 +2271,13 @@ int unit_file_mask( if (!config_path) return -ENXIO; + r = 0; + STRV_FOREACH(name, names) { _cleanup_free_ char *path = NULL; - int q; if (!unit_name_is_valid(*name, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; + RET_GATHER(r, -EINVAL); continue; } @@ -2285,9 +2285,7 @@ int unit_file_mask( if (!path) return -ENOMEM; - q = create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes); - if (q < 0 && r >= 0) - r = q; + RET_GATHER(r, create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes)); } return r; @@ -2383,8 +2381,7 @@ int unit_file_unmask( if (!dry_run && unlink(path) < 0) { if (errno != ENOENT) { - if (r >= 0) - r = -errno; + RET_GATHER(r, -errno); install_changes_add(changes, n_changes, -errno, path, NULL); } @@ -2401,9 +2398,7 @@ int unit_file_unmask( return q; } - q = remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes); - if (r >= 0) - r = q; + RET_GATHER(r, remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes)); return r; } diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index b11e9268aff..8434587286c 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -149,8 +149,9 @@ static int loop_configure_verify(int fd, const struct loop_config *c) { * effect hence. And if not use classic LOOP_SET_STATUS64. */ uint64_t z; - if (ioctl(fd, BLKGETSIZE64, &z) < 0) - return -errno; + r = blockdev_get_device_size(fd, &z); + if (r < 0) + return r; if (z != c->info.lo_sizelimit) { log_debug("LOOP_CONFIGURE is broken, doesn't honour .info.lo_sizelimit. Falling back to LOOP_SET_STATUS64."); @@ -404,6 +405,11 @@ static int loop_configure( assert_not_reached(); } + uint64_t device_size; + r = blockdev_get_device_size(loop_with_fd, &device_size); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get loopback device size: %m"); + LoopDevice *d = new(LoopDevice, 1); if (!d) return log_oom_debug(); @@ -420,6 +426,8 @@ static int loop_configure( .uevent_seqnum_not_before = seqnum, .timestamp_not_before = timestamp, .sector_size = c->block_size, + .device_size = device_size, + .created = true, }; *ret = TAKE_PTR(d); @@ -944,6 +952,11 @@ int loop_device_open( if (r < 0) return r; + uint64_t device_size; + r = blockdev_get_device_size(fd, &device_size); + if (r < 0) + return r; + r = sd_device_get_devnum(dev, &devnum); if (r < 0) return r; @@ -976,6 +989,8 @@ int loop_device_open( .uevent_seqnum_not_before = UINT64_MAX, .timestamp_not_before = USEC_INFINITY, .sector_size = sector_size, + .device_size = device_size, + .created = false, }; *ret = d; @@ -1057,8 +1072,9 @@ static int resize_partition(int partition_fd, uint64_t offset, uint64_t size) { return -EINVAL; current_offset *= 512U; - if (ioctl(partition_fd, BLKGETSIZE64, ¤t_size) < 0) - return -EINVAL; + r = blockdev_get_device_size(partition_fd, ¤t_size); + if (r < 0) + return r; if (size == UINT64_MAX && offset == UINT64_MAX) return 0; diff --git a/src/shared/loop-util.h b/src/shared/loop-util.h index d77c314af83..94357bba717 100644 --- a/src/shared/loop-util.h +++ b/src/shared/loop-util.h @@ -22,12 +22,14 @@ struct LoopDevice { sd_device *dev; char *backing_file; bool relinquished; + bool created; /* If we created the device */ dev_t backing_devno; /* The backing file's dev_t */ ino_t backing_inode; /* The backing file's ino_t */ uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach, or 0 if we don't know */ uint64_t uevent_seqnum_not_before; /* uevent sequm right before we attached the loopback device, or UINT64_MAX if we don't know */ usec_t timestamp_not_before; /* CLOCK_MONOTONIC timestamp taken immediately before attaching the loopback device, or USEC_INFINITY if we don't know */ uint32_t sector_size; + uint64_t device_size; }; /* Returns true if LoopDevice object is not actually a loopback device but some other block device we just wrap */ diff --git a/src/shared/machine-credential.c b/src/shared/machine-credential.c index 17f7afc4a0f..c1e76e468e5 100644 --- a/src/shared/machine-credential.c +++ b/src/shared/machine-credential.c @@ -19,76 +19,82 @@ static void machine_credential_done(MachineCredential *cred) { cred->size = 0; } -void machine_credential_free_all(MachineCredential *creds, size_t n) { - assert(creds || n == 0); +void machine_credential_context_done(MachineCredentialContext *ctx) { + assert(ctx); - FOREACH_ARRAY(cred, creds, n) + FOREACH_ARRAY(cred, ctx->credentials, ctx->n_credentials) machine_credential_done(cred); - free(creds); + free(ctx->credentials); } -int machine_credential_set(MachineCredential **credentials, size_t *n_credentials, const char *cred_string) { - _cleanup_free_ char *word = NULL, *data = NULL; +bool machine_credentials_contains(const MachineCredentialContext *ctx, const char *id) { + assert(ctx); + assert(id); + + FOREACH_ARRAY(cred, ctx->credentials, ctx->n_credentials) + if (streq(cred->id, id)) + return true; + + return false; +} + +int machine_credential_set(MachineCredentialContext *ctx, const char *cred_str) { + _cleanup_(machine_credential_done) MachineCredential cred = {}; ssize_t l; int r; - const char *p = ASSERT_PTR(cred_string); - assert(credentials && n_credentials); - assert(*credentials || *n_credentials == 0); + assert(ctx); - r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + const char *p = ASSERT_PTR(cred_str); + + r = extract_first_word(&p, &cred.id, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return log_error_errno(r, "Failed to parse --set-credential= parameter: %m"); if (r == 0 || !p) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for --set-credential=: %s", cred_string); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Missing value for --set-credential=: %s", cred_str); - if (!credential_name_valid(word)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", word); + if (!credential_name_valid(cred.id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", cred.id); - FOREACH_ARRAY(cred, *credentials, *n_credentials) - if (streq(cred->id, word)) - return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", word); + if (machine_credentials_contains(ctx, cred.id)) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", cred.id); - l = cunescape(p, UNESCAPE_ACCEPT_NUL, &data); + l = cunescape(p, UNESCAPE_ACCEPT_NUL, &cred.data); if (l < 0) return log_error_errno(l, "Failed to unescape credential data: %s", p); + cred.size = l; - if (!GREEDY_REALLOC(*credentials, *n_credentials + 1)) + if (!GREEDY_REALLOC(ctx->credentials, ctx->n_credentials + 1)) return log_oom(); - (*credentials)[(*n_credentials)++] = (MachineCredential) { - .id = TAKE_PTR(word), - .data = TAKE_PTR(data), - .size = l, - }; + ctx->credentials[ctx->n_credentials++] = TAKE_STRUCT(cred); return 0; } -int machine_credential_load(MachineCredential **credentials, size_t *n_credentials, const char *cred_path) { +int machine_credential_load(MachineCredentialContext *ctx, const char *cred_path) { + _cleanup_(machine_credential_done) MachineCredential cred = {}; + _cleanup_free_ char *path_alloc = NULL; ReadFullFileFlags flags = READ_FULL_FILE_SECURE; - _cleanup_(erase_and_freep) char *data = NULL; - _cleanup_free_ char *word = NULL, *j = NULL; - const char *p = ASSERT_PTR(cred_path); - size_t size; int r; - assert(credentials && n_credentials); - assert(*credentials || *n_credentials == 0); + assert(ctx); + + const char *p = ASSERT_PTR(cred_path); - r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + r = extract_first_word(&p, &cred.id, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return log_error_errno(r, "Failed to parse --load-credential= parameter: %m"); if (r == 0 || !p) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for --load-credential=: %s", cred_path); - if (!credential_name_valid(word)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", word); + if (!credential_name_valid(cred.id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", cred.id); - FOREACH_ARRAY(cred, *credentials, *n_credentials) - if (streq(cred->id, word)) - return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", word); + if (machine_credentials_contains(ctx, cred.id)) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", cred.id); if (is_path(p) && path_is_valid(p)) flags |= READ_FULL_FILE_CONNECT_SOCKET; @@ -97,31 +103,29 @@ int machine_credential_load(MachineCredential **credentials, size_t *n_credentia r = get_credentials_dir(&e); if (r < 0) - return log_error_errno(r, "Credential not available (no credentials passed at all): %s", word); + return log_error_errno(r, + "Credential not available (no credentials passed at all): %s", cred.id); - j = path_join(e, p); - if (!j) + path_alloc = path_join(e, p); + if (!path_alloc) return log_oom(); - p = j; + p = path_alloc; } else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential source appears to be neither a valid path nor a credential name: %s", p); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Credential source appears to be neither a valid path nor a credential name: %s", p); r = read_full_file_full(AT_FDCWD, p, UINT64_MAX, SIZE_MAX, flags, NULL, - &data, &size); + &cred.data, &cred.size); if (r < 0) return log_error_errno(r, "Failed to read credential '%s': %m", p); - if (!GREEDY_REALLOC(*credentials, *n_credentials + 1)) + if (!GREEDY_REALLOC(ctx->credentials, ctx->n_credentials + 1)) return log_oom(); - (*credentials)[(*n_credentials)++] = (MachineCredential) { - .id = TAKE_PTR(word), - .data = TAKE_PTR(data), - .size = size, - }; + ctx->credentials[ctx->n_credentials++] = TAKE_STRUCT(cred); return 0; } diff --git a/src/shared/machine-credential.h b/src/shared/machine-credential.h index c9044a2edcc..182f077fe7e 100644 --- a/src/shared/machine-credential.h +++ b/src/shared/machine-credential.h @@ -5,10 +5,18 @@ typedef struct MachineCredential { char *id; - void *data; + char *data; size_t size; } MachineCredential; -void machine_credential_free_all(MachineCredential *creds, size_t n); -int machine_credential_set(MachineCredential **credentials, size_t *n_credentials, const char *cred_string); -int machine_credential_load(MachineCredential **credentials, size_t *n_credentials, const char *cred_path); +typedef struct MachineCredentialContext { + MachineCredential *credentials; + size_t n_credentials; +} MachineCredentialContext; + +void machine_credential_context_done(MachineCredentialContext *ctx); + +bool machine_credentials_contains(const MachineCredentialContext *ctx, const char *id); + +int machine_credential_set(MachineCredentialContext *ctx, const char *cred_str); +int machine_credential_load(MachineCredentialContext *ctx, const char *cred_path); diff --git a/src/shared/meson.build b/src/shared/meson.build index b24a541de5d..bcd02d4cfb4 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -39,6 +39,7 @@ shared_sources = files( 'chown-recursive.c', 'clean-ipc.c', 'clock-util.c', + 'color-util.c', 'common-signal.c', 'compare-operator.c', 'condition.c', @@ -172,6 +173,7 @@ shared_sources = files( 'varlink.c', 'varlink-idl.c', 'varlink-io.systemd.c', + 'varlink-io.systemd.Credentials.c', 'varlink-io.systemd.Journal.c', 'varlink-io.systemd.ManagedOOM.c', 'varlink-io.systemd.PCRExtend.c', diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index 4f2acce513d..ba3a9e995d1 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -457,10 +457,6 @@ static int mount_switch_root_pivot(int fd_newroot, const char *path) { assert(fd_newroot >= 0); assert(path); - /* Change into the new rootfs. */ - if (fchdir(fd_newroot) < 0) - return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path); - /* Let the kernel tuck the new root under the old one. */ if (pivot_root(".", ".") < 0) return log_debug_errno(errno, "Failed to pivot root to new rootfs '%s': %m", path); @@ -477,10 +473,6 @@ static int mount_switch_root_move(int fd_newroot, const char *path) { assert(fd_newroot >= 0); assert(path); - /* Change into the new rootfs. */ - if (fchdir(fd_newroot) < 0) - return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path); - /* Move the new root fs */ if (mount(".", "/", NULL, MS_MOVE, NULL) < 0) return log_debug_errno(errno, "Failed to move new rootfs '%s': %m", path); @@ -494,7 +486,7 @@ static int mount_switch_root_move(int fd_newroot, const char *path) { int mount_switch_root_full(const char *path, unsigned long mount_propagation_flag, bool force_ms_move) { _cleanup_close_ int fd_newroot = -EBADF; - int r; + int r, is_current_root; assert(path); assert(mount_propagation_flag_is_valid(mount_propagation_flag)); @@ -503,19 +495,31 @@ int mount_switch_root_full(const char *path, unsigned long mount_propagation_fla if (fd_newroot < 0) return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path); - if (!force_ms_move) { - r = mount_switch_root_pivot(fd_newroot, path); - if (r < 0) { - log_debug_errno(r, "Failed to pivot into new rootfs '%s', will try to use MS_MOVE instead: %m", path); - force_ms_move = true; + is_current_root = path_is_root_at(fd_newroot, NULL); + if (is_current_root < 0) + return log_debug_errno(is_current_root, "Failed to determine if target dir is our root already: %m"); + + /* Change into the new rootfs. */ + if (fchdir(fd_newroot) < 0) + return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path); + + /* Make this a NOP if we are supposed to switch to our current root fs. After all, both pivot_root() + * and MS_MOVE don't like that. */ + if (!is_current_root) { + if (!force_ms_move) { + r = mount_switch_root_pivot(fd_newroot, path); + if (r < 0) { + log_debug_errno(r, "Failed to pivot into new rootfs '%s', will try to use MS_MOVE instead: %m", path); + force_ms_move = true; + } + } + if (force_ms_move) { + /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the rootfs is + * an initramfs in which case pivot_root() isn't supported. */ + r = mount_switch_root_move(fd_newroot, path); + if (r < 0) + return log_debug_errno(r, "Failed to switch to new rootfs '%s' with MS_MOVE: %m", path); } - } - if (force_ms_move) { - /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the rootfs is - * an initramfs in which case pivot_root() isn't supported. */ - r = mount_switch_root_move(fd_newroot, path); - if (r < 0) - return log_debug_errno(r, "Failed to switch to new rootfs '%s' with MS_MOVE: %m", path); } /* Finally, let's establish the requested propagation flags. */ diff --git a/src/shared/netif-naming-scheme.c b/src/shared/netif-naming-scheme.c index fbaf5c5a608..38c24760f0a 100644 --- a/src/shared/netif-naming-scheme.c +++ b/src/shared/netif-naming-scheme.c @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-device.h" + #include "alloc-util.h" +#include "device-private.h" #include "netif-naming-scheme.h" #include "proc-cmdline.h" #include "string-util.h" @@ -101,3 +104,81 @@ static const char* const alternative_names_policy_table[_NAMEPOLICY_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(alternative_names_policy, NamePolicy); + +static int naming_sysattr_allowed_by_default(sd_device *dev) { + int r; + + assert(dev); + + r = device_get_property_bool(dev, "ID_NET_NAME_ALLOW"); + if (r == -ENOENT) + return true; + + return r; +} + +static int naming_sysattr_allowed(sd_device *dev, const char *sysattr) { + char *sysattr_property; + int r; + + assert(dev); + assert(sysattr); + + sysattr_property = strjoina("ID_NET_NAME_ALLOW_", sysattr); + ascii_strupper(sysattr_property); + + r = device_get_property_bool(dev, sysattr_property); + if (r == -ENOENT) + /* If ID_NET_NAME_ALLOW is not set or set to 1 default is to allow */ + return naming_sysattr_allowed_by_default(dev); + + return r; +} + +int device_get_sysattr_int_filtered(sd_device *device, const char *sysattr, int *ret_value) { + int r; + + r = naming_sysattr_allowed(device, sysattr); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + return device_get_sysattr_int(device, sysattr, ret_value); +} + +int device_get_sysattr_unsigned_filtered(sd_device *device, const char *sysattr, unsigned *ret_value) { + int r; + + r = naming_sysattr_allowed(device, sysattr); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + return device_get_sysattr_unsigned(device, sysattr, ret_value); +} + +int device_get_sysattr_bool_filtered(sd_device *device, const char *sysattr) { + int r; + + r = naming_sysattr_allowed(device, sysattr); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + return device_get_sysattr_bool(device, sysattr); +} + +int device_get_sysattr_value_filtered(sd_device *device, const char *sysattr, const char **ret_value) { + int r; + + r = naming_sysattr_allowed(device, sysattr); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + return sd_device_get_sysattr_value(device, sysattr, ret_value); +} diff --git a/src/shared/netif-naming-scheme.h b/src/shared/netif-naming-scheme.h index 3f7be088305..62afdc514a9 100644 --- a/src/shared/netif-naming-scheme.h +++ b/src/shared/netif-naming-scheme.h @@ -3,6 +3,8 @@ #include +#include "sd-device.h" + #include "macro.h" /* So here's the deal: net_id is supposed to be an exercise in providing stable names for network devices. However, we @@ -95,3 +97,8 @@ NamePolicy name_policy_from_string(const char *p) _pure_; const char *alternative_names_policy_to_string(NamePolicy p) _const_; NamePolicy alternative_names_policy_from_string(const char *p) _pure_; + +int device_get_sysattr_int_filtered(sd_device *device, const char *sysattr, int *ret_value); +int device_get_sysattr_unsigned_filtered(sd_device *device, const char *sysattr, unsigned *ret_value); +int device_get_sysattr_bool_filtered(sd_device *device, const char *sysattr); +int device_get_sysattr_value_filtered(sd_device *device, const char *sysattr, const char **ret_value); diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 311fb4d9cbb..d4a689cf852 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -4,11 +4,12 @@ #include "fd-util.h" #include "hexdecoct.h" #include "openssl-util.h" +#include "random-util.h" #include "string-util.h" #if HAVE_OPENSSL -/* For each error in the the OpenSSL thread error queue, log the provided message and the OpenSSL error - * string. If there are no errors in the OpenSSL thread queue, this logs the message with "No openssl +/* For each error in the OpenSSL thread error queue, log the provided message and the OpenSSL error + * string. If there are no errors in the OpenSSL thread queue, this logs the message with "No OpenSSL * errors." This logs at level debug. Returns -EIO (or -ENOMEM). */ #define log_openssl_errors(fmt, ...) _log_openssl_errors(UNIQ, fmt, ##__VA_ARGS__) #define _log_openssl_errors(u, fmt, ...) \ @@ -523,7 +524,6 @@ int rsa_encrypt_bytes( *ret_encrypt_key = TAKE_PTR(b); *ret_encrypt_key_size = l; - return 0; } @@ -989,7 +989,7 @@ int ecc_ecdh(const EVP_PKEY *private_pkey, if (EVP_PKEY_derive(ctx, NULL, &shared_secret_size) <= 0) return log_openssl_errors("Failed to get ECC shared secret size"); - _cleanup_free_ void *shared_secret = malloc(shared_secret_size); + _cleanup_(erase_and_freep) void *shared_secret = malloc(shared_secret_size); if (!shared_secret) return log_oom_debug(); @@ -1109,7 +1109,7 @@ int string_hashsum( _cleanup_free_ void *hash = NULL; size_t hash_size; - _cleanup_free_ char *enc; + _cleanup_free_ char *enc = NULL; int r; assert(s || len == 0); @@ -1128,6 +1128,170 @@ int string_hashsum( return 0; } # endif + +static int ecc_pkey_generate_volume_keys( + EVP_PKEY *pkey, + void **ret_decrypted_key, + size_t *ret_decrypted_key_size, + void **ret_saved_key, + size_t *ret_saved_key_size) { + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_new = NULL; + _cleanup_(erase_and_freep) void *decrypted_key = NULL; + _cleanup_free_ unsigned char *saved_key = NULL; + size_t decrypted_key_size, saved_key_size; + int nid = NID_undef; + int r; + +#if OPENSSL_VERSION_MAJOR >= 3 + _cleanup_free_ char *curve_name = NULL; + size_t len = 0; + + if (EVP_PKEY_get_group_name(pkey, NULL, 0, &len) != 1 || len == 0) + return log_openssl_errors("Failed to determine PKEY group name length"); + + len++; + curve_name = new(char, len); + if (!curve_name) + return log_oom_debug(); + + if (EVP_PKEY_get_group_name(pkey, curve_name, len, &len) != 1) + return log_openssl_errors("Failed to get PKEY group name"); + + nid = OBJ_sn2nid(curve_name); +#else + EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); + if (!ec_key) + return log_openssl_errors("PKEY doesn't have EC_KEY associated"); + + if (EC_KEY_check_key(ec_key) != 1) + return log_openssl_errors("EC_KEY associated with PKEY is not valid"); + + nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); +#endif + + r = ecc_pkey_new(nid, &pkey_new); + if (r < 0) + return log_debug_errno(r, "Failed to generate a new EC keypair: %m"); + + r = ecc_ecdh(pkey_new, pkey, &decrypted_key, &decrypted_key_size); + if (r < 0) + return log_debug_errno(r, "Failed to derive shared secret: %m"); + +#if OPENSSL_VERSION_MAJOR >= 3 + /* EVP_PKEY_get1_encoded_public_key() always returns uncompressed format of EC points. + See https://github.com/openssl/openssl/discussions/22835 */ + saved_key_size = EVP_PKEY_get1_encoded_public_key(pkey_new, &saved_key); + if (saved_key_size == 0) + return log_openssl_errors("Failed to convert the generated public key to SEC1 format"); +#else + EC_KEY *ec_key_new = EVP_PKEY_get0_EC_KEY(pkey_new); + if (!ec_key_new) + return log_openssl_errors("The generated key doesn't have associated EC_KEY"); + + if (EC_KEY_check_key(ec_key_new) != 1) + return log_openssl_errors("EC_KEY associated with the generated key is not valid"); + + saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new), + EC_KEY_get0_public_key(ec_key_new), + POINT_CONVERSION_UNCOMPRESSED, + NULL, 0, NULL); + if (saved_key_size == 0) + return log_openssl_errors("Failed to determine size of the generated public key"); + + saved_key = malloc(saved_key_size); + if (!saved_key) + return log_oom_debug(); + + saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new), + EC_KEY_get0_public_key(ec_key_new), + POINT_CONVERSION_UNCOMPRESSED, + saved_key, saved_key_size, NULL); + if (saved_key_size == 0) + return log_openssl_errors("Failed to convert the generated public key to SEC1 format"); +#endif + + *ret_decrypted_key = TAKE_PTR(decrypted_key); + *ret_decrypted_key_size = decrypted_key_size; + *ret_saved_key = TAKE_PTR(saved_key); + *ret_saved_key_size = saved_key_size; + return 0; +} + +static int rsa_pkey_generate_volume_keys( + EVP_PKEY *pkey, + void **ret_decrypted_key, + size_t *ret_decrypted_key_size, + void **ret_saved_key, + size_t *ret_saved_key_size) { + + _cleanup_(erase_and_freep) void *decrypted_key = NULL; + _cleanup_free_ void *saved_key = NULL; + size_t decrypted_key_size, saved_key_size; + int r; + + r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size); + if (r < 0) + return log_debug_errno(r, "Failed to determine RSA public key size."); + + log_debug("Generating %zu bytes random key.", decrypted_key_size); + + decrypted_key = malloc(decrypted_key_size); + if (!decrypted_key) + return log_oom_debug(); + + r = crypto_random_bytes(decrypted_key, decrypted_key_size); + if (r < 0) + return log_debug_errno(r, "Failed to generate random key: %m"); + + r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &saved_key, &saved_key_size); + if (r < 0) + return log_debug_errno(r, "Failed to encrypt random key: %m"); + + *ret_decrypted_key = TAKE_PTR(decrypted_key); + *ret_decrypted_key_size = decrypted_key_size; + *ret_saved_key = TAKE_PTR(saved_key); + *ret_saved_key_size = saved_key_size; + return 0; +} + +int x509_generate_volume_keys( + X509 *cert, + void **ret_decrypted_key, + size_t *ret_decrypted_key_size, + void **ret_saved_key, + size_t *ret_saved_key_size) { + + assert(cert); + assert(ret_decrypted_key); + assert(ret_decrypted_key_size); + assert(ret_saved_key); + assert(ret_saved_key_size); + + EVP_PKEY *pkey = X509_get0_pubkey(cert); + if (!pkey) + return log_openssl_errors("Failed to extract public key from X.509 certificate."); + +#if OPENSSL_VERSION_MAJOR >= 3 + int type = EVP_PKEY_get_base_id(pkey); +#else + int type = EVP_PKEY_base_id(pkey); +#endif + switch (type) { + + case EVP_PKEY_RSA: + return rsa_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size); + + case EVP_PKEY_EC: + return ecc_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size); + + case NID_undef: + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine a type of public key"); + + default: + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported public key type: %s", OBJ_nid2sn(type)); + } +} #endif int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index e3f34a8576e..2ca3e8c1ce0 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -108,6 +108,8 @@ int ecc_pkey_new(int curve_id, EVP_PKEY **ret); int ecc_ecdh(const EVP_PKEY *private_pkey, const EVP_PKEY *peer_pkey, void **ret_shared_secret, size_t *ret_shared_secret_size); +int x509_generate_volume_keys(X509 *cert, void **ret_decrypted_key, size_t *ret_decrypted_key_size, void **ret_saved_key, size_t *ret_saved_key_size); + int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size); int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size); diff --git a/src/shared/pkcs11-util.c b/src/shared/pkcs11-util.c index 6e88dc38038..c3d97b80f9e 100644 --- a/src/shared/pkcs11-util.c +++ b/src/shared/pkcs11-util.c @@ -586,143 +586,419 @@ int pkcs11_token_find_private_key( P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object) { - bool found_decrypt = false, found_class = false, found_key_type = false; + uint_fast8_t n_objects = 0; + bool found_class = false; _cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL; - CK_ULONG n_attributes, a, n_objects; - CK_ATTRIBUTE *attributes = NULL; - CK_OBJECT_HANDLE objects[2]; - CK_RV rv, rv2; - int r; + CK_OBJECT_HANDLE object, candidate; + static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY; + CK_BBOOL decrypt_value, derive_value; + CK_ATTRIBUTE optional_attributes[] = { + { CKA_DECRYPT, &decrypt_value, sizeof(decrypt_value) }, + { CKA_DERIVE, &derive_value, sizeof(derive_value) } + }; + CK_RV rv; assert(m); assert(search_uri); assert(ret_object); - r = dlopen_p11kit(); - if (r < 0) - return r; - - attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes); - for (a = 0; a < n_attributes; a++) { + CK_ULONG n_attributes; + CK_ATTRIBUTE *attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes); + for (CK_ULONG i = 0; i < n_attributes; i++) { /* We use the URI's included match attributes, but make them more strict. This allows users * to specify a token URL instead of an object URL and the right thing should happen if * there's only one suitable key on the token. */ - switch (attributes[a].type) { - + switch (attributes[i].type) { case CKA_CLASS: { CK_OBJECT_CLASS c; - if (attributes[a].ulValueLen != sizeof(c)) + if (attributes[i].ulValueLen != sizeof(c)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size."); - memcpy(&c, attributes[a].pValue, sizeof(c)); + memcpy(&c, attributes[i].pValue, sizeof(c)); if (c != CKO_PRIVATE_KEY) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not a private key, refusing."); found_class = true; break; - } + }} + } + + if (!found_class) { + /* Hmm, let's slightly extend the attribute list we search for */ - case CKA_DECRYPT: { - CK_BBOOL b; + attributes_buffer = new(CK_ATTRIBUTE, n_attributes + 1); + if (!attributes_buffer) + return log_oom(); - if (attributes[a].ulValueLen != sizeof(b)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_DECRYPT attribute size."); + memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes); - memcpy(&b, attributes[a].pValue, sizeof(b)); - if (!b) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Selected PKCS#11 object is not suitable for decryption, refusing."); + attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) { + .type = CKA_CLASS, + .pValue = (CK_OBJECT_CLASS*) &class, + .ulValueLen = sizeof(class), + }; + + attributes = attributes_buffer; + } + + rv = m->C_FindObjectsInit(session, attributes, n_attributes); + if (rv != CKR_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv)); + + for (;;) { + CK_ULONG b; + rv = m->C_FindObjects(session, &candidate, 1, &b); + if (rv != CKR_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to find objects: %s", sym_p11_kit_strerror(rv)); - found_decrypt = true; + if (b == 0) break; + + bool can_decrypt = false, can_derive = false; + optional_attributes[0].ulValueLen = sizeof(decrypt_value); + optional_attributes[1].ulValueLen = sizeof(derive_value); + + rv = m->C_GetAttributeValue(session, candidate, optional_attributes, ELEMENTSOF(optional_attributes)); + if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to get attributes of a selected private key: %s", sym_p11_kit_strerror(rv)); + + if (optional_attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION && decrypt_value == CK_TRUE) + can_decrypt = true; + + if (optional_attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION && derive_value == CK_TRUE) + can_derive = true; + + if (can_decrypt || can_derive) { + n_objects++; + if (n_objects > 1) + break; + object = candidate; } + } - case CKA_KEY_TYPE: { - CK_KEY_TYPE t; + rv = m->C_FindObjectsFinal(session); + if (rv != CKR_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv)); - if (attributes[a].ulValueLen != sizeof(t)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_KEY_TYPE attribute size."); + if (n_objects == 0) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "Failed to find selected private key suitable for decryption or derivation on token."); - memcpy(&t, attributes[a].pValue, sizeof(t)); - if (t != CKK_RSA) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an RSA key, refusing."); + if (n_objects > 1) + return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), + "Configured private key URI matches multiple keys, refusing."); - found_key_type = true; - break; - }} + *ret_object = object; + return 0; +} + +static const char* object_class_to_string(CK_OBJECT_CLASS class) { + switch (class) { + case CKO_CERTIFICATE: + return "CKO_CERTIFICATE"; + case CKO_PUBLIC_KEY: + return "CKO_PUBLIC_KEY"; + case CKO_PRIVATE_KEY: + return "CKO_PRIVATE_KEY"; + case CKO_SECRET_KEY: + return "CKO_SECRET_KEY"; + default: + return NULL; } +} - if (!found_decrypt || !found_class || !found_key_type) { - /* Hmm, let's slightly extend the attribute list we search for */ +/* Returns an object with the given class and the same CKA_ID or CKA_LABEL as prototype */ +int pkcs11_token_find_related_object( + CK_FUNCTION_LIST *m, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE prototype, + CK_OBJECT_CLASS class, + CK_OBJECT_HANDLE *ret_object ) { - attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_decrypt + !found_class + !found_key_type); - if (!attributes_buffer) + _cleanup_free_ void *buffer = NULL; + CK_ATTRIBUTE attributes[] = { + { CKA_ID, NULL_PTR, 0 }, + { CKA_LABEL, NULL_PTR, 0 } + }; + CK_OBJECT_CLASS search_class = class; + CK_ATTRIBUTE search_attributes[2] = { + { CKA_CLASS, &search_class, sizeof(search_class) } + }; + CK_ULONG n_objects; + CK_OBJECT_HANDLE objects[2]; + CK_RV rv; + + rv = m->C_GetAttributeValue(session, prototype, attributes, ELEMENTSOF(attributes)); + if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve length of attributes: %s", sym_p11_kit_strerror(rv)); + + if (attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION) { + buffer = malloc(attributes[0].ulValueLen); + if (!buffer) return log_oom(); - memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes); + attributes[0].pValue = buffer; + rv = m->C_GetAttributeValue(session, prototype, &attributes[0], 1); + if (rv != CKR_OK) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Failed to retrieve CKA_ID: %s", sym_p11_kit_strerror(rv)); - if (!found_decrypt) { - static const CK_BBOOL yes = true; + search_attributes[1] = attributes[0]; - attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) { - .type = CKA_DECRYPT, - .pValue = (CK_BBOOL*) &yes, - .ulValueLen = sizeof(yes), - }; - } + } else if (attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION) { + buffer = malloc(attributes[1].ulValueLen); + if (!buffer) + return log_oom(); - if (!found_class) { - static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY; + attributes[1].pValue = buffer; + rv = m->C_GetAttributeValue(session, prototype, &attributes[1], 1); + if (rv != CKR_OK) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Failed to retrieve CKA_LABEL: %s", sym_p11_kit_strerror(rv)); - attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) { - .type = CKA_CLASS, - .pValue = (CK_OBJECT_CLASS*) &class, - .ulValueLen = sizeof(class), - }; - } + search_attributes[1] = attributes[1]; - if (!found_key_type) { - static const CK_KEY_TYPE type = CKK_RSA; + } else + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "The prototype does not have CKA_ID or CKA_LABEL"); - attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) { - .type = CKA_KEY_TYPE, - .pValue = (CK_KEY_TYPE*) &type, - .ulValueLen = sizeof(type), - }; - } + rv = m->C_FindObjectsInit(session, search_attributes, 2); + if (rv != CKR_OK) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv)); - attributes = attributes_buffer; + rv = m->C_FindObjects(session, objects, 2, &n_objects); + if (rv != CKR_OK) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Failed to find objects: %s", sym_p11_kit_strerror(rv)); + + rv = m->C_FindObjectsFinal(session); + if (rv != CKR_OK) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv)); + + if (n_objects == 0) + return log_debug_errno(SYNTHETIC_ERRNO(ENOENT), + "Failed to find a related object with class %s", object_class_to_string(class)); + + if (n_objects > 1) + log_warning("Found multiple related objects with class %s, using the first object.", + object_class_to_string(class)); + + *ret_object = objects[0]; + return 0; +} + +#if HAVE_OPENSSL +static int ecc_convert_to_compressed( + CK_FUNCTION_LIST *m, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, + const void *uncompressed_point, + size_t uncompressed_point_size, + void **ret_compressed_point, + size_t *ret_compressed_point_size) { + + _cleanup_free_ void *ec_params_buffer = NULL; + CK_ATTRIBUTE ec_params_attr = { CKA_EC_PARAMS, NULL_PTR, 0 }; + CK_RV rv; + int r; + + rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1); + if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv)); + + if (ec_params_attr.ulValueLen != CK_UNAVAILABLE_INFORMATION) { + ec_params_buffer = malloc(ec_params_attr.ulValueLen); + if (!ec_params_buffer) + return log_oom(); + + ec_params_attr.pValue = ec_params_buffer; + rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1); + if (rv != CKR_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to retrieve CKA_EC_PARAMS from a private key: %s", sym_p11_kit_strerror(rv)); + } else { + CK_OBJECT_HANDLE public_key; + r = pkcs11_token_find_related_object(m, session, object, CKO_PUBLIC_KEY, &public_key); + if (r < 0) + return log_error_errno(r, "Failed to find a public key for compressing a EC point"); + + ec_params_attr.ulValueLen = 0; + rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1); + if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv)); + + if (ec_params_attr.ulValueLen == CK_UNAVAILABLE_INFORMATION) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "The public key does not have CKA_EC_PARAMS"); + + ec_params_buffer = malloc(ec_params_attr.ulValueLen); + if (!ec_params_buffer) + return log_oom(); + + ec_params_attr.pValue = ec_params_buffer; + rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1); + if (rv != CKR_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to retrieve CKA_EC_PARAMS from a public key: %s", sym_p11_kit_strerror(rv)); } - rv = m->C_FindObjectsInit(session, attributes, n_attributes); + _cleanup_(EC_GROUP_freep) EC_GROUP *group = NULL; + _cleanup_(EC_POINT_freep) EC_POINT *point = NULL; + _cleanup_(BN_CTX_freep) BN_CTX *bnctx = NULL; + _cleanup_free_ void *compressed_point = NULL; + size_t compressed_point_size; + + const unsigned char *ec_params_value = ec_params_attr.pValue; + group = d2i_ECPKParameters(NULL, &ec_params_value, ec_params_attr.ulValueLen); + if (!group) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode CKA_EC_PARAMS"); + + point = EC_POINT_new(group); + if (!point) + return log_oom(); + + bnctx = BN_CTX_new(); + if (!bnctx) + return log_oom(); + + if (EC_POINT_oct2point(group, point, uncompressed_point, uncompressed_point_size, bnctx) != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode an uncompressed EC point"); + + compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, NULL, 0, bnctx); + if (compressed_point_size == 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine size of a compressed EC point"); + + compressed_point = malloc(compressed_point_size); + if (!compressed_point) + return log_oom(); + + compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, compressed_point, compressed_point_size, bnctx); + if (compressed_point_size == 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert a EC point to compressed format"); + + *ret_compressed_point = TAKE_PTR(compressed_point); + *ret_compressed_point_size = compressed_point_size; + return 0; +} +#endif + +/* Since EC keys doesn't support encryption directly, we use ECDH protocol to derive shared secret here. + * We use PKCS#11 C_DeriveKey function to derive a shared secret with a private key stored in the token and + * a public key saved on enrollment. */ +static int pkcs11_token_decrypt_data_ecc( + CK_FUNCTION_LIST *m, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, + const void *encrypted_data, + size_t encrypted_data_size, + void **ret_decrypted_data, + size_t *ret_decrypted_data_size) { + + static const CK_BBOOL yes = CK_TRUE, no = CK_FALSE; + static const CK_OBJECT_CLASS shared_secret_class = CKO_SECRET_KEY; + static const CK_KEY_TYPE shared_secret_type = CKK_GENERIC_SECRET; + static const CK_ATTRIBUTE shared_secret_template[] = { + { CKA_TOKEN, (void*) &no, sizeof(no) }, + { CKA_CLASS, (void*) &shared_secret_class, sizeof(shared_secret_class) }, + { CKA_KEY_TYPE, (void*) &shared_secret_type, sizeof(shared_secret_type) }, + { CKA_SENSITIVE, (void*) &no, sizeof(no) }, + { CKA_EXTRACTABLE, (void*) &yes, sizeof(yes) } + }; + CK_ECDH1_DERIVE_PARAMS params = { + .kdf = CKD_NULL, + .pPublicData = (void*) encrypted_data, + .ulPublicDataLen = encrypted_data_size + }; + CK_MECHANISM mechanism = { + .mechanism = CKM_ECDH1_DERIVE, + .pParameter = ¶ms, + .ulParameterLen = sizeof(params) + }; + CK_OBJECT_HANDLE shared_secret_handle; + CK_SESSION_INFO session_info; + CK_MECHANISM_INFO mechanism_info; + CK_RV rv, rv2; +#if HAVE_OPENSSL + _cleanup_free_ void *compressed_point = NULL; + int r; +#endif + + rv = m->C_GetSessionInfo(session, &session_info); if (rv != CKR_OK) return log_error_errno(SYNTHETIC_ERRNO(EIO), - "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv)); + "Failed to get information about the PKCS#11 session: %s", sym_p11_kit_strerror(rv)); - rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects); - rv2 = m->C_FindObjectsFinal(session); + rv = m->C_GetMechanismInfo(session_info.slotID, CKM_ECDH1_DERIVE, &mechanism_info); if (rv != CKR_OK) return log_error_errno(SYNTHETIC_ERRNO(EIO), - "Failed to find objects: %s", sym_p11_kit_strerror(rv)); + "Failed to get information about CKM_ECDH1_DERIVE: %s", sym_p11_kit_strerror(rv)); + + if (!(mechanism_info.flags & CKF_EC_UNCOMPRESS)) { + if (mechanism_info.flags & CKF_EC_COMPRESS) { +#if HAVE_OPENSSL + log_debug("CKM_ECDH1_DERIVE accepts compressed EC points only, trying to convert."); + size_t compressed_point_size; + r = ecc_convert_to_compressed(m, session, object, encrypted_data, encrypted_data_size, &compressed_point, &compressed_point_size); + if (r < 0) + return r; + + params.pPublicData = compressed_point; + params.ulPublicDataLen = compressed_point_size; +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "CKM_ECDH1_DERIVE does not support uncompressed format of EC points"); +#endif + } else + log_debug("Both CKF_EC_UNCOMPRESS and CKF_EC_COMPRESS are false for CKM_ECDH1_DERIVE, ignoring."); + } + + rv = m->C_DeriveKey(session, &mechanism, object, (CK_ATTRIBUTE*) shared_secret_template, ELEMENTSOF(shared_secret_template), &shared_secret_handle); + if (rv != CKR_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to derive a shared secret: %s", sym_p11_kit_strerror(rv)); + + CK_ATTRIBUTE shared_secret_attr = { CKA_VALUE, NULL_PTR, 0}; + + rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1); + if (rv != CKR_OK) { + rv2 = m->C_DestroyObject(session, shared_secret_handle); + if (rv2 != CKR_OK) + log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2)); + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve shared secret length: %s", sym_p11_kit_strerror(rv)); + } + + shared_secret_attr.pValue = malloc(shared_secret_attr.ulValueLen); + if (!shared_secret_attr.pValue) + return log_oom(); + + rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1); + rv2 = m->C_DestroyObject(session, shared_secret_handle); if (rv2 != CKR_OK) - return log_error_errno(SYNTHETIC_ERRNO(EIO), - "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv)); - if (n_objects == 0) - return log_error_errno(SYNTHETIC_ERRNO(ENOENT), - "Failed to find selected private key suitable for decryption on token."); - if (n_objects > 1) - return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), - "Configured private key URI matches multiple keys, refusing."); + log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2)); - *ret_object = objects[0]; + if (rv != CKR_OK) { + erase_and_free(shared_secret_attr.pValue); + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve a shared secret: %s", sym_p11_kit_strerror(rv)); + } + + log_info("Successfully derived key with security token."); + + *ret_decrypted_data = shared_secret_attr.pValue; + *ret_decrypted_data_size = shared_secret_attr.ulValueLen; return 0; } -int pkcs11_token_decrypt_data( +static int pkcs11_token_decrypt_data_rsa( CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, @@ -737,17 +1013,6 @@ int pkcs11_token_decrypt_data( _cleanup_(erase_and_freep) CK_BYTE *dbuffer = NULL; CK_ULONG dbuffer_size = 0; CK_RV rv; - int r; - - assert(m); - assert(encrypted_data); - assert(encrypted_data_size > 0); - assert(ret_decrypted_data); - assert(ret_decrypted_data_size); - - r = dlopen_p11kit(); - if (r < 0) - return r; rv = m->C_DecryptInit(session, (CK_MECHANISM*) &mechanism, object); if (rv != CKR_OK) @@ -780,6 +1045,42 @@ int pkcs11_token_decrypt_data( return 0; } +int pkcs11_token_decrypt_data( + CK_FUNCTION_LIST *m, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, + const void *encrypted_data, + size_t encrypted_data_size, + void **ret_decrypted_data, + size_t *ret_decrypted_data_size) { + + CK_KEY_TYPE key_type; + CK_ATTRIBUTE key_type_template = { CKA_KEY_TYPE, &key_type, sizeof(key_type) }; + CK_RV rv; + + assert(m); + assert(encrypted_data); + assert(encrypted_data_size > 0); + assert(ret_decrypted_data); + assert(ret_decrypted_data_size); + + rv = m->C_GetAttributeValue(session, object, &key_type_template, 1); + if (rv != CKR_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve private key type"); + + switch (key_type) { + + case CKK_RSA: + return pkcs11_token_decrypt_data_rsa(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size); + + case CKK_EC: + return pkcs11_token_decrypt_data_ecc(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size); + + default: + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported private key type: %lu", key_type); + } +} + int pkcs11_token_acquire_rng( CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session) { diff --git a/src/shared/pkcs11-util.h b/src/shared/pkcs11-util.h index 5bc23c14c4c..2ff6997823e 100644 --- a/src/shared/pkcs11-util.h +++ b/src/shared/pkcs11-util.h @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#if HAVE_OPENSSL +# include +#endif #include #if HAVE_P11KIT @@ -10,7 +13,6 @@ #include "ask-password-api.h" #include "macro.h" -#include "openssl-util.h" #include "time-util.h" bool pkcs11_uri_valid(const char *uri); @@ -50,6 +52,7 @@ 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, AskPasswordFlags ask_password_flags, bool headless, char **ret_used_pin); +int pkcs11_token_find_related_object(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE prototype, CK_OBJECT_CLASS class, CK_OBJECT_HANDLE *ret_object); int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object); #if HAVE_OPENSSL int pkcs11_token_read_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, X509 **ret_cert); diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 4950932a967..03660ad4178 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -18,14 +18,26 @@ #include "alloc-util.h" #include "errno-util.h" +#include "extract-word.h" #include "fd-util.h" +#include "io-util.h" #include "log.h" #include "macro.h" #include "ptyfwd.h" #include "stat-util.h" +#include "strv.h" #include "terminal-util.h" #include "time-util.h" +typedef enum AnsiColorState { + ANSI_COLOR_STATE_TEXT, + ANSI_COLOR_STATE_ESC, + ANSI_COLOR_STATE_CSI_SEQUENCE, + ANSI_COLOR_STATE_NEWLINE, + _ANSI_COLOR_STATE_MAX, + _ANSI_COLOR_STATE_INVALID = -EINVAL, +} AnsiColorState; + struct PTYForward { sd_event *event; @@ -66,7 +78,8 @@ struct PTYForward { bool last_char_set:1; char last_char; - char in_buffer[LINE_MAX], out_buffer[LINE_MAX]; + char in_buffer[LINE_MAX], *out_buffer; + size_t out_buffer_size; size_t in_buffer_full, out_buffer_full; usec_t escape_timestamp; @@ -74,6 +87,10 @@ struct PTYForward { PTYForwardHandler handler; void *userdata; + + char *background_color; + AnsiColorState ansi_color_state; + char *csi_sequence; }; #define ESCAPE_USEC (1*USEC_PER_SEC) @@ -96,6 +113,10 @@ static void pty_forward_disconnect(PTYForward *f) { /* STDIN/STDOUT should not be non-blocking normally, so let's reset it */ (void) fd_nonblock(f->output_fd, false); + + if (colors_enabled()) + (void) loop_write(f->output_fd, ANSI_NORMAL ANSI_ERASE_TO_END_OF_LINE, SIZE_MAX); + if (f->close_output_fd) f->output_fd = safe_close(f->output_fd); } @@ -110,6 +131,14 @@ static void pty_forward_disconnect(PTYForward *f) { } f->saved_stdout = f->saved_stdin = false; + + f->out_buffer = mfree(f->out_buffer); + f->out_buffer_size = 0; + f->out_buffer_full = 0; + f->in_buffer_full = 0; + + f->csi_sequence = mfree(f->csi_sequence); + f->ansi_color_state = _ANSI_COLOR_STATE_INVALID; } static int pty_forward_done(PTYForward *f, int rcode) { @@ -197,11 +226,244 @@ static bool drained(PTYForward *f) { return true; } +static char *background_color_sequence(PTYForward *f) { + assert(f); + assert(f->background_color); + + /* This sets the background color to the desired one, and erase the rest of the line with it */ + + return strjoin("\x1B[", f->background_color, "m", ANSI_ERASE_TO_END_OF_LINE); +} + +static int insert_string(PTYForward *f, size_t offset, const char *s) { + assert(f); + assert(offset <= f->out_buffer_full); + assert(s); + + size_t l = strlen(s); + assert(l <= INT_MAX); /* Make sure we can still return this */ + + void *p = realloc(f->out_buffer, MAX(f->out_buffer_full + l, (size_t) LINE_MAX)); + if (!p) + return -ENOMEM; + + f->out_buffer = p; + f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer); + + memmove(f->out_buffer + offset + l, f->out_buffer + offset, f->out_buffer_full - offset); + memcpy(f->out_buffer + offset, s, l); + f->out_buffer_full += l; + + return (int) l; +} + +static int insert_erase_newline(PTYForward *f, size_t offset) { + _cleanup_free_ char *s = NULL; + + assert(f); + assert(f->background_color); + + s = background_color_sequence(f); + if (!s) + return -ENOMEM; + + return insert_string(f, offset, s); +} + +static int is_csi_background_reset_sequence(const char *seq) { + enum { + COLOR_TOKEN_NO, + COLOR_TOKEN_START, + COLOR_TOKEN_8BIT, + COLOR_TOKEN_24BIT_R, + COLOR_TOKEN_24BIT_G, + COLOR_TOKEN_24BIT_B, + } token_state = COLOR_TOKEN_NO; + + bool b = false; + int r; + + assert(seq); + + /* This parses CSI "m" sequences, and determines if they reset the background color. If so returns + * 1. This can then be used to insert another sequence that sets the color to the desired + * replacement. */ + + for (;;) { + _cleanup_free_ char *token = NULL; + + r = extract_first_word(&seq, &token, ";", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return r; + if (r == 0) + break; + + switch (token_state) { + + case COLOR_TOKEN_NO: + + if (STR_IN_SET(token, "", "0", "00", "49")) + b = true; /* These tokens set the background back to normal */ + else if (STR_IN_SET(token, "40", "41", "42", "43", "44", "45", "46", "47", "48")) + b = false; /* And these tokens set them to something other than normal */ + + if (STR_IN_SET(token, "38", "48", "58")) + token_state = COLOR_TOKEN_START; /* These tokens mean an 8bit or 24bit color will follow */ + break; + + case COLOR_TOKEN_START: + + if (STR_IN_SET(token, "5", "05")) + token_state = COLOR_TOKEN_8BIT; /* 8bit color */ + else if (STR_IN_SET(token, "2", "02")) + token_state = COLOR_TOKEN_24BIT_R; /* 24bit color */ + else + token_state = COLOR_TOKEN_NO; /* something weird? */ + break; + + case COLOR_TOKEN_24BIT_R: + token_state = COLOR_TOKEN_24BIT_G; + break; + + case COLOR_TOKEN_24BIT_G: + token_state = COLOR_TOKEN_24BIT_B; + break; + + case COLOR_TOKEN_8BIT: + case COLOR_TOKEN_24BIT_B: + token_state = COLOR_TOKEN_NO; + break; + } + } + + return b; +} + +static int insert_background_fix(PTYForward *f, size_t offset) { + assert(f); + assert(f->background_color); + + if (!is_csi_background_reset_sequence(strempty(f->csi_sequence))) + return 0; + + _cleanup_free_ char *s = NULL; + s = strjoin(";", f->background_color); + if (!s) + return -ENOMEM; + + return insert_string(f, offset, s); +} + +static int pty_forward_ansi_process(PTYForward *f, size_t offset) { + int r; + + assert(f); + assert(offset <= f->out_buffer_full); + + if (!f->background_color) + return 0; + + for (size_t i = offset; i < f->out_buffer_full; i++) { + char c = f->out_buffer[i]; + + switch (f->ansi_color_state) { + + case ANSI_COLOR_STATE_TEXT: + if (c == '\n') + f->ansi_color_state = ANSI_COLOR_STATE_NEWLINE; + if (c == 0x1B) /* ESC */ + f->ansi_color_state = ANSI_COLOR_STATE_ESC; + break; + + case ANSI_COLOR_STATE_NEWLINE: { + /* Immediately after a newline insert an ANSI sequence to erase the line with a background color */ + + r = insert_erase_newline(f, i); + if (r < 0) + return r; + + i += r; + + f->ansi_color_state = ANSI_COLOR_STATE_TEXT; + break; + } + + case ANSI_COLOR_STATE_ESC: { + + if (c == '[') + f->ansi_color_state = ANSI_COLOR_STATE_CSI_SEQUENCE; + else + f->ansi_color_state = ANSI_COLOR_STATE_TEXT; + + break; + } + + case ANSI_COLOR_STATE_CSI_SEQUENCE: { + + if (c >= 0x20 && c <= 0x3F) { + /* If this is a "parameter" or "intermediary" byte (i.e. ranges 0x20…0x2F and + * 0x30…0x3F) then we are still in the CSI sequence */ + + if (strlen_ptr(f->csi_sequence) >= 64) { + /* Safety check: lets not accept unbounded CSI sequences */ + + f->csi_sequence = mfree(f->csi_sequence); + f->ansi_color_state = ANSI_COLOR_STATE_TEXT; + } else if (!strextend(&f->csi_sequence, CHAR_TO_STR(c))) + return -ENOMEM; + } else { + /* Otherwise, the CSI sequence is over */ + + if (c == 'm') { + /* This is an "SGR" (Select Graphic Rendition) sequence. Patch in our background color. */ + r = insert_background_fix(f, i); + if (r < 0) + return r; + + i += r; + } + + f->csi_sequence = mfree(f->csi_sequence); + f->ansi_color_state = ANSI_COLOR_STATE_TEXT; + } + + break; + } + + default: + assert_not_reached(); + } + } + + return 0; +} + static int shovel(PTYForward *f) { ssize_t k; + int r; assert(f); + if (f->out_buffer_size == 0 && f->background_color) { + /* Erase the first line when we start */ + f->out_buffer = background_color_sequence(f); + if (!f->out_buffer) + return pty_forward_done(f, log_oom()); + + f->out_buffer_full = strlen(f->out_buffer); + f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer); + } + + if (f->out_buffer_size < LINE_MAX) { + /* Make sure we always have room for at least one "line" */ + void *p = realloc(f->out_buffer, LINE_MAX); + if (!p) + return pty_forward_done(f, log_oom()); + + f->out_buffer = p; + f->out_buffer_size = MALLOC_SIZEOF_SAFE(p); + } + while ((f->stdin_readable && f->in_buffer_full <= 0) || (f->master_writable && f->in_buffer_full > 0) || (f->master_readable && f->out_buffer_full <= 0) || @@ -262,9 +524,9 @@ static int shovel(PTYForward *f) { } } - if (f->master_readable && f->out_buffer_full < LINE_MAX) { + if (f->master_readable && f->out_buffer_full < MIN(f->out_buffer_size, (size_t) LINE_MAX)) { - k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full); + k = read(f->master, f->out_buffer + f->out_buffer_full, f->out_buffer_size - f->out_buffer_full); if (k < 0) { /* Note that EIO on the master device might be caused by vhangup() or @@ -284,7 +546,12 @@ static int shovel(PTYForward *f) { } } else { f->read_from_master = true; + size_t scan_index = f->out_buffer_full; f->out_buffer_full += (size_t) k; + + r = pty_forward_ansi_process(f, scan_index); + if (r < 0) + return pty_forward_done(f, log_error_errno(r, "Failed to scan for ANSI sequences: %m")); } } @@ -547,7 +814,10 @@ int pty_forward_new( } PTYForward *pty_forward_free(PTYForward *f) { + if (!f) + return NULL; pty_forward_disconnect(f); + free(f->background_color); return mfree(f); } @@ -682,3 +952,9 @@ int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height) return 0; } + +int pty_forward_set_background_color(PTYForward *f, const char *color) { + assert(f); + + return free_and_strdup(&f->background_color, color); +} diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h index f0ae6e949d0..bae8d3591e4 100644 --- a/src/shared/ptyfwd.h +++ b/src/shared/ptyfwd.h @@ -39,4 +39,6 @@ int pty_forward_set_priority(PTYForward *f, int64_t priority); int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height); +int pty_forward_set_background_color(PTYForward *f, const char *color); + DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free); diff --git a/src/shared/specifier.c b/src/shared/specifier.c index e5a1f94f2a2..a56b8365fb4 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -78,8 +78,7 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[ if (!GREEDY_REALLOC(result, j + k + l + 1)) return -ENOMEM; - memcpy(result + j, w, k); - t = result + j + k; + t = mempcpy(result + j, w, k); } else if (strchr(POSSIBLE_SPECIFIERS, *f)) /* Oops, an unknown specifier. */ return -EBADSLT; diff --git a/src/shared/tests.c b/src/shared/tests.c index 3882a180c4a..195be70b039 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -21,6 +21,7 @@ #include "fd-util.h" #include "fs-util.h" #include "log.h" +#include "mountpoint-util.h" #include "namespace-util.h" #include "path-util.h" #include "process-util.h" @@ -249,7 +250,7 @@ static int allocate_scope(void) { if (r < 0) return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, false, NULL); + r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL); if (r < 0) return r; @@ -266,7 +267,7 @@ static int enter_cgroup(char **ret_cgroup, bool enter_subroot) { log_warning_errno(r, "Couldn't allocate a scope unit for this test, proceeding without."); r = cg_pid_get_path(NULL, 0, &cgroup_root); - if (r == -ENOMEDIUM) + if (IN_SET(r, -ENOMEDIUM, -ENOENT)) return log_warning_errno(r, "cg_pid_get_path(NULL, 0, ...) failed: %m"); assert(r >= 0); diff --git a/src/shared/varlink-io.systemd.Credentials.c b/src/shared/varlink-io.systemd.Credentials.c new file mode 100644 index 00000000000..b827337eedf --- /dev/null +++ b/src/shared/varlink-io.systemd.Credentials.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "varlink-io.systemd.Credentials.h" + +static VARLINK_DEFINE_METHOD( + Encrypt, + VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_DEFINE_INPUT(text, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_DEFINE_INPUT(data, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE), + VARLINK_DEFINE_INPUT(notAfter, VARLINK_INT, VARLINK_NULLABLE), + VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE), + VARLINK_DEFINE_OUTPUT(blob, VARLINK_STRING, 0)); + +static VARLINK_DEFINE_METHOD( + Decrypt, + VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_DEFINE_INPUT(blob, VARLINK_STRING, 0), + VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE), + VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE), + VARLINK_DEFINE_OUTPUT(data, VARLINK_STRING, 0)); + +static VARLINK_DEFINE_ERROR(BadFormat); +static VARLINK_DEFINE_ERROR(NameMismatch); +static VARLINK_DEFINE_ERROR(TimeMismatch); + +VARLINK_DEFINE_INTERFACE( + io_systemd_Credentials, + "io.systemd.Credentials", + &vl_method_Encrypt, + &vl_method_Decrypt, + &vl_error_BadFormat, + &vl_error_NameMismatch, + &vl_error_TimeMismatch); diff --git a/src/shared/varlink-io.systemd.Credentials.h b/src/shared/varlink-io.systemd.Credentials.h new file mode 100644 index 00000000000..c0ecc3d840d --- /dev/null +++ b/src/shared/varlink-io.systemd.Credentials.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "varlink-idl.h" + +extern const VarlinkInterface vl_interface_io_systemd_Credentials; diff --git a/src/shared/varlink.c b/src/shared/varlink.c index 2ba942f30e2..12a27fcb8df 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -1006,7 +1006,7 @@ static int varlink_dispatch_local_error(Varlink *v, const char *error) { r = v->reply_callback(v, NULL, error, VARLINK_REPLY_ERROR|VARLINK_REPLY_LOCAL, v->userdata); if (r < 0) - log_debug_errno(r, "Reply callback returned error, ignoring: %m"); + varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m"); return 1; } @@ -1038,12 +1038,25 @@ static int varlink_dispatch_disconnect(Varlink *v) { } static int varlink_sanitize_parameters(JsonVariant **v) { + int r; + assert(v); /* Varlink always wants a parameters list, hence make one if the caller doesn't want any */ if (!*v) return json_variant_new_object(v, NULL, 0); - else if (!json_variant_is_object(*v)) + if (json_variant_is_null(*v)) { + JsonVariant *empty; + + r = json_variant_new_object(&empty, NULL, 0); + if (r < 0) + return r; + + json_variant_unref(*v); + *v = empty; + return 0; + } + if (!json_variant_is_object(*v)) return -EINVAL; return 0; @@ -1083,7 +1096,7 @@ static int varlink_dispatch_reply(Varlink *v) { } else if (streq(k, "parameters")) { if (parameters) goto invalid; - if (!json_variant_is_object(e)) + if (!json_variant_is_object(e) && !json_variant_is_null(e)) goto invalid; parameters = json_variant_ref(e); @@ -1119,7 +1132,7 @@ static int varlink_dispatch_reply(Varlink *v) { if (v->reply_callback) { r = v->reply_callback(v, parameters, error, flags, v->userdata); if (r < 0) - log_debug_errno(r, "Reply callback returned error, ignoring: %m"); + varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m"); } varlink_clear_current(v); @@ -1163,9 +1176,7 @@ static int generic_method_get_info( assert(link); if (json_variant_elements(parameters) != 0) - return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, - JSON_BUILD_OBJECT( - JSON_BUILD_PAIR_VARIANT("parameter", json_variant_by_index(parameters, 0)))); + return varlink_error_invalid_parameter(link, parameters); product = strjoin("systemd (", program_invocation_short_name, ")"); if (!product) @@ -1256,7 +1267,7 @@ static int varlink_dispatch_method(Varlink *v) { } else if (streq(k, "parameters")) { if (parameters) goto invalid; - if (!json_variant_is_object(e)) + if (!json_variant_is_object(e) && !json_variant_is_null(e)) goto invalid; parameters = json_variant_ref(e); @@ -1314,16 +1325,16 @@ static int varlink_dispatch_method(Varlink *v) { v->current_method = hashmap_get(v->server->symbols, method); if (!v->current_method) - log_debug("No interface description defined for method '%s', not validating.", method); + varlink_log(v, "No interface description defined for method '%s', not validating.", method); else { const char *bad_field; r = varlink_idl_validate_method_call(v->current_method, parameters, &bad_field); if (r < 0) { - log_debug_errno(r, "Parameters for method %s() didn't pass validation on field '%s': %m", method, strna(bad_field)); + varlink_log_errno(v, r, "Parameters for method %s() didn't pass validation on field '%s': %m", method, strna(bad_field)); - if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) { - r = varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", bad_field))); + if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) { + r = varlink_error_invalid_parameter_name(v, bad_field); if (r < 0) return r; } @@ -1334,24 +1345,21 @@ static int varlink_dispatch_method(Varlink *v) { if (!invalid) { r = callback(v, parameters, flags, v->userdata); if (r < 0) { - log_debug_errno(r, "Callback for %s returned error: %m", method); + varlink_log_errno(v, r, "Callback for %s returned error: %m", method); /* We got an error back from the callback. Propagate it to the client if the method call remains unanswered. */ - if (v->state == VARLINK_PROCESSED_METHOD) - r = 0; /* already processed */ - else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) { + if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) { r = varlink_error_errno(v, r); if (r < 0) return r; } } } - } else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) { + } else if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) { r = varlink_errorb(v, VARLINK_ERROR_METHOD_NOT_FOUND, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method)))); if (r < 0) return r; - } else - r = 0; + } switch (v->state) { @@ -1373,7 +1381,7 @@ static int varlink_dispatch_method(Varlink *v) { assert_not_reached(); } - return r; + return 1; invalid: r = -EINVAL; @@ -2259,7 +2267,7 @@ int varlink_reply(Varlink *v, JsonVariant *parameters) { r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field); if (r < 0) - log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field)); + varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field)); } r = varlink_enqueue_json(v, m); @@ -2330,13 +2338,13 @@ int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters) { VarlinkSymbol *symbol = hashmap_get(v->server->symbols, error_id); if (!symbol) - log_debug("No interface description defined for error '%s', not validating.", error_id); + varlink_log(v, "No interface description defined for error '%s', not validating.", error_id); else { const char *bad_field = NULL; r = varlink_idl_validate_error(symbol, parameters, &bad_field); if (r < 0) - log_debug_errno(r, "Parameters for error %s didn't pass validation on field '%s', ignoring: %m", error_id, strna(bad_field)); + varlink_log_errno(v, r, "Parameters for error %s didn't pass validation on field '%s', ignoring: %m", error_id, strna(bad_field)); } r = varlink_enqueue_json(v, m); @@ -2412,6 +2420,13 @@ int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters) { return -EINVAL; } +int varlink_error_invalid_parameter_name(Varlink *v, const char *name) { + return varlink_errorb( + v, + VARLINK_ERROR_INVALID_PARAMETER, + JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameter", JSON_BUILD_STRING(name)))); +} + int varlink_error_errno(Varlink *v, int error) { return varlink_errorb( v, @@ -2451,7 +2466,7 @@ int varlink_notify(Varlink *v, JsonVariant *parameters) { r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field); if (r < 0) - log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field)); + varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field)); } r = varlink_enqueue_json(v, m); @@ -2491,8 +2506,7 @@ int varlink_dispatch(Varlink *v, JsonVariant *parameters, const JsonDispatch tab r = json_dispatch_full(parameters, table, /* bad= */ NULL, /* flags= */ 0, userdata, &bad_field); if (r < 0) { if (bad_field) - return varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER, - JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameter", JSON_BUILD_STRING(bad_field)))); + return varlink_error_invalid_parameter_name(v, bad_field); return r; } @@ -3008,9 +3022,11 @@ static int count_connection(VarlinkServer *server, const struct ucred *ucred) { server->n_connections++; if (FLAGS_SET(server->flags, VARLINK_SERVER_ACCOUNT_UID)) { + assert(uid_is_valid(ucred->uid)); + r = hashmap_ensure_allocated(&server->by_uid, NULL); if (r < 0) - return log_debug_errno(r, "Failed to allocate UID hash table: %m"); + return varlink_server_log_errno(server, r, "Failed to allocate UID hash table: %m"); c = PTR_TO_UINT(hashmap_get(server->by_uid, UID_TO_PTR(ucred->uid))); @@ -3019,7 +3035,7 @@ static int count_connection(VarlinkServer *server, const struct ucred *ucred) { r = hashmap_replace(server->by_uid, UID_TO_PTR(ucred->uid), UINT_TO_PTR(c + 1)); if (r < 0) - return log_debug_errno(r, "Failed to increment counter in UID hash table: %m"); + return varlink_server_log_errno(server, r, "Failed to increment counter in UID hash table: %m"); } return 0; @@ -3459,7 +3475,7 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth if (varlink_symbol_in_interface(method, "org.varlink.service") || varlink_symbol_in_interface(method, "io.systemd")) - return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Cannot bind server to '%s'.", method); + return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EEXIST), "Cannot bind server to '%s'.", method); m = strdup(method); if (!m) @@ -3469,7 +3485,7 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth if (r == -ENOMEM) return log_oom_debug(); if (r < 0) - return log_debug_errno(r, "Failed to register callback: %m"); + return varlink_server_log_errno(s, r, "Failed to register callback: %m"); if (r > 0) TAKE_PTR(m); @@ -3506,7 +3522,7 @@ int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect callback) { assert_return(s, -EINVAL); if (callback && s->connect_callback && callback != s->connect_callback) - return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "A different callback was already set."); + return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBUSY), "A different callback was already set."); s->connect_callback = callback; return 0; @@ -3516,7 +3532,7 @@ int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect callback) assert_return(s, -EINVAL); if (callback && s->disconnect_callback && callback != s->disconnect_callback) - return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "A different callback was already set."); + return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBUSY), "A different callback was already set."); s->disconnect_callback = callback; return 0; @@ -3530,7 +3546,7 @@ int varlink_server_add_interface(VarlinkServer *s, const VarlinkInterface *inter assert_return(interface->name, -EINVAL); if (hashmap_contains(s->interfaces, interface->name)) - return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate registration of interface '%s'.", interface->name); + return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EEXIST), "Duplicate registration of interface '%s'.", interface->name); r = hashmap_ensure_put(&s->interfaces, &string_hash_ops, interface->name, (void*) interface); if (r < 0) @@ -3683,11 +3699,11 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f return log_oom_debug(); if (v[n] != ' ') - return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EINVAL), "Failed to deserialize VarlinkServerSocket: %s: %m", value); v = startswith(v + n + 1, "varlink-server-socket-fd="); if (!v) - return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EINVAL), "Failed to deserialize VarlinkServerSocket fd %s: %m", value); n = strcspn(v, " "); @@ -3695,9 +3711,9 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f fd = parse_fd(buf); if (fd < 0) - return log_debug_errno(fd, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf); + return varlink_server_log_errno(s, fd, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf); if (!fdset_contains(fds, fd)) - return log_debug_errno(SYNTHETIC_ERRNO(EBADF), + return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBADF), "VarlinkServerSocket varlink-server-socket-fd= has unknown fd %d: %m", fd); ss = new(VarlinkServerSocket, 1); @@ -3712,7 +3728,7 @@ int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *f r = varlink_server_add_socket_event_source(s, ss, SD_EVENT_PRIORITY_NORMAL); if (r < 0) - return log_debug_errno(r, "Failed to add VarlinkServerSocket event source to the event loop: %m"); + return varlink_server_log_errno(s, r, "Failed to add VarlinkServerSocket event source to the event loop: %m"); LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss)); return 0; diff --git a/src/shared/varlink.h b/src/shared/varlink.h index 6ec708aba20..c60f695be78 100644 --- a/src/shared/varlink.h +++ b/src/shared/varlink.h @@ -109,6 +109,7 @@ int varlink_replyb(Varlink *v, ...); int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters); int varlink_errorb(Varlink *v, const char *error_id, ...); int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters); +int varlink_error_invalid_parameter_name(Varlink *v, const char *name); int varlink_error_errno(Varlink *v, int error); /* Enqueue a "more" reply */ @@ -218,7 +219,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref); #define VARLINK_ERROR_METHOD_NOT_FOUND "org.varlink.service.MethodNotFound" #define VARLINK_ERROR_METHOD_NOT_IMPLEMENTED "org.varlink.service.MethodNotImplemented" #define VARLINK_ERROR_INVALID_PARAMETER "org.varlink.service.InvalidParameter" - -/* These are errors we came up with and squatted the namespace with */ #define VARLINK_ERROR_PERMISSION_DENIED "org.varlink.service.PermissionDenied" #define VARLINK_ERROR_EXPECTED_MORE "org.varlink.service.ExpectedMore" diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index b976b7d8cf6..b709078afed 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -387,6 +387,13 @@ int main(int argc, char *argv[]) { goto error; } + /* This is primarily useful when running systemd in a VM, as it provides the user running the VM with + * a mechanism to pick up systemd's exit status in the VM. Note that we execute this as early as + * possible since otherwise we might shut down the VM before the AF_VSOCK buffers have been flushed. + * While this doesn't guarantee the message will arrive, in practice we do enough work after this + * that the message should always arrive on the host */ + (void) sd_notifyf(0, "EXIT_STATUS=%i", arg_exit_code); + (void) cg_get_root_path(&cgroup); bool in_container = detect_container() > 0; @@ -582,10 +589,6 @@ int main(int argc, char *argv[]) { if (!in_container) sync_with_progress(); - /* This is primarily useful when running systemd in a VM, as it provides the user running the VM with - * a mechanism to pick up systemd's exit status in the VM. */ - (void) sd_notifyf(0, "EXIT_STATUS=%i", arg_exit_code); - if (streq(arg_verb, "exit")) { if (in_container) { log_info("Exiting container."); diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 8dc515e4d52..33cb8c19635 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -342,7 +342,7 @@ static int parse_image_class_parameter(Varlink *link, const char *value, ImageCl c = image_class_from_string(value); if (!IN_SET(c, IMAGE_SYSEXT, IMAGE_CONFEXT)) - return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "class"))); + return varlink_error_invalid_parameter_name(link, "class"); if (hierarchies) { r = parse_env_extension_hierarchies(&h, image_class_info[c].name_env); diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c index 2cf746c5a60..d486b406da5 100644 --- a/src/systemctl/systemctl-start-special.c +++ b/src/systemctl/systemctl-start-special.c @@ -121,8 +121,7 @@ static int set_exit_code(uint8_t code) { } int verb_start_special(int argc, char *argv[], void *userdata) { - bool termination_action; /* An action that terminates the manager, can be performed also by - * signal. */ + bool termination_action; /* An action that terminates the system, can be performed also by signal. */ enum action a; int r; @@ -140,17 +139,21 @@ int verb_start_special(int argc, char *argv[], void *userdata) { return r; } - r = prepare_firmware_setup(); - if (r < 0) - return r; + termination_action = IN_SET(a, ACTION_HALT, ACTION_POWEROFF, ACTION_REBOOT); - r = prepare_boot_loader_menu(); - if (r < 0) - return r; + if (termination_action) { + r = prepare_firmware_setup(); + if (r < 0) + return r; - r = prepare_boot_loader_entry(); - if (r < 0) - return r; + r = prepare_boot_loader_menu(); + if (r < 0) + return r; + + r = prepare_boot_loader_entry(); + if (r < 0) + return r; + } if (a == ACTION_REBOOT) { if (arg_reboot_argument) { @@ -181,10 +184,6 @@ int verb_start_special(int argc, char *argv[], void *userdata) { return r; } - termination_action = IN_SET(a, - ACTION_HALT, - ACTION_POWEROFF, - ACTION_REBOOT); if (termination_action && arg_force >= 2) return halt_now(a); diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c index ae7e25eedb6..c844f7e1e7f 100644 --- a/src/systemctl/systemctl-start-unit.c +++ b/src/systemctl/systemctl-start-unit.c @@ -388,8 +388,11 @@ int verb_start(int argc, char *argv[], void *userdata) { if (!arg_no_block) { const char* extra_args[4]; + WaitJobsFlags flags = 0; - r = bus_wait_for_jobs(w, arg_quiet, make_extra_args(extra_args)); + SET_FLAG(flags, BUS_WAIT_JOBS_LOG_ERROR, !arg_quiet); + SET_FLAG(flags, BUS_WAIT_JOBS_LOG_SUCCESS, arg_show_transaction); + r = bus_wait_for_jobs(w, flags, make_extra_args(extra_args)); if (r < 0) return r; diff --git a/src/test/meson.build b/src/test/meson.build index a59461acc44..aec125d483a 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -57,6 +57,7 @@ simple_tests += files( 'test-cgroup.c', 'test-chase.c', 'test-clock.c', + 'test-color-util.c', 'test-compare-operator.c', 'test-condition.c', 'test-conf-files.c', @@ -578,12 +579,18 @@ executables += [ }, test_template + { 'sources' : files('../libsystemd/sd-device/test-sd-device-thread.c'), - 'link_with' : libsystemd, + 'link_with' : [ + libbasic, + libsystemd, + ], 'dependencies' : threads, }, test_template + { 'sources' : files('../libudev/test-udev-device-thread.c'), - 'link_with' : libudev, + 'link_with' : [ + libbasic, + libudev, + ], 'dependencies' : threads, }, test_template + { diff --git a/src/test/test-bootspec.c b/src/test/test-bootspec.c index 18611fc051f..86d7702bf8a 100644 --- a/src/test/test-bootspec.c +++ b/src/test/test-bootspec.c @@ -188,23 +188,22 @@ TEST_RET(bootspec_boot_config_find_entry) { assert_se(boot_config_load(&config, d, NULL) >= 0); assert_se(config.n_entries == 2); - // Test finding the first entry + /* Test finding the first entry */ BootEntry *entry = boot_config_find_entry(&config, "a-10.conf"); assert_se(entry && streq(entry->id, "a-10.conf")); - // Test finding the second entry + /* Test finding the second entry */ entry = boot_config_find_entry(&config, "a-05.conf"); assert_se(entry && streq(entry->id, "a-05.conf")); - // Test finding a non-existent entry + /* Test finding a non-existent entry */ entry = boot_config_find_entry(&config, "nonexistent.conf"); assert_se(entry == NULL); - // Test case-insensitivity + /* Test case-insensitivity */ entry = boot_config_find_entry(&config, "A-10.CONF"); assert_se(entry && streq(entry->id, "a-10.conf")); - return 0; } diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index 51f52d9a4ef..cb5a981dc3c 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -362,7 +362,7 @@ TEST(cg_tests) { int all, hybrid, systemd, r; r = cg_unified(); - if (r == -ENOMEDIUM) { + if (IN_SET(r, -ENOENT, -ENOMEDIUM)) { log_tests_skipped("cgroup not mounted"); return; } @@ -395,7 +395,7 @@ TEST(cg_get_keyed_attribute) { int i, r; r = cg_get_keyed_attribute("cpu", "/init.scope", "no_such_file", STRV_MAKE("no_such_attr"), &val); - if (r == -ENOMEDIUM || ERRNO_IS_PRIVILEGE(r)) { + if (IN_SET(r, -ENOMEDIUM, -ENOENT) || ERRNO_IS_PRIVILEGE(r)) { log_info_errno(r, "Skipping most of %s, /sys/fs/cgroup not accessible: %m", __func__); return; } diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c index 0fbd6351a4a..9b174d708b5 100644 --- a/src/test/test-cgroup.c +++ b/src/test/test-cgroup.c @@ -44,8 +44,8 @@ TEST(cg_create) { int r; r = cg_unified_cached(false); - if (r == -ENOMEDIUM) { - log_tests_skipped("cgroup not mounted"); + if (IN_SET(r, -ENOMEDIUM, -ENOENT)) { + log_tests_skipped("cgroupfs is not mounted"); return; } assert_se(r >= 0); diff --git a/src/test/test-color-util.c b/src/test/test-color-util.c new file mode 100644 index 00000000000..3d00d8719d8 --- /dev/null +++ b/src/test/test-color-util.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "color-util.h" +#include "tests.h" + +TEST(hsv_to_rgb) { + uint8_t r, g, b; + + hsv_to_rgb(0, 0, 0, &r, &g, &b); + assert(r == 0 && g == 0 && b == 0); + + hsv_to_rgb(60, 0, 0, &r, &g, &b); + assert(r == 0 && g == 0 && b == 0); + + hsv_to_rgb(0, 0, 100, &r, &g, &b); + assert(r == 255 && g == 255 && b == 255); + + hsv_to_rgb(0, 100, 100, &r, &g, &b); + assert(r == 255 && g == 0 && b == 0); + + hsv_to_rgb(120, 100, 100, &r, &g, &b); + assert(r == 0 && g == 255 && b == 0); + + hsv_to_rgb(240, 100, 100, &r, &g, &b); + assert(r == 0 && g == 0 && b == 255); + + hsv_to_rgb(311, 52, 62, &r, &g, &b); + assert(r == 158 && g == 75 && b == 143); +} + +TEST(rgb_to_hsv) { + + double h, s, v; + rgb_to_hsv(0, 0, 0, &h, &s, &v); + assert(s <= 0); + assert(v <= 0); + + rgb_to_hsv(1, 1, 1, &h, &s, &v); + assert(s <= 0); + assert(v >= 100); + + rgb_to_hsv(1, 0, 0, &h, &s, &v); + assert(h >= 359 || h <= 1); + assert(s >= 100); + assert(v >= 100); + + rgb_to_hsv(0, 1, 0, &h, &s, &v); + assert(h >= 119 && h <= 121); + assert(s >= 100); + assert(v >= 100); + + rgb_to_hsv(0, 0, 1, &h, &s, &v); + assert(h >= 239 && h <= 241); + assert(s >= 100); + assert(v >= 100); + + rgb_to_hsv(0.5, 0.6, 0.7, &h, &s, &v); + assert(h >= 209 && h <= 211); + assert(s >= 28 && s <= 31); + assert(v >= 69 && v <= 71); +} + +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-condition.c b/src/test/test-condition.c index bb987613e15..66208463606 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -139,8 +139,8 @@ TEST(condition_test_control_group_hierarchy) { int r; r = cg_unified(); - if (r == -ENOMEDIUM) { - log_tests_skipped("cgroup not mounted"); + if (IN_SET(r, -ENOMEDIUM, -ENOENT)) { + log_tests_skipped("cgroupfs is not mounted"); return; } assert_se(r >= 0); @@ -163,8 +163,8 @@ TEST(condition_test_control_group_controller) { int r; r = cg_unified(); - if (r == -ENOMEDIUM) { - log_tests_skipped("cgroup not mounted"); + if (IN_SET(r, -ENOMEDIUM, -ENOENT)) { + log_tests_skipped("cgroupfs is not mounted"); return; } assert_se(r >= 0); diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c index dffbad6327b..c267c2e3418 100644 --- a/src/test/test-env-util.c +++ b/src/test/test-env-util.c @@ -133,6 +133,27 @@ TEST(strv_env_assign) { assert_se(streq(a[0], "a=A")); } +TEST(strv_env_assignf) { + _cleanup_strv_free_ char **a = NULL; + + assert_se(strv_env_assignf(&a, "a", "a") > 0); + assert_se(strv_env_assignf(&a, "a", "%c", 'a') == 0); + + assert_se(strv_env_assignf(&a, "c", "xxx%iyyy", 5) > 0); + assert_se(strv_length(a) == 2); + assert_se(strv_equal(a, STRV_MAKE("a=a", "c=xxx5yyy"))); + assert_se(strv_env_assignf(&a, "c", NULL) == 0); + + assert_se(strv_env_assignf(&a, "b", "b") > 0); + assert_se(strv_env_assignf(&a, "a", "A") == 0); + assert_se(strv_env_assignf(&a, "b", NULL) == 0); + + assert_se(strv_env_assignf(&a, "a=", "B") == -EINVAL); + + assert_se(strv_length(a) == 1); + assert_se(streq(a[0], "a=A")); +} + TEST(strv_env_assign_many) { _cleanup_strv_free_ char **a = NULL; diff --git a/src/test/test-mempress.c b/src/test/test-mempress.c index 26ce4cee797..23d09813d91 100644 --- a/src/test/test-mempress.c +++ b/src/test/test-mempress.c @@ -220,7 +220,7 @@ TEST(real_pressure) { assert_se(sd_bus_message_read(reply, "o", &object) >= 0); - assert_se(bus_wait_for_jobs_one(w, object, /* quiet= */ false, /* extra_args= */ NULL) >= 0); + assert_se(bus_wait_for_jobs_one(w, object, /* flags= */ BUS_WAIT_JOBS_LOG_ERROR, /* extra_args= */ NULL) >= 0); assert_se(sd_event_default(&e) >= 0); diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c index c3d0acb6afe..069a63290ce 100644 --- a/src/test/test-mount-util.c +++ b/src/test/test-mount-util.c @@ -276,7 +276,17 @@ TEST(make_mount_switch_root) { assert_se(s); assert_se(touch(s) >= 0); - for (int force_ms_move = 0; force_ms_move < 2; force_ms_move++) { + struct { + const char *path; + bool force_ms_move; + } table[] = { + { t, false }, + { t, true }, + { "/", false }, + { "/", true }, + }; + + FOREACH_ARRAY(i, table, ELEMENTSOF(table)) { r = safe_fork("(switch-root)", FORK_RESET_SIGNALS | FORK_CLOSE_ALL_FDS | @@ -290,12 +300,14 @@ TEST(make_mount_switch_root) { assert_se(r >= 0); if (r == 0) { - assert_se(make_mount_point(t) >= 0); - assert_se(mount_switch_root_full(t, /* mount_propagation_flag= */ 0, force_ms_move) >= 0); + assert_se(make_mount_point(i->path) >= 0); + assert_se(mount_switch_root_full(i->path, /* mount_propagation_flag= */ 0, i->force_ms_move) >= 0); - assert_se(access(ASSERT_PTR(strrchr(s, '/')), F_OK) >= 0); /* absolute */ - assert_se(access(ASSERT_PTR(strrchr(s, '/')) + 1, F_OK) >= 0); /* relative */ - assert_se(access(s, F_OK) < 0 && errno == ENOENT); /* doesn't exist in our new environment */ + if (!path_equal(i->path, "/")) { + assert_se(access(ASSERT_PTR(strrchr(s, '/')), F_OK) >= 0); /* absolute */ + assert_se(access(ASSERT_PTR(strrchr(s, '/')) + 1, F_OK) >= 0); /* relative */ + assert_se(access(s, F_OK) < 0 && errno == ENOENT); /* doesn't exist in our new environment */ + } _exit(EXIT_SUCCESS); } diff --git a/src/test/test-mountpoint-util.c b/src/test/test-mountpoint-util.c index ff447c65821..40b12dd0880 100644 --- a/src/test/test-mountpoint-util.c +++ b/src/test/test-mountpoint-util.c @@ -138,11 +138,6 @@ TEST(path_is_mount_point) { assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0); assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0); - assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0); - assert_se(path_is_mount_point("/sys", NULL, 0) > 0); - assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0); - assert_se(path_is_mount_point("/sys/", NULL, 0) > 0); - /* we'll create a hierarchy of different kinds of dir/file/link * layouts: * diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index 7a485f390fb..58d22b6cfee 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -914,15 +914,30 @@ TEST(parse_mtu) { assert_se(parse_mtu(AF_UNSPEC, "4294967295", &mtu) >= 0 && mtu == 4294967295); assert_se(parse_mtu(AF_UNSPEC, "500", &mtu) >= 0 && mtu == 500); assert_se(parse_mtu(AF_UNSPEC, "1280", &mtu) >= 0 && mtu == 1280); + assert_se(parse_mtu(AF_UNSPEC, "4294967296", &mtu) == -ERANGE); + assert_se(parse_mtu(AF_UNSPEC, "68", &mtu) >= 0 && mtu == 68); + assert_se(parse_mtu(AF_UNSPEC, "67", &mtu) >= 0 && mtu == 67); + assert_se(parse_mtu(AF_UNSPEC, "0", &mtu) >= 0 && mtu == 0); + assert_se(parse_mtu(AF_UNSPEC, "", &mtu) == -EINVAL); + + assert_se(parse_mtu(AF_INET, "1500", &mtu) >= 0 && mtu == 1500); + assert_se(parse_mtu(AF_INET, "1400", &mtu) >= 0 && mtu == 1400); + assert_se(parse_mtu(AF_INET, "65535", &mtu) >= 0 && mtu == 65535); + assert_se(parse_mtu(AF_INET, "65536", &mtu) >= 0 && mtu == 65536); + assert_se(parse_mtu(AF_INET, "4294967295", &mtu) >= 0 && mtu == 4294967295); + assert_se(parse_mtu(AF_INET, "500", &mtu) >= 0 && mtu == 500); + assert_se(parse_mtu(AF_INET, "1280", &mtu) >= 0 && mtu == 1280); + assert_se(parse_mtu(AF_INET, "4294967296", &mtu) == -ERANGE); + assert_se(parse_mtu(AF_INET, "68", &mtu) >= 0 && mtu == 68); + assert_se(parse_mtu(AF_INET, "67", &mtu) == -ERANGE); + assert_se(parse_mtu(AF_INET, "0", &mtu) == -ERANGE); + assert_se(parse_mtu(AF_INET, "", &mtu) == -EINVAL); + assert_se(parse_mtu(AF_INET6, "1280", &mtu) >= 0 && mtu == 1280); assert_se(parse_mtu(AF_INET6, "1279", &mtu) == -ERANGE); - assert_se(parse_mtu(AF_UNSPEC, "4294967296", &mtu) == -ERANGE); assert_se(parse_mtu(AF_INET6, "4294967296", &mtu) == -ERANGE); assert_se(parse_mtu(AF_INET6, "68", &mtu) == -ERANGE); - assert_se(parse_mtu(AF_UNSPEC, "68", &mtu) >= 0 && mtu == 68); - assert_se(parse_mtu(AF_UNSPEC, "67", &mtu) == -ERANGE); - assert_se(parse_mtu(AF_UNSPEC, "0", &mtu) == -ERANGE); - assert_se(parse_mtu(AF_UNSPEC, "", &mtu) == -EINVAL); + assert_se(parse_mtu(AF_INET6, "", &mtu) == -EINVAL); } TEST(parse_loadavg_fixed_point) { diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c index 86d0c045555..e14f27df793 100644 --- a/src/test/test-rlimit-util.c +++ b/src/test/test-rlimit-util.c @@ -1,15 +1,20 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include +#if HAVE_VALGRIND_VALGRIND_H +#include +#endif #include "alloc-util.h" #include "capability-util.h" #include "macro.h" #include "missing_resource.h" +#include "process-util.h" #include "rlimit-util.h" #include "string-util.h" #include "tests.h" #include "time-util.h" +#include "user-util.h" static void test_rlimit_parse_format_one(int resource, const char *string, rlim_t soft, rlim_t hard, int ret, const char *formatted) { _cleanup_free_ char *f = NULL; @@ -136,4 +141,45 @@ TEST(setrlimit) { assert_se(old.rlim_max == new.rlim_max); } +TEST(pid_getrlimit) { + int r; + + /* We fork off a child and read the parent's resource limit from there (i.e. our own), and compare + * with what getrlimit() gives us */ + + for (int resource = 0; resource < _RLIMIT_MAX; resource++) { + struct rlimit direct; + + assert_se(getrlimit(resource, &direct) >= 0); + + /* We fork off a child so that getrlimit() doesn't work anymore */ + r = safe_fork("(getrlimit)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, /* ret_pid= */ NULL); + assert_se(r >= 0); + + if (r == 0) { + struct rlimit indirect; + /* child */ + + /* Drop privs, so that prlimit() doesn't work anymore */ + (void) setresgid(GID_NOBODY, GID_NOBODY, GID_NOBODY); + (void) setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY); + + assert_se(pid_getrlimit(getppid(), resource, &indirect) >= 0); + +#if HAVE_VALGRIND_VALGRIND_H + /* Valgrind fakes some changes in RLIMIT_NOFILE getrlimit() returns, work around that */ + if (RUNNING_ON_VALGRIND && resource == RLIMIT_NOFILE) { + log_info("Skipping pid_getrlimit() check for RLIMIT_NOFILE, running in valgrind"); + _exit(EXIT_SUCCESS); + } +#endif + + assert_se(direct.rlim_cur == indirect.rlim_cur); + assert_se(direct.rlim_max == indirect.rlim_max); + + _exit(EXIT_SUCCESS); + } + } +} + DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index a2b71015734..0b46cad9827 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -165,4 +165,15 @@ TEST(get_ctty) { log_notice("Not invoked with stdin == ctty, cutting get_ctty() test short"); } +TEST(get_default_background_color) { + double red, green, blue; + int r; + + r = get_default_background_color(&red, &green, &blue); + if (r < 0) + log_notice_errno(r, "Can't get terminal default background color: %m"); + else + log_notice("R=%g G=%g B=%g", red, green, blue); +} + DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/test/test-varlink-idl.c b/src/test/test-varlink-idl.c index cbdb9c64fbb..6aa1fa6b1df 100644 --- a/src/test/test-varlink-idl.c +++ b/src/test/test-varlink-idl.c @@ -8,6 +8,7 @@ #include "varlink.h" #include "varlink-idl.h" #include "varlink-io.systemd.h" +#include "varlink-io.systemd.Credentials.h" #include "varlink-io.systemd.Journal.h" #include "varlink-io.systemd.ManagedOOM.h" #include "varlink-io.systemd.PCRExtend.h" @@ -143,6 +144,8 @@ TEST(parse_format) { print_separator(); test_parse_format_one(&vl_interface_io_systemd_sysext); print_separator(); + test_parse_format_one(&vl_interface_io_systemd_Credentials); + print_separator(); test_parse_format_one(&vl_interface_xyz_test); } diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c index b0c2c065e42..271af24c804 100644 --- a/src/test/test-watch-pid.c +++ b/src/test/test-watch-pid.c @@ -18,7 +18,7 @@ int main(int argc, char *argv[]) { if (getuid() != 0) return log_tests_skipped("not root"); r = enter_cgroup_subroot(NULL); - if (r == -ENOMEDIUM) + if (r < 0) return log_tests_skipped("cgroupfs not available"); _cleanup_free_ char *unit_dir = NULL; diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index c7be30f563f..53c2a6fb710 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -665,13 +665,12 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error * if (streq_ptr(z, c->zone)) return sd_bus_reply_method_return(m, NULL); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_TIME, "org.freedesktop.timedate1.set-timezone", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) @@ -740,13 +739,12 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error if (lrtc == c->local_rtc && !fix_system) return sd_bus_reply_method_return(m, NULL); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_TIME, "org.freedesktop.timedate1.set-local-rtc", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) @@ -860,13 +858,12 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro } else timespec_store(&ts, (usec_t) utc); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_TIME, "org.freedesktop.timedate1.set-time", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) @@ -924,13 +921,12 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error if (context_ntp_service_exists(c) <= 0) return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported"); - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( m, - CAP_SYS_TIME, "org.freedesktop.timedate1.set-ntp", - NULL, + /* details= */ NULL, interactive, - UID_INVALID, + /* good_user= */ UID_INVALID, &c->polkit_registry, error); if (r < 0) diff --git a/src/timesync/timesyncd-bus.c b/src/timesync/timesyncd-bus.c index 7237080f325..d1d2a14c0f5 100644 --- a/src/timesync/timesyncd-bus.c +++ b/src/timesync/timesyncd-bus.c @@ -67,10 +67,12 @@ static int method_set_runtime_servers(sd_bus_message *message, void *userdata, s return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server name or address, refusing: %s", *name); } - r = bus_verify_polkit_async(message, CAP_NET_ADMIN, - "org.freedesktop.timesync1.set-runtime-servers", - NULL, true, UID_INVALID, - &m->polkit_registry, error); + r = bus_verify_polkit_async( + message, + "org.freedesktop.timesync1.set-runtime-servers", + /* details= */ NULL, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) diff --git a/src/udev/test-udev-format.c b/src/udev/test-udev-format.c index d8e38082fdc..18a60b351e5 100644 --- a/src/udev/test-udev-format.c +++ b/src/udev/test-udev-format.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "mountpoint-util.h" #include "string-util.h" #include "tests.h" #include "udev-format.h" @@ -34,4 +35,11 @@ TEST(udev_resolve_subsys_kernel) { test_udev_resolve_subsys_kernel_one("[net/lo]/address", true, 0, "00:00:00:00:00:00"); } -DEFINE_TEST_MAIN(LOG_DEBUG); +static int intro(void) { + if (path_is_mount_point("/sys", NULL, 0) <= 0) + return log_tests_skipped("/sys is not mounted"); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/udev/test-udev-spawn.c b/src/udev/test-udev-spawn.c index 4f43facd805..447e50d978f 100644 --- a/src/udev/test-udev-spawn.c +++ b/src/udev/test-udev-spawn.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "mountpoint-util.h" #include "path-util.h" #include "signal-util.h" #include "strv.h" @@ -80,6 +81,9 @@ static void test2(void) { int main(int argc, char *argv[]) { _cleanup_free_ char *self = NULL; + if (path_is_mount_point("/sys", NULL, 0) <= 0) + return log_tests_skipped("/sys is not mounted"); + if (argc > 1) { if (streq(argv[1], "test1")) test1(); diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 91b40088f49..841e4615fcb 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -188,7 +188,7 @@ static int get_dev_port(sd_device *dev, bool fallback_to_dev_id, unsigned *ret) /* Get kernel provided port index for the case when multiple ports on a single PCI function. */ - r = device_get_sysattr_unsigned(dev, "dev_port", &v); + r = device_get_sysattr_unsigned_filtered(dev, "dev_port", &v); if (r < 0) return r; if (r > 0) { @@ -204,7 +204,7 @@ static int get_dev_port(sd_device *dev, bool fallback_to_dev_id, unsigned *ret) if (fallback_to_dev_id) { unsigned iftype; - r = device_get_sysattr_unsigned(dev, "type", &iftype); + r = device_get_sysattr_unsigned_filtered(dev, "type", &iftype); if (r < 0) return r; @@ -212,7 +212,7 @@ static int get_dev_port(sd_device *dev, bool fallback_to_dev_id, unsigned *ret) } if (fallback_to_dev_id) - return device_get_sysattr_unsigned(dev, "dev_id", ret); + return device_get_sysattr_unsigned_filtered(dev, "dev_id", ret); /* Otherwise, return the original index 0. */ *ret = 0; @@ -229,7 +229,7 @@ static int get_port_specifier(sd_device *dev, bool fallback_to_dev_id, char **re assert(ret); /* First, try to use the kernel provided front panel port name for multiple port PCI device. */ - r = sd_device_get_sysattr_value(dev, "phys_port_name", &phys_port_name); + r = device_get_sysattr_value_filtered(dev, "phys_port_name", &phys_port_name); if (r >= 0 && !isempty(phys_port_name)) { if (naming_scheme_has(NAMING_SR_IOV_R)) { int vf_id = -1; @@ -292,10 +292,10 @@ static int pci_get_onboard_index(sd_device *dev, unsigned *ret) { assert(ret); /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */ - r = device_get_sysattr_unsigned(dev, "acpi_index", &idx); + r = device_get_sysattr_unsigned_filtered(dev, "acpi_index", &idx); if (r < 0) /* SMBIOS type 41 — Onboard Devices Extended Information */ - r = device_get_sysattr_unsigned(dev, "index", &idx); + r = device_get_sysattr_unsigned_filtered(dev, "index", &idx); if (r < 0) return log_device_debug_errno(dev, r, "Could not obtain onboard index: %m"); @@ -347,7 +347,7 @@ static int names_pci_onboard_label(sd_device *dev, sd_device *pci_dev, const cha assert(prefix); /* retrieve on-board label from firmware */ - r = sd_device_get_sysattr_value(pci_dev, "label", &label); + r = device_get_sysattr_value_filtered(pci_dev, "label", &label); if (r < 0) return log_device_debug_errno(pci_dev, r, "Failed to get PCI onboard label: %m"); @@ -392,7 +392,7 @@ static int is_pci_multifunction(sd_device *dev) { static bool is_pci_ari_enabled(sd_device *dev) { assert(dev); - return device_get_sysattr_bool(dev, "ari_enabled") > 0; + return device_get_sysattr_bool_filtered(dev, "ari_enabled") > 0; } static bool is_pci_bridge(sd_device *dev) { @@ -400,7 +400,7 @@ static bool is_pci_bridge(sd_device *dev) { assert(dev); - if (sd_device_get_sysattr_value(dev, "modalias", &v) < 0) + if (device_get_sysattr_value_filtered(dev, "modalias", &v) < 0) return false; if (!startswith(v, "pci:")) @@ -442,7 +442,7 @@ static int parse_hotplug_slot_from_function_id(sd_device *dev, int slots_dirfd, return 0; } - if (sd_device_get_sysattr_value(dev, "function_id", &attr) < 0) { + if (device_get_sysattr_value_filtered(dev, "function_id", &attr) < 0) { *ret = 0; return 0; } @@ -505,7 +505,7 @@ static int pci_get_hotplug_slot_from_address( if (!path) return log_oom_debug(); - if (sd_device_get_sysattr_value(pci, path, &address) < 0) + if (device_get_sysattr_value_filtered(pci, path, &address) < 0) continue; /* match slot address with device by stripping the function */ @@ -845,7 +845,7 @@ static int names_devicetree(sd_device *dev, const char *prefix, bool test) { if (!alias_index) continue; - if (sd_device_get_sysattr_value(aliases_dev, alias, &alias_path) < 0) + if (device_get_sysattr_value_filtered(aliases_dev, alias, &alias_path) < 0) continue; if (!path_equal(ofnode_path, alias_path)) @@ -864,7 +864,7 @@ static int names_devicetree(sd_device *dev, const char *prefix, bool test) { } /* ...but make sure we don't have an alias conflict */ - if (i == 0 && sd_device_get_sysattr_value(aliases_dev, conflict, NULL) >= 0) + if (i == 0 && device_get_sysattr_value_filtered(aliases_dev, conflict, NULL) >= 0) return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST), "Ethernet alias conflict: ethernet and ethernet0 both exist"); @@ -1133,7 +1133,7 @@ static int names_mac(sd_device *dev, const char *prefix, bool test) { assert(dev); assert(prefix); - r = device_get_sysattr_unsigned(dev, "type", &iftype); + r = device_get_sysattr_unsigned_filtered(dev, "type", &iftype); if (r < 0) return log_device_debug_errno(dev, r, "Failed to read 'type' attribute: %m"); @@ -1145,7 +1145,7 @@ static int names_mac(sd_device *dev, const char *prefix, bool test) { "Not generating MAC name for infiniband device."); /* check for NET_ADDR_PERM, skip random MAC addresses */ - r = device_get_sysattr_unsigned(dev, "addr_assign_type", &assign_type); + r = device_get_sysattr_unsigned_filtered(dev, "addr_assign_type", &assign_type); if (r < 0) return log_device_debug_errno(dev, r, "Failed to read/parse addr_assign_type: %m"); @@ -1153,7 +1153,7 @@ static int names_mac(sd_device *dev, const char *prefix, bool test) { return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "addr_assign_type=%u, MAC address is not permanent.", assign_type); - r = sd_device_get_sysattr_value(dev, "address", &s); + r = device_get_sysattr_value_filtered(dev, "address", &s); if (r < 0) return log_device_debug_errno(dev, r, "Failed to read 'address' attribute: %m"); @@ -1203,7 +1203,7 @@ static int names_netdevsim(sd_device *dev, const char *prefix, bool test) { if (r < 0) return log_device_debug_errno(netdevsimdev, r, "Failed to parse device sysnum: %m"); - r = sd_device_get_sysattr_value(dev, "phys_port_name", &phys_port_name); + r = device_get_sysattr_value_filtered(dev, "phys_port_name", &phys_port_name); if (r < 0) return log_device_debug_errno(dev, r, "Failed to get 'phys_port_name' attribute: %m"); if (isempty(phys_port_name)) @@ -1265,7 +1265,7 @@ static int get_ifname_prefix(sd_device *dev, const char **ret) { assert(dev); assert(ret); - r = device_get_sysattr_unsigned(dev, "type", &iftype); + r = device_get_sysattr_unsigned_filtered(dev, "type", &iftype); if (r < 0) return r; @@ -1311,7 +1311,7 @@ static int device_is_stacked(sd_device *dev) { if (r < 0) return r; - r = device_get_sysattr_int(dev, "iflink", &iflink); + r = device_get_sysattr_int_filtered(dev, "iflink", &iflink); if (r < 0) return r; diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c index 2f5429fd89d..f306a4ffd6d 100644 --- a/src/udev/udevadm-hwdb.c +++ b/src/udev/udevadm-hwdb.c @@ -89,7 +89,7 @@ int hwdb_main(int argc, char *argv[], void *userdata) { log_notice("udevadm hwdb is deprecated. Use systemd-hwdb instead."); - if (arg_update) { + if (arg_update && !hwdb_bypass()) { r = hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, true); if (r < 0) return r; diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index b33c8cf744c..6e9d86b783d 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -773,7 +773,7 @@ def make_uki(opts): if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1: pcrpkey = opts.pcr_public_keys[0] elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1: - import cryptography.hazmat.primitives.serialization as serialization + from cryptography.hazmat.primitives import serialization privkey = serialization.load_pem_private_key(opts.pcr_private_keys[0].read_bytes(), password=None) pcrpkey = privkey.public_key().public_bytes( encoding=serialization.Encoding.PEM, diff --git a/src/userdb/userdbd-manager.c b/src/userdb/userdbd-manager.c index 62a1fa5f4bf..bdaaab2c055 100644 --- a/src/userdb/userdbd-manager.c +++ b/src/userdb/userdbd-manager.c @@ -13,6 +13,7 @@ #include "signal-util.h" #include "socket-util.h" #include "stdio-util.h" +#include "strv.h" #include "umask-util.h" #include "userdbd-manager.h" @@ -124,6 +125,8 @@ Manager* manager_free(Manager *m) { m->deferred_start_worker_event_source = sd_event_source_unref(m->deferred_start_worker_event_source); + safe_close(m->listen_fd); + sd_event_unref(m->event); return mfree(m); @@ -183,7 +186,6 @@ static int start_one_worker(Manager *m) { _exit(EXIT_FAILURE); } - if (setenv("USERDB_FIXED_WORKER", one_zero(fixed), 1) < 0) { log_error_errno(errno, "Failed to set $USERDB_FIXED_WORKER: %m"); _exit(EXIT_FAILURE); @@ -263,57 +265,85 @@ static int start_workers(Manager *m, bool explicit_request) { return 0; } -int manager_startup(Manager *m) { - int n, r; +static int manager_make_listen_socket(Manager *m) { + static const union sockaddr_union sockaddr = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/userdb/io.systemd.Multiplexer", + }; + int r; + + assert(m); + + if (m->listen_fd >= 0) + return 0; + + r = mkdir_p("/run/systemd/userdb", 0755); + if (r < 0) + return log_error_errno(r, "Failed to create /run/systemd/userdb: %m"); + + m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (m->listen_fd < 0) + return log_error_errno(errno, "Failed to bind on socket: %m"); + + (void) sockaddr_un_unlink(&sockaddr.un); + + WITH_UMASK(0000) + if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0) + return log_error_errno(errno, "Failed to bind socket: %m"); + + FOREACH_STRING(alias, + "/run/systemd/userdb/io.systemd.NameServiceSwitch", + "/run/systemd/userdb/io.systemd.DropIn") { + + r = symlink_idempotent("io.systemd.Multiplexer", alias, /* make_relative= */ false); + if (r < 0) + return log_error_errno(r, "Failed to symlink '%s': %m", alias); + } + + if (listen(m->listen_fd, SOMAXCONN_DELUXE) < 0) + return log_error_errno(errno, "Failed to listen on socket: %m"); + + return 1; +} + +static int manager_scan_listen_fds(Manager *m) { + int n; assert(m); - assert(m->listen_fd < 0); - n = sd_listen_fds(false); + n = sd_listen_fds(/* unset_environment= */ true); if (n < 0) return log_error_errno(n, "Failed to determine number of passed file descriptors: %m"); if (n > 1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one listening fd, got %i.", n); if (n == 1) m->listen_fd = SD_LISTEN_FDS_START; - else { - static const union sockaddr_union sockaddr = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/userdb/io.systemd.Multiplexer", - }; - - r = mkdir_p("/run/systemd/userdb", 0755); - if (r < 0) - return log_error_errno(r, "Failed to create /run/systemd/userdb: %m"); - m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); - if (m->listen_fd < 0) - return log_error_errno(errno, "Failed to bind on socket: %m"); - - (void) sockaddr_un_unlink(&sockaddr.un); + return 0; +} - WITH_UMASK(0000) - if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0) - return log_error_errno(errno, "Failed to bind socket: %m"); +int manager_startup(Manager *m) { + int r; - r = symlink_idempotent("io.systemd.Multiplexer", - "/run/systemd/userdb/io.systemd.NameServiceSwitch", false); - if (r < 0) - return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m"); + assert(m); + assert(m->listen_fd < 0); - r = symlink_idempotent("io.systemd.Multiplexer", - "/run/systemd/userdb/io.systemd.DropIn", false); - if (r < 0) - return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m"); + r = manager_scan_listen_fds(m); + if (r < 0) + return r; - if (listen(m->listen_fd, SOMAXCONN_DELUXE) < 0) - return log_error_errno(errno, "Failed to listen on socket: %m"); - } + r = manager_make_listen_socket(m); + if (r < 0) + return r; /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be * GC'ed on idle */ if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0) return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m"); - return start_workers(m, /* explicit_request= */ false); + r = start_workers(m, /* explicit_request= */ false); + if (r < 0) + return r; + + return 0; } diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c index b49dbbd5230..729a9a1bb6f 100644 --- a/src/userdb/userwork.c +++ b/src/userdb/userwork.c @@ -353,9 +353,9 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { static const JsonDispatch dispatch_table[] = { - { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 }, + { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 }, { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 }, - { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, {} }; @@ -425,16 +425,16 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name)))); } -static int process_connection(VarlinkServer *server, int fd) { +static int process_connection(VarlinkServer *server, int _fd) { + _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */ _cleanup_(varlink_close_unrefp) Varlink *vl = NULL; int r; r = varlink_server_add_connection(server, fd, &vl); - if (r < 0) { - fd = safe_close(fd); + if (r < 0) return log_error_errno(r, "Failed to add connection: %m"); - } + TAKE_FD(fd); vl = varlink_ref(vl); for (;;) { @@ -461,6 +461,7 @@ static int process_connection(VarlinkServer *server, int fd) { static int run(int argc, char *argv[]) { usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY; _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL; + _cleanup_(pidref_done) PidRef parent = PIDREF_NULL; unsigned n_iterations = 0; int m, listen_fd, r; @@ -505,6 +506,12 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m"); + r = pidref_set_parent(&parent); + if (r < 0) + return log_error_errno(r, "Failed to acquire pidfd of parent process: %m"); + if (parent.pid == 1) /* We got reparented away from userdbd? */ + return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died, exiting."); + start_time = now(CLOCK_MONOTONIC); for (;;) { @@ -554,14 +561,11 @@ static int run(int argc, char *argv[]) { return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m"); if (FLAGS_SET(r, POLLIN)) { - pid_t parent; - - parent = getppid(); - if (parent <= 1) - return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died?"); - - if (kill(parent, SIGUSR2) < 0) - return log_error_errno(errno, "Failed to kill our own parent: %m"); + r = pidref_kill(&parent, SIGUSR2); + if (r == -ESRCH) + return log_error_errno(r, "Parent already died?"); + if (r < 0) + return log_error_errno(r, "Failed to send SIGUSR2 signal to parent: %m"); } } diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 4d82c65f0af..83e43b16ff9 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -217,6 +217,8 @@ static int verify_vc_allocation_byfd(int fd) { static int verify_vc_kbmode(int fd) { int curr_mode; + assert(fd >= 0); + /* * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode. * Otherwise we would (likely) interfere with X11's processing of the @@ -231,6 +233,20 @@ static int verify_vc_kbmode(int fd) { return IN_SET(curr_mode, K_XLATE, K_UNICODE) ? 0 : -EBUSY; } +static int verify_vc_display_mode(int fd) { + int mode; + + assert(fd >= 0); + + /* Similarly the vc is likely busy if it is in KD_GRAPHICS mode. If it's not the case and it's been + * left in graphics mode, the kernel will refuse to operate on the font settings anyway. */ + + if (ioctl(fd, KDGETMODE, &mode) < 0) + return -errno; + + return mode != KD_TEXT ? -EBUSY : 0; +} + static int toggle_utf8_vc(const char *name, int fd, bool utf8) { int r; struct termios tc = {}; @@ -470,24 +486,14 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { if (cfo.op != KD_FONT_OP_SET) continue; - r = ioctl(fd_d, KDFONTOP, &cfo); + r = verify_vc_display_mode(fd_d); if (r < 0) { - int last_errno, mode; - - /* The fonts couldn't have been copied. It might be due to the - * terminal being in graphical mode. In this case the kernel - * returns -EINVAL which is too generic for distinguishing this - * specific case. So we need to retrieve the terminal mode and if - * the graphical mode is in used, let's assume that something else - * is using the terminal and the failure was expected as we - * shouldn't have tried to copy the fonts. */ - - last_errno = errno; - if (ioctl(fd_d, KDGETMODE, &mode) >= 0 && mode != KD_TEXT) - log_debug("KD_FONT_OP_SET skipped: tty%u is not in text mode", i); - else - log_warning_errno(last_errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i); + log_debug_errno(r, "KD_FONT_OP_SET skipped: tty%u is not in text mode", i); + continue; + } + if (ioctl(fd_d, KDFONTOP, &cfo) < 0) { + log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i); continue; } @@ -531,14 +537,19 @@ static int find_source_vc(char **ret_path, unsigned *ret_idx) { fd = open_terminal(path, O_RDWR|O_CLOEXEC|O_NOCTTY); if (fd < 0) { - log_debug_errno(fd, "Failed to open terminal %s, ignoring: %m", path); - RET_GATHER(err, r); + RET_GATHER(err, log_debug_errno(fd, "Failed to open terminal %s, ignoring: %m", path)); continue; } + r = verify_vc_kbmode(fd); if (r < 0) { - log_debug_errno(r, "Failed to check VC %s keyboard mode: %m", path); - RET_GATHER(err, r); + RET_GATHER(err, log_debug_errno(r, "Failed to check VC %s keyboard mode: %m", path)); + continue; + } + + r = verify_vc_display_mode(fd); + if (r < 0) { + RET_GATHER(err, log_debug_errno(r, "Failed to check VC %s display mode: %m", path)); continue; } @@ -572,6 +583,13 @@ static int verify_source_vc(char **ret_path, const char *src_vc) { if (r < 0) return log_error_errno(r, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", src_vc); + /* setfont(8) silently ignores when the font can't be applied due to the vc being in + * KD_GRAPHICS. Hence we continue to accept this case however we now let the user know that the vc + * will be initialized only partially.*/ + r = verify_vc_display_mode(fd); + if (r < 0) + log_notice_errno(r, "Virtual console %s is not in KD_TEXT, font settings likely won't be applied.", src_vc); + path = strdup(src_vc); if (!path) return log_oom(); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index ebae681893e..4ccda8bae8b 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -45,8 +45,7 @@ static int arg_qemu_vsock = -1; static uint64_t arg_vsock_cid = UINT64_MAX; static bool arg_qemu_gui = false; static int arg_secure_boot = -1; -static MachineCredential *arg_credentials = NULL; -static size_t arg_n_credentials = 0; +static MachineCredentialContext arg_credentials = {}; static SettingsMask arg_settings_mask = 0; static char **arg_parameters = NULL; @@ -54,6 +53,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_machine, freep); STATIC_DESTRUCTOR_REGISTER(arg_qemu_smp, freep); STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done); static int help(void) { _cleanup_free_ char *link = NULL; @@ -224,7 +224,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SET_CREDENTIAL: { - r = machine_credential_set(&arg_credentials, &arg_n_credentials, optarg); + r = machine_credential_set(&arg_credentials, optarg); if (r < 0) return r; arg_settings_mask |= SETTING_CREDENTIALS; @@ -232,7 +232,7 @@ static int parse_argv(int argc, char *argv[]) { } case ARG_LOAD_CREDENTIAL: { - r = machine_credential_load(&arg_credentials, &arg_n_credentials, optarg); + r = machine_credential_load(&arg_credentials, optarg); if (r < 0) return r; @@ -536,10 +536,10 @@ static int run_virtual_machine(void) { return log_oom(); } - if (ARCHITECTURE_SUPPORTS_SMBIOS) { - ssize_t n; - FOREACH_ARRAY(cred, arg_credentials, arg_n_credentials) { + if (ARCHITECTURE_SUPPORTS_SMBIOS) + FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) { _cleanup_free_ char *cred_data_b64 = NULL; + ssize_t n; n = base64mem(cred->data, cred->size, &cred_data_b64); if (n < 0) @@ -553,7 +553,6 @@ static int run_virtual_machine(void) { if (r < 0) return log_oom(); } - } r = strv_extend(&cmdline, "-drive"); if (r < 0) @@ -737,30 +736,21 @@ static int determine_names(void) { } static int run(int argc, char *argv[]) { - int r, ret = EXIT_SUCCESS; + int r; log_setup(); r = parse_argv(argc, argv); if (r <= 0) - goto finish; + return r; r = determine_names(); if (r < 0) - goto finish; + return r; assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18, -1) >= 0); - r = run_virtual_machine(); - if (r > 0) - ret = r; -finish: - machine_credential_free_all(arg_credentials, arg_n_credentials); - - if (r < 0) - return r; - - return ret; + return run_virtual_machine(); } DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); diff --git a/test/TEST-08-INITRD/deny-list-ubuntu-ci b/test/TEST-08-INITRD/deny-list-ubuntu-ci deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test/TEST-08-INITRD/test.sh b/test/TEST-08-INITRD/test.sh index 29fd1f78998..caa27f69fdf 100755 --- a/test/TEST-08-INITRD/test.sh +++ b/test/TEST-08-INITRD/test.sh @@ -44,8 +44,13 @@ EOF } check_result_qemu_hook() { + local workspace="${1:?}" local console_log="${TESTDIR:?}/console.log" + if [[ -e "$workspace/skipped" ]]; then + return 0 + fi + if [[ ! -e "$console_log" ]]; then dfatal "Missing console log - this shouldn't happen" return 1 diff --git a/test/TEST-24-CRYPTSETUP/template.cfg b/test/TEST-24-CRYPTSETUP/template.cfg new file mode 100644 index 00000000000..42f8b9d7484 --- /dev/null +++ b/test/TEST-24-CRYPTSETUP/template.cfg @@ -0,0 +1,8 @@ +dn = "cn = systemd" +expiration_days = 30 + +signing_key +encryption_key + +tls_www_client +email_protection_key diff --git a/test/TEST-24-CRYPTSETUP/test.sh b/test/TEST-24-CRYPTSETUP/test.sh index d0ec63d8708..a6739eee1cd 100755 --- a/test/TEST-24-CRYPTSETUP/test.sh +++ b/test/TEST-24-CRYPTSETUP/test.sh @@ -27,7 +27,7 @@ check_result_qemu() { mount_initdir - cryptsetup luksOpen "${LOOPDEV:?}p2" "${DM_NAME:?}" <"$TESTDIR/keyfile" + cryptsetup luksOpen "${LOOPDEV:?}p4" "${DM_NAME:?}" <"$TESTDIR/keyfile" mount "/dev/mapper/$DM_NAME" "$initdir/var" check_result_common "${initdir:?}" && ret=0 || ret=$? @@ -39,12 +39,123 @@ check_result_qemu() { return $ret } +can_test_pkcs11() { + if ! command -v "softhsm2-util" >/dev/null; then + ddebug "softhsm2-util not available, skipping the PKCS#11 test" + return 1 + fi + if ! command -v "pkcs11-tool" >/dev/null; then + ddebug "pkcs11-tool not available, skipping the PKCS#11 test" + return 1 + fi + if ! command -v "certtool" >/dev/null; then + ddebug "certtool not available, skipping the PKCS#11 test" + return 1 + fi + if ! "${SYSTEMCTL:?}" --version | grep -q "+P11KIT"; then + ddebug "Support for p11-kit is disabled, skipping the PKCS#11 test" + return 1 + fi + if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP\b"; then + ddebug "Support for libcryptsetup is disabled, skipping the PKCS#11 test" + return 1 + fi + if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP_PLUGINS"; then + ddebug "Support for libcryptsetup plugins is disabled, skipping the PKCS#11 test" + return 1 + fi + + return 0 +} + +setup_pkcs11_token() { + dinfo "Setup PKCS#11 token" + local P11_MODULE_CONFIGS_DIR P11_MODULE_DIR SOFTHSM_MODULE + + export SOFTHSM2_CONF="/tmp/softhsm2.conf" + mkdir -p "$initdir/var/lib/softhsm/tokens/" + cat >${SOFTHSM2_CONF} <&2 + P11_MODULE_CONFIGS_DIR="/usr/share/p11-kit/modules" + fi + + if ! P11_MODULE_DIR=$(pkg-config --variable=p11_module_path p11-kit-1); then + echo "WARNING! Cannot get p11_module_path from p11-kit-1.pc, assuming /usr/lib/pkcs11" >&2 + P11_MODULE_DIR="/usr/lib/pkcs11" + fi + + SOFTHSM_MODULE=$(grep -F 'module:' "$P11_MODULE_CONFIGS_DIR/softhsm2.module"| cut -d ':' -f 2| xargs) + if [[ "$SOFTHSM_MODULE" =~ ^[^/] ]]; then + SOFTHSM_MODULE="$P11_MODULE_DIR/$SOFTHSM_MODULE" + fi + + # RSA ##################################################### + pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "RSA:2048" --label "RSATestKey" --usage-decrypt + + certtool --generate-self-signed \ + --load-privkey="pkcs11:token=TestToken;object=RSATestKey;type=private" \ + --load-pubkey="pkcs11:token=TestToken;object=RSATestKey;type=public" \ + --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \ + --outder --outfile "/tmp/rsa_test.crt" + + pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/rsa_test.crt" --type cert --label "RSATestKey" + rm "/tmp/rsa_test.crt" + + # prime256v1 ############################################## + pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "EC:prime256v1" --label "ECTestKey" --usage-derive + + certtool --generate-self-signed \ + --load-privkey="pkcs11:token=TestToken;object=ECTestKey;type=private" \ + --load-pubkey="pkcs11:token=TestToken;object=ECTestKey;type=public" \ + --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \ + --outder --outfile "/tmp/ec_test.crt" + + pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/ec_test.crt" --type cert --label "ECTestKey" + rm "/tmp/ec_test.crt" + + ########################################################### + rm ${SOFTHSM2_CONF} + unset SOFTHSM2_CONF + + inst_libs "$SOFTHSM_MODULE" + inst_library "$SOFTHSM_MODULE" + inst_simple "$P11_MODULE_CONFIGS_DIR/softhsm2.module" + + cat >"$initdir/etc/softhsm2.conf" <"$initdir/etc/systemd/system/systemd-cryptsetup@.service.d/PKCS11.conf" <"${TESTDIR:?}/keyfile" - cryptsetup -q luksFormat --uuid="$PART_UUID" --pbkdf pbkdf2 --pbkdf-force-iterations 1000 "${LOOPDEV:?}p2" "$TESTDIR/keyfile" - cryptsetup luksOpen "${LOOPDEV}p2" "${DM_NAME:?}" <"$TESTDIR/keyfile" + cryptsetup -q luksFormat --uuid="$PART_UUID" --pbkdf pbkdf2 --pbkdf-force-iterations 1000 "${LOOPDEV:?}p4" "$TESTDIR/keyfile" + cryptsetup luksOpen "${LOOPDEV}p4" "${DM_NAME:?}" <"$TESTDIR/keyfile" mkfs.ext4 -L var "/dev/mapper/$DM_NAME" mkdir -p "${initdir:?}/var" mount "/dev/mapper/$DM_NAME" "$initdir/var" @@ -57,6 +168,10 @@ test_create_image() { install_dmevent generate_module_dependencies + if can_test_pkcs11; then + setup_pkcs11_token + fi + # Create a keydev dd if=/dev/zero of="${STATEDIR:?}/keydev.img" bs=1M count=16 mkfs.ext4 -L varcrypt_keydev "$STATEDIR/keydev.img" @@ -83,7 +198,7 @@ EOF if command -v dracut >/dev/null; then dracut --force --verbose --add crypt "$INITRD" elif command -v mkinitcpio >/dev/null; then - mkinitcpio --addhooks sd-encrypt --generate "$INITRD" + mkinitcpio -S autodetect --addhooks sd-encrypt --generate "$INITRD" elif command -v mkinitramfs >/dev/null; then # The cryptroot hook is provided by the cryptsetup-initramfs package if ! dpkg-query -s cryptsetup-initramfs; then diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh index d41a4f00f9a..b9e7bdf18ad 100755 --- a/test/TEST-64-UDEV-STORAGE/test.sh +++ b/test/TEST-64-UDEV-STORAGE/test.sh @@ -24,7 +24,7 @@ _host_has_feature() {( case "${1:?}" in btrfs) - modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $? + host_has_btrfs ;; iscsi) # Client/initiator (Open-iSCSI) @@ -36,7 +36,7 @@ _host_has_feature() {( command -v lvm || return $? ;; mdadm) - command -v mdadm || return $? + host_has_mdadm ;; multipath) command -v multipath && command -v multipathd || return $? diff --git a/test/TEST-74-AUX-UTILS/test.sh b/test/TEST-74-AUX-UTILS/test.sh index f033ec469f3..7940600612d 100755 --- a/test/TEST-74-AUX-UTILS/test.sh +++ b/test/TEST-74-AUX-UTILS/test.sh @@ -16,6 +16,12 @@ test_append_files() { # the QEMU test, as nspawn refuses the invalid machine ID with -EUCLEAN printf "556f48e837bc4424a710fa2e2c9d3e3c\ne3d\n" >"$workspace/etc/machine-id" fi + + if host_has_btrfs && host_has_mdadm; then + install_btrfs + install_mdadm + generate_module_dependencies + fi } do_test "$@" diff --git a/test/hwdb-test.sh b/test/hwdb-test.sh index 432a49fa1b4..89a5c7ed4e1 100755 --- a/test/hwdb-test.sh +++ b/test/hwdb-test.sh @@ -10,6 +10,7 @@ set -e export SYSTEMD_LOG_LEVEL=info +export SYSTEMD_HWDB_UPDATE_BYPASS=0 ROOTDIR="$(dirname "$(dirname "$(readlink -f "$0")")")" SYSTEMD_HWDB="${1:?}" diff --git a/test/test-functions b/test/test-functions index 42b00387898..5af7b8cd91d 100644 --- a/test/test-functions +++ b/test/test-functions @@ -1167,29 +1167,42 @@ install_multipath() { } install_lvm() { + local lvm_rules rule_prefix + image_install lvm image_install "${ROOTLIBDIR:?}"/system/lvm2-lvmpolld.{service,socket} image_install "${ROOTLIBDIR:?}"/system/{blk-availability,lvm2-monitor}.service image_install -o "/lib/tmpfiles.d/lvm2.conf" + if get_bool "$LOOKS_LIKE_DEBIAN"; then - inst_rules 56-lvm.rules 69-lvm-metad.rules + lvm_rules="56-lvm.rules" + rule_prefix="" else - # Support the new udev autoactivation introduced in lvm 2.03.14 - # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6 - # Static autoactivation (via lvm2-activation-generator) was dropped - # in lvm 2.03.15 - # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c - if [[ -f /lib/udev/rules.d/69-dm-lvm.rules ]]; then - inst_rules 11-dm-lvm.rules 69-dm-lvm.rules - else - image_install "${ROOTLIBDIR:?}"/system-generators/lvm2-activation-generator - image_install "${ROOTLIBDIR:?}"/system/lvm2-pvscan@.service - inst_rules 11-dm-lvm.rules 69-dm-lvm-metad.rules - fi + lvm_rules="11-dm-lvm.rules" + rule_prefix="dm-" + fi + + # Support the new udev autoactivation introduced in lvm 2.03.14 + # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6 + # Static autoactivation (via lvm2-activation-generator) was dropped + # in lvm 2.03.15 + # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c + if [[ -f "/lib/udev/rules.d/69-${rule_prefix}lvm.rules" ]]; then + inst_rules "$lvm_rules" "69-${rule_prefix}lvm.rules" + else + image_install "${ROOTLIBDIR:?}"/system-generators/lvm2-activation-generator + image_install "${ROOTLIBDIR:?}"/system/lvm2-pvscan@.service + inst_rules "$lvm_rules" "69-${rule_prefix}lvm-metad.rules" fi + mkdir -p "${initdir:?}/etc/lvm" } +host_has_btrfs() ( + set -e + modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $? +) + install_btrfs() { instmods btrfs # Not all utilities provided by btrfs-progs are listed here; extend the list @@ -1257,6 +1270,11 @@ install_iscsi() { fi } +host_has_mdadm() ( + set -e + command -v mdadm || return $? +) + install_mdadm() { local unit local mdadm_units=( @@ -1270,6 +1288,7 @@ install_mdadm() { system-shutdown/mdadm.shutdown ) + instmods "=md" image_install mdadm mdmon inst_rules 01-md-raid-creating.rules 63-md-raid-arrays.rules 64-md-raid-assembly.rules 69-md-clustered-confirm-device.rules # Fedora/CentOS/RHEL ships this rule file @@ -1278,6 +1297,10 @@ install_mdadm() { for unit in "${mdadm_units[@]}"; do image_install "${ROOTLIBDIR:?}/$unit" done + + # Disable the mdmonitor service, since it fails if there's no valid email address + # configured in /etc/mdadm.conf, which just unnecessarily pollutes the logs + "${SYSTEMCTL:?}" mask --root "${initdir:?}" mdmonitor.service || : } install_compiled_systemd() { @@ -1302,19 +1325,35 @@ install_compiled_systemd() { fi } +install_package_file() { + local file="${1:?}" + + # Skip missing files (like /etc/machine-info) + [[ ! -e "$file" ]] && return 0 + # Skip python unit tests, since the image_install machinery will try to pull + # in the whole python stack in a very questionable state, making the tests fail. + # And given we're trying to transition to mkosi-based images anyway I'm not even + # going to bother + [[ "$file" =~ /tests/unit-tests/.*.py$ ]] && return 0 + # If the current file is a directory, create it with the original + # mode; if it's a symlink to a directory, copy it as-is + if [[ -d "$file" ]]; then + inst_dir "$file" + else + inst "$file" + fi +} + install_debian_systemd() { dinfo "Install debian systemd" - local files + local deb file while read -r deb; do - files="$(dpkg-query -L "$deb" 2>/dev/null)" || continue ddebug "Install debian files from package $deb" - for file in $files; do - [ -e "$file" ] || continue - [ ! -L "$file" ] && [ -d "$file" ] && continue - inst "$file" - done + while read -r file; do + install_package_file "$file" + done < <(dpkg-query -L "$deb" 2>/dev/null) done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2) } @@ -1329,17 +1368,7 @@ install_rpm() { dinfo "Installing contents of RPM $rpm" while read -r file; do - # Skip missing files (like /etc/machine-info) - [[ ! -e "$file" ]] && continue - # Skip directories unless they are a symlink (both -L and -d pass in this case) - [[ -d "$file" && ! -L "$file" ]] && continue - # Skip python unit tests, since the image_install machinery will try to pull - # in the whole python stack in a very questionable state, making the tests fail. - # And given we're trying to transition to mkosi-based images anyway I'm not even - # going to bother - [[ "$file" =~ /tests/unit-tests/.*.py$ ]] && continue - - image_install "$file" + install_package_file "$file" done < <(rpm -ql "$rpm") } @@ -1583,6 +1612,9 @@ create_empty_image() { # Partition sizes are in MiBs local root_size=768 local data_size=100 + local esp_size=128 + local boot_size=128 + local total= if ! get_bool "$NO_BUILD"; then if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then root_size=$((root_size + 200)) @@ -1605,28 +1637,44 @@ create_empty_image() { data_size=$((data_size + IMAGE_ADDITIONAL_DATA_SIZE)) fi - echo "Setting up ${IMAGE_PUBLIC:?} (${root_size} MB)" + total=$((root_size + data_size + esp_size + boot_size)) + + echo "Setting up ${IMAGE_PUBLIC:?} (${total} MB)" rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC" # Create the blank file to use as a root filesystem - truncate -s "${root_size}M" "$IMAGE_PUBLIC" + truncate -s "${total}M" "$IMAGE_PUBLIC" LOOPDEV="$(losetup --show -P -f "$IMAGE_PUBLIC")" [[ -b "$LOOPDEV" ]] || return 1 # Create two partitions - a root one and a data one (utilized by some tests) sfdisk "$LOOPDEV" </dev/null && \ - [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then + if ! get_bool "$NO_BUILD" && \ + get_bool "${TEST_REQUIRE_INSTALL_TESTS:?}" && \ + command -v meson >/dev/null && \ + [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then dfatal "$BUILD_DIR needs to be built with -Dinstall-tests=true" exit 1 fi diff --git a/test/test-network-generator-conversion.sh b/test/test-network-generator-conversion.sh index 6224a4d04f9..9a4732c981d 100755 --- a/test/test-network-generator-conversion.sh +++ b/test/test-network-generator-conversion.sh @@ -296,7 +296,7 @@ INVALID_COMMAND_LINES=( "ip=10.0.0.1:::255.255.255::foo99:off" "ip=10.0.0.1:::255.255.255.0:invalid_hostname:foo99:off" "ip=10.0.0.1:::255.255.255.0::verylonginterfacename:off" - "ip=:::::dhcp99:dhcp6:0" + "ip=:::::dhcp99:dhcp6:4294967296" "ip=:::::dhcp99:dhcp6:-1" "ip=:::::dhcp99:dhcp6:666:52:54:00" "ip=fdef:c400:bd01:1096::2::[fdef:c400:bd01:1096::1]:64::ipv6:off:[fdef:c400:bd01:1096::aaaa]" diff --git a/test/test-network/conf/11-test-unit-file.link b/test/test-network/conf/11-test-unit-file.link new file mode 100644 index 00000000000..429ac31e806 --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.link @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: MIT-0 +# +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the MIT No Attribution license). +# +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/99-default.link.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-udevd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. + +[Match] +OriginalName=* + +[Link] +NamePolicy=keep kernel database onboard slot path +AlternativeNamesPolicy=database onboard slot path +MACAddressPolicy=persistent diff --git a/test/test-network/conf/11-test-unit-file.link.d/dropin.conf b/test/test-network/conf/11-test-unit-file.link.d/dropin.conf new file mode 100644 index 00000000000..dfddc0a46fd --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.link.d/dropin.conf @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Link] diff --git a/test/test-network/conf/11-test-unit-file.netdev b/test/test-network/conf/11-test-unit-file.netdev new file mode 100644 index 00000000000..86af17ff9f1 --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.netdev @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=test1 +Kind=dummy diff --git a/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf b/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf new file mode 100644 index 00000000000..6fde5989e4f --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] diff --git a/test/test-network/conf/11-test-unit-file.network b/test/test-network/conf/11-test-unit-file.network new file mode 100644 index 00000000000..0a4511bfb31 --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.network @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=test1 + +[Network] +IPv6AcceptRA=no diff --git a/test/test-network/conf/11-test-unit-file.network.d/dropin.conf b/test/test-network/conf/11-test-unit-file.network.d/dropin.conf new file mode 100644 index 00000000000..f2f6099599b --- /dev/null +++ b/test/test-network/conf/11-test-unit-file.network.d/dropin.conf @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Network] diff --git a/test/test-network/conf/25-vcan.netdev b/test/test-network/conf/25-vcan.netdev index 29bd98e5c9d..2762dd23747 100644 --- a/test/test-network/conf/25-vcan.netdev +++ b/test/test-network/conf/25-vcan.netdev @@ -2,3 +2,4 @@ [NetDev] Name=vcan99 Kind=vcan +MTUBytes=16 diff --git a/test/test-network/conf/25-vcan98.netdev b/test/test-network/conf/25-vcan98.netdev new file mode 100644 index 00000000000..5333c82da45 --- /dev/null +++ b/test/test-network/conf/25-vcan98.netdev @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=vcan98 +Kind=vcan diff --git a/test/test-network/conf/25-vcan98.network b/test/test-network/conf/25-vcan98.network new file mode 100644 index 00000000000..97f824d2443 --- /dev/null +++ b/test/test-network/conf/25-vcan98.network @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=vcan98 + +[Link] +MTUBytes=16 diff --git a/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf b/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf new file mode 100644 index 00000000000..9bf5d7eb5d4 --- /dev/null +++ b/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Network] +ManageForeignNextHops=no diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index ea4f8d4b157..adb60cd4893 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -258,6 +258,8 @@ def compare_kernel_version(min_kernel_version): # Get only the actual kernel version without any build/distro/arch stuff # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5' kver = platform.release().split('-')[0] + # Get also rid of '+' + kver = kver.split('+')[0] return version.parse(kver) >= version.parse(min_kernel_version) @@ -1236,15 +1238,20 @@ class NetworkctlTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'Type: loopback') - def test_udev_link_file(self): - copy_network_unit('11-dummy.netdev', '11-dummy.network', '25-default.link') + def test_unit_file(self): + copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link') start_networkd() self.wait_online(['test1:degraded']) output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env) print(output) - self.assertRegex(output, r'Link File: /run/systemd/network/25-default.link') - self.assertRegex(output, r'Network File: /run/systemd/network/11-dummy.network') + self.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output) + self.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output) + self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output) + self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output) + + output = read_networkd_log() + self.assertIn('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).', output) # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249). # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property. @@ -1252,8 +1259,8 @@ class NetworkctlTests(unittest.TestCase, Utilities): check_output(*udevadm_cmd, 'trigger', '--settle', '--action=add', '/sys/class/net/lo') output = check_output(*networkctl_cmd, '-n', '0', 'status', 'lo', env=env) print(output) - self.assertRegex(output, r'Link File: n/a') - self.assertRegex(output, r'Network File: n/a') + self.assertIn('Link File: n/a', output) + self.assertIn('Network File: n/a', output) def test_delete_links(self): copy_network_unit('11-dummy.netdev', '11-dummy.network', @@ -1727,10 +1734,20 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('vcan') def test_vcan(self): - copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network') + copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network', + '25-vcan98.netdev', '25-vcan98.network') start_networkd() - self.wait_online(['vcan99:carrier']) + self.wait_online(['vcan99:carrier', 'vcan98:carrier']) + + # https://github.com/systemd/systemd/issues/30140 + output = check_output('ip -d link show vcan99') + print(output) + self.assertIn('mtu 16 ', output) + + output = check_output('ip -d link show vcan98') + print(output) + self.assertIn('mtu 16 ', output) @expectedFailureIfModuleIsNotAvailable('vxcan') def test_vxcan(self): @@ -2552,10 +2569,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): def test_address_static(self): copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False) - start_networkd() self.setup_nftset('addr4', 'ipv4_addr') self.setup_nftset('network4', 'ipv4_addr', 'flags interval;') self.setup_nftset('ifindex', 'iface_index') + start_networkd() self.wait_online(['dummy98:routable']) @@ -3273,8 +3290,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertEqual(output, '') - self.tearDown() - def test_route_static(self): first = True for manage_foreign_routes in [True, False]: @@ -3554,6 +3569,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'inet6 .* scope link') + @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved") def test_sysctl(self): copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf') copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False) @@ -3832,74 +3848,86 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98') self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98') - @expectedFailureIfNexthopIsNotAvailable() - def test_nexthop(self): - def check_nexthop(self): - self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable']) + def check_nexthop(self, manage_foreign_nexthops): + self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable']) - output = check_output('ip nexthop list dev veth99') - print(output) - self.assertIn('id 1 via 192.168.5.1 dev veth99', output) - self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output) - 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) + output = check_output('ip nexthop list dev veth99') + print(output) + self.assertIn('id 1 via 192.168.5.1 dev veth99', output) + self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output) + 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) + if manage_foreign_nexthops: self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99') - output = check_output('ip nexthop list dev dummy98') - print(output) - self.assertIn('id 20 via 192.168.20.1 dev dummy98', output) + output = check_output('ip nexthop list dev dummy98') + print(output) + self.assertIn('id 20 via 192.168.20.1 dev dummy98', output) + if manage_foreign_nexthops: + self.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output) + else: + self.assertIn('id 42 via 192.168.20.2 dev dummy98', output) - # kernel manages blackhole nexthops on lo - output = check_output('ip nexthop list dev lo') - print(output) - self.assertIn('id 6 blackhole', output) - self.assertIn('id 7 blackhole', output) + # kernel manages blackhole nexthops on lo + output = check_output('ip nexthop list dev lo') + print(output) + self.assertIn('id 6 blackhole', output) + self.assertIn('id 7 blackhole', output) - # group nexthops are shown with -0 option - output = check_output('ip -0 nexthop list id 21') - print(output) - self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)') + # group nexthops are shown with -0 option + output = check_output('ip -0 nexthop list id 21') + print(output) + self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)') - output = check_output('ip route show dev veth99 10.10.10.10') - print(output) - self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output) + output = check_output('ip route show dev veth99 10.10.10.10') + print(output) + self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output) - output = check_output('ip route show dev veth99 10.10.10.11') - print(output) - self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output) + output = check_output('ip route show dev veth99 10.10.10.11') + print(output) + self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output) - output = check_output('ip route show dev veth99 10.10.10.12') - print(output) - self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output) + output = check_output('ip route show dev veth99 10.10.10.12') + print(output) + self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output) - output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1') - print(output) - self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output) + output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1') + print(output) + self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output) - output = check_output('ip route show 10.10.10.13') - print(output) - self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output) + output = check_output('ip route show 10.10.10.13') + print(output) + self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output) - output = check_output('ip -6 route show 2001:1234:5:8f62::2') - print(output) - self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output) + output = check_output('ip -6 route show 2001:1234:5:8f62::2') + print(output) + self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output) - output = check_output('ip route show 10.10.10.14') - print(output) - self.assertIn('10.10.10.14 nhid 21 proto static', output) - self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output) - self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output) + output = check_output('ip route show 10.10.10.14') + print(output) + self.assertIn('10.10.10.14 nhid 21 proto static', output) + self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output) + self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output) - output = check_output(*networkctl_cmd, '--json=short', 'status', env=env) - check_json(output) + output = check_output(*networkctl_cmd, '--json=short', 'status', env=env) + check_json(output) + + def _test_nexthop(self, manage_foreign_nexthops): + if not manage_foreign_nexthops: + copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf') + + check_output('ip link add dummy98 type dummy') + check_output('ip link set dummy98 up') + check_output('ip address add 192.168.20.20/24 dev dummy98') + check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98') copy_network_unit('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network', '12-dummy.netdev', '25-nexthop-dummy.network') start_networkd() - check_nexthop(self) + self.check_nexthop(manage_foreign_nexthops) remove_network_unit('25-nexthop.network') copy_network_unit('25-nexthop-nothing.network') @@ -3918,7 +3946,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): networkctl_reconfigure('dummy98') networkctl_reload() - check_nexthop(self) + self.check_nexthop(manage_foreign_nexthops) remove_link('veth99') time.sleep(2) @@ -3927,6 +3955,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertEqual(output, '') + @expectedFailureIfNexthopIsNotAvailable() + def test_nexthop(self): + first = True + for manage_foreign_nexthops in [True, False]: + if first: + first = False + else: + self.tearDown() + + print(f'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})') + with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops): + self._test_nexthop(manage_foreign_nexthops) + class NetworkdTCTests(unittest.TestCase, Utilities): def setUp(self): diff --git a/test/test-systemctl-enable.sh b/test/test-systemctl-enable.sh index 9ed02248ad1..bdb1c8e2d36 100644 --- a/test/test-systemctl-enable.sh +++ b/test/test-systemctl-enable.sh @@ -97,7 +97,7 @@ test ! -e "$root/etc/systemd/system/test1-badalias.target" test ! -e "$root/etc/systemd/system/test1-badalias.socket" test -h "$root/etc/systemd/system/test1-goodalias2.service" -: '-------aliases in reeanable----------------------------------' +: '-------aliases in reenable----------------------------------' ( ! "$systemctl" --root="$root" reenable test1 ) test -h "$root/etc/systemd/system/default.target.wants/test1.service" test ! -e "$root/etc/systemd/system/test1-goodalias.service" diff --git a/test/units/testsuite-04.bsod.sh b/test/units/testsuite-04.bsod.sh index 8f5ff5f35c8..1d4ad7ec6a2 100755 --- a/test/units/testsuite-04.bsod.sh +++ b/test/units/testsuite-04.bsod.sh @@ -22,6 +22,9 @@ at_exit() { journalctl --flush fi + rm -f /run/systemd/journald.conf.d/99-forward-to-console.conf + systemctl restart systemd-journald + return 0 } @@ -32,9 +35,9 @@ vcs_dump_and_check() { # so try it a couple of times for _ in {0..9}; do setterm --term linux --dump --file /tmp/console.dump - if grep -aq "Press any key to exit" /tmp/console.dump - grep -aq "$expected_message" /tmp/console.dump - grep -aq "The current boot has failed" /tmp/console.dump; then + if grep -aq "Press any key to exit" /tmp/console.dump && + grep -aq "$expected_message" /tmp/console.dump && + grep -aq "The current boot has failed" /tmp/console.dump; then return 0 fi @@ -49,7 +52,12 @@ vcs_dump_and_check() { # current boot, let's temporarily overmount /var/log/journal with a tmpfs, # as we're going to wipe it multiple times, but we need to keep the original # journal intact for the other tests to work correctly. +# +# Also, since we'll eventually lose the journal from this test, let's temporarily +# forward everything to console, to make potential fails debug-able. trap at_exit EXIT +mkdir -p /run/systemd/journald.conf.d/ +echo -ne '[Journal]\nForwardToConsole=yes' >/run/systemd/journald.conf.d/99-forward-to-console.conf mount -t tmpfs tmpfs /var/log/journal systemctl restart systemd-journald diff --git a/test/units/testsuite-07.exec-context.sh b/test/units/testsuite-07.exec-context.sh index 5fb7dddf891..c84974f1de4 100755 --- a/test/units/testsuite-07.exec-context.sh +++ b/test/units/testsuite-07.exec-context.sh @@ -32,10 +32,19 @@ proc_supports_option() { # the transient stuff from systemd-run. Let's just skip the following tests # in that case instead of complicating the test setup even more */ if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then + if ! systemd-detect-virt -cq && command -v bootctl >/dev/null; then + boot_path="$(bootctl --print-boot-path)" + esp_path="$(bootctl --print-esp-path)" + + # If the mount points are handled by automount units, make sure we trigger + # them before proceeding further + ls -l "$boot_path" "$esp_path" + fi + systemd-run --wait --pipe -p ProtectSystem=yes \ - bash -xec "test ! -w /usr; test ! -w /boot; test -w /etc; test -w /var" + bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test -w /etc; test -w /var" systemd-run --wait --pipe -p ProtectSystem=full \ - bash -xec "test ! -w /usr; test ! -w /boot; test ! -w /etc; test -w /var" + bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test ! -w /etc; test -w /var" systemd-run --wait --pipe -p ProtectSystem=strict \ bash -xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc" systemd-run --wait --pipe -p ProtectSystem=no \ diff --git a/test/units/testsuite-07.issue-30412.sh b/test/units/testsuite-07.issue-30412.sh index 333b95f9bb1..61801c543af 100755 --- a/test/units/testsuite-07.issue-30412.sh +++ b/test/units/testsuite-07.issue-30412.sh @@ -14,7 +14,7 @@ chmod 744 /tmp/badbin cat >/run/systemd/system/badbin_assert.service </run/systemd/system/badbin_assert.socket < dead'; then +if journalctl -b -t systemd --grep '(? dead'; then exit 1 fi diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh index bcbe7a1e6a9..c7d11cffe6e 100755 --- a/test/units/testsuite-54.sh +++ b/test/units/testsuite-54.sh @@ -314,6 +314,16 @@ if ! systemd-detect-virt -q -c ; then systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service fi +# Decrypt/encrypt via varlink + +echo -n '{"data":"Zm9vYmFyCg=="}' > /tmp/vlcredsdata + +varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \ + varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt > /tmp/vlcredsdata2 + +cmp /tmp/vlcredsdata /tmp/vlcredsdata2 +rm /tmp/vlcredsdata /tmp/vlcredsdata2 + systemd-analyze log-level info touch /testok diff --git a/test/units/testsuite-60.sh b/test/units/testsuite-60.sh index e800a7a12c2..3d0723e7dcc 100755 --- a/test/units/testsuite-60.sh +++ b/test/units/testsuite-60.sh @@ -6,6 +6,9 @@ set -o pipefail # shellcheck source=test/units/util.sh . "$(dirname "$0")"/util.sh +maybe_mount_usr_overlay +trap 'maybe_umount_usr_overlay' EXIT + teardown_test_dependencies() ( set +eux diff --git a/test/units/testsuite-62-6.service b/test/units/testsuite-62-6.service new file mode 100644 index 00000000000..876d8f36e77 --- /dev/null +++ b/test/units/testsuite-62-6.service @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=TEST-62-RESTRICT-IFACES-altname +[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-altname-with-more-than-15-chars +RestrictNetworkInterfaces=veth1-altname-with-more-than-15-chars +Type=oneshot diff --git a/test/units/testsuite-62.sh b/test/units/testsuite-62.sh index ed408218fab..0dbedc4c868 100755 --- a/test/units/testsuite-62.sh +++ b/test/units/testsuite-62.sh @@ -17,6 +17,7 @@ setup() { 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 link property add dev "veth${i}" altname "veth${i}-altname-with-more-than-15-chars" ip addr add "192.168.113."$((4*i+2))/30 dev "veth${i}" done } diff --git a/test/units/testsuite-65.sh b/test/units/testsuite-65.sh index 6c819dfe4a4..078bc8b5f53 100755 --- a/test/units/testsuite-65.sh +++ b/test/units/testsuite-65.sh @@ -180,6 +180,9 @@ if [[ ! -v ASAN_OPTIONS ]]; then # check that systemd-analyze cat-config paths work in a chroot mkdir -p /tmp/root mount --bind / /tmp/root + if mountpoint -q /usr; then + mount --bind /usr /tmp/root/usr + fi systemd-analyze cat-config systemd/system-preset >/tmp/out1 chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2 diff /tmp/out{1,2} @@ -334,6 +337,17 @@ systemd-analyze verify /tmp/hoge@test.service (! systemd-analyze verify /tmp/hoge@nonexist.service) (! systemd-analyze verify /tmp/hoge@.service) +# test that all commands are verified. +cat </tmp/multi-exec-start.service +[Service] +Type=oneshot +ExecStart=true +ExecStart=ls +EOF +systemd-analyze verify /tmp/multi-exec-start.service +echo 'ExecStart=command-should-not-exist' >>/tmp/multi-exec-start.service +(! systemd-analyze verify /tmp/multi-exec-start.service) + # Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed # The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in # values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight diff --git a/test/units/testsuite-73.sh b/test/units/testsuite-73.sh index df5af4ba873..3e5b78879ad 100755 --- a/test/units/testsuite-73.sh +++ b/test/units/testsuite-73.sh @@ -9,6 +9,9 @@ set -o pipefail # shellcheck source=test/units/util.sh . "$(dirname "$0")"/util.sh +maybe_mount_usr_overlay +trap 'maybe_umount_usr_overlay' EXIT + enable_debug() { mkdir -p /run/systemd/system/systemd-localed.service.d cat >>/run/systemd/system/systemd-localed.service.d/override.conf </dev/null; then + echo "bootctl not found, skipping." + exit 0 +fi + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + +basic_tests() { + bootctl "$@" --help + bootctl "$@" --version + + bootctl "$@" install --make-entry-directory=yes + bootctl "$@" remove --make-entry-directory=yes + + bootctl "$@" install --all-architectures + bootctl "$@" remove --all-architectures + + bootctl "$@" install --make-entry-directory=yes --all-architectures + bootctl "$@" remove --make-entry-directory=yes --all-architectures + + bootctl "$@" install + (! bootctl "$@" update) + bootctl "$@" update --graceful + + bootctl "$@" is-installed + bootctl "$@" is-installed --graceful + bootctl "$@" random-seed + + bootctl "$@" + bootctl "$@" status + bootctl "$@" status --quiet + bootctl "$@" list + bootctl "$@" list --quiet + bootctl "$@" list --json=short + bootctl "$@" list --json=pretty + + bootctl "$@" remove + (! bootctl "$@" is-installed) + (! bootctl "$@" is-installed --graceful) +} + +testcase_bootctl_basic() { + assert_eq "$(bootctl --print-esp-path)" "/efi" + assert_eq "$(bootctl --print-boot-path)" "/boot" + bootctl --print-root-device + + basic_tests +} + +cleanup_image() ( + set +e + + if [[ -z "${IMAGE_DIR:-}" ]]; then + return 0 + fi + + umount "${IMAGE_DIR}/root" + + if [[ -n "${LOOPDEV:-}" ]]; then + losetup -d "${LOOPDEV}" + unset LOOPDEV + fi + + udevadm settle + + rm -rf "${IMAGE_DIR}" + unset IMAGE_DIR + + return 0 +) + +testcase_bootctl_image() { + IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)" + trap cleanup_image RETURN + + truncate -s 256m "${IMAGE_DIR}/image" + + cat >"${IMAGE_DIR}/partscript" </dev/null; then + echo "mdadm not found, skipping." + return 0 + fi + + if ! command -v mkfs.btrfs >/dev/null; then + echo "mkfs.btrfs not found, skipping." + return 0 + fi + + IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)" + trap cleanup_raid RETURN + + truncate -s 256m "${IMAGE_DIR}/image1" + truncate -s 256m "${IMAGE_DIR}/image2" + + cat >"${IMAGE_DIR}/partscript" <new <"+4" <