From 08e29ac77d7a262176756b900c3d605061f2ebd6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Oct 2023 16:01:41 +0200 Subject: [PATCH 1/6] json: add json_build() support for env block serialization --- src/shared/json.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/shared/json.h | 2 ++ 2 files changed, 45 insertions(+) diff --git a/src/shared/json.c b/src/shared/json.c index e17f89564d..f7e61f1676 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -3709,6 +3709,49 @@ int json_buildv(JsonVariant **ret, va_list ap) { break; } + case _JSON_BUILD_STRV_ENV_PAIR: { + char **l; + + if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { + r = -EINVAL; + goto finish; + } + + l = va_arg(ap, char **); + + _cleanup_strv_free_ char **el = NULL; + STRV_FOREACH_PAIR(x, y, l) { + char *n = NULL; + + n = strjoin(*x, "=", *y); + if (!n) { + r = -ENOMEM; + goto finish; + } + + r = strv_consume(&el, n); + if (r < 0) + goto finish; + } + + if (current->n_suppress == 0) { + r = json_variant_new_array_strv(&add, el); + if (r < 0) + goto finish; + } + + n_subtract = 1; + + if (current->expect == EXPECT_TOPLEVEL) + current->expect = EXPECT_END; + else if (current->expect == EXPECT_OBJECT_VALUE) + current->expect = EXPECT_OBJECT_KEY; + else + assert(current->expect == EXPECT_ARRAY_ELEMENT); + + break; + } + case _JSON_BUILD_BASE64: case _JSON_BUILD_BASE32HEX: case _JSON_BUILD_HEX: diff --git a/src/shared/json.h b/src/shared/json.h index 861ef495b5..8ddff6be98 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -266,6 +266,7 @@ enum { _JSON_BUILD_VARIANT_ARRAY, _JSON_BUILD_LITERAL, _JSON_BUILD_STRV, + _JSON_BUILD_STRV_ENV_PAIR, _JSON_BUILD_BASE64, _JSON_BUILD_BASE32HEX, _JSON_BUILD_HEX, @@ -304,6 +305,7 @@ enum { #define JSON_BUILD_VARIANT_ARRAY(v, n) _JSON_BUILD_VARIANT_ARRAY, (JsonVariant **) { v }, (size_t) { n } #define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, (const char*) { l } #define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, (char**) { l } +#define JSON_BUILD_STRV_ENV_PAIR(l) _JSON_BUILD_STRV_ENV_PAIR, (char**) { l } #define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, (const void*) { p }, (size_t) { n } #define JSON_BUILD_BASE32HEX(p, n) _JSON_BUILD_BASE32HEX, (const void*) { p }, (size_t) { n } #define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n } From e83d902bceabefefb619c89a6bec6035b59f9658 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Oct 2023 18:18:27 +0200 Subject: [PATCH 2/6] dissect: port to new JSON_BUILD_STRV_ENV_PAIR() --- src/dissect/dissect.c | 49 ++++--------------------------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index c84325620b..a1ef16406a 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -755,23 +755,6 @@ static int parse_argv_as_mount_helper(int argc, char *argv[]) { return 1; } -static int strv_pair_to_json(char **l, JsonVariant **ret) { - _cleanup_strv_free_ char **jl = NULL; - - STRV_FOREACH_PAIR(a, b, l) { - char *j; - - j = strjoin(*a, "=", *b); - if (!j) - return log_oom(); - - if (strv_consume(&jl, j) < 0) - return log_oom(); - } - - return json_variant_new_array_strv(ret, jl); -} - static void strv_pair_print(char **l, const char *prefix) { assert(prefix); @@ -915,30 +898,6 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { _cleanup_(json_variant_unrefp) JsonVariant *mi = NULL, *osr = NULL, *irdr = NULL, *exr = NULL; _cleanup_strv_free_ char **extension_scopes = NULL; - if (!strv_isempty(m->machine_info)) { - r = strv_pair_to_json(m->machine_info, &mi); - if (r < 0) - return log_oom(); - } - - if (!strv_isempty(m->os_release)) { - r = strv_pair_to_json(m->os_release, &osr); - if (r < 0) - return log_oom(); - } - - if (!strv_isempty(m->initrd_release)) { - r = strv_pair_to_json(m->initrd_release, &irdr); - if (r < 0) - return log_oom(); - } - - if (!strv_isempty(m->extension_release)) { - r = strv_pair_to_json(m->extension_release, &exr); - if (r < 0) - return log_oom(); - } - r = get_extension_scopes(m, &extension_scopes); if (r < 0) return log_error_errno(r, "Failed to parse scope: %m"); @@ -950,10 +909,10 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { JSON_BUILD_PAIR("sectorSize", JSON_BUILD_INTEGER(m->sector_size)), JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)), JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)), - JSON_BUILD_PAIR_CONDITION(mi, "machineInfo", JSON_BUILD_VARIANT(mi)), - JSON_BUILD_PAIR_CONDITION(osr, "osRelease", JSON_BUILD_VARIANT(osr)), - JSON_BUILD_PAIR_CONDITION(osr, "initrdRelease", JSON_BUILD_VARIANT(irdr)), - JSON_BUILD_PAIR_CONDITION(exr, "extensionRelease", JSON_BUILD_VARIANT(exr)), + JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->machine_info), "machineInfo", JSON_BUILD_STRV_ENV_PAIR(m->machine_info)), + JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->os_release), "osRelease", JSON_BUILD_STRV_ENV_PAIR(m->os_release)), + JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->initrd_release), "initrdRelease", JSON_BUILD_STRV_ENV_PAIR(m->initrd_release)), + JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->extension_release), "extensionRelease", JSON_BUILD_STRV_ENV_PAIR(m->extension_release)), JSON_BUILD_PAIR("useBootableUefi", JSON_BUILD_BOOLEAN(m->partitions[PARTITION_ESP].found)), JSON_BUILD_PAIR_CONDITION(m->has_init_system >= 0, "useBootableContainer", JSON_BUILD_BOOLEAN(m->has_init_system)), JSON_BUILD_PAIR("useInitrd", JSON_BUILD_BOOLEAN(!strv_isempty(m->initrd_release))), From 6e0b5cd36b07cc6b28358bec69eb9fa3842ac150 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Oct 2023 18:25:32 +0200 Subject: [PATCH 3/6] dissect: show architecture in JSON output too We show it in the human readable output, and we should include the same data in the JSON output too. --- src/dissect/dissect.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index a1ef16406a..888e84923f 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -902,11 +902,14 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { if (r < 0) return log_error_errno(r, "Failed to parse scope: %m"); + Architecture a = dissected_image_architecture(m); + r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR("name", JSON_BUILD_STRING(bn)), JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->image_uuid), "imageUuid", JSON_BUILD_UUID(m->image_uuid)), JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)), JSON_BUILD_PAIR("sectorSize", JSON_BUILD_INTEGER(m->sector_size)), + JSON_BUILD_PAIR_CONDITION(a >= 0, "architecture", JSON_BUILD_STRING(architecture_to_string(a))), JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)), JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)), JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->machine_info), "machineInfo", JSON_BUILD_STRV_ENV_PAIR(m->machine_info)), From 1c5cc6c552cd9e178b4e4293ced42085c0caff96 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Oct 2023 18:27:33 +0200 Subject: [PATCH 4/6] dissect: image size can be unset, suppres in JSON output then The ioctl() failure we handle gracefully, hence also reflect that in the JSON output. (While we are at it, bring JSON output into same order as textual output, i.e. move uuid down a bit.) --- src/dissect/dissect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 888e84923f..28cced4173 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -906,10 +906,10 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR("name", JSON_BUILD_STRING(bn)), - JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->image_uuid), "imageUuid", JSON_BUILD_UUID(m->image_uuid)), - JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)), + JSON_BUILD_PAIR_CONDITION(size != UINT64_MAX, "size", JSON_BUILD_INTEGER(size)), JSON_BUILD_PAIR("sectorSize", JSON_BUILD_INTEGER(m->sector_size)), JSON_BUILD_PAIR_CONDITION(a >= 0, "architecture", JSON_BUILD_STRING(architecture_to_string(a))), + JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->image_uuid), "imageUuid", JSON_BUILD_UUID(m->image_uuid)), JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)), JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)), JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->machine_info), "machineInfo", JSON_BUILD_STRV_ENV_PAIR(m->machine_info)), From a81fe93e9532bd3286f21ca0696208eb474f686c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Oct 2023 16:04:51 +0200 Subject: [PATCH 5/6] dissect: allow confext/sysext to be in the same image This reworks the image discovery logic, and conceptually allows DDIs that are both confext and sysext to exist. Previously we'd only extract one type of exension data from a DDI, with this we allow to extract both if both exist. This doesn't add support for true "multi-modal" DDIs, that qualify as various things at once, it just lays some ground work that ensures we at least can dissect such images. This reworks 484d26dac1e8e543fc9e300e3c1fa36be0769f7d quite a bit. This changes systemd-dissect's JSON output, but given the version with the fields it changes/dops has never been released (as the above patch was merged post-v254) this shouldn't be an issue. --- src/basic/os-util.h | 2 + src/dissect/dissect.c | 86 +++++++++++++++++++---------- src/shared/discover-image.c | 23 ++++---- src/shared/discover-image.h | 14 ++++- src/shared/dissect-image.c | 105 ++++++++++++++++++------------------ src/shared/dissect-image.h | 4 +- src/sysext/sysext.c | 2 +- test/units/testsuite-29.sh | 4 +- test/units/testsuite-50.sh | 8 +-- 9 files changed, 149 insertions(+), 99 deletions(-) diff --git a/src/basic/os-util.h b/src/basic/os-util.h index 11afc4c6ca..7cee3dd119 100644 --- a/src/basic/os-util.h +++ b/src/basic/os-util.h @@ -10,7 +10,9 @@ typedef enum ImageClass { IMAGE_MACHINE, IMAGE_PORTABLE, IMAGE_SYSEXT, + _IMAGE_CLASS_EXTENSION_FIRST = IMAGE_SYSEXT, /* First "extension" image type, so that we can easily generically iterate through them */ IMAGE_CONFEXT, + _IMAGE_CLASS_EXTENSION_LAST = IMAGE_CONFEXT, /* Last "extension image type */ _IMAGE_CLASS_MAX, _IMAGE_CLASS_INVALID = -EINVAL, } ImageClass; diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 28cced4173..a03f9a64db 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -765,24 +765,40 @@ static void strv_pair_print(char **l, const char *prefix) { printf("%*s %s=%s\n", (int) strlen(prefix), "", *p, *q); } -static int get_extension_scopes(DissectedImage *m, char ***ret_scopes) { +static int get_extension_scopes(DissectedImage *m, ImageClass class, char ***ret_scopes) { _cleanup_strv_free_ char **l = NULL; - const char *e; + const char *e, *field_name; + char **release_data; assert(m); assert(ret_scopes); + switch (class) { + + case IMAGE_SYSEXT: + release_data = m->sysext_release; + field_name = "SYSEXT_SCOPE"; + break; + + case IMAGE_CONFEXT: + release_data = m->confext_release; + field_name = "CONFEXT_SCOPE"; + break; + + default: + return -EINVAL; + } + /* If there's no extension-release file its not a system extension. Otherwise the SYSEXT_SCOPE * field for sysext images and the CONFEXT_SCOPE field for confext images indicates which scope * it is for — and it defaults to "system" + "portable" if unset. */ - if (!m->extension_release) { + + if (!release_data) { *ret_scopes = NULL; return 0; } - e = strv_env_pairs_get(m->extension_release, "SYSEXT_SCOPE"); - if (!e) - e = strv_env_pairs_get(m->extension_release, "CONFEXT_SCOPE"); + e = strv_env_pairs_get(release_data, field_name); if (e) l = strv_split(e, WHITESPACE); else @@ -843,7 +859,6 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { else if (r < 0) return log_error_errno(r, "Failed to acquire image metadata: %m"); else if (arg_json_format_flags & JSON_FORMAT_OFF) { - _cleanup_strv_free_ char **extension_scopes = NULL; if (!sd_id128_is_null(m->image_uuid)) printf("Image UUID: %s\n", SD_ID128_TO_UUID_STRING(m->image_uuid)); @@ -860,15 +875,18 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { "OS Release:"); strv_pair_print(m->initrd_release, "initrd R.:"); - strv_pair_print(m->extension_release, - " Ext. Rel.:"); + strv_pair_print(m->sysext_release, + " sysext R.:"); + strv_pair_print(m->confext_release, + "confext R.:"); if (m->hostname || !sd_id128_is_null(m->machine_id) || !strv_isempty(m->machine_info) || !strv_isempty(m->os_release) || !strv_isempty(m->initrd_release) || - !strv_isempty(m->extension_release)) + !strv_isempty(m->sysext_release) || + !strv_isempty(m->confext_release)) putc('\n', stdout); printf(" Use As: %s bootable system for UEFI\n", COLOR_MARK_BOOL(m->partitions[PARTITION_ESP].found)); @@ -881,26 +899,33 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { printf(" %s initrd\n", COLOR_MARK_BOOL(!strv_isempty(m->initrd_release))); - r = get_extension_scopes(m, &extension_scopes); - if (r < 0) - return log_error_errno(r, "Failed to parse scope: %m"); + for (ImageClass c = _IMAGE_CLASS_EXTENSION_FIRST; c <= _IMAGE_CLASS_EXTENSION_LAST; c++) { + const char *string_class = image_class_to_string(c); + _cleanup_strv_free_ char **extension_scopes = NULL; - const char *string_class = image_class_to_string(m->image_class); - printf(" %s %s extension for system\n", - COLOR_MARK_BOOL(strv_contains(extension_scopes, "system")), string_class); - printf(" %s %s extension for initrd\n", - COLOR_MARK_BOOL(strv_contains(extension_scopes, "initrd")), string_class); - printf(" %s %s extension for portable service\n", - COLOR_MARK_BOOL(strv_contains(extension_scopes, "portable")), string_class); + r = get_extension_scopes(m, c, &extension_scopes); + if (r < 0) + return log_error_errno(r, "Failed to parse scopes: %m"); + + printf(" %s %s for system\n", + COLOR_MARK_BOOL(strv_contains(extension_scopes, "system")), string_class); + printf(" %s %s for portable service\n", + COLOR_MARK_BOOL(strv_contains(extension_scopes, "portable")), string_class); + printf(" %s %s for initrd\n", + COLOR_MARK_BOOL(strv_contains(extension_scopes, "initrd")), string_class); + } putc('\n', stdout); } else { - _cleanup_(json_variant_unrefp) JsonVariant *mi = NULL, *osr = NULL, *irdr = NULL, *exr = NULL; - _cleanup_strv_free_ char **extension_scopes = NULL; + _cleanup_strv_free_ char **sysext_scopes = NULL, **confext_scopes = NULL; - r = get_extension_scopes(m, &extension_scopes); + r = get_extension_scopes(m, IMAGE_SYSEXT, &sysext_scopes); if (r < 0) - return log_error_errno(r, "Failed to parse scope: %m"); + return log_error_errno(r, "Failed to parse sysext scopes: %m"); + + r = get_extension_scopes(m, IMAGE_CONFEXT, &confext_scopes); + if (r < 0) + return log_error_errno(r, "Failed to parse confext scopes: %m"); Architecture a = dissected_image_architecture(m); @@ -915,15 +940,18 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->machine_info), "machineInfo", JSON_BUILD_STRV_ENV_PAIR(m->machine_info)), JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->os_release), "osRelease", JSON_BUILD_STRV_ENV_PAIR(m->os_release)), JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->initrd_release), "initrdRelease", JSON_BUILD_STRV_ENV_PAIR(m->initrd_release)), - JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->extension_release), "extensionRelease", JSON_BUILD_STRV_ENV_PAIR(m->extension_release)), + JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->sysext_release), "sysextRelease", JSON_BUILD_STRV_ENV_PAIR(m->sysext_release)), + JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->confext_release), "confextRelease", JSON_BUILD_STRV_ENV_PAIR(m->confext_release)), JSON_BUILD_PAIR("useBootableUefi", JSON_BUILD_BOOLEAN(m->partitions[PARTITION_ESP].found)), JSON_BUILD_PAIR_CONDITION(m->has_init_system >= 0, "useBootableContainer", JSON_BUILD_BOOLEAN(m->has_init_system)), JSON_BUILD_PAIR("useInitrd", JSON_BUILD_BOOLEAN(!strv_isempty(m->initrd_release))), JSON_BUILD_PAIR("usePortableService", JSON_BUILD_BOOLEAN(strv_env_pairs_get(m->os_release, "PORTABLE_MATCHES"))), - JSON_BUILD_PAIR("ExtensionType", JSON_BUILD_STRING(image_class_to_string(m->image_class))), - JSON_BUILD_PAIR("useSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(extension_scopes, "system"))), - JSON_BUILD_PAIR("useInitRDExtension", JSON_BUILD_BOOLEAN(strv_contains(extension_scopes, "initrd"))), - JSON_BUILD_PAIR("usePortableExtension", JSON_BUILD_BOOLEAN(strv_contains(extension_scopes, "portable"))))); + JSON_BUILD_PAIR("useSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "system"))), + JSON_BUILD_PAIR("useInitRDSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "initrd"))), + JSON_BUILD_PAIR("usePortableSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "portable"))), + JSON_BUILD_PAIR("useConfigurationExtension", JSON_BUILD_BOOLEAN(strv_contains(confext_scopes, "system"))), + JSON_BUILD_PAIR("useInitRDConfigurationExtension", JSON_BUILD_BOOLEAN(strv_contains(confext_scopes, "initrd"))), + JSON_BUILD_PAIR("usePortableConfigurationExtension", JSON_BUILD_BOOLEAN(strv_contains(confext_scopes, "portable"))))); if (r < 0) return log_oom(); } diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index e407820e18..094337616d 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -101,7 +101,8 @@ static Image *image_free(Image *i) { free(i->hostname); strv_free(i->machine_info); strv_free(i->os_release); - strv_free(i->extension_release); + strv_free(i->sysext_release); + strv_free(i->confext_release); return mfree(i); } @@ -1180,10 +1181,9 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) { case IMAGE_SUBVOLUME: case IMAGE_DIRECTORY: { - _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **extension_release = NULL; + _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **sysext_release = NULL, **confext_release = NULL; + _cleanup_free_ char *hostname = NULL, *path = NULL; sd_id128_t machine_id = SD_ID128_NULL; - _cleanup_free_ char *hostname = NULL; - _cleanup_free_ char *path = NULL; if (i->class == IMAGE_SYSEXT) { r = extension_has_forbidden_content(i->path); @@ -1223,16 +1223,20 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) { if (r < 0) log_debug_errno(r, "Failed to read os-release in image, ignoring: %m"); - r = load_extension_release_pairs(i->path, i->class, i->name, /* relax_extension_release_check= */ false, &extension_release); + r = load_extension_release_pairs(i->path, IMAGE_SYSEXT, i->name, /* relax_extension_release_check= */ false, &sysext_release); if (r < 0) - log_debug_errno(r, "Failed to read extension-release in image, ignoring: %m"); + log_debug_errno(r, "Failed to read sysext-release in image, ignoring: %m"); + + r = load_extension_release_pairs(i->path, IMAGE_CONFEXT, i->name, /* relax_extension_release_check= */ false, &confext_release); + if (r < 0) + log_debug_errno(r, "Failed to read confext-release in image, ignoring: %m"); free_and_replace(i->hostname, hostname); i->machine_id = machine_id; strv_free_and_replace(i->machine_info, machine_info); strv_free_and_replace(i->os_release, os_release); - strv_free_and_replace(i->extension_release, extension_release); - + strv_free_and_replace(i->sysext_release, sysext_release); + strv_free_and_replace(i->confext_release, confext_release); break; } @@ -1271,7 +1275,8 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) { i->machine_id = m->machine_id; strv_free_and_replace(i->machine_info, m->machine_info); strv_free_and_replace(i->os_release, m->os_release); - strv_free_and_replace(i->extension_release, m->extension_release); + strv_free_and_replace(i->sysext_release, m->sysext_release); + strv_free_and_replace(i->confext_release, m->confext_release); break; } diff --git a/src/shared/discover-image.h b/src/shared/discover-image.h index edfb1412a4..bb046fae1e 100644 --- a/src/shared/discover-image.h +++ b/src/shared/discover-image.h @@ -45,7 +45,8 @@ typedef struct Image { sd_id128_t machine_id; char **machine_info; char **os_release; - char **extension_release; + char **sysext_release; + char **confext_release; bool metadata_valid:1; bool discoverable:1; /* true if we know for sure that image_find() would find the image given just the short name */ @@ -80,6 +81,17 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy); bool image_in_search_path(ImageClass class, const char *root, const char *image); +static inline char **image_extension_release(Image *image, ImageClass class) { + assert(image); + + if (class == IMAGE_SYSEXT) + return image->sysext_release; + if (class == IMAGE_CONFEXT) + return image->confext_release; + + return NULL; +} + static inline bool IMAGE_IS_HIDDEN(const struct Image *i) { assert(i); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 278d2291a2..e63545312a 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1665,7 +1665,8 @@ DissectedImage* dissected_image_unref(DissectedImage *m) { strv_free(m->machine_info); strv_free(m->os_release); strv_free(m->initrd_release); - strv_free(m->extension_release); + strv_free(m->confext_release); + strv_free(m->sysext_release); return mfree(m); } @@ -3352,7 +3353,8 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ META_MACHINE_INFO, META_OS_RELEASE, META_INITRD_RELEASE, - META_EXTENSION_RELEASE, + META_SYSEXT_RELEASE, + META_CONFEXT_RELEASE, META_HAS_INIT_SYSTEM, _META_MAX, }; @@ -3361,15 +3363,16 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ [META_HOSTNAME] = "/etc/hostname\0", [META_MACHINE_ID] = "/etc/machine-id\0", [META_MACHINE_INFO] = "/etc/machine-info\0", - [META_OS_RELEASE] = ("/etc/os-release\0" - "/usr/lib/os-release\0"), - [META_INITRD_RELEASE] = ("/etc/initrd-release\0" - "/usr/lib/initrd-release\0"), - [META_EXTENSION_RELEASE] = "extension-release\0", /* Used only for logging. */ + [META_OS_RELEASE] = "/etc/os-release\0" + "/usr/lib/os-release\0", + [META_INITRD_RELEASE] = "/etc/initrd-release\0" + "/usr/lib/initrd-release\0", + [META_SYSEXT_RELEASE] = "sysext-release\0", /* String used only for logging. */ + [META_CONFEXT_RELEASE] = "confext-release\0", /* ditto */ [META_HAS_INIT_SYSTEM] = "has-init-system\0", /* ditto */ }; - _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **initrd_release = NULL, **extension_release = NULL; + _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **initrd_release = NULL, **sysext_release = NULL, **confext_release = NULL; _cleanup_close_pair_ int error_pipe[2] = PIPE_EBADF; _cleanup_(rmdir_and_freep) char *t = NULL; _cleanup_(sigkill_waitp) pid_t child = 0; @@ -3379,12 +3382,10 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ int fds[2 * _META_MAX], r, v; int has_init_system = -1; ssize_t n; - ImageClass image_class = IMAGE_SYSEXT; BLOCK_SIGNALS(SIGCHLD); assert(m); - assert(image_class); for (; n_meta_initialized < _META_MAX; n_meta_initialized ++) { if (!paths[n_meta_initialized]) { @@ -3439,40 +3440,46 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ switch (k) { - case META_EXTENSION_RELEASE: { - /* As per the os-release spec, if the image is an extension it will have a file - * named after the image name in extension-release.d/ - we use the image name - * and try to resolve it with the extension-release helpers, as sometimes - * the image names are mangled on deployment and do not match anymore. - * Unlike other paths this is not fixed, and the image name - * can be mangled on deployment, so by calling into the helper - * we allow a fallback that matches on the first extension-release - * file found in the directory, if one named after the image cannot - * be found first. */ - ImageClass class = IMAGE_SYSEXT; - r = open_extension_release(t, IMAGE_SYSEXT, m->image_name, /* relax_extension_release_check= */ false, NULL, &fd); - if (r == -ENOENT) { - r = open_extension_release(t, IMAGE_CONFEXT, m->image_name, /* relax_extension_release_check= */ false, NULL, &fd); - if (r >= 0) - class = IMAGE_CONFEXT; - } + case META_SYSEXT_RELEASE: + /* As per the os-release spec, if the image is an extension it will have a + * file named after the image name in extension-release.d/ - we use the image + * name and try to resolve it with the extension-release helpers, as + * sometimes the image names are mangled on deployment and do not match + * anymore. Unlike other paths this is not fixed, and the image name can be + * mangled on deployment, so by calling into the helper we allow a fallback + * that matches on the first extension-release file found in the directory, + * if one named after the image cannot be found first. */ + r = open_extension_release( + t, + IMAGE_SYSEXT, + m->image_name, + /* relax_extension_release_check= */ false, + /* ret_path= */ NULL, + &fd); + if (r < 0) + fd = r; + break; + + case META_CONFEXT_RELEASE: + /* As above */ + r = open_extension_release( + t, + IMAGE_CONFEXT, + m->image_name, + /* relax_extension_release_check= */ false, + /* ret_path= */ NULL, + &fd); if (r < 0) fd = r; - else { - r = loop_write(fds[2*k+1], &class, sizeof(class)); - if (r < 0) - goto inner_fail; /* Propagate the error to the parent */ - } break; - } case META_HAS_INIT_SYSTEM: { bool found = false; FOREACH_STRING(init, - "/usr/lib/systemd/systemd", /* systemd on /usr merged system */ - "/lib/systemd/systemd", /* systemd on /usr non-merged systems */ + "/usr/lib/systemd/systemd", /* systemd on /usr/ merged system */ + "/lib/systemd/systemd", /* systemd on /usr/ non-merged systems */ "/sbin/init") { /* traditional path the Linux kernel invokes */ r = chase(init, t, CHASE_PREFIX_ROOT, NULL, NULL); @@ -3587,23 +3594,19 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ break; - case META_EXTENSION_RELEASE: { - ImageClass cl = IMAGE_SYSEXT; - size_t nr; + case META_SYSEXT_RELEASE: + r = load_env_file_pairs(f, "sysext-release", &sysext_release); + if (r < 0) + log_debug_errno(r, "Failed to read sysext release file of image: %m"); - errno = 0; - nr = fread(&cl, 1, sizeof(cl), f); - if (nr != sizeof(cl)) - log_debug_errno(errno_or_else(EIO), "Failed to read class of extension image: %m"); - else { - image_class = cl; - r = load_env_file_pairs(f, "extension-release", &extension_release); - if (r < 0) - log_debug_errno(r, "Failed to read extension release file of image: %m"); - } + break; + + case META_CONFEXT_RELEASE: + r = load_env_file_pairs(f, "confext-release", &confext_release); + if (r < 0) + log_debug_errno(r, "Failed to read confext release file of image: %m"); break; - } case META_HAS_INIT_SYSTEM: { bool b = false; @@ -3641,9 +3644,9 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ strv_free_and_replace(m->machine_info, machine_info); strv_free_and_replace(m->os_release, os_release); strv_free_and_replace(m->initrd_release, initrd_release); - strv_free_and_replace(m->extension_release, extension_release); + strv_free_and_replace(m->sysext_release, sysext_release); + strv_free_and_replace(m->confext_release, confext_release); m->has_init_system = has_init_system; - m->image_class = image_class; finish: for (unsigned k = 0; k < n_meta_initialized; k++) diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index eb0841bd2e..21a0f22bcc 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -108,9 +108,9 @@ struct DissectedImage { char **machine_info; char **os_release; char **initrd_release; - char **extension_release; + char **confext_release; + char **sysext_release; int has_init_system; - ImageClass image_class; }; struct MountOptions { diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 784accc15d..becfbab44e 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -741,7 +741,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) { host_os_release_version_id, host_os_release_api_level, in_initrd() ? "initrd" : "system", - img->extension_release, + image_extension_release(img, arg_image_class), arg_image_class); if (r < 0) return r; diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh index 36e5cdc126..4bbbd38bee 100755 --- a/test/units/testsuite-29.sh +++ b/test/units/testsuite-29.sh @@ -31,8 +31,8 @@ fi systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service' systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service' -systemd-dissect --no-pager /usr/share/app0.raw | grep -q '✓ sysext extension for portable service' -systemd-dissect --no-pager /usr/share/app1.raw | grep -q '✓ sysext extension for portable service' +systemd-dissect --no-pager /usr/share/app0.raw | grep -q '✓ sysext for portable service' +systemd-dissect --no-pager /usr/share/app1.raw | grep -q '✓ sysext for portable service' export SYSTEMD_LOG_LEVEL=debug mkdir -p /run/systemd/system/systemd-portabled.service.d/ diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index b7943397c4..0cec747397 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -573,8 +573,8 @@ echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile mksquashfs testkit/ testkit.raw cp testkit.raw /run/extensions/ unsquashfs -l /run/extensions/testkit.raw -systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext extension for portable service' -systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext extension for system' +systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service' +systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system' systemd-sysext merge systemd-sysext status grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile @@ -589,8 +589,8 @@ echo "MARKER_CONFEXT_123" >testjob/etc/testfile mksquashfs testjob/ testjob.raw cp testjob.raw /run/confexts/ unsquashfs -l /run/confexts/testjob.raw -systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext extension for system' -systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext extension for portable service' +systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system' +systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service' systemd-confext merge systemd-confext status grep -q -F "MARKER_CONFEXT_123" /etc/testfile From b2466e05c79e2d1b9c7e42f52574cb299bd77575 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Oct 2023 23:09:45 +0200 Subject: [PATCH 6/6] dissect: move helpers for categorizing DDIs into generic code These tests are already done at two places, let's unify them in one place, and tweak them slightly (specifically: require for considering a DDI bootable in UEFI we also need need an init system inside). --- src/dissect/dissect.c | 21 ++++++++++----------- src/shared/dissect-image.h | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index a03f9a64db..e232e4f7e5 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -889,15 +889,14 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { !strv_isempty(m->confext_release)) putc('\n', stdout); - printf(" Use As: %s bootable system for UEFI\n", COLOR_MARK_BOOL(m->partitions[PARTITION_ESP].found)); - - if (m->has_init_system >= 0) - printf(" %s bootable system for container\n", COLOR_MARK_BOOL(m->has_init_system)); - + printf(" Use As: %s bootable system for UEFI\n", + COLOR_MARK_BOOL(dissected_image_is_bootable_uefi(m))); + printf(" %s bootable system for container\n", + COLOR_MARK_BOOL(dissected_image_is_bootable_os(m))); printf(" %s portable service\n", - COLOR_MARK_BOOL(strv_env_pairs_get(m->os_release, "PORTABLE_PREFIXES"))); + COLOR_MARK_BOOL(dissected_image_is_portable(m))); printf(" %s initrd\n", - COLOR_MARK_BOOL(!strv_isempty(m->initrd_release))); + COLOR_MARK_BOOL(dissected_image_is_initrd(m))); for (ImageClass c = _IMAGE_CLASS_EXTENSION_FIRST; c <= _IMAGE_CLASS_EXTENSION_LAST; c++) { const char *string_class = image_class_to_string(c); @@ -942,10 +941,10 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->initrd_release), "initrdRelease", JSON_BUILD_STRV_ENV_PAIR(m->initrd_release)), JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->sysext_release), "sysextRelease", JSON_BUILD_STRV_ENV_PAIR(m->sysext_release)), JSON_BUILD_PAIR_CONDITION(!strv_isempty(m->confext_release), "confextRelease", JSON_BUILD_STRV_ENV_PAIR(m->confext_release)), - JSON_BUILD_PAIR("useBootableUefi", JSON_BUILD_BOOLEAN(m->partitions[PARTITION_ESP].found)), - JSON_BUILD_PAIR_CONDITION(m->has_init_system >= 0, "useBootableContainer", JSON_BUILD_BOOLEAN(m->has_init_system)), - JSON_BUILD_PAIR("useInitrd", JSON_BUILD_BOOLEAN(!strv_isempty(m->initrd_release))), - JSON_BUILD_PAIR("usePortableService", JSON_BUILD_BOOLEAN(strv_env_pairs_get(m->os_release, "PORTABLE_MATCHES"))), + JSON_BUILD_PAIR("useBootableUefi", JSON_BUILD_BOOLEAN(dissected_image_is_bootable_uefi(m))), + JSON_BUILD_PAIR("useBootableContainer", JSON_BUILD_BOOLEAN(dissected_image_is_bootable_os(m))), + JSON_BUILD_PAIR("useInitrd", JSON_BUILD_BOOLEAN(dissected_image_is_initrd(m))), + JSON_BUILD_PAIR("usePortableService", JSON_BUILD_BOOLEAN(dissected_image_is_portable(m))), JSON_BUILD_PAIR("useSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "system"))), JSON_BUILD_PAIR("useInitRDSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "initrd"))), JSON_BUILD_PAIR("usePortableSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "portable"))), diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 21a0f22bcc..8be632acc2 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -6,11 +6,13 @@ #include "sd-id128.h" #include "architecture.h" +#include "env-util.h" #include "gpt.h" #include "list.h" #include "loop-util.h" #include "macro.h" #include "os-util.h" +#include "strv.h" typedef struct DissectedImage DissectedImage; typedef struct DissectedPartition DissectedPartition; @@ -169,6 +171,22 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ Architecture dissected_image_architecture(DissectedImage *m); +static inline bool dissected_image_is_bootable_os(DissectedImage *m) { + return m && m->has_init_system > 0; +} + +static inline bool dissected_image_is_bootable_uefi(DissectedImage *m) { + return m && m->partitions[PARTITION_ESP].found && dissected_image_is_bootable_os(m); +} + +static inline bool dissected_image_is_portable(DissectedImage *m) { + return m && strv_env_pairs_get(m->os_release, "PORTABLE_PREFIXES"); +} + +static inline bool dissected_image_is_initrd(DissectedImage *m) { + return m && !strv_isempty(m->initrd_release); +} + DecryptedImage* decrypted_image_ref(DecryptedImage *p); DecryptedImage* decrypted_image_unref(DecryptedImage *p); DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);