#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"
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;
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();
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;
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";
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;
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);