From: dongshengyuan <545258830@qq.com> Date: Thu, 2 Jul 2026 08:09:21 +0000 (+0800) Subject: portablectl: retry inspect with PORTABLE_PREFIXES X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=63b30468a5c2e1a7796a68df36e5ff3088504967;p=thirdparty%2Fsystemd.git portablectl: retry inspect with PORTABLE_PREFIXES When no prefix is specified, portablectl inspect first tries the prefix derived from the image name. This keeps inspect aligned with attach behavior. If that lookup finds no matching units, retry with validated PORTABLE_PREFIXES read from the image os-release. This makes inspect work for images whose filename does not match their portable service prefix. Keep metadata error handling explicit so request-construction failures are not logged twice, while sd_bus_call() failures still include the inspect context. Add a TEST-29-PORTABLE regression case for a directory image whose name does not match the portable service prefix. Fixes #37296. Signed-off-by: dongshengyuan --- diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 6c2b0fd8814..63dfa186266 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -48,6 +48,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, ENOENT), SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE, EMEDIUMTYPE), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_MATCHING_UNIT_FILES, ENOENT), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index b4788433bfc..2c2a57eae52 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -47,6 +47,7 @@ #define BUS_ERROR_NO_SUCH_PORTABLE_IMAGE "org.freedesktop.portable1.NoSuchImage" #define BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE "org.freedesktop.portable1.BadImageType" +#define BUS_ERROR_NO_MATCHING_UNIT_FILES "org.freedesktop.portable1.NoMatchingUnitFiles" #define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession" #define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID" diff --git a/src/portable/portable.c b/src/portable/portable.c index 6779ddd5514..d49c8a76b5e 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -1098,7 +1098,7 @@ int portable_extract( return -ENOMEM; return sd_bus_error_setf(error, - SD_BUS_ERROR_INVALID_ARGS, + BUS_ERROR_NO_MATCHING_UNIT_FILES, "Couldn't find any matching unit files in image '%s%s%s', refusing.", image->path, isempty(extensions) ? "" : "' or any of its extensions '", @@ -2102,7 +2102,7 @@ int portable_attach( return sd_bus_error_setf( error, - SD_BUS_ERROR_INVALID_ARGS, + BUS_ERROR_NO_MATCHING_UNIT_FILES, "Couldn't find any matching unit files in image '%s%s%s', refusing.", image->path, isempty(extensions_joined) ? "" : "' or any of its extensions '", diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index 831b4543722..c6eed7ae0f0 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "ansi-color.h" #include "build.h" +#include "bus-common-errors.h" #include "bus-error.h" #include "bus-locator.h" #include "bus-unit-util.h" @@ -169,6 +170,27 @@ static int extract_prefix(const char *path, char **ret) { return 0; } +static int log_unit_file_matches(char **matches) { + if (arg_quiet) + return 0; + + if (strv_isempty(matches)) + log_info("(Matching all unit files.)"); + else if (strv_length(matches) == 1) + log_info("(Matching unit files with prefix '%s'.)", matches[0]); + else { + _cleanup_free_ char *joined = NULL; + + joined = strv_join(matches, "', '"); + if (!joined) + return log_oom(); + + log_info("(Matching unit files with prefixes '%s'.)", joined); + } + + return 0; +} + static int determine_matches(const char *image, char **l, bool allow_any, char ***ret) { _cleanup_strv_free_ char **k = NULL; int r; @@ -184,9 +206,6 @@ static int determine_matches(const char *image, char **l, bool allow_any, char * if (r < 0) return log_error_errno(r, "Failed to extract prefix of image name '%s': %m", image); - if (!arg_quiet) - log_info("(Matching unit files with prefix '%s'.)", prefix); - r = strv_consume(&k, prefix); if (r < 0) return log_oom(); @@ -197,25 +216,17 @@ static int determine_matches(const char *image, char **l, bool allow_any, char * return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Refusing all unit file match."); - if (!arg_quiet) - log_info("(Matching all unit files.)"); } else { k = strv_copy(l); if (!k) return log_oom(); - - if (!arg_quiet) { - _cleanup_free_ char *joined = NULL; - - joined = strv_join(k, "', '"); - if (!joined) - return log_oom(); - - log_info("(Matching unit files with prefixes '%s'.)", joined); - } } + r = log_unit_file_matches(k); + if (r < 0) + return r; + *ret = TAKE_PTR(k); return 0; @@ -329,15 +340,78 @@ static int verb_list_images(int argc, char *argv[], uintptr_t _data, void *userd return 0; } -static int get_image_metadata(sd_bus *bus, const char *image, char **matches, sd_bus_message **reply) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; +static int determine_matches_from_os_release(sd_bus *bus, const char *image, char ***ret) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_strv_free_ char **matches = NULL; + int r; + + assert(bus); + assert(image); + assert(ret); + + r = bus_call_method(bus, bus_portable_mgr, "GetImageOSRelease", &error, &reply, "s", image); + if (r < 0) { + log_debug_errno(r, "Failed to inspect image os-release: %s", bus_error_message(&error, r)); + *ret = NULL; + return 0; + } + + r = sd_bus_message_enter_container(reply, 'a', "{ss}"); + if (r < 0) + return bus_log_parse_error(r); + + for (;;) { + _cleanup_strv_free_ char **split = NULL; + const char *key, *value; + + r = sd_bus_message_read(reply, "{ss}", &key, &value); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; + + if (!streq(key, "PORTABLE_PREFIXES")) + continue; + + split = strv_split(value, WHITESPACE); + if (!split) + return log_oom(); + + STRV_FOREACH(prefix, split) + if (!string_is_safe(*prefix, STRING_FILENAME_PART)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid PORTABLE_PREFIXES= entry in image os-release, refusing."); + + strv_free_and_replace(matches, split); + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + if (strv_isempty(matches)) { + *ret = NULL; + return 0; + } + + *ret = TAKE_PTR(matches); + return 0; +} + +static int make_get_image_metadata_message( + sd_bus *bus, + const char *image, + char **matches, + sd_bus_message **ret) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; uint64_t flags = arg_force ? PORTABLE_FORCE_EXTENSION : 0; const char *method; int r; assert(bus); - assert(reply); + assert(ret); method = strv_isempty(arg_extension_images) && !arg_force ? "GetImageMetadata" : "GetImageMetadataWithExtensions"; @@ -363,19 +437,42 @@ static int get_image_metadata(sd_bus *bus, const char *image, char **matches, sd return bus_log_create_error(r); } + *ret = TAKE_PTR(m); + return 0; +} + +static int log_image_metadata_error(int r, const sd_bus_error *error) { + return log_error_errno(r, "Failed to inspect image metadata: %s", bus_error_message(error, r)); +} + +static int get_image_metadata(sd_bus *bus, const char *image, char **matches, sd_bus_message **reply) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + r = make_get_image_metadata_message(bus, image, matches, &m); + if (r < 0) + return r; + r = sd_bus_call(bus, m, 0, &error, reply); if (r < 0) - return log_error_errno(r, "Failed to inspect image metadata: %s", bus_error_message(&error, r)); + return log_image_metadata_error(r, &error); return 0; } +static bool image_metadata_error_is_no_match(const sd_bus_error *error) { + return sd_bus_error_has_name(error, BUS_ERROR_NO_MATCHING_UNIT_FILES); +} + VERB(verb_inspect_image, "inspect", "NAME|PATH [PREFIX…]", 2, VERB_ANY, 0, "Show details of specified portable service image"); static int verb_inspect_image(int argc, char *argv[], uintptr_t _data, void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_strv_free_ char **matches = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_strv_free_ char **matches = NULL, **fallback_matches = NULL; _cleanup_free_ char *image = NULL; bool nl = false, header = false; const char *path; @@ -387,18 +484,44 @@ static int verb_inspect_image(int argc, char *argv[], uintptr_t _data, void *use if (r < 0) return r; - r = determine_matches(argv[1], argv + 2, true, &matches); + r = acquire_bus(&bus); if (r < 0) return r; - r = acquire_bus(&bus); + r = determine_matches(argv[1], argv + 2, true, &matches); if (r < 0) return r; - r = get_image_metadata(bus, image, matches, &reply); + r = make_get_image_metadata_message(bus, image, matches, &m); if (r < 0) return r; + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + int first_error = r; + + if (!strv_isempty(argv + 2) || !image_metadata_error_is_no_match(&error)) + return log_image_metadata_error(r, &error); + + r = determine_matches_from_os_release(bus, image, &fallback_matches); + if (r < 0) + return r; + + if (strv_isempty(fallback_matches)) + return log_image_metadata_error(first_error, &error); + + if (!arg_quiet) + log_info("(No matching unit files found with the image name prefix, retrying with PORTABLE_PREFIXES= from os-release.)"); + + r = log_unit_file_matches(fallback_matches); + if (r < 0) + return r; + + r = get_image_metadata(bus, image, fallback_matches, &reply); + if (r < 0) + return r; + } + r = sd_bus_message_read(reply, "s", &path); if (r < 0) return bus_log_parse_error(r); diff --git a/test/units/TEST-29-PORTABLE.directory.sh b/test/units/TEST-29-PORTABLE.directory.sh index 42f280b78ad..e6aafcb6af0 100755 --- a/test/units/TEST-29-PORTABLE.directory.sh +++ b/test/units/TEST-29-PORTABLE.directory.sh @@ -20,6 +20,10 @@ fi unsquashfs -force -no-xattrs -d /tmp/minimal_0 /usr/share/minimal_0.raw unsquashfs -force -no-xattrs -d /tmp/minimal_1 /usr/share/minimal_1.raw +rm -rf /tmp/mismatched-name +cp -a /tmp/minimal_0 /tmp/mismatched-name +portablectl inspect /tmp/mismatched-name | grep -F "minimal-app0.service" >/dev/null + portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0 systemctl is-active minimal-app0.service