1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-22 13:33:56 +03:00

portable: add flag to return extension-releases in GetImageMetadataWithExtensions

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

(cherry picked from commit e3f7ed944a)
This commit is contained in:
Luca Boccassi 2022-01-25 15:49:22 +00:00 committed by Luca Boccassi
parent 85cc27fe88
commit a87fdd2af2
6 changed files with 163 additions and 17 deletions

View File

@ -187,7 +187,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:

View File

@ -505,6 +505,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,
@ -512,7 +513,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;
@ -533,6 +534,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;
@ -581,6 +588,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;
@ -592,10 +600,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;
@ -623,6 +636,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);
@ -631,6 +651,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)
@ -646,12 +668,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;
@ -666,6 +689,7 @@ int portable_extract(
/* validate_sysext= */ false,
&image,
&extension_images,
&extension_releases,
&os_release,
&unit_files,
ret_valid_prefixes ? &valid_prefixes : NULL,
@ -688,6 +712,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)
@ -1261,6 +1287,7 @@ int portable_attach(
/* validate_sysext= */ true,
&image,
&extension_images,
/* extension_releases= */ NULL,
/* os_release= */ NULL,
&unit_files,
&valid_prefixes,

View 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);

View 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;

View 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");

View File

@ -102,6 +102,10 @@ portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app1.ra
systemctl is-active app1.service
portablectl inspect --cat --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 | grep -F "MARKER=2"
portablectl inspect --cat --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 | grep -F "PORTABLE_PREFIXES=app1"
portablectl inspect --cat --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 | grep -F "ExecStart=/opt/script1.sh"
portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
# Ensure that the combination of read-only images, state directory and dynamic user works, and that