From ab4d43c54e977e5e53a98da3e77fd71a62b4fbfb Mon Sep 17 00:00:00 2001 From: Kai Lueke Date: Tue, 9 Aug 2022 13:34:18 +0200 Subject: [PATCH 1/2] sysext: support distribution-independent extensions using ID=_any A sysext image that merely contains static binaries has no dependency on the host distribution and should be able to be used anywhere. Support the special '_any' value for the ID field in the extension to opt-out of ID and VERSION_ID/SYSEXT_LEVEL matching. See https://github.com/systemd/systemd/issues/24061 --- man/systemd-sysext.xml | 5 +++-- src/shared/extension-release.c | 9 ++++++++- test/test-functions | 6 ++++++ test/units/testsuite-50.sh | 5 ++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/man/systemd-sysext.xml b/man/systemd-sysext.xml index 42e8e12df9..c2cf87c9cb 100644 --- a/man/systemd-sysext.xml +++ b/man/systemd-sysext.xml @@ -115,8 +115,9 @@ A simple mechanism for version compatibility is enforced: a system extension image must carry a /usr/lib/extension-release.d/extension-release.$name file, which must match its image name, that is compared with the host os-release - file: the contained ID= fields have to match, as well as the - SYSEXT_LEVEL= field (if defined). If the latter is not defined, the + file: the contained ID= fields have to match unless _any is set + for the extension. If the extension ID= is not _any, the + SYSEXT_LEVEL= field (if defined) has to match. If the latter is not defined, the VERSION_ID= field has to match instead. System extensions should not ship a /usr/lib/os-release file (as that would be merged into the host /usr/ tree, overriding the host OS version data, which is not desirable). The diff --git a/src/shared/extension-release.c b/src/shared/extension-release.c index db87b23a71..681dcbf7f8 100644 --- a/src/shared/extension-release.c +++ b/src/shared/extension-release.c @@ -50,11 +50,18 @@ int extension_release_validate( extension_release_id = strv_env_pairs_get(extension_release, "ID"); if (isempty(extension_release_id)) { - log_debug("Extension '%s' does not contain ID in extension-release but requested to match '%s'", + log_debug("Extension '%s' does not contain ID in extension-release but requested to match '%s' or be '_any'", name, host_os_release_id); return 0; } + /* A sysext with no host OS dependency (static binaries or scripts) can match + * '_any' host OS, and VERSION_ID or SYSEXT_LEVEL are not required anywhere */ + if (streq(extension_release_id, "_any")) { + log_debug("Extension '%s' matches '_any' OS.", name); + return 1; + } + if (!streq(host_os_release_id, extension_release_id)) { log_debug("Extension '%s' is for OS '%s', but deployed on top of '%s'.", name, extension_release_id, host_os_release_id); diff --git a/test/test-functions b/test/test-functions index b208825e67..f7f467dfca 100644 --- a/test/test-functions +++ b/test/test-functions @@ -712,6 +712,12 @@ EOF chmod +x "$initdir/opt/script1.sh" echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file" mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw" -noappend + + export initdir="$TESTDIR/app-nodistro" + mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" + ( echo "ID=_any" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro" + echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" + mksquashfs "$initdir" "$oldinitdir/usr/share/app-nodistro.raw" -noappend ) } diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index 31cb52064e..ae6dd0b3d7 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -305,6 +305,7 @@ systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.r systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1" systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2" systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1" +systemd-run -P --property ExtensionImages=/usr/share/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1" cat >/run/systemd/system/testservice-50e.service </run/systemd/system/testservice-50f.service < Date: Tue, 9 Aug 2022 17:07:20 +0200 Subject: [PATCH 2/2] sysext: introduce ARCHITECTURE field to match host architecture When an extension image has binaries they should match the host architecture. Currently there is no way to specify this requirement. Introduce an ARCHITECTURE field in the extension's release file that may be set to prevent loading on the wrong host architecture. Since this new field is introduced late, we don't want to make specifying it mandatory as it would break existing sysext images. See https://github.com/systemd/systemd/issues/24061 --- man/os-release.xml | 12 ++++++++++++ man/systemd-sysext.xml | 12 ++++++++---- src/shared/extension-release.c | 13 ++++++++++++- test/test-functions | 3 ++- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/man/os-release.xml b/man/os-release.xml index fc880c4765..168c1675a9 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -411,6 +411,18 @@ determines the fallback hostname. + + ARCHITECTURE= + A string that specifies which CPU architecture the userspace binaries require. + The architecture identifiers are the same as for ConditionArchitecture= + described in systemd.unit5. + The field is optional and should only be used when just single architecture is supported. + It may provide redundant information when used in a GPT partition with a GUID type that already + encodes the architecture. If this is not the case, the architecture should be specified in + e.g., an extension image, to prevent an incompatible host from loading it. + + + SYSEXT_LEVEL= diff --git a/man/systemd-sysext.xml b/man/systemd-sysext.xml index c2cf87c9cb..8da6bd26ad 100644 --- a/man/systemd-sysext.xml +++ b/man/systemd-sysext.xml @@ -118,10 +118,14 @@ file: the contained ID= fields have to match unless _any is set for the extension. If the extension ID= is not _any, the SYSEXT_LEVEL= field (if defined) has to match. If the latter is not defined, the - VERSION_ID= field has to match instead. System extensions should not ship a - /usr/lib/os-release file (as that would be merged into the host - /usr/ tree, overriding the host OS version data, which is not desirable). The - extension-release file follows the same format and semantics, and carries the same + VERSION_ID= field has to match instead. If the extension defines the + ARCHITECTURE= field and the value is not _any it has to match the kernel's + architecture reported by uname2 + but the used architecture identifiers are the same as for ConditionArchitecture= + described in systemd.unit5. + System extensions should not ship a /usr/lib/os-release file (as that would be merged + into the host /usr/ tree, overriding the host OS version data, which is not desirable). + The extension-release file follows the same format and semantics, and carries the same content, as the os-release file of the OS, but it describes the resources carried in the extension image. diff --git a/src/shared/extension-release.c b/src/shared/extension-release.c index 681dcbf7f8..2da8e7ea94 100644 --- a/src/shared/extension-release.c +++ b/src/shared/extension-release.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "architecture.h" #include "env-util.h" #include "extension-release.h" #include "log.h" @@ -15,7 +16,7 @@ int extension_release_validate( const char *host_sysext_scope, char **extension_release) { - const char *extension_release_id = NULL, *extension_release_sysext_level = NULL; + const char *extension_release_id = NULL, *extension_release_sysext_level = NULL, *extension_architecture = NULL; assert(name); assert(!isempty(host_os_release_id)); @@ -48,6 +49,16 @@ int extension_release_validate( } } + /* When the architecture field is present and not '_any' it must match the host - for now just look at uname but in + * the future we could check if the kernel also supports 32 bit or binfmt has a translator set up for the architecture */ + extension_architecture = strv_env_pairs_get(extension_release, "ARCHITECTURE"); + if (!isempty(extension_architecture) && !streq(extension_architecture, "_any") && + !streq(architecture_to_string(uname_architecture()), extension_architecture)) { + log_debug("Extension '%s' is for architecture '%s', but deployed on top of '%s'.", + name, extension_architecture, architecture_to_string(uname_architecture())); + return 0; + } + extension_release_id = strv_env_pairs_get(extension_release, "ID"); if (isempty(extension_release_id)) { log_debug("Extension '%s' does not contain ID in extension-release but requested to match '%s' or be '_any'", diff --git a/test/test-functions b/test/test-functions index f7f467dfca..5dec5e28de 100644 --- a/test/test-functions +++ b/test/test-functions @@ -715,7 +715,8 @@ EOF export initdir="$TESTDIR/app-nodistro" mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" - ( echo "ID=_any" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro" + ( echo "ID=_any" + echo "ARCHITECTURE=_any" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro" echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file" mksquashfs "$initdir" "$oldinitdir/usr/share/app-nodistro.raw" -noappend )