]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
portable: add flag to return extension-releases in GetImageMetadataWithExtensions
authorLuca Boccassi <luca.boccassi@microsoft.com>
Tue, 25 Jan 2022 15:49:22 +0000 (15:49 +0000)
committerLuca Boccassi <luca.boccassi@gmail.com>
Tue, 25 Jan 2022 22:22:47 +0000 (22:22 +0000)
Return the name of each extension and the associated extension-release
file, and pretty-print them in 'portablectl inspect', if a new flag
is passed.

$ portablectl inspect --extension app2 --extension app0  minimal app0 app1
(Matching unit files with prefixes 'app0', 'app1'.)
Image:
        /run/portables/minimal.raw
Portable Service:
        n/a
Operating System:
        Debian GNU/Linux 10 (buster)
Extension:
        /run/portables/app2.raw
        Extension Scope:
                n/a
        Extension Compatibility Level:
                n/a
        Portable Service:
                n/a
        Portable Prefixes:
                n/a
        Operating System:
                n/a (debian 10)
Extension:
        /run/portables/app0.raw
        Extension Scope:
                n/a
        Extension Compatibility Level:
                n/a
        Portable Service:
                n/a
        Portable Prefixes:
                n/a
        Operating System:
                n/a (debian 10)
Unit files:
        app0.service

man/org.freedesktop.portable1.xml
src/portable/portable.c
src/portable/portable.h
src/portable/portablectl.c
src/portable/portabled-image-bus.c
test/units/testsuite-29.sh

