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 42e8e12df9..8da6bd26ad 100644 --- a/man/systemd-sysext.xml +++ b/man/systemd-sysext.xml @@ -115,12 +115,17 @@ 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 - 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 + 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. 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 db87b23a71..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,13 +49,30 @@ 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'", + 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..5dec5e28de 100644 --- a/test/test-functions +++ b/test/test-functions @@ -712,6 +712,13 @@ 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" + 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 ) } 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 <