Merge pull request #29839 from goenkam/maanya/portabled_support_for_confext

portable: add support for confext
This commit is contained in:
Luca Boccassi
2023-11-03 20:37:46 +00:00
committed by GitHub
12 changed files with 195 additions and 98 deletions
+9 -7
View File
@@ -277,12 +277,14 @@ following must be also be observed:
2. The upper extension images must contain an extension-release file in
`/usr/lib/extension-release.d/`, with an `ID=` and `SYSEXT_LEVEL=`/`VERSION_ID=`
matching the base image.
matching the base image for sysexts, or `/etc/extension-release.d/`, with an
`ID=` and `CONFEXT_LEVEL=`/`VERSION_ID=` matching the base image for confexts.
3. The base/OS image does not need to have any unit files.
4. The upper extension images must contain at least one matching unit file
each, with the right name prefix and suffix (see above).
4. The upper sysext images must contain at least one matching unit file each,
with the right name prefix and suffix (see above). Confext images do not have
to contain units.
5. As with the base/OS image, each upper extension image must be a plain
sub-directory, btrfs subvolume, or a raw disk image.
@@ -354,10 +356,10 @@ underscore (`_`) as separator. If only either one is found, it will be used by i
The field will be named `PORTABLE_NAME_AND_VERSION=`.
In case extensions are used, the same fields in the same order are, but prefixed by
`SYSEXT_`, are parsed from each `extension-release` file, and are appended to the
journal as log entries, using `PORTABLE_EXTENSION_NAME_AND_VERSION=` as the field
name. The base layer's field will be named `PORTABLE_ROOT_NAME_AND_VERSION=` instead
of `PORTABLE_NAME_AND_VERSION=` in this case.
`SYSEXT_`/`CONFEXT_`, are parsed from each `extension-release` file, and are appended
to the journal as log entries, using `PORTABLE_EXTENSION_NAME_AND_VERSION=` as the
field name. The base layer's field will be named `PORTABLE_ROOT_NAME_AND_VERSION=`
instead of `PORTABLE_NAME_AND_VERSION=` in this case.
For example, a portable service `app0` using two extensions `app0.raw` and
`app1.raw` (with `SYSEXT_ID=app`, and `SYSEXT_VERSION_ID=` `0` and `1` in their
+4 -4
View File
@@ -309,14 +309,14 @@ node /org/freedesktop/portable1 {
<function>ReattachImageWithExtensions()</function> methods take in options as flags instead of
booleans to allow for extendability. <varname>SD_SYSTEMD_PORTABLE_FORCE_ATTACH</varname> will cause
safety checks that ensure the units are not running while the new image is attached or detached
to be skipped. <varname>SD_SYSTEMD_PORTABLE_FORCE_SYSEXT</varname> will cause the check that the
to be skipped. <varname>SD_SYSTEMD_PORTABLE_FORCE_EXTENSION</varname> will cause the check that the
<filename>extension-release.<replaceable>NAME</replaceable></filename> file in the extension image
matches the image name to be skipped. They are defined as follows:</para>
<programlisting>
#define SD_SYSTEMD_PORTABLE_RUNTIME (UINT64_C(1) &lt;&lt; 0)
#define SD_SYSTEMD_PORTABLE_FORCE_ATTACH (UINT64_C(1) &lt;&lt; 1)
#define SD_SYSTEMD_PORTABLE_FORCE_SYSEXT (UINT64_C(1) &lt;&lt; 2)
#define SD_SYSTEMD_PORTABLE_RUNTIME (UINT64_C(1) &lt;&lt; 0)
#define SD_SYSTEMD_PORTABLE_FORCE_ATTACH (UINT64_C(1) &lt;&lt; 1)
#define SD_SYSTEMD_PORTABLE_FORCE_EXTENSION (UINT64_C(1) &lt;&lt; 2)
</programlisting>
</refsect2>
+2 -1
View File
@@ -397,7 +397,8 @@
multiple times, in which case the order in which images are laid down follows the rules specified in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the <varname>ExtensionImages=</varname> directive and for the
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> tool.
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> and.
<citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>8</manvolnum></citerefentry> tools.
The images must contain an <filename>extension-release</filename> file with metadata that matches
what is defined in the <filename>os-release</filename> of <replaceable>IMAGE</replaceable>. See:
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+9
View File
@@ -88,6 +88,15 @@ char** strv_free_erase(char **l) {
return mfree(l);
}
void strv_free_many(char ***strvs, size_t n) {
assert(strvs || n == 0);
FOREACH_ARRAY (i, strvs, n)
strv_free(*i);
free(strvs);
}
char** strv_copy_n(char * const *l, size_t m) {
_cleanup_strv_free_ char **result = NULL;
char **k;
+2
View File
@@ -32,6 +32,8 @@ char** strv_free_erase(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
void strv_free_many(char ***strvs, size_t n);
char** strv_copy_n(char * const *l, size_t n);
static inline char** strv_copy(char * const *l) {
return strv_copy_n(l, SIZE_MAX);
+62 -47
View File
@@ -107,6 +107,7 @@ typedef struct MountEntry {
unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */
unsigned n_followed;
LIST_HEAD(MountOptions, image_options_const);
char **overlay_layers;
} MountEntry;
typedef struct MountList {
@@ -336,6 +337,7 @@ static void mount_entry_done(MountEntry *p) {
p->unprefixed_path_malloc = mfree(p->unprefixed_path_malloc);
p->source_malloc = mfree(p->source_malloc);
p->options_malloc = mfree(p->options_malloc);
p->overlay_layers = strv_free(p->overlay_layers);
}
static void mount_list_done(MountList *ml) {
@@ -470,7 +472,8 @@ static int append_extensions(
size_t n,
char **extension_directories) {
_cleanup_strv_free_ char **overlays = NULL;
char ***overlays = NULL;
size_t n_overlays = 0;
int r;
assert(ml);
@@ -480,21 +483,19 @@ static int append_extensions(
assert(extension_dir);
/* Prepare a list of overlays, that will have as each element a string suitable for being
* passed as a lowerdir= parameter, so start with the hierarchy on the root.
n_overlays = strv_length(hierarchies);
if (n_overlays == 0)
return 0;
/* Prepare a list of overlays, that will have as each element a strv containing all the layers that
* will later be concatenated as a lowerdir= parameter for the mount operation.
* The overlays vector will have the same number of elements and will correspond to the
* hierarchies vector, so they can be iterated upon together. */
STRV_FOREACH(hierarchy, hierarchies) {
_cleanup_free_ char *prefixed_hierarchy = NULL;
overlays = new0(char**, n_overlays);
if (!overlays)
return -ENOMEM;
prefixed_hierarchy = path_join(root, *hierarchy);
if (!prefixed_hierarchy)
return -ENOMEM;
r = strv_consume(&overlays, TAKE_PTR(prefixed_hierarchy));
if (r < 0)
return r;
}
CLEANUP_ARRAY(overlays, n_overlays, strv_free_many);
/* First, prepare a mount for each image, but these won't be visible to the unit, instead
* they will be mounted in our propagate directory, and used as a source for the overlay. */
@@ -506,28 +507,18 @@ static int append_extensions(
return -ENOMEM;
for (size_t j = 0; hierarchies && hierarchies[j]; ++j) {
_cleanup_free_ char *prefixed_hierarchy = NULL, *escaped = NULL, *lowerdir = NULL;
prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
char *prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
if (!prefixed_hierarchy)
return -ENOMEM;
escaped = shell_escape(prefixed_hierarchy, ",:");
if (!escaped)
return -ENOMEM;
/* Note that lowerdir= parameters are in 'reverse' order, so the
* top-most directory in the overlay comes first in the list. */
lowerdir = strjoin(escaped, ":", overlays[j]);
if (!lowerdir)
return -ENOMEM;
free_and_replace(overlays[j], lowerdir);
r = strv_consume(&overlays[j], TAKE_PTR(prefixed_hierarchy));
if (r < 0)
return r;
}
MountEntry *me = mount_list_extend(ml);
if (!me)
return log_oom_debug();
return -ENOMEM;
*me = (MountEntry) {
.path_malloc = TAKE_PTR(mount_point),
@@ -565,28 +556,18 @@ static int append_extensions(
return -ENOMEM;
for (size_t j = 0; hierarchies && hierarchies[j]; ++j) {
_cleanup_free_ char *prefixed_hierarchy = NULL, *escaped = NULL, *lowerdir = NULL;
prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
char *prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
if (!prefixed_hierarchy)
return -ENOMEM;
escaped = shell_escape(prefixed_hierarchy, ",:");
if (!escaped)
return -ENOMEM;
/* Note that lowerdir= parameters are in 'reverse' order, so the
* top-most directory in the overlay comes first in the list. */
lowerdir = strjoin(escaped, ":", overlays[j]);
if (!lowerdir)
return -ENOMEM;
free_and_replace(overlays[j], lowerdir);
r = strv_consume(&overlays[j], TAKE_PTR(prefixed_hierarchy));
if (r < 0)
return r;
}
MountEntry *me = mount_list_extend(ml);
if (!me)
return log_oom_debug();
return -ENOMEM;
*me = (MountEntry) {
.path_malloc = TAKE_PTR(mount_point),
@@ -609,11 +590,11 @@ static int append_extensions(
MountEntry *me = mount_list_extend(ml);
if (!me)
return log_oom_debug();
return -ENOMEM;
*me = (MountEntry) {
.path_malloc = TAKE_PTR(prefixed_hierarchy),
.options_malloc = TAKE_PTR(overlays[i]),
.overlay_layers = TAKE_PTR(overlays[i]),
.mode = MOUNT_OVERLAY,
.has_prefix = true,
.ignore = true, /* If the source image doesn't set the ignore bit it will fail earlier. */
@@ -1413,12 +1394,46 @@ static int mount_image(
}
static int mount_overlay(const MountEntry *m) {
const char *options;
_cleanup_free_ char *options = NULL, *layers = NULL;
int r;
assert(m);
options = strjoina("lowerdir=", mount_entry_options(m));
/* Extension hierarchies are optional (e.g.: confext might not have /opt) so check if they actually
* exist in an image before attempting to create an overlay with them, otherwise the mount will
* fail. We can't check before this, as the images will not be mounted until now. */
/* Note that lowerdir= parameters are in 'reverse' order, so the top-most directory in the overlay
* comes first in the list. */
STRV_FOREACH_BACKWARDS(o, m->overlay_layers) {
_cleanup_free_ char *escaped = NULL;
r = is_dir(*o, /* follow= */ false);
if (r <= 0) {
if (r != -ENOENT)
log_debug_errno(r,
"Failed to check whether overlay layer source path '%s' exists, ignoring: %m",
*o);
continue;
}
escaped = shell_escape(*o, ",:");
if (!escaped)
return log_oom_debug();
if (!strextend_with_separator(&layers, ":", escaped))
return log_oom_debug();
}
if (!layers) {
log_debug("None of the overlays specified in '%s' exist at the source, skipping.",
mount_entry_options(m));
return 0; /* Only the root is set? Then there's nothing to overlay */
}
options = strjoin("lowerdir=", layers, ":", mount_entry_path(m)); /* The root goes in last */
if (!options)
return log_oom_debug();
(void) mkdir_p_label(mount_entry_path(m), 0755);
+35 -12
View File
@@ -198,8 +198,18 @@ static int extract_now(
/* First, find os-release/extension-release and send it upstream (or just save it). */
if (path_is_extension) {
os_release_id = strjoina("/usr/lib/extension-release.d/extension-release.", image_name);
ImageClass class = IMAGE_SYSEXT;
r = open_extension_release(where, IMAGE_SYSEXT, image_name, relax_extension_release_check, &os_release_path, &os_release_fd);
if (r == -ENOENT) {
r = open_extension_release(where, IMAGE_CONFEXT, image_name, relax_extension_release_check, &os_release_path, &os_release_fd);
if (r >= 0)
class = IMAGE_CONFEXT;
}
if (r < 0)
return log_error_errno(r, "Failed to open extension release from '%s': %m", image_name);
os_release_id = strjoina((class == IMAGE_SYSEXT) ? "/usr/lib" : "/etc", "/extension-release.d/extension-release.", image_name);
} else {
os_release_id = "/etc/os-release";
r = open_os_release(where, &os_release_path, &os_release_fd);
@@ -530,7 +540,7 @@ static int extract_image_and_extensions(
const char *name_or_path,
char **matches,
char **extension_image_paths,
bool validate_sysext,
bool validate_extension,
bool relax_extension_release_check,
const ImagePolicy *image_policy,
Image **ret_image,
@@ -541,7 +551,7 @@ static int extract_image_and_extensions(
char ***ret_valid_prefixes,
sd_bus_error *error) {
_cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL;
_cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL, *confext_level = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
@@ -596,13 +606,14 @@ static int extract_image_and_extensions(
/* If we are layering extension images on top of a runtime image, check that the os-release and
* extension-release metadata match, otherwise reject it immediately as invalid, or it will fail when
* the units are started. Also, collect valid portable prefixes if caller requested that. */
if (validate_sysext || ret_valid_prefixes) {
if (validate_extension || ret_valid_prefixes) {
_cleanup_free_ char *prefixes = NULL;
r = parse_env_file_fd(os_release->fd, os_release->name,
"ID", &id,
"VERSION_ID", &version_id,
"SYSEXT_LEVEL", &sysext_level,
"CONFEXT_LEVEL", &confext_level,
"PORTABLE_PREFIXES", &prefixes);
if (r < 0)
return r;
@@ -638,15 +649,18 @@ static int extract_image_and_extensions(
if (r < 0)
return r;
if (!validate_sysext && !ret_valid_prefixes && !ret_extension_releases)
if (!validate_extension && !ret_valid_prefixes && !ret_extension_releases)
continue;
r = load_env_file_pairs_fd(extension_release_meta->fd, extension_release_meta->name, &extension_release);
if (r < 0)
return r;
if (validate_sysext) {
if (validate_extension) {
r = extension_release_validate(ext->path, id, version_id, sysext_level, "portable", extension_release, IMAGE_SYSEXT);
if (r < 0)
r = extension_release_validate(ext->path, id, version_id, confext_level, "portable", extension_release, IMAGE_CONFEXT);
if (r == 0)
return sd_bus_error_set_errnof(error, SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", ext->path);
if (r < 0)
@@ -717,8 +731,8 @@ int portable_extract(
name_or_path,
matches,
extension_image_paths,
/* validate_sysext= */ false,
/* relax_extension_release_check= */ FLAGS_SET(flags, PORTABLE_FORCE_SYSEXT),
/* validate_extension= */ false,
/* relax_extension_release_check= */ FLAGS_SET(flags, PORTABLE_FORCE_EXTENSION),
image_policy,
&image,
&extension_images,
@@ -979,16 +993,18 @@ static int append_release_log_fields(
static const char *const field_versions[_IMAGE_CLASS_MAX][4]= {
[IMAGE_PORTABLE] = { "IMAGE_VERSION", "VERSION_ID", "BUILD_ID", NULL },
[IMAGE_SYSEXT] = { "SYSEXT_IMAGE_VERSION", "SYSEXT_VERSION_ID", "SYSEXT_BUILD_ID", NULL },
[IMAGE_CONFEXT] = { "CONFEXT_IMAGE_VERSION", "CONFEXT_VERSION_ID", "CONFEXT_BUILD_ID", NULL },
};
static const char *const field_ids[_IMAGE_CLASS_MAX][3]= {
[IMAGE_PORTABLE] = { "IMAGE_ID", "ID", NULL },
[IMAGE_SYSEXT] = { "SYSEXT_IMAGE_ID", "SYSEXT_ID", NULL },
[IMAGE_CONFEXT] = { "CONFEXT_IMAGE_ID", "CONFEXT_ID", NULL },
};
_cleanup_strv_free_ char **fields = NULL;
const char *id = NULL, *version = NULL;
int r;
assert(IN_SET(type, IMAGE_PORTABLE, IMAGE_SYSEXT));
assert(IN_SET(type, IMAGE_PORTABLE, IMAGE_SYSEXT, IMAGE_CONFEXT));
assert(!strv_isempty((char *const *)field_ids[type]));
assert(!strv_isempty((char *const *)field_versions[type]));
assert(field_name);
@@ -1111,7 +1127,7 @@ static int install_chroot_dropin(
/* With --force tell PID1 to avoid enforcing that the image <name> and
* extension-release.<name> have to match. */
!IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) &&
FLAGS_SET(flags, PORTABLE_FORCE_SYSEXT) ?
FLAGS_SET(flags, PORTABLE_FORCE_EXTENSION) ?
":x-systemd.relax-extension-release-check\n" :
"\n",
/* In PORTABLE= we list the 'main' image name for this unit
@@ -1131,6 +1147,13 @@ static int install_chroot_dropin(
"PORTABLE_EXTENSION_NAME_AND_VERSION");
if (r < 0)
return r;
r = append_release_log_fields(&text,
ordered_hashmap_get(extension_releases, ext->name),
IMAGE_CONFEXT,
"PORTABLE_EXTENSION_NAME_AND_VERSION");
if (r < 0)
return r;
}
}
@@ -1432,8 +1455,8 @@ int portable_attach(
name_or_path,
matches,
extension_image_paths,
/* validate_sysext= */ true,
/* relax_extension_release_check= */ FLAGS_SET(flags, PORTABLE_FORCE_SYSEXT),
/* validate_extension= */ true,
/* relax_extension_release_check= */ FLAGS_SET(flags, PORTABLE_FORCE_EXTENSION),
image_policy,
&image,
&extension_images,
+9 -9
View File
@@ -18,19 +18,19 @@ typedef struct PortableMetadata {
} PortableMetadata;
#define PORTABLE_METADATA_IS_OS_RELEASE(m) (streq((m)->name, "/etc/os-release"))
#define PORTABLE_METADATA_IS_EXTENSION_RELEASE(m) (startswith((m)->name, "/usr/lib/extension-release.d/extension-release."))
#define PORTABLE_METADATA_IS_EXTENSION_RELEASE(m) (startswith_strv((m)->name, STRV_MAKE("/usr/lib/extension-release.d/extension-release.", "/etc/extension-release.d/extension-release.")))
#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_FORCE_ATTACH = 1 << 1, /* Public API via DBUS, do not change */
PORTABLE_FORCE_SYSEXT = 1 << 2, /* Public API via DBUS, do not change */
PORTABLE_PREFER_COPY = 1 << 3,
PORTABLE_PREFER_SYMLINK = 1 << 4,
PORTABLE_REATTACH = 1 << 5,
_PORTABLE_MASK_PUBLIC = PORTABLE_RUNTIME | PORTABLE_FORCE_ATTACH | PORTABLE_FORCE_SYSEXT,
PORTABLE_RUNTIME = 1 << 0, /* Public API via DBUS, do not change */
PORTABLE_FORCE_ATTACH = 1 << 1, /* Public API via DBUS, do not change */
PORTABLE_FORCE_EXTENSION = 1 << 2, /* Public API via DBUS, do not change */
PORTABLE_PREFER_COPY = 1 << 3,
PORTABLE_PREFER_SYMLINK = 1 << 4,
PORTABLE_REATTACH = 1 << 5,
_PORTABLE_MASK_PUBLIC = PORTABLE_RUNTIME | PORTABLE_FORCE_ATTACH | PORTABLE_FORCE_EXTENSION,
_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
+33 -18
View File
@@ -248,7 +248,7 @@ 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;
uint64_t flags = arg_force ? PORTABLE_FORCE_SYSEXT : 0;
uint64_t flags = arg_force ? PORTABLE_FORCE_EXTENSION : 0;
const char *method;
int r;
@@ -384,9 +384,16 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
fflush(stdout);
nl = true;
} else {
_cleanup_free_ char *pretty_portable = NULL, *pretty_os = NULL, *sysext_level = NULL,
*sysext_id = NULL, *sysext_version_id = NULL, *sysext_scope = NULL, *portable_prefixes = NULL,
*id = NULL, *version_id = NULL, *image_id = NULL, *image_version = NULL, *build_id = NULL;
_cleanup_free_ char *pretty_portable = NULL, *sysext_pretty_os = NULL,
*sysext_level = NULL, *sysext_id = NULL,
*sysext_version_id = NULL, *sysext_scope = NULL,
*portable_prefixes = NULL, *id = NULL, *version_id = NULL,
*sysext_image_id = NULL, *sysext_image_version = NULL,
*sysext_build_id = NULL, *confext_pretty_os = NULL,
*confext_level = NULL, *confext_id = NULL,
*confext_version_id = NULL, *confext_scope = NULL,
*confext_image_id = NULL, *confext_image_version = NULL,
*confext_build_id = NULL;
_cleanup_fclose_ FILE *f = NULL;
f = fmemopen_unlocked((void*) data, sz, "r");
@@ -396,12 +403,20 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
r = parse_env_file(f, name,
"SYSEXT_ID", &sysext_id,
"SYSEXT_VERSION_ID", &sysext_version_id,
"SYSEXT_BUILD_ID", &build_id,
"SYSEXT_IMAGE_ID", &image_id,
"SYSEXT_IMAGE_VERSION", &image_version,
"SYSEXT_PRETTY_NAME", &pretty_os,
"SYSEXT_BUILD_ID", &sysext_build_id,
"SYSEXT_IMAGE_ID", &sysext_image_id,
"SYSEXT_IMAGE_VERSION", &sysext_image_version,
"SYSEXT_SCOPE", &sysext_scope,
"SYSEXT_LEVEL", &sysext_level,
"SYSEXT_PRETTY_NAME", &sysext_pretty_os,
"CONFEXT_ID", &confext_id,
"CONFEXT_VERSION_ID", &confext_version_id,
"CONFEXT_BUILD_ID", &confext_build_id,
"CONFEXT_IMAGE_ID", &confext_image_id,
"CONFEXT_IMAGE_VERSION", &confext_image_version,
"CONFEXT_SCOPE", &confext_scope,
"CONFEXT_LEVEL", &confext_level,
"CONFEXT_PRETTY_NAME", &confext_pretty_os,
"ID", &id,
"VERSION_ID", &version_id,
"PORTABLE_PRETTY_NAME", &pretty_portable,
@@ -418,18 +433,18 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
"\tPortable Prefixes:\n\t\t%s\n"
"\tExtension Image:\n\t\t%s%s%s %s%s%s\n",
name,
strna(sysext_scope),
strna(sysext_level),
strna(sysext_scope ?: confext_scope),
strna(sysext_level ?: confext_level),
strna(id),
strna(version_id),
strna(pretty_portable),
strna(portable_prefixes),
strempty(pretty_os),
pretty_os ? " (" : "ID: ",
strna(sysext_id ?: image_id),
pretty_os ? "" : "Version: ",
strna(sysext_version_id ?: image_version ?: build_id),
pretty_os ? ")" : "");
strempty(sysext_pretty_os ?: confext_pretty_os),
(sysext_pretty_os ?: confext_pretty_os) ? " (" : "ID: ",
strna(sysext_id ?: sysext_image_id ?: confext_id ?: confext_image_id),
(sysext_pretty_os ?: confext_pretty_os) ? "" : "Version: ",
strna(sysext_version_id ?: sysext_image_version ?: sysext_build_id ?: confext_version_id ?: confext_image_version ?: confext_build_id),
(sysext_pretty_os ?: confext_pretty_os) ? ")" : "");
}
r = sd_bus_message_exit_container(reply);
@@ -871,7 +886,7 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) {
return bus_log_create_error(r);
if (STR_IN_SET(method, "AttachImageWithExtensions", "ReattachImageWithExtensions")) {
uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE_ATTACH | PORTABLE_FORCE_SYSEXT : 0);
uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE_ATTACH | PORTABLE_FORCE_EXTENSION : 0);
r = sd_bus_message_append(m, "st", arg_copy_mode, flags);
} else
@@ -943,7 +958,7 @@ static int detach_image(int argc, char *argv[], void *userdata) {
if (streq(method, "DetachImage"))
r = sd_bus_message_append(m, "b", arg_runtime);
else {
uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE_ATTACH | PORTABLE_FORCE_SYSEXT : 0);
uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE_ATTACH | PORTABLE_FORCE_EXTENSION : 0);
r = sd_bus_message_append(m, "t", flags);
}
+9
View File
@@ -814,6 +814,15 @@ EOF
echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw" -noappend
export initdir="$TESTDIR/conf0"
mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system" "$initdir/opt"
grep "^ID=" "$os_release" >"$initdir/etc/extension-release.d/extension-release.conf0"
echo "${version_id}" >>"$initdir/etc/extension-release.d/extension-release.conf0"
( echo "${version_id}"
echo "CONFEXT_IMAGE_ID=app" ) >>"$initdir/etc/extension-release.d/extension-release.conf0"
echo MARKER_1 >"$initdir/etc/systemd/system/some_file"
mksquashfs "$initdir" "$oldinitdir/usr/share/conf0.raw" -noappend
export initdir="$TESTDIR/app1"
mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2"
+12
View File
@@ -33,6 +33,7 @@ systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable serv
systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ 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'
systemd-dissect --no-pager /usr/share/conf0.raw | grep -q '✓ confext for portable service'
export SYSTEMD_LOG_LEVEL=debug
mkdir -p /run/systemd/system/systemd-portabled.service.d/
@@ -187,6 +188,17 @@ portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_
portablectl detach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
# portablectl also accepts confexts
portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw)"
[[ "${status}" == "running-runtime" ]]
portablectl inspect --force --cat --extension /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /usr/share/conf0.raw"
portablectl detach --now --runtime --extension /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0
# portablectl also works with directory paths rather than images
mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
+9
View File
@@ -397,6 +397,15 @@ mkdir -p /etc/symlink-test/
cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
systemd-run -P --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
# And again mixing sysext and confext
systemd-run -P \
--property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
--property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
--property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
systemd-run -P \
--property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
--property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
--property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
cat >/run/systemd/system/testservice-50e.service <<EOF
[Service]