index a63b6aeebe98ab3b7a49622fef6923489ff97c2a..4842885c0cc50f079b540ad3be99e5252bfc2cf2 100644 (file)
@@ -193,7 +193,15 @@ node /org/freedesktop/portable1 {
       This method is a superset of <function>GetImageMetadata()</function> with the addition of
       a list of extensions as input parameter, which were overlaid on top of the main
       image via <function>AttachImageWithExtensions()</function>.
-      The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
+      The <varname>flag</varname> parameter can be used to request that, before the units, the path of
+      each extension and an array of bytes with the content of the respective extension-release file
+      are sent. One such structure will be sent for each extension named in the input arguments. The
+      flag value to enable this functionality is defined as follows:</para>
+
+      <programlisting>
+#define PORTABLE_INSPECT_EXTENSION_RELEASES  (UINT64_C(1) &lt;&lt; 1)
+      </programlisting>
+
 
       <para><function>GetImageState()</function> retrieves the image state as one of the following
       strings:
index f460a6b2242e7cb92c3299fe4aba462980149f79..325cb702c51c24b1d3866803f00a4444a3b0c921 100644 (file)
@@ -512,6 +512,7 @@ static int extract_image_and_extensions(
                 bool validate_sysext,
                 Image **ret_image,
                 OrderedHashmap **ret_extension_images,
+                OrderedHashmap **ret_extension_releases,
                 PortableMetadata **ret_os_release,
                 Hashmap **ret_unit_files,
                 char ***ret_valid_prefixes,
@@ -519,7 +520,7 @@ static int extract_image_and_extensions(
 
         _cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL;
         _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
-        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
+        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
         _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
         _cleanup_strv_free_ char **valid_prefixes = NULL;
         _cleanup_(image_unrefp) Image *image = NULL;
@@ -540,6 +541,12 @@ static int extract_image_and_extensions(
                 if (!extension_images)
                         return -ENOMEM;
 
+                if (ret_extension_releases) {
+                        extension_releases = ordered_hashmap_new(&portable_metadata_hash_ops);
+                        if (!extension_releases)
+                                return -ENOMEM;
+                }
+
                 STRV_FOREACH(p, extension_image_paths) {
                         _cleanup_(image_unrefp) Image *new = NULL;
 
@@ -588,6 +595,7 @@ static int extract_image_and_extensions(
                 _cleanup_(portable_metadata_unrefp) PortableMetadata *extension_release_meta = NULL;
                 _cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
                 _cleanup_strv_free_ char **extension_release = NULL;
+                _cleanup_close_ int extension_release_fd = -1;
                 _cleanup_fclose_ FILE *f = NULL;
                 const char *e;
 
@@ -599,10 +607,15 @@ static int extract_image_and_extensions(
                 if (r < 0)
                         return r;
 
-                if (!validate_sysext && !ret_valid_prefixes)
+                if (!validate_sysext && !ret_valid_prefixes && !ret_extension_releases)
                         continue;
 
-                r = take_fdopen_unlocked(&extension_release_meta->fd, "r", &f);
+                /* We need to keep the fd valid, to return the PortableMetadata to the caller. */
+                extension_release_fd = fd_reopen(extension_release_meta->fd, O_CLOEXEC);
+                if (extension_release_fd < 0)
+                        return extension_release_fd;
+
+                r = take_fdopen_unlocked(&extension_release_fd, "r", &f);
                 if (r < 0)
                         return r;
 
@@ -630,6 +643,13 @@ static int extract_image_and_extensions(
                         if (r < 0)
                                 return r;
                 }
+
+                if (ret_extension_releases) {
+                        r = ordered_hashmap_put(extension_releases, ext->name, extension_release_meta);
+                        if (r < 0)
+                                return r;
+                        TAKE_PTR(extension_release_meta);
+                }
         }
 
         strv_sort(valid_prefixes);
@@ -638,6 +658,8 @@ static int extract_image_and_extensions(
                 *ret_image = TAKE_PTR(image);
         if (ret_extension_images)
                 *ret_extension_images = TAKE_PTR(extension_images);
+        if (ret_extension_releases)
+                *ret_extension_releases = TAKE_PTR(extension_releases);
         if (ret_os_release)
                 *ret_os_release = TAKE_PTR(os_release);
         if (ret_unit_files)
@@ -653,12 +675,13 @@ int portable_extract(
                 char **matches,
                 char **extension_image_paths,
                 PortableMetadata **ret_os_release,
+                OrderedHashmap **ret_extension_releases,
                 Hashmap **ret_unit_files,
                 char ***ret_valid_prefixes,
                 sd_bus_error *error) {
 
         _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
-        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
+        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
         _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
         _cleanup_(strv_freep) char **valid_prefixes = NULL;
         _cleanup_(image_unrefp) Image *image = NULL;
@@ -673,6 +696,7 @@ int portable_extract(
                         /* validate_sysext= */ false,
                         &image,
                         &extension_images,
+                        &extension_releases,
                         &os_release,
                         &unit_files,
                         ret_valid_prefixes ? &valid_prefixes : NULL,
@@ -695,6 +719,8 @@ int portable_extract(
 
         if (ret_os_release)
                 *ret_os_release = TAKE_PTR(os_release);
+        if (ret_extension_releases)
+                *ret_extension_releases = TAKE_PTR(extension_releases);
         if (ret_unit_files)
                 *ret_unit_files = TAKE_PTR(unit_files);
         if (ret_valid_prefixes)
@@ -1272,6 +1298,7 @@ int portable_attach(
                         /* validate_sysext= */ true,
                         &image,
                         &extension_images,
+                        /* extension_releases= */ NULL,
                         /* os_release= */ NULL,
                         &unit_files,
                         &valid_prefixes,
index fddb4e46ce5eae520d1fbbaf9cbd8b98b38894ab..62e1cb70328aa2cc785ae30cbb964f4f9e6dd4c1 100644 (file)
@@ -21,13 +21,14 @@ typedef struct PortableMetadata {
 #define PORTABLE_METADATA_IS_UNIT(m) (!IN_SET((m)->name[0], 0, '/'))
 
 typedef enum PortableFlags {
-        PORTABLE_RUNTIME        = 1 << 0, /* Public API via DBUS, do not change */
-        PORTABLE_PREFER_COPY    = 1 << 1,
-        PORTABLE_PREFER_SYMLINK = 1 << 2,
-        PORTABLE_REATTACH       = 1 << 3,
-        _PORTABLE_MASK_PUBLIC   = PORTABLE_RUNTIME,
+        PORTABLE_RUNTIME                    = 1 << 0,
+        PORTABLE_INSPECT_EXTENSION_RELEASES = 1 << 1, /* Public API via DBUS, do not change */
+        PORTABLE_PREFER_COPY                = 1 << 2,
+        PORTABLE_PREFER_SYMLINK             = 1 << 3,
+        PORTABLE_REATTACH                   = 1 << 4,
+        _PORTABLE_MASK_PUBLIC               = PORTABLE_RUNTIME | PORTABLE_INSPECT_EXTENSION_RELEASES,
         _PORTABLE_TYPE_MAX,
-        _PORTABLE_TYPE_INVALID  = -EINVAL,
+        _PORTABLE_TYPE_INVALID              = -EINVAL,
 } PortableFlags;
 
 /* This enum is anonymous, since we usually store it in an 'int', as we overload it with negative errno
@@ -65,7 +66,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref);
 
 int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
 
-int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
+int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
 
 int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
 int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
index 27883eb867dd2b46db0e974f344920c5c45ee6d4..eee26d00331d369a3615b46d97f09521b3f24655 100644 (file)
@@ -260,8 +260,8 @@ static int maybe_reload(sd_bus **bus) {
 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;
+        PortableFlags flags = PORTABLE_INSPECT_EXTENSION_RELEASES;
         const char *method;
-        uint64_t flags = 0;
         int r;
 
         assert(bus);
@@ -366,6 +366,74 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
+        /* If we specified any extensions, we'll first get back exactly the
+         * paths (and extension-release content) for each one of the arguments. */
+        for (size_t i = 0; i < strv_length(arg_extension_images); ++i) {
+                const char *name;
+
+                r = sd_bus_message_enter_container(reply, 'e', "say");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+                if (r == 0)
+                        break;
+
+                r = sd_bus_message_read(reply, "s", &name);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_read_array(reply, 'y', &data, &sz);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                if (arg_cat) {
+                        if (nl)
+                                fputc('\n', stdout);
+
+                        printf("%s-- Extension Release: %s --%s\n", ansi_highlight(), name, ansi_normal());
+                        fwrite(data, sz, 1, stdout);
+                        fflush(stdout);
+                        nl = true;
+                } else {
+                        _cleanup_free_ char *pretty_portable = NULL, *pretty_os = NULL, *sysext_level = NULL,
+                                *id = NULL, *version_id = NULL, *sysext_scope = NULL, *portable_prefixes = NULL;
+                        _cleanup_fclose_ FILE *f = NULL;
+
+                        f = fmemopen_unlocked((void*) data, sz, "re");
+                        if (!f)
+                                return log_error_errno(errno, "Failed to open extension-release buffer: %m");
+
+                        r = parse_env_file(f, name,
+                                           "ID", &id,
+                                           "VERSION_ID", &version_id,
+                                           "SYSEXT_SCOPE", &sysext_scope,
+                                           "SYSEXT_LEVEL", &sysext_level,
+                                           "PORTABLE_PRETTY_NAME", &pretty_portable,
+                                           "PORTABLE_PREFIXES", &portable_prefixes,
+                                           "PRETTY_NAME", &pretty_os);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse extension release from '%s': %m", name);
+
+                        printf("Extension:\n\t%s\n"
+                                "\tExtension Scope:\n\t\t%s\n"
+                                "\tExtension Compatibility Level:\n\t\t%s\n"
+                                "\tPortable Service:\n\t\t%s\n"
+                                "\tPortable Prefixes:\n\t\t%s\n"
+                                "\tOperating System:\n\t\t%s (%s %s)\n",
+                                name,
+                                strna(sysext_scope),
+                                strna(sysext_level),
+                                strna(pretty_portable),
+                                strna(portable_prefixes),
+                                strna(pretty_os),
+                                strna(id),
+                                strna(version_id));
+                }
+
+                r = sd_bus_message_exit_container(reply);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+        }
+
         for (;;) {
                 const char *name;
 
@@ -700,6 +768,14 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
+        /* If we specified any extensions, we'll first get back exactly the
+         * paths (and extension-release content) for each one of the arguments. */
+        for (size_t i = 0; i < strv_length(arg_extension_images); ++i) {
+                r = sd_bus_message_skip(reply, "{say}");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+        }
+
         for (;;) {
                 const char *name;
 
index 4a1798ac528f72238e5e5f3d2b42dcd19450ef04..6660498e51fbffdd12b685484091c5787e05ffdc 100644 (file)
@@ -102,13 +102,13 @@ int bus_image_common_get_metadata(
                 Image *image,
                 sd_bus_error *error) {
 
+        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_releases = NULL;
         _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
         _cleanup_strv_free_ char **matches = NULL, **extension_images = NULL;
         _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_free_ PortableMetadata **sorted = NULL;
-        /* Unused for now, but added to the DBUS methods for future-proofing */
-        uint64_t input_flags = 0;
+        PortableFlags flags = 0;
         size_t i;
         int r;
 
@@ -133,14 +133,17 @@ int bus_image_common_get_metadata(
 
         if (sd_bus_message_is_method_call(message, NULL, "GetImageMetadataWithExtensions") ||
             sd_bus_message_is_method_call(message, NULL, "GetMetadataWithExtensions")) {
+                uint64_t input_flags = 0;
+
                 r = sd_bus_message_read(message, "t", &input_flags);
                 if (r < 0)
                         return r;
-                /* Let clients know that this version doesn't support any flags */
-                if (input_flags != 0)
+
+                if ((input_flags & ~_PORTABLE_MASK_PUBLIC) != 0)
                         return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
                                                           "Invalid 'flags' parameter '%" PRIu64 "'",
                                                           input_flags);
+                flags |= input_flags;
         }
 
         r = bus_image_acquire(m,
@@ -161,6 +164,7 @@ int bus_image_common_get_metadata(
                         matches,
                         extension_images,
                         &os_release,
+                        &extension_releases,
                         &unit_files,
                         NULL,
                         error);
@@ -187,6 +191,32 @@ int bus_image_common_get_metadata(
         if (r < 0)
                 return r;
 
+        /* If it was requested, also send back the extension path and the content
+         * of each extension-release file. Behind a flag, as it's an incompatible
+         * change. */
+        if (FLAGS_SET(flags, PORTABLE_INSPECT_EXTENSION_RELEASES)) {
+                PortableMetadata *extension_release;
+
+                ORDERED_HASHMAP_FOREACH(extension_release, extension_releases) {
+
+                        r = sd_bus_message_open_container(reply, 'e', "say");
+                        if (r < 0)
+                                return r;
+
+                        r = sd_bus_message_append(reply, "s", extension_release->image_path);
+                        if (r < 0)
+                                return r;
+
+                        r = append_fd(reply, extension_release);
+                        if (r < 0)
+                                return r;
+
+                        r = sd_bus_message_close_container(reply);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
         for (i = 0; i < hashmap_size(unit_files); i++) {
 
                 r = sd_bus_message_open_container(reply, 'e', "say");
index b4e41495a30d370d3d7fd28da973faeb02b9d876..fdb84b6b457c31e76fb4f7a05754d0692bef18f3 100755 (executable)
@@ -139,6 +139,12 @@ portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/
 systemctl is-active app0.service
 systemctl is-active app1.service
 
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service
+
 portablectl detach --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
 
 umount /tmp/rootdir