mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
portabled: add --extension parameter for layered images support
Add an --extension parameter to portablectl, and new DBUS methods to attach/detach/reattach/inspect. Allows to append separate images on top of the root directory (os-release will be searched in there) and mount the images using an overlay-like setup (unit files will be searched in there) using the new ExtensionImages service option.
This commit is contained in:
parent
248b1e0aa4
commit
907952bbc9
@ -48,6 +48,13 @@ node /org/freedesktop/portable1 {
|
||||
out s image,
|
||||
out ay os_release,
|
||||
out a{say} units);
|
||||
GetImageMetadataWithExtensions(in s image,
|
||||
in as extensions,
|
||||
in as matches,
|
||||
in t flags,
|
||||
out s image,
|
||||
out ay os_release,
|
||||
out a{say} units);
|
||||
GetImageState(in s image,
|
||||
out s state);
|
||||
AttachImage(in s image,
|
||||
@ -56,9 +63,20 @@ node /org/freedesktop/portable1 {
|
||||
in b runtime,
|
||||
in s copy_mode,
|
||||
out a(sss) changes);
|
||||
AttachImageWithExtensions(in s image,
|
||||
in as extensions,
|
||||
in as matches,
|
||||
in s profile,
|
||||
in s copy_mode,
|
||||
in t flags,
|
||||
out a(sss) changes);
|
||||
DetachImage(in s image,
|
||||
in b runtime,
|
||||
out a(sss) changes);
|
||||
DetachImageWithExtensions(in s image,
|
||||
in as extensions,
|
||||
in t flags,
|
||||
out a(sss) changes);
|
||||
ReattachImage(in s image,
|
||||
in as matches,
|
||||
in s profile,
|
||||
@ -66,6 +84,14 @@ node /org/freedesktop/portable1 {
|
||||
in s copy_mode,
|
||||
out a(sss) changes_removed,
|
||||
out a(sss) changes_updated);
|
||||
ReattachImageWithExtensions(in s image,
|
||||
in as extensions,
|
||||
in as matches,
|
||||
in s profile,
|
||||
in s copy_mode,
|
||||
in t flags,
|
||||
out a(sss) changes_removed,
|
||||
out a(sss) changes_updated);
|
||||
RemoveImage(in s image);
|
||||
MarkImageReadOnly(in s image,
|
||||
in b read_only);
|
||||
@ -102,14 +128,22 @@ node /org/freedesktop/portable1 {
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="GetImageMetadata()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="GetImageMetadataWithExtensions()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="GetImageState()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="AttachImage()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="AttachImageWithExtensions()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="DetachImage()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="DetachImageWithExtensions()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="ReattachImage()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="ReattachImageWithExtensions()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="RemoveImage()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="MarkImageReadOnly()"/>
|
||||
@ -149,6 +183,12 @@ node /org/freedesktop/portable1 {
|
||||
and a list of portable units contained in the image, in the form of a string (unit name) and
|
||||
an array of bytes with the content.</para>
|
||||
|
||||
<para><function>GetImageMetadataWithExtensions()</function> retrieves metadata associated with an image.
|
||||
This method is a superset of <function>GetImageMetadata()</function> with the addition of
|
||||
a list of extensions as input parameter, which were overlayed on top of the main
|
||||
image via <function>AttachImageWithExtensions()</function>.
|
||||
The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
|
||||
|
||||
<para><function>GetImageState()</function> retrieves the image state as one of the following
|
||||
strings:
|
||||
<itemizedlist>
|
||||
@ -197,6 +237,16 @@ node /org/freedesktop/portable1 {
|
||||
Note that an image cannot be attached if a unit that it contains is already present
|
||||
on the system.</para>
|
||||
|
||||
<para><function>AttachImageWithExtensions()</function> attaches a portable image to the system.
|
||||
This method is a superset of <function>AttachImage()</function> with the addition of
|
||||
a list of extensions as input parameter, which will be overlayed on top of the main
|
||||
image. When this method is used, detaching must be done by passing the same arguments via the
|
||||
<function>DetachImageWithExtensions()</function> method. For more details on this functionality,
|
||||
see the <varname>MountImages=</varname> entry on
|
||||
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
and <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
|
||||
|
||||
<para><function>DetachImage()</function> detaches a portable image from the system.
|
||||
This method takes an image path or name, and a boolean indicating whether the image to
|
||||
detach was attached only for the current boot session or persistently. This method
|
||||
@ -209,6 +259,12 @@ node /org/freedesktop/portable1 {
|
||||
</itemizedlist>
|
||||
Note that an image cannot be detached if a unit that it contains is running.</para>
|
||||
|
||||
<para><function>DetachImageWithExtensions()</function> detaches a portable image from the system.
|
||||
This method is a superset of <function>DetachImage()</function> with the addition of
|
||||
a list of extensions as input parameter, which were overlayed on top of the main
|
||||
image via <function>AttachImageWithExtensions()</function>.
|
||||
The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
|
||||
|
||||
<para><function>ReattachImage()</function> combines the effects of the
|
||||
<function>AttachImage()</function> method and the <function>DetachImage()</function> method.
|
||||
The difference is that it is allowed to reattach an image while one or more of its units
|
||||
@ -218,6 +274,14 @@ node /org/freedesktop/portable1 {
|
||||
<function>DetachImage()</function> method (first array, units that were removed) and the
|
||||
<function>AttachImage()</function> method (second array, units that were updated or added).</para>
|
||||
|
||||
<para><function>ReattachImageWithExtensions()</function> reattaches a portable image to the system.
|
||||
This method is a superset of <function>ReattachImage()</function> with the addition of
|
||||
a list of extensions as input parameter, which will be overlayed on top of the main
|
||||
image. For more details on this functionality, see the <varname>MountImages=</varname> entry on
|
||||
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
and <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
The <varname>flag</varname> parameter is currently unused and reserved for future purposes</para>
|
||||
|
||||
<para><function>RemoveImage()</function> removes the image with the specified name.</para>
|
||||
|
||||
<para><function>MarkImageReadOnly()</function> toggles the read-only flag of an image.</para>
|
||||
@ -225,6 +289,15 @@ node /org/freedesktop/portable1 {
|
||||
<para><function>SetPoolLimit()</function> sets an overall quota limit on the pool of images.</para>
|
||||
|
||||
<para><function>SetImageLimit()</function> sets a per-image quota limit.</para>
|
||||
|
||||
<para>The <function>AttachImageWithExtensions()</function>,
|
||||
<function>DetachImageWithExtensions()</function> and
|
||||
<function>ReattachImageWithExtensions()</function> methods take in options as flags instead of
|
||||
booleans to allow for extendability, defined as follows:</para>
|
||||
|
||||
<programlisting>
|
||||
#define SD_SYSTEMD_PORTABLE_RUNTIME (UINT64_C(1) << 0)
|
||||
</programlisting>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
@ -254,20 +327,42 @@ node /org/freedesktop/portable1 {
|
||||
out s image,
|
||||
out ay os_release,
|
||||
out a{say} units);
|
||||
GetMetadataWithExtensions(in as extensions,
|
||||
in as matches,
|
||||
in t flags,
|
||||
out s image,
|
||||
out ay os_release,
|
||||
out a{say} units);
|
||||
GetState(out s UNNAMED);
|
||||
Attach(in as matches,
|
||||
in s profile,
|
||||
in b runtime,
|
||||
in s copy_mode,
|
||||
out a(sss) changes);
|
||||
AttachWithExtensions(in as extensions,
|
||||
in as matches,
|
||||
in s profile,
|
||||
in s copy_mode,
|
||||
in t flags,
|
||||
out a(sss) changes);
|
||||
Detach(in b runtime,
|
||||
out a(sss) changes);
|
||||
DetachWithExtensions(in as extensions,
|
||||
in t flags,
|
||||
out a(sss) changes);
|
||||
Reattach(in as matches,
|
||||
in s profile,
|
||||
in b runtime,
|
||||
in s copy_mode,
|
||||
out a(sss) changes_removed,
|
||||
out a(sss) changes_updated);
|
||||
ReattacheWithExtensions(in as extensions,
|
||||
in as matches,
|
||||
in s profile,
|
||||
in s copy_mode,
|
||||
in t flags,
|
||||
out a(sss) changes_removed,
|
||||
out a(sss) changes_updated);
|
||||
Remove();
|
||||
MarkReadOnly(in b read_only);
|
||||
SetLimit(in t limit);
|
||||
@ -303,14 +398,22 @@ node /org/freedesktop/portable1 {
|
||||
|
||||
<!--method GetMetadata is not documented!-->
|
||||
|
||||
<!--method GetMetadataWithExtensions is not documented!-->
|
||||
|
||||
<!--method GetState is not documented!-->
|
||||
|
||||
<!--method Attach is not documented!-->
|
||||
|
||||
<!--method AttachWithExtensions is not documented!-->
|
||||
|
||||
<!--method Detach is not documented!-->
|
||||
|
||||
<!--method DetachWithExtensions is not documented!-->
|
||||
|
||||
<!--method Reattach is not documented!-->
|
||||
|
||||
<!--method ReattacheWithExtensions is not documented!-->
|
||||
|
||||
<!--method Remove is not documented!-->
|
||||
|
||||
<!--method MarkReadOnly is not documented!-->
|
||||
@ -327,14 +430,22 @@ node /org/freedesktop/portable1 {
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="GetMetadata()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="GetMetadataWithExtensions()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="GetState()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="Attach()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="AttachWithExtensions()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="Detach()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="DetachWithExtensions()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="Reattach()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="ReattacheWithExtensions()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="Remove()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="MarkReadOnly()"/>
|
||||
@ -377,14 +488,22 @@ node /org/freedesktop/portable1 {
|
||||
|
||||
<listitem><para>GetMetadata()</para></listitem>
|
||||
|
||||
<listitem><para>GetMetadataWithExtensions()</para></listitem>
|
||||
|
||||
<listitem><para>GetState()</para></listitem>
|
||||
|
||||
<listitem><para>Attach()</para></listitem>
|
||||
|
||||
<listitem><para>AttachWithExtensions()</para></listitem>
|
||||
|
||||
<listitem><para>Detach()</para></listitem>
|
||||
|
||||
<listitem><para>DetachWithExtensions()</para></listitem>
|
||||
|
||||
<listitem><para>Reattach()</para></listitem>
|
||||
|
||||
<listitem><para>ReattacheWithExtensions()</para></listitem>
|
||||
|
||||
<listitem><para>Remove()</para></listitem>
|
||||
|
||||
<listitem><para>MarkReadOnly()</para></listitem>
|
||||
|
@ -352,6 +352,19 @@
|
||||
<listitem><para>Don't block waiting for attach --now to complete.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--extension=</option><replaceable>PATH</replaceable></term>
|
||||
|
||||
<listitem><para>Add an additional image <replaceable>PATH</replaceable> as an overlay on
|
||||
top of <replaceable>IMAGE</replaceable> when attaching/detaching. This argument can be specified
|
||||
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.</para>
|
||||
|
||||
<para>Note that the same extensions have to be specified, in the same order, when attaching
|
||||
and detaching.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="user-system-options.xml" xpointer="host" />
|
||||
<xi:include href="user-system-options.xml" xpointer="machine" />
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "discover-image.h"
|
||||
#include "dissect-image.h"
|
||||
#include "errno-list.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
@ -74,17 +75,26 @@ static bool unit_match(const char *unit, char **matches) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static PortableMetadata *portable_metadata_new(const char *name, int fd) {
|
||||
static PortableMetadata *portable_metadata_new(const char *name, const char *path, int fd) {
|
||||
PortableMetadata *m;
|
||||
|
||||
m = malloc0(offsetof(PortableMetadata, name) + strlen(name) + 1);
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
/* In case of a layered attach, we want to remember which image the unit came from */
|
||||
if (path) {
|
||||
m->image_path = strdup(path);
|
||||
if (!m->image_path) {
|
||||
free(m);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(m->name, name);
|
||||
m->fd = fd;
|
||||
|
||||
return m;
|
||||
return TAKE_PTR(m);
|
||||
}
|
||||
|
||||
PortableMetadata *portable_metadata_unref(PortableMetadata *i) {
|
||||
@ -93,6 +103,7 @@ PortableMetadata *portable_metadata_unref(PortableMetadata *i) {
|
||||
|
||||
safe_close(i->fd);
|
||||
free(i->source);
|
||||
free(i->image_path);
|
||||
|
||||
return mfree(i);
|
||||
}
|
||||
@ -255,7 +266,7 @@ static int extract_now(
|
||||
}
|
||||
|
||||
if (ret_os_release) {
|
||||
os_release = portable_metadata_new("/etc/os-release", os_release_fd);
|
||||
os_release = portable_metadata_new("/etc/os-release", NULL, os_release_fd);
|
||||
if (!os_release)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -316,7 +327,7 @@ static int extract_now(
|
||||
return log_debug_errno(r, "Failed to send unit metadata to parent: %m");
|
||||
}
|
||||
|
||||
m = portable_metadata_new(de->d_name, fd);
|
||||
m = portable_metadata_new(de->d_name, NULL, fd);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
fd = -1;
|
||||
@ -342,6 +353,7 @@ static int extract_now(
|
||||
|
||||
static int portable_extract_by_path(
|
||||
const char *path,
|
||||
bool extract_os_release,
|
||||
char **matches,
|
||||
PortableMetadata **ret_os_release,
|
||||
Hashmap **ret_unit_files,
|
||||
@ -412,7 +424,7 @@ static int portable_extract_by_path(
|
||||
if (r == 0) {
|
||||
seq[0] = safe_close(seq[0]);
|
||||
|
||||
r = dissected_image_mount(m, tmpdir, UID_INVALID, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_VALIDATE_OS);
|
||||
r = dissected_image_mount(m, tmpdir, UID_INVALID, DISSECT_IMAGE_READ_ONLY);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to mount dissected image: %m");
|
||||
goto child_finish;
|
||||
@ -448,7 +460,7 @@ static int portable_extract_by_path(
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid item sent from child.");
|
||||
|
||||
add = portable_metadata_new(name, fd);
|
||||
add = portable_metadata_new(name, path, fd);
|
||||
if (!add)
|
||||
return -ENOMEM;
|
||||
fd = -1;
|
||||
@ -478,10 +490,12 @@ static int portable_extract_by_path(
|
||||
child = 0;
|
||||
}
|
||||
|
||||
if (!os_release)
|
||||
/* When the portable image is layered, the image with units will not
|
||||
* have a full filesystem, so no os-release - it will be in the root layer */
|
||||
if (extract_os_release && !os_release)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image '%s' lacks os-release data, refusing.", path);
|
||||
|
||||
if (hashmap_isempty(unit_files))
|
||||
if (!extract_os_release && hashmap_isempty(unit_files))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't find any matching unit files in image '%s', refusing.", path);
|
||||
|
||||
if (ret_unit_files)
|
||||
@ -496,11 +510,16 @@ static int portable_extract_by_path(
|
||||
int portable_extract(
|
||||
const char *name_or_path,
|
||||
char **matches,
|
||||
char **extension_image_paths,
|
||||
PortableMetadata **ret_os_release,
|
||||
Hashmap **ret_unit_files,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
|
||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
|
||||
_cleanup_(image_unrefp) Image *image = NULL;
|
||||
Image *ext;
|
||||
int r;
|
||||
|
||||
assert(name_or_path);
|
||||
@ -509,7 +528,46 @@ int portable_extract(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return portable_extract_by_path(image->path, matches, ret_os_release, ret_unit_files, error);
|
||||
if (!strv_isempty(extension_image_paths)) {
|
||||
char **p;
|
||||
|
||||
extension_images = ordered_hashmap_new(&image_hash_ops);
|
||||
if (!extension_images)
|
||||
return -ENOMEM;
|
||||
|
||||
STRV_FOREACH(p, extension_image_paths) {
|
||||
_cleanup_(image_unrefp) Image *new = NULL;
|
||||
|
||||
r = image_find_harder(IMAGE_PORTABLE, *p, NULL, &new);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ordered_hashmap_put(extension_images, new->name, new);
|
||||
if (r < 0)
|
||||
return r;
|
||||
TAKE_PTR(new);
|
||||
}
|
||||
}
|
||||
|
||||
r = portable_extract_by_path(image->path, true, matches, &os_release, &unit_files, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
|
||||
_cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
|
||||
|
||||
r = portable_extract_by_path(ext->path, false, matches, NULL, &extra_unit_files, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = hashmap_move(unit_files, extra_unit_files);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret_os_release = TAKE_PTR(os_release);
|
||||
*ret_unit_files = TAKE_PTR(unit_files);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unit_file_is_active(
|
||||
@ -684,9 +742,49 @@ void portable_changes_free(PortableChange *changes, size_t n_changes) {
|
||||
free(changes);
|
||||
}
|
||||
|
||||
static const char *root_setting_from_image(ImageType type) {
|
||||
return IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "RootDirectory=" : "RootImage=";
|
||||
}
|
||||
|
||||
static int make_marker_text(const char *image_path, OrderedHashmap *extension_images, char **ret_text) {
|
||||
_cleanup_free_ char *text = NULL, *escaped_image_path = NULL;
|
||||
Image *ext;
|
||||
|
||||
assert(image_path);
|
||||
assert(ret_text);
|
||||
|
||||
escaped_image_path = xescape(image_path, ":");
|
||||
if (!escaped_image_path)
|
||||
return -ENOMEM;
|
||||
|
||||
/* If the image is layered, include all layers in the marker as a colon-separated
|
||||
* list of paths, so that we can do exact matches on removal. */
|
||||
text = strjoin(PORTABLE_DROPIN_MARKER_BEGIN, escaped_image_path);
|
||||
if (!text)
|
||||
return -ENOMEM;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
|
||||
_cleanup_free_ char *escaped = NULL;
|
||||
|
||||
escaped = xescape(ext->path, ":");
|
||||
if (!escaped)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!strextend(&text, ":", escaped))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!strextend(&text, PORTABLE_DROPIN_MARKER_END "\n"))
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_text = TAKE_PTR(text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int install_chroot_dropin(
|
||||
const char *image_path,
|
||||
ImageType type,
|
||||
OrderedHashmap *extension_images,
|
||||
const PortableMetadata *m,
|
||||
const char *dropin_dir,
|
||||
char **ret_dropin,
|
||||
@ -694,6 +792,7 @@ static int install_chroot_dropin(
|
||||
size_t *n_changes) {
|
||||
|
||||
_cleanup_free_ char *text = NULL, *dropin = NULL;
|
||||
Image *ext;
|
||||
int r;
|
||||
|
||||
assert(image_path);
|
||||
@ -704,12 +803,15 @@ static int install_chroot_dropin(
|
||||
if (!dropin)
|
||||
return -ENOMEM;
|
||||
|
||||
text = strjoin(PORTABLE_DROPIN_MARKER_BEGIN, image_path, PORTABLE_DROPIN_MARKER_END "\n");
|
||||
if (!text)
|
||||
return -ENOMEM;
|
||||
r = make_marker_text(image_path, extension_images, &text);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to generate marker string for portable drop-in: %m");
|
||||
|
||||
if (endswith(m->name, ".service")) {
|
||||
const char *os_release_source;
|
||||
const char *os_release_source, *root_type;
|
||||
_cleanup_free_ char *base_name = NULL;
|
||||
|
||||
root_type = root_setting_from_image(type);
|
||||
|
||||
if (access("/etc/os-release", F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
@ -719,14 +821,23 @@ static int install_chroot_dropin(
|
||||
} else
|
||||
os_release_source = "/etc/os-release";
|
||||
|
||||
r = path_extract_filename(m->image_path ?: image_path, &base_name);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to extract basename from '%s': %m", m->image_path ?: image_path);
|
||||
|
||||
if (!strextend(&text,
|
||||
"\n"
|
||||
"[Service]\n",
|
||||
IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "RootDirectory=" : "RootImage=", image_path, "\n"
|
||||
"Environment=PORTABLE=", basename(image_path), "\n"
|
||||
root_type, image_path, "\n"
|
||||
"Environment=PORTABLE=", base_name, "\n"
|
||||
"BindReadOnlyPaths=", os_release_source, ":/run/host/os-release\n"
|
||||
"LogExtraFields=PORTABLE=", basename(image_path), "\n"))
|
||||
"LogExtraFields=PORTABLE=", base_name, "\n"))
|
||||
return -ENOMEM;
|
||||
|
||||
if (m->image_path && !path_equal(m->image_path, image_path))
|
||||
ORDERED_HASHMAP_FOREACH(ext, extension_images)
|
||||
if (!strextend(&text, "ExtensionImages=", ext->path, "\n"))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = write_string_file(dropin, text, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
|
||||
@ -841,6 +952,7 @@ static int attach_unit_file(
|
||||
const LookupPaths *paths,
|
||||
const char *image_path,
|
||||
ImageType type,
|
||||
OrderedHashmap *extension_images,
|
||||
const PortableMetadata *m,
|
||||
const char *profile,
|
||||
PortableFlags flags,
|
||||
@ -881,7 +993,7 @@ static int attach_unit_file(
|
||||
* is reloaded while we are creating things here: as long as only the drop-ins exist the unit doesn't exist at
|
||||
* all for PID 1. */
|
||||
|
||||
r = install_chroot_dropin(image_path, type, m, dropin_dir, &chroot_dropin, changes, n_changes);
|
||||
r = install_chroot_dropin(image_path, type, extension_images, m, dropin_dir, &chroot_dropin, changes, n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -984,20 +1096,49 @@ static int install_image_symlink(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int install_image_and_extensions_symlinks(
|
||||
const Image *image,
|
||||
OrderedHashmap *extension_images,
|
||||
PortableFlags flags,
|
||||
PortableChange **changes,
|
||||
size_t *n_changes) {
|
||||
|
||||
Image *ext;
|
||||
int r;
|
||||
|
||||
assert(image);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
|
||||
r = install_image_symlink(ext->path, flags, changes, n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = install_image_symlink(image->path, flags, changes, n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int portable_attach(
|
||||
sd_bus *bus,
|
||||
const char *name_or_path,
|
||||
char **matches,
|
||||
const char *profile,
|
||||
char **extension_image_paths,
|
||||
PortableFlags flags,
|
||||
PortableChange **changes,
|
||||
size_t *n_changes,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
|
||||
_cleanup_(lookup_paths_free) LookupPaths paths = {};
|
||||
_cleanup_(image_unrefp) Image *image = NULL;
|
||||
PortableMetadata *item;
|
||||
Image *ext;
|
||||
char **p;
|
||||
int r;
|
||||
|
||||
assert(name_or_path);
|
||||
@ -1005,11 +1146,40 @@ int portable_attach(
|
||||
r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!strv_isempty(extension_image_paths)) {
|
||||
extension_images = ordered_hashmap_new(&image_hash_ops);
|
||||
if (!extension_images)
|
||||
return -ENOMEM;
|
||||
|
||||
r = portable_extract_by_path(image->path, matches, NULL, &unit_files, error);
|
||||
STRV_FOREACH(p, extension_image_paths) {
|
||||
_cleanup_(image_unrefp) Image *new = NULL;
|
||||
|
||||
r = image_find_harder(IMAGE_PORTABLE, *p, NULL, &new);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ordered_hashmap_put(extension_images, new->name, new);
|
||||
if (r < 0)
|
||||
return r;
|
||||
TAKE_PTR(new);
|
||||
}
|
||||
}
|
||||
|
||||
r = portable_extract_by_path(image->path, true, matches, NULL, &unit_files, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
|
||||
_cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
|
||||
|
||||
r = portable_extract_by_path(ext->path, false, matches, NULL, &extra_unit_files, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = hashmap_move(unit_files, extra_unit_files);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1029,62 +1199,95 @@ int portable_attach(
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH(item, unit_files) {
|
||||
r = attach_unit_file(&paths, image->path, image->type, item, profile, flags, changes, n_changes);
|
||||
r = attach_unit_file(&paths, image->path, image->type, extension_images,
|
||||
item, profile, flags, changes, n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* We don't care too much for the image symlink, it's just a convenience thing, it's not necessary for proper
|
||||
* operation otherwise. */
|
||||
(void) install_image_symlink(image->path, flags, changes, n_changes);
|
||||
(void) install_image_and_extensions_symlinks(image, extension_images, flags, changes, n_changes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool marker_matches_image(const char *marker, const char *name_or_path) {
|
||||
static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths) {
|
||||
_cleanup_strv_free_ char **root_and_extensions = NULL;
|
||||
char **image_name_or_path;
|
||||
const char *a;
|
||||
int r;
|
||||
|
||||
assert(marker);
|
||||
assert(name_or_path);
|
||||
|
||||
a = last_path_component(marker);
|
||||
/* If extensions were used when attaching, the marker will be a colon-separated
|
||||
* list of images/paths. We enforce strict 1:1 matching, so that we are sure
|
||||
* we are detaching exactly what was attached.
|
||||
* For each image, starting with the root, we look for a token in the marker,
|
||||
* and return a negative answer on any non-matching combination. */
|
||||
|
||||
if (image_name_is_valid(name_or_path)) {
|
||||
const char *e, *underscore;
|
||||
root_and_extensions = strv_new(name_or_path);
|
||||
if (!root_and_extensions)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We shall match against an image name. In that case let's compare the last component, and optionally
|
||||
* allow either a suffix of ".raw" or a series of "/".
|
||||
* But allow matching on a different version of the same image, when a "_" is used as a separator. */
|
||||
underscore = strchr(name_or_path, '_');
|
||||
if (underscore)
|
||||
return strneq(a, name_or_path, underscore - name_or_path);
|
||||
r = strv_extend_strv(&root_and_extensions, extension_image_paths, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
e = startswith(a, name_or_path);
|
||||
if (!e)
|
||||
STRV_FOREACH(image_name_or_path, root_and_extensions) {
|
||||
_cleanup_free_ char *image = NULL;
|
||||
|
||||
r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse marker: %s", marker);
|
||||
if (r == 0)
|
||||
return false;
|
||||
|
||||
return
|
||||
e[strspn(e, "/")] == 0 ||
|
||||
streq(e, ".raw");
|
||||
} else {
|
||||
const char *b, *underscore;
|
||||
size_t l;
|
||||
a = last_path_component(image);
|
||||
|
||||
/* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to
|
||||
* reach the same file. However, in this mode, let's validate any file suffix. */
|
||||
if (image_name_is_valid(*image_name_or_path)) {
|
||||
const char *e, *underscore;
|
||||
|
||||
l = strcspn(a, "/");
|
||||
b = last_path_component(name_or_path);
|
||||
/* We shall match against an image name. In that case let's compare the last component, and optionally
|
||||
* allow either a suffix of ".raw" or a series of "/".
|
||||
* But allow matching on a different version of the same image, when a "_" is used as a separator. */
|
||||
underscore = strchr(*image_name_or_path, '_');
|
||||
if (underscore) {
|
||||
if (strneq(a, *image_name_or_path, underscore - *image_name_or_path))
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcspn(b, "/") != l)
|
||||
return false;
|
||||
e = startswith(a, *image_name_or_path);
|
||||
if (!e)
|
||||
return false;
|
||||
|
||||
underscore = strchr(b, '_');
|
||||
if (underscore)
|
||||
l = underscore - b;
|
||||
if(!(e[strspn(e, "/")] == 0 || streq(e, ".raw")))
|
||||
return false;
|
||||
} else {
|
||||
const char *b, *underscore;
|
||||
size_t l;
|
||||
|
||||
return strneq(a, b, l);
|
||||
/* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to
|
||||
* reach the same file. However, in this mode, let's validate any file suffix. */
|
||||
|
||||
l = strcspn(a, "/");
|
||||
b = last_path_component(*image_name_or_path);
|
||||
|
||||
if (strcspn(b, "/") != l)
|
||||
return false;
|
||||
|
||||
underscore = strchr(b, '_');
|
||||
if (underscore)
|
||||
l = underscore - b;
|
||||
|
||||
if (!strneq(a, b, l))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int test_chroot_dropin(
|
||||
@ -1092,6 +1295,7 @@ static int test_chroot_dropin(
|
||||
const char *where,
|
||||
const char *fname,
|
||||
const char *name_or_path,
|
||||
char **extension_image_paths,
|
||||
char **ret_marker) {
|
||||
|
||||
_cleanup_free_ char *line = NULL, *marker = NULL;
|
||||
@ -1138,7 +1342,7 @@ static int test_chroot_dropin(
|
||||
if (!name_or_path)
|
||||
r = true;
|
||||
else
|
||||
r = marker_matches_image(marker, name_or_path);
|
||||
r = marker_matches_images(marker, name_or_path, extension_image_paths);
|
||||
|
||||
if (ret_marker)
|
||||
*ret_marker = TAKE_PTR(marker);
|
||||
@ -1149,6 +1353,7 @@ static int test_chroot_dropin(
|
||||
int portable_detach(
|
||||
sd_bus *bus,
|
||||
const char *name_or_path,
|
||||
char **extension_image_paths,
|
||||
PortableFlags flags,
|
||||
PortableChange **changes,
|
||||
size_t *n_changes,
|
||||
@ -1193,7 +1398,7 @@ int portable_detach(
|
||||
if (!IN_SET(de->d_type, DT_LNK, DT_REG))
|
||||
continue;
|
||||
|
||||
r = test_chroot_dropin(d, where, de->d_name, name_or_path, &marker);
|
||||
r = test_chroot_dropin(d, where, de->d_name, name_or_path, extension_image_paths, &marker);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@ -1215,12 +1420,20 @@ int portable_detach(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name);
|
||||
|
||||
if (path_is_absolute(marker) &&
|
||||
!image_in_search_path(IMAGE_PORTABLE, NULL, marker)) {
|
||||
for (const char *p = marker;;) {
|
||||
_cleanup_free_ char *image = NULL;
|
||||
|
||||
r = set_ensure_consume(&markers, &path_hash_ops_free, TAKE_PTR(marker));
|
||||
r = extract_first_word(&p, &image, ":", EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_RETAIN_ESCAPE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_debug_errno(r, "Failed to parse marker: %s", p);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (path_is_absolute(image) && !image_in_search_path(IMAGE_PORTABLE, NULL, image)) {
|
||||
r = set_ensure_consume(&markers, &path_hash_ops_free, TAKE_PTR(image));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1358,7 +1571,7 @@ static int portable_get_state_internal(
|
||||
if (!IN_SET(de->d_type, DT_LNK, DT_REG))
|
||||
continue;
|
||||
|
||||
r = test_chroot_dropin(d, where, de->d_name, name_or_path, NULL);
|
||||
r = test_chroot_dropin(d, where, de->d_name, name_or_path, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
|
@ -11,6 +11,7 @@
|
||||
typedef struct PortableMetadata {
|
||||
int fd;
|
||||
char *source;
|
||||
char *image_path;
|
||||
char name[];
|
||||
} PortableMetadata;
|
||||
|
||||
@ -18,10 +19,13 @@ typedef struct PortableMetadata {
|
||||
#define PORTABLE_METADATA_IS_UNIT(m) (!IN_SET((m)->name[0], 0, '/'))
|
||||
|
||||
typedef enum PortableFlags {
|
||||
PORTABLE_PREFER_COPY = 1 << 0,
|
||||
PORTABLE_PREFER_SYMLINK = 1 << 1,
|
||||
PORTABLE_RUNTIME = 1 << 2,
|
||||
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_TYPE_MAX,
|
||||
_PORTABLE_TYPE_INVALID = -EINVAL,
|
||||
} PortableFlags;
|
||||
|
||||
/* This enum is anonymous, since we usually store it in an 'int', as we overload it with negative errno
|
||||
@ -59,10 +63,10 @@ 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, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, sd_bus_error *error);
|
||||
int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, sd_bus_error *error);
|
||||
|
||||
int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
int portable_detach(sd_bus *bus, const char *name_or_path, PortableFlags flags, PortableChange **changes, size_t *n_changes, 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);
|
||||
|
||||
int portable_get_state(sd_bus *bus, const char *name_or_path, PortableFlags flags, PortableState *ret, sd_bus_error *error);
|
||||
|
||||
|
@ -21,9 +21,11 @@
|
||||
#include "main-func.h"
|
||||
#include "os-util.h"
|
||||
#include "pager.h"
|
||||
#include "parse-argument.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "portable.h"
|
||||
#include "spawn-polkit-agent.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@ -44,6 +46,9 @@ static const char *arg_host = NULL;
|
||||
static bool arg_enable = false;
|
||||
static bool arg_now = false;
|
||||
static bool arg_no_block = false;
|
||||
static char **arg_extension_images = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_extension_images, strv_freep);
|
||||
|
||||
static bool is_portable_managed(const char *unit) {
|
||||
return ENDSWITH_SET(unit, ".service", ".target", ".socket", ".path", ".timer");
|
||||
@ -83,6 +88,38 @@ static int determine_image(const char *image, bool permit_non_existing, char **r
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int attach_extensions_to_message(sd_bus_message *m, char **extensions) {
|
||||
char **p;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (strv_isempty(extensions))
|
||||
return 0;
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "s");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
STRV_FOREACH(p, extensions) {
|
||||
_cleanup_free_ char *resolved_extension_image = NULL;
|
||||
|
||||
r = determine_image(*p, false, &resolved_extension_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(m, "s", resolved_extension_image);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int extract_prefix(const char *path, char **ret) {
|
||||
_cleanup_free_ char *name = NULL;
|
||||
const char *bn, *underscore;
|
||||
@ -219,15 +256,55 @@ static int maybe_reload(sd_bus **bus) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inspect_image(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
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;
|
||||
const char *method;
|
||||
uint64_t flags = 0;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
|
||||
method = strv_isempty(arg_extension_images) ? "GetImageMetadata" : "GetImageMetadataWithExtensions";
|
||||
|
||||
r = bus_message_new_method_call(bus, &m, bus_portable_mgr, method);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "s", image);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = attach_extensions_to_message(m, arg_extension_images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append_strv(m, matches);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
if (!strv_isempty(arg_extension_images)) {
|
||||
r = sd_bus_message_append(m, "t", flags);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(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 0;
|
||||
}
|
||||
|
||||
static int inspect_image(int argc, char *argv[], void *userdata) {
|
||||
_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_free_ char *image = NULL;
|
||||
bool nl = false, header = false;
|
||||
const void *data;
|
||||
const char *path;
|
||||
const void *data;
|
||||
size_t sz;
|
||||
int r;
|
||||
|
||||
@ -243,21 +320,9 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "GetImageMetadata");
|
||||
r = get_image_metadata(bus, image, matches, &reply);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "s", image);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append_strv(m, matches);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(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 r;
|
||||
|
||||
r = sd_bus_message_read(reply, "s", &path);
|
||||
if (r < 0)
|
||||
@ -607,8 +672,7 @@ static int maybe_stop_enable_restart(sd_bus *bus, sd_bus_message *reply) {
|
||||
|
||||
static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
|
||||
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_strv_free_ char **matches = NULL;
|
||||
int r;
|
||||
|
||||
@ -623,21 +687,9 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not watch jobs: %m");
|
||||
|
||||
r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "GetImageMetadata");
|
||||
r = get_image_metadata(bus, image, matches, &reply);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "s", image);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append_strv(m, matches);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(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 r;
|
||||
|
||||
r = sd_bus_message_skip(reply, "say");
|
||||
if (r < 0)
|
||||
@ -693,7 +745,7 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) {
|
||||
int r;
|
||||
|
||||
assert(method);
|
||||
assert(STR_IN_SET(method, "AttachImage", "ReattachImage"));
|
||||
assert(STR_IN_SET(method, "AttachImage", "ReattachImage", "AttachImageWithExtensions", "ReattachImageWithExtensions"));
|
||||
|
||||
r = determine_image(argv[1], false, &image);
|
||||
if (r < 0)
|
||||
@ -717,11 +769,24 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) {
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = attach_extensions_to_message(m, arg_extension_images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append_strv(m, matches);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "sbs", arg_profile, arg_runtime, arg_copy_mode);
|
||||
r = sd_bus_message_append(m, "s", arg_profile);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
if (STR_IN_SET(method, "AttachImageWithExtensions", "ReattachImageWithExtensions")) {
|
||||
uint64_t flags = arg_runtime ? PORTABLE_RUNTIME : 0;
|
||||
|
||||
r = sd_bus_message_append(m, "st", arg_copy_mode, flags);
|
||||
} else
|
||||
r = sd_bus_message_append(m, "bs", arg_runtime, arg_copy_mode);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
@ -733,7 +798,7 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) {
|
||||
|
||||
print_changes(reply);
|
||||
|
||||
if (streq(method, "AttachImage"))
|
||||
if (STR_IN_SET(method, "AttachImage", "AttachImageWithExtensions"))
|
||||
(void) maybe_enable_start(bus, reply);
|
||||
else {
|
||||
/* ReattachImage returns 2 lists - removed units first, and changed/added second */
|
||||
@ -745,18 +810,19 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) {
|
||||
}
|
||||
|
||||
static int attach_image(int argc, char *argv[], void *userdata) {
|
||||
return attach_reattach_image(argc, argv, "AttachImage");
|
||||
return attach_reattach_image(argc, argv, strv_isempty(arg_extension_images) ? "AttachImage" : "AttachImageWithExtensions");
|
||||
}
|
||||
|
||||
static int reattach_image(int argc, char *argv[], void *userdata) {
|
||||
return attach_reattach_image(argc, argv, "ReattachImage");
|
||||
return attach_reattach_image(argc, argv, strv_isempty(arg_extension_images) ? "ReattachImage" : "ReattachImageWithExtensions");
|
||||
}
|
||||
|
||||
static int detach_image(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_free_ char *image = NULL;
|
||||
const char *method;
|
||||
int r;
|
||||
|
||||
r = determine_image(argv[1], true, &image);
|
||||
@ -771,9 +837,32 @@ static int detach_image(int argc, char *argv[], void *userdata) {
|
||||
|
||||
(void) maybe_stop_disable(bus, image, argv);
|
||||
|
||||
r = bus_call_method(bus, bus_portable_mgr, "DetachImage", &error, &reply, "sb", image, arg_runtime);
|
||||
method = strv_isempty(arg_extension_images) ? "DetachImage" : "DetachImageWithExtensions";
|
||||
|
||||
r = bus_message_new_method_call(bus, &m, bus_portable_mgr, method);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to detach image: %s", bus_error_message(&error, r));
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append(m, "s", image);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = attach_extensions_to_message(m, arg_extension_images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!strv_isempty(arg_extension_images)) {
|
||||
uint64_t flags = arg_runtime ? PORTABLE_RUNTIME : 0;
|
||||
|
||||
r = sd_bus_message_append(m, "t", flags);
|
||||
} else
|
||||
r = sd_bus_message_append(m, "b", arg_runtime);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_call(bus, m, 0, &error, &reply);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "%s failed: %s", method, bus_error_message(&error, r));
|
||||
|
||||
(void) maybe_reload(&bus);
|
||||
|
||||
@ -1045,6 +1134,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --now Immediately start/stop the portable service after\n"
|
||||
" attach/before detach\n"
|
||||
" --no-block Don't block waiting for attach --now to complete\n"
|
||||
" --extension=PATH Extend the image with an overlay\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
@ -1055,6 +1145,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
@ -1068,6 +1159,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_ENABLE,
|
||||
ARG_NOW,
|
||||
ARG_NO_BLOCK,
|
||||
ARG_EXTENSION,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -1087,6 +1179,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "enable", no_argument, NULL, ARG_ENABLE },
|
||||
{ "now", no_argument, NULL, ARG_NOW },
|
||||
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
||||
{ "extension", required_argument, NULL, ARG_EXTENSION },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1185,6 +1278,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_no_block = true;
|
||||
break;
|
||||
|
||||
case ARG_EXTENSION:
|
||||
r = strv_extend(&arg_extension_images, optarg);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -252,11 +252,13 @@ static int method_attach_image(sd_bus_message *message, void *userdata, sd_bus_e
|
||||
}
|
||||
|
||||
static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_strv_free_ char **extension_images = NULL;
|
||||
PortableChange *changes = NULL;
|
||||
PortableFlags flags = 0;
|
||||
Manager *m = userdata;
|
||||
size_t n_changes = 0;
|
||||
const char *name_or_path;
|
||||
int r, runtime;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
@ -265,10 +267,37 @@ static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_e
|
||||
* detach already deleted images too, in case the user already deleted an image before properly detaching
|
||||
* it. */
|
||||
|
||||
r = sd_bus_message_read(message, "sb", &name_or_path, &runtime);
|
||||
r = sd_bus_message_read(message, "s", &name_or_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_bus_message_is_method_call(message, NULL, "DetachImageWithExtensions")) {
|
||||
uint64_t input_flags = 0;
|
||||
|
||||
r = sd_bus_message_read_strv(message, &extension_images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_read(message, "t", &input_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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;
|
||||
} else {
|
||||
int runtime;
|
||||
|
||||
r = sd_bus_message_read(message, "b", &runtime);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (runtime)
|
||||
flags |= PORTABLE_RUNTIME;
|
||||
}
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
message,
|
||||
CAP_SYS_ADMIN,
|
||||
@ -286,7 +315,8 @@ static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_e
|
||||
r = portable_detach(
|
||||
sd_bus_message_get_bus(message),
|
||||
name_or_path,
|
||||
runtime ? PORTABLE_RUNTIME : 0,
|
||||
extension_images,
|
||||
flags,
|
||||
&changes,
|
||||
&n_changes,
|
||||
error);
|
||||
@ -383,6 +413,16 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
"a{say}", units),
|
||||
method_get_image_metadata,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("GetImageMetadataWithExtensions",
|
||||
SD_BUS_ARGS("s", image,
|
||||
"as", extensions,
|
||||
"as", matches,
|
||||
"t", flags),
|
||||
SD_BUS_RESULT("s", image,
|
||||
"ay", os_release,
|
||||
"a{say}", units),
|
||||
method_get_image_metadata,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("GetImageState",
|
||||
SD_BUS_ARGS("s", image),
|
||||
SD_BUS_RESULT("s", state),
|
||||
@ -397,12 +437,29 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_RESULT("a(sss)", changes),
|
||||
method_attach_image,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("AttachImageWithExtensions",
|
||||
SD_BUS_ARGS("s", image,
|
||||
"as", extensions,
|
||||
"as", matches,
|
||||
"s", profile,
|
||||
"s", copy_mode,
|
||||
"t", flags),
|
||||
SD_BUS_RESULT("a(sss)", changes),
|
||||
method_attach_image,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("DetachImage",
|
||||
SD_BUS_ARGS("s", image,
|
||||
"b", runtime),
|
||||
SD_BUS_RESULT("a(sss)", changes),
|
||||
method_detach_image,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("DetachImageWithExtensions",
|
||||
SD_BUS_ARGS("s", image,
|
||||
"as", extensions,
|
||||
"t", flags),
|
||||
SD_BUS_RESULT("a(sss)", changes),
|
||||
method_detach_image,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("ReattachImage",
|
||||
SD_BUS_ARGS("s", image,
|
||||
"as", matches,
|
||||
@ -413,6 +470,17 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
"a(sss)", changes_updated),
|
||||
method_reattach_image,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("ReattachImageWithExtensions",
|
||||
SD_BUS_ARGS("s", image,
|
||||
"as", extensions,
|
||||
"as", matches,
|
||||
"s", profile,
|
||||
"s", copy_mode,
|
||||
"t", flags),
|
||||
SD_BUS_RESULT("a(sss)", changes_removed,
|
||||
"a(sss)", changes_updated),
|
||||
method_reattach_image,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("RemoveImage",
|
||||
SD_BUS_ARGS("s", image),
|
||||
SD_BUS_NO_RESULT,
|
||||
|
@ -75,20 +75,22 @@ static int bus_image_method_get_os_release(sd_bus_message *message, void *userda
|
||||
static int append_fd(sd_bus_message *m, PortableMetadata *d) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
size_t n;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(d);
|
||||
assert(d->fd >= 0);
|
||||
|
||||
f = take_fdopen(&d->fd, "r");
|
||||
if (!f)
|
||||
return -errno;
|
||||
if (d) {
|
||||
assert(d->fd >= 0);
|
||||
|
||||
r = read_full_stream(f, &buf, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
f = take_fdopen(&d->fd, "r");
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
r = read_full_stream(f, &buf, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_append_array(m, 'y', buf, n);
|
||||
}
|
||||
@ -101,10 +103,12 @@ int bus_image_common_get_metadata(
|
||||
sd_bus_error *error) {
|
||||
|
||||
_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;
|
||||
_cleanup_strv_free_ char **matches = NULL;
|
||||
/* Unused for now, but added to the DBUS methods for future-proofing */
|
||||
uint64_t input_flags = 0;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
@ -116,10 +120,29 @@ int bus_image_common_get_metadata(
|
||||
m = image->userdata;
|
||||
}
|
||||
|
||||
if (sd_bus_message_is_method_call(message, NULL, "GetImageMetadataWithExtensions") ||
|
||||
sd_bus_message_is_method_call(message, NULL, "GetMetadataWithExtensions")) {
|
||||
r = sd_bus_message_read_strv(message, &extension_images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_read_strv(message, &matches);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_bus_message_is_method_call(message, NULL, "GetImageMetadataWithExtensions") ||
|
||||
sd_bus_message_is_method_call(message, NULL, "GetMetadataWithExtensions")) {
|
||||
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)
|
||||
return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Invalid 'flags' parameter '%" PRIu64 "'",
|
||||
input_flags);
|
||||
}
|
||||
|
||||
r = bus_image_acquire(m,
|
||||
message,
|
||||
name_or_path,
|
||||
@ -136,6 +159,7 @@ int bus_image_common_get_metadata(
|
||||
r = portable_extract(
|
||||
image->path,
|
||||
matches,
|
||||
extension_images,
|
||||
&os_release,
|
||||
&unit_files,
|
||||
error);
|
||||
@ -223,12 +247,12 @@ int bus_image_common_attach(
|
||||
Image *image,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_strv_free_ char **matches = NULL;
|
||||
_cleanup_strv_free_ char **matches = NULL, **extension_images = NULL;
|
||||
PortableChange *changes = NULL;
|
||||
PortableFlags flags = 0;
|
||||
const char *profile, *copy_mode;
|
||||
size_t n_changes = 0;
|
||||
int runtime, r;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(name_or_path || image);
|
||||
@ -238,14 +262,44 @@ int bus_image_common_attach(
|
||||
m = image->userdata;
|
||||
}
|
||||
|
||||
if (sd_bus_message_is_method_call(message, NULL, "AttachImageWithExtensions") ||
|
||||
sd_bus_message_is_method_call(message, NULL, "AttachWithExtensions")) {
|
||||
r = sd_bus_message_read_strv(message, &extension_images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_read_strv(message, &matches);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_read(message, "sbs", &profile, &runtime, ©_mode);
|
||||
r = sd_bus_message_read(message, "s", &profile);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_bus_message_is_method_call(message, NULL, "AttachImageWithExtensions") ||
|
||||
sd_bus_message_is_method_call(message, NULL, "AttachWithExtensions")) {
|
||||
uint64_t input_flags = 0;
|
||||
|
||||
r = sd_bus_message_read(message, "st", ©_mode, &input_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
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;
|
||||
} else {
|
||||
int runtime;
|
||||
|
||||
r = sd_bus_message_read(message, "bs", &runtime, ©_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (runtime)
|
||||
flags |= PORTABLE_RUNTIME;
|
||||
}
|
||||
|
||||
if (streq(copy_mode, "symlink"))
|
||||
flags |= PORTABLE_PREFER_SYMLINK;
|
||||
else if (streq(copy_mode, "copy"))
|
||||
@ -253,9 +307,6 @@ int bus_image_common_attach(
|
||||
else if (!isempty(copy_mode))
|
||||
return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
|
||||
|
||||
if (runtime)
|
||||
flags |= PORTABLE_RUNTIME;
|
||||
|
||||
r = bus_image_acquire(m,
|
||||
message,
|
||||
name_or_path,
|
||||
@ -274,6 +325,7 @@ int bus_image_common_attach(
|
||||
image->path,
|
||||
matches,
|
||||
profile,
|
||||
extension_images,
|
||||
flags,
|
||||
&changes,
|
||||
&n_changes,
|
||||
@ -297,19 +349,46 @@ static int bus_image_method_detach(
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_strv_free_ char **extension_images = NULL;
|
||||
PortableChange *changes = NULL;
|
||||
Image *image = userdata;
|
||||
Manager *m = image->userdata;
|
||||
PortableFlags flags = 0;
|
||||
size_t n_changes = 0;
|
||||
int r, runtime;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(image);
|
||||
assert(m);
|
||||
|
||||
r = sd_bus_message_read(message, "b", &runtime);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (sd_bus_message_is_method_call(message, NULL, "DetachWithExtensions")) {
|
||||
r = sd_bus_message_read_strv(message, &extension_images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (sd_bus_message_is_method_call(message, NULL, "DetachWithExtensions")) {
|
||||
uint64_t input_flags = 0;
|
||||
|
||||
r = sd_bus_message_read(message, "t", &input_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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;
|
||||
} else {
|
||||
int runtime;
|
||||
|
||||
r = sd_bus_message_read(message, "b", &runtime);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (runtime)
|
||||
flags |= PORTABLE_RUNTIME;
|
||||
}
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
message,
|
||||
@ -328,7 +407,8 @@ static int bus_image_method_detach(
|
||||
r = portable_detach(
|
||||
sd_bus_message_get_bus(message),
|
||||
image->path,
|
||||
runtime ? PORTABLE_RUNTIME : 0,
|
||||
extension_images,
|
||||
flags,
|
||||
&changes,
|
||||
&n_changes,
|
||||
error);
|
||||
@ -510,10 +590,10 @@ int bus_image_common_reattach(
|
||||
|
||||
PortableChange *changes_detached = NULL, *changes_attached = NULL, *changes_gone = NULL;
|
||||
size_t n_changes_detached = 0, n_changes_attached = 0, n_changes_gone = 0;
|
||||
_cleanup_strv_free_ char **matches = NULL;
|
||||
_cleanup_strv_free_ char **matches = NULL, **extension_images = NULL;
|
||||
PortableFlags flags = PORTABLE_REATTACH;
|
||||
const char *profile, *copy_mode;
|
||||
int runtime, r;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(name_or_path || image);
|
||||
@ -523,14 +603,45 @@ int bus_image_common_reattach(
|
||||
m = image->userdata;
|
||||
}
|
||||
|
||||
if (sd_bus_message_is_method_call(message, NULL, "ReattachImageWithExtensions") ||
|
||||
sd_bus_message_is_method_call(message, NULL, "ReattachWithExtensions")) {
|
||||
r = sd_bus_message_read_strv(message, &extension_images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_read_strv(message, &matches);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_read(message, "sbs", &profile, &runtime, ©_mode);
|
||||
r = sd_bus_message_read(message, "s", &profile);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_bus_message_is_method_call(message, NULL, "ReattachImageWithExtensions") ||
|
||||
sd_bus_message_is_method_call(message, NULL, "ReattachWithExtensions")) {
|
||||
uint64_t input_flags = 0;
|
||||
|
||||
r = sd_bus_message_read(message, "st", ©_mode, &input_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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;
|
||||
} else {
|
||||
int runtime;
|
||||
|
||||
r = sd_bus_message_read(message, "bs", &runtime, ©_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (runtime)
|
||||
flags |= PORTABLE_RUNTIME;
|
||||
}
|
||||
|
||||
if (streq(copy_mode, "symlink"))
|
||||
flags |= PORTABLE_PREFER_SYMLINK;
|
||||
else if (streq(copy_mode, "copy"))
|
||||
@ -538,9 +649,6 @@ int bus_image_common_reattach(
|
||||
else if (!isempty(copy_mode))
|
||||
return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
|
||||
|
||||
if (runtime)
|
||||
flags |= PORTABLE_RUNTIME;
|
||||
|
||||
r = bus_image_acquire(m,
|
||||
message,
|
||||
name_or_path,
|
||||
@ -557,6 +665,7 @@ int bus_image_common_reattach(
|
||||
r = portable_detach(
|
||||
sd_bus_message_get_bus(message),
|
||||
image->path,
|
||||
extension_images,
|
||||
flags,
|
||||
&changes_detached,
|
||||
&n_changes_detached,
|
||||
@ -569,6 +678,7 @@ int bus_image_common_reattach(
|
||||
image->path,
|
||||
matches,
|
||||
profile,
|
||||
extension_images,
|
||||
flags,
|
||||
&changes_attached,
|
||||
&n_changes_attached,
|
||||
@ -721,6 +831,15 @@ const sd_bus_vtable image_vtable[] = {
|
||||
"a{say}", units),
|
||||
bus_image_method_get_metadata,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("GetMetadataWithExtensions",
|
||||
SD_BUS_ARGS("as", extensions,
|
||||
"as", matches,
|
||||
"t", flags),
|
||||
SD_BUS_RESULT("s", image,
|
||||
"ay", os_release,
|
||||
"a{say}", units),
|
||||
bus_image_method_get_metadata,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("GetState",
|
||||
SD_BUS_NO_ARGS,
|
||||
SD_BUS_RESULT("s", state),
|
||||
@ -734,11 +853,26 @@ const sd_bus_vtable image_vtable[] = {
|
||||
SD_BUS_RESULT("a(sss)", changes),
|
||||
bus_image_method_attach,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("AttachWithExtensions",
|
||||
SD_BUS_ARGS("as", extensions,
|
||||
"as", matches,
|
||||
"s", profile,
|
||||
"s", copy_mode,
|
||||
"t", flags),
|
||||
SD_BUS_RESULT("a(sss)", changes),
|
||||
bus_image_method_attach,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("Detach",
|
||||
SD_BUS_ARGS("b", runtime),
|
||||
SD_BUS_RESULT("a(sss)", changes),
|
||||
bus_image_method_detach,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("DetachWithExtensions",
|
||||
SD_BUS_ARGS("as", extensions,
|
||||
"t", flags),
|
||||
SD_BUS_RESULT("a(sss)", changes),
|
||||
bus_image_method_detach,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("Reattach",
|
||||
SD_BUS_ARGS("as", matches,
|
||||
"s", profile,
|
||||
@ -748,6 +882,16 @@ const sd_bus_vtable image_vtable[] = {
|
||||
"a(sss)", changes_updated),
|
||||
bus_image_method_reattach,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("ReattacheWithExtensions",
|
||||
SD_BUS_ARGS("as", extensions,
|
||||
"as", matches,
|
||||
"s", profile,
|
||||
"s", copy_mode,
|
||||
"t", flags),
|
||||
SD_BUS_RESULT("a(sss)", changes_removed,
|
||||
"a(sss)", changes_updated),
|
||||
bus_image_method_reattach,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("Remove",
|
||||
SD_BUS_NO_ARGS,
|
||||
SD_BUS_NO_RESULT,
|
||||
|
@ -15,6 +15,7 @@ test_append_files() {
|
||||
instmods loop =block
|
||||
instmods squashfs =squashfs
|
||||
instmods dm_verity =md
|
||||
instmods overlay =overlayfs
|
||||
install_dmevent
|
||||
generate_module_dependencies
|
||||
inst_binary losetup
|
||||
|
@ -63,6 +63,36 @@ portablectl detach --now --enable --runtime /tmp/minimal_1 app0
|
||||
|
||||
portablectl list | grep -q -F "No images."
|
||||
|
||||
root="/usr/share/minimal_0.raw"
|
||||
app1="/usr/share/app1.raw"
|
||||
|
||||
portablectl attach --now --runtime --extension ${app1} ${root} app1
|
||||
|
||||
systemctl is-active app1.service
|
||||
|
||||
portablectl reattach --now --runtime --extension ${app1} ${root} app1
|
||||
|
||||
systemctl is-active app1.service
|
||||
|
||||
portablectl detach --now --runtime --extension ${app1} ${root} app1
|
||||
|
||||
# portablectl also works with directory paths rather than images
|
||||
|
||||
mkdir /tmp/rootdir /tmp/app1 /tmp/overlay
|
||||
mount ${app1} /tmp/app1
|
||||
mount ${root} /tmp/rootdir
|
||||
mount -t overlay overlay -o lowerdir=/tmp/app1:/tmp/rootdir /tmp/overlay
|
||||
|
||||
portablectl attach --copy=symlink --now --runtime /tmp/overlay app1
|
||||
|
||||
systemctl is-active app1.service
|
||||
|
||||
portablectl detach --now --runtime overlay app1
|
||||
|
||||
umount /tmp/overlay
|
||||
umount /tmp/rootdir
|
||||
umount /tmp/app1
|
||||
|
||||
echo OK > /testok
|
||||
|
||||
exit 0
|
||||
|
Loading…
Reference in New Issue
Block a user