mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 04:03:36 +08: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 s image,
|
||||||
out ay os_release,
|
out ay os_release,
|
||||||
out a{say} units);
|
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,
|
GetImageState(in s image,
|
||||||
out s state);
|
out s state);
|
||||||
AttachImage(in s image,
|
AttachImage(in s image,
|
||||||
@ -56,9 +63,20 @@ node /org/freedesktop/portable1 {
|
|||||||
in b runtime,
|
in b runtime,
|
||||||
in s copy_mode,
|
in s copy_mode,
|
||||||
out a(sss) changes);
|
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,
|
DetachImage(in s image,
|
||||||
in b runtime,
|
in b runtime,
|
||||||
out a(sss) changes);
|
out a(sss) changes);
|
||||||
|
DetachImageWithExtensions(in s image,
|
||||||
|
in as extensions,
|
||||||
|
in t flags,
|
||||||
|
out a(sss) changes);
|
||||||
ReattachImage(in s image,
|
ReattachImage(in s image,
|
||||||
in as matches,
|
in as matches,
|
||||||
in s profile,
|
in s profile,
|
||||||
@ -66,6 +84,14 @@ node /org/freedesktop/portable1 {
|
|||||||
in s copy_mode,
|
in s copy_mode,
|
||||||
out a(sss) changes_removed,
|
out a(sss) changes_removed,
|
||||||
out a(sss) changes_updated);
|
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);
|
RemoveImage(in s image);
|
||||||
MarkImageReadOnly(in s image,
|
MarkImageReadOnly(in s image,
|
||||||
in b read_only);
|
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="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="GetImageState()"/>
|
||||||
|
|
||||||
<variablelist class="dbus-method" generated="True" extra-ref="AttachImage()"/>
|
<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="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="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="RemoveImage()"/>
|
||||||
|
|
||||||
<variablelist class="dbus-method" generated="True" extra-ref="MarkImageReadOnly()"/>
|
<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
|
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>
|
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
|
<para><function>GetImageState()</function> retrieves the image state as one of the following
|
||||||
strings:
|
strings:
|
||||||
<itemizedlist>
|
<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
|
Note that an image cannot be attached if a unit that it contains is already present
|
||||||
on the system.</para>
|
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.
|
<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
|
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
|
detach was attached only for the current boot session or persistently. This method
|
||||||
@ -209,6 +259,12 @@ node /org/freedesktop/portable1 {
|
|||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
Note that an image cannot be detached if a unit that it contains is running.</para>
|
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
|
<para><function>ReattachImage()</function> combines the effects of the
|
||||||
<function>AttachImage()</function> method and the <function>DetachImage()</function> method.
|
<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
|
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>DetachImage()</function> method (first array, units that were removed) and the
|
||||||
<function>AttachImage()</function> method (second array, units that were updated or added).</para>
|
<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>RemoveImage()</function> removes the image with the specified name.</para>
|
||||||
|
|
||||||
<para><function>MarkImageReadOnly()</function> toggles the read-only flag of an image.</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>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><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>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
@ -254,20 +327,42 @@ node /org/freedesktop/portable1 {
|
|||||||
out s image,
|
out s image,
|
||||||
out ay os_release,
|
out ay os_release,
|
||||||
out a{say} units);
|
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);
|
GetState(out s UNNAMED);
|
||||||
Attach(in as matches,
|
Attach(in as matches,
|
||||||
in s profile,
|
in s profile,
|
||||||
in b runtime,
|
in b runtime,
|
||||||
in s copy_mode,
|
in s copy_mode,
|
||||||
out a(sss) changes);
|
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,
|
Detach(in b runtime,
|
||||||
out a(sss) changes);
|
out a(sss) changes);
|
||||||
|
DetachWithExtensions(in as extensions,
|
||||||
|
in t flags,
|
||||||
|
out a(sss) changes);
|
||||||
Reattach(in as matches,
|
Reattach(in as matches,
|
||||||
in s profile,
|
in s profile,
|
||||||
in b runtime,
|
in b runtime,
|
||||||
in s copy_mode,
|
in s copy_mode,
|
||||||
out a(sss) changes_removed,
|
out a(sss) changes_removed,
|
||||||
out a(sss) changes_updated);
|
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();
|
Remove();
|
||||||
MarkReadOnly(in b read_only);
|
MarkReadOnly(in b read_only);
|
||||||
SetLimit(in t limit);
|
SetLimit(in t limit);
|
||||||
@ -303,14 +398,22 @@ node /org/freedesktop/portable1 {
|
|||||||
|
|
||||||
<!--method GetMetadata is not documented!-->
|
<!--method GetMetadata is not documented!-->
|
||||||
|
|
||||||
|
<!--method GetMetadataWithExtensions is not documented!-->
|
||||||
|
|
||||||
<!--method GetState is not documented!-->
|
<!--method GetState is not documented!-->
|
||||||
|
|
||||||
<!--method Attach is not documented!-->
|
<!--method Attach is not documented!-->
|
||||||
|
|
||||||
|
<!--method AttachWithExtensions is not documented!-->
|
||||||
|
|
||||||
<!--method Detach is not documented!-->
|
<!--method Detach is not documented!-->
|
||||||
|
|
||||||
|
<!--method DetachWithExtensions is not documented!-->
|
||||||
|
|
||||||
<!--method Reattach is not documented!-->
|
<!--method Reattach is not documented!-->
|
||||||
|
|
||||||
|
<!--method ReattacheWithExtensions is not documented!-->
|
||||||
|
|
||||||
<!--method Remove is not documented!-->
|
<!--method Remove is not documented!-->
|
||||||
|
|
||||||
<!--method MarkReadOnly 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="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="GetState()"/>
|
||||||
|
|
||||||
<variablelist class="dbus-method" generated="True" extra-ref="Attach()"/>
|
<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="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="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="Remove()"/>
|
||||||
|
|
||||||
<variablelist class="dbus-method" generated="True" extra-ref="MarkReadOnly()"/>
|
<variablelist class="dbus-method" generated="True" extra-ref="MarkReadOnly()"/>
|
||||||
@ -377,14 +488,22 @@ node /org/freedesktop/portable1 {
|
|||||||
|
|
||||||
<listitem><para>GetMetadata()</para></listitem>
|
<listitem><para>GetMetadata()</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>GetMetadataWithExtensions()</para></listitem>
|
||||||
|
|
||||||
<listitem><para>GetState()</para></listitem>
|
<listitem><para>GetState()</para></listitem>
|
||||||
|
|
||||||
<listitem><para>Attach()</para></listitem>
|
<listitem><para>Attach()</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>AttachWithExtensions()</para></listitem>
|
||||||
|
|
||||||
<listitem><para>Detach()</para></listitem>
|
<listitem><para>Detach()</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>DetachWithExtensions()</para></listitem>
|
||||||
|
|
||||||
<listitem><para>Reattach()</para></listitem>
|
<listitem><para>Reattach()</para></listitem>
|
||||||
|
|
||||||
|
<listitem><para>ReattacheWithExtensions()</para></listitem>
|
||||||
|
|
||||||
<listitem><para>Remove()</para></listitem>
|
<listitem><para>Remove()</para></listitem>
|
||||||
|
|
||||||
<listitem><para>MarkReadOnly()</para></listitem>
|
<listitem><para>MarkReadOnly()</para></listitem>
|
||||||
|
@ -352,6 +352,19 @@
|
|||||||
<listitem><para>Don't block waiting for attach --now to complete.</para></listitem>
|
<listitem><para>Don't block waiting for attach --now to complete.</para></listitem>
|
||||||
</varlistentry>
|
</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="host" />
|
||||||
<xi:include href="user-system-options.xml" xpointer="machine" />
|
<xi:include href="user-system-options.xml" xpointer="machine" />
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "discover-image.h"
|
#include "discover-image.h"
|
||||||
#include "dissect-image.h"
|
#include "dissect-image.h"
|
||||||
#include "errno-list.h"
|
#include "errno-list.h"
|
||||||
|
#include "escape.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
@ -74,17 +75,26 @@ static bool unit_match(const char *unit, char **matches) {
|
|||||||
return false;
|
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;
|
PortableMetadata *m;
|
||||||
|
|
||||||
m = malloc0(offsetof(PortableMetadata, name) + strlen(name) + 1);
|
m = malloc0(offsetof(PortableMetadata, name) + strlen(name) + 1);
|
||||||
if (!m)
|
if (!m)
|
||||||
return NULL;
|
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);
|
strcpy(m->name, name);
|
||||||
m->fd = fd;
|
m->fd = fd;
|
||||||
|
|
||||||
return m;
|
return TAKE_PTR(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
PortableMetadata *portable_metadata_unref(PortableMetadata *i) {
|
PortableMetadata *portable_metadata_unref(PortableMetadata *i) {
|
||||||
@ -93,6 +103,7 @@ PortableMetadata *portable_metadata_unref(PortableMetadata *i) {
|
|||||||
|
|
||||||
safe_close(i->fd);
|
safe_close(i->fd);
|
||||||
free(i->source);
|
free(i->source);
|
||||||
|
free(i->image_path);
|
||||||
|
|
||||||
return mfree(i);
|
return mfree(i);
|
||||||
}
|
}
|
||||||
@ -255,7 +266,7 @@ static int extract_now(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ret_os_release) {
|
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)
|
if (!os_release)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -316,7 +327,7 @@ static int extract_now(
|
|||||||
return log_debug_errno(r, "Failed to send unit metadata to parent: %m");
|
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)
|
if (!m)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
fd = -1;
|
fd = -1;
|
||||||
@ -342,6 +353,7 @@ static int extract_now(
|
|||||||
|
|
||||||
static int portable_extract_by_path(
|
static int portable_extract_by_path(
|
||||||
const char *path,
|
const char *path,
|
||||||
|
bool extract_os_release,
|
||||||
char **matches,
|
char **matches,
|
||||||
PortableMetadata **ret_os_release,
|
PortableMetadata **ret_os_release,
|
||||||
Hashmap **ret_unit_files,
|
Hashmap **ret_unit_files,
|
||||||
@ -412,7 +424,7 @@ static int portable_extract_by_path(
|
|||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
seq[0] = safe_close(seq[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) {
|
if (r < 0) {
|
||||||
log_debug_errno(r, "Failed to mount dissected image: %m");
|
log_debug_errno(r, "Failed to mount dissected image: %m");
|
||||||
goto child_finish;
|
goto child_finish;
|
||||||
@ -448,7 +460,7 @@ static int portable_extract_by_path(
|
|||||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"Invalid item sent from child.");
|
"Invalid item sent from child.");
|
||||||
|
|
||||||
add = portable_metadata_new(name, fd);
|
add = portable_metadata_new(name, path, fd);
|
||||||
if (!add)
|
if (!add)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
fd = -1;
|
fd = -1;
|
||||||
@ -478,10 +490,12 @@ static int portable_extract_by_path(
|
|||||||
child = 0;
|
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);
|
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);
|
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)
|
if (ret_unit_files)
|
||||||
@ -496,11 +510,16 @@ static int portable_extract_by_path(
|
|||||||
int portable_extract(
|
int portable_extract(
|
||||||
const char *name_or_path,
|
const char *name_or_path,
|
||||||
char **matches,
|
char **matches,
|
||||||
|
char **extension_image_paths,
|
||||||
PortableMetadata **ret_os_release,
|
PortableMetadata **ret_os_release,
|
||||||
Hashmap **ret_unit_files,
|
Hashmap **ret_unit_files,
|
||||||
sd_bus_error *error) {
|
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;
|
_cleanup_(image_unrefp) Image *image = NULL;
|
||||||
|
Image *ext;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(name_or_path);
|
assert(name_or_path);
|
||||||
@ -509,7 +528,46 @@ int portable_extract(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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(
|
static int unit_file_is_active(
|
||||||
@ -684,9 +742,49 @@ void portable_changes_free(PortableChange *changes, size_t n_changes) {
|
|||||||
free(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(
|
static int install_chroot_dropin(
|
||||||
const char *image_path,
|
const char *image_path,
|
||||||
ImageType type,
|
ImageType type,
|
||||||
|
OrderedHashmap *extension_images,
|
||||||
const PortableMetadata *m,
|
const PortableMetadata *m,
|
||||||
const char *dropin_dir,
|
const char *dropin_dir,
|
||||||
char **ret_dropin,
|
char **ret_dropin,
|
||||||
@ -694,6 +792,7 @@ static int install_chroot_dropin(
|
|||||||
size_t *n_changes) {
|
size_t *n_changes) {
|
||||||
|
|
||||||
_cleanup_free_ char *text = NULL, *dropin = NULL;
|
_cleanup_free_ char *text = NULL, *dropin = NULL;
|
||||||
|
Image *ext;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(image_path);
|
assert(image_path);
|
||||||
@ -704,12 +803,15 @@ static int install_chroot_dropin(
|
|||||||
if (!dropin)
|
if (!dropin)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
text = strjoin(PORTABLE_DROPIN_MARKER_BEGIN, image_path, PORTABLE_DROPIN_MARKER_END "\n");
|
r = make_marker_text(image_path, extension_images, &text);
|
||||||
if (!text)
|
if (r < 0)
|
||||||
return -ENOMEM;
|
return log_debug_errno(r, "Failed to generate marker string for portable drop-in: %m");
|
||||||
|
|
||||||
if (endswith(m->name, ".service")) {
|
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 (access("/etc/os-release", F_OK) < 0) {
|
||||||
if (errno != ENOENT)
|
if (errno != ENOENT)
|
||||||
@ -719,14 +821,23 @@ static int install_chroot_dropin(
|
|||||||
} else
|
} else
|
||||||
os_release_source = "/etc/os-release";
|
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,
|
if (!strextend(&text,
|
||||||
"\n"
|
"\n"
|
||||||
"[Service]\n",
|
"[Service]\n",
|
||||||
IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "RootDirectory=" : "RootImage=", image_path, "\n"
|
root_type, image_path, "\n"
|
||||||
"Environment=PORTABLE=", basename(image_path), "\n"
|
"Environment=PORTABLE=", base_name, "\n"
|
||||||
"BindReadOnlyPaths=", os_release_source, ":/run/host/os-release\n"
|
"BindReadOnlyPaths=", os_release_source, ":/run/host/os-release\n"
|
||||||
"LogExtraFields=PORTABLE=", basename(image_path), "\n"))
|
"LogExtraFields=PORTABLE=", base_name, "\n"))
|
||||||
return -ENOMEM;
|
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);
|
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 LookupPaths *paths,
|
||||||
const char *image_path,
|
const char *image_path,
|
||||||
ImageType type,
|
ImageType type,
|
||||||
|
OrderedHashmap *extension_images,
|
||||||
const PortableMetadata *m,
|
const PortableMetadata *m,
|
||||||
const char *profile,
|
const char *profile,
|
||||||
PortableFlags flags,
|
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
|
* 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. */
|
* 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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -984,20 +1096,49 @@ static int install_image_symlink(
|
|||||||
return 0;
|
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(
|
int portable_attach(
|
||||||
sd_bus *bus,
|
sd_bus *bus,
|
||||||
const char *name_or_path,
|
const char *name_or_path,
|
||||||
char **matches,
|
char **matches,
|
||||||
const char *profile,
|
const char *profile,
|
||||||
|
char **extension_image_paths,
|
||||||
PortableFlags flags,
|
PortableFlags flags,
|
||||||
PortableChange **changes,
|
PortableChange **changes,
|
||||||
size_t *n_changes,
|
size_t *n_changes,
|
||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
|
||||||
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
|
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
|
||||||
_cleanup_(lookup_paths_free) LookupPaths paths = {};
|
_cleanup_(lookup_paths_free) LookupPaths paths = {};
|
||||||
_cleanup_(image_unrefp) Image *image = NULL;
|
_cleanup_(image_unrefp) Image *image = NULL;
|
||||||
PortableMetadata *item;
|
PortableMetadata *item;
|
||||||
|
Image *ext;
|
||||||
|
char **p;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(name_or_path);
|
assert(name_or_path);
|
||||||
@ -1005,11 +1146,40 @@ int portable_attach(
|
|||||||
r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
|
r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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)
|
if (r < 0)
|
||||||
return r;
|
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);
|
r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -1029,62 +1199,95 @@ int portable_attach(
|
|||||||
}
|
}
|
||||||
|
|
||||||
HASHMAP_FOREACH(item, unit_files) {
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We don't care too much for the image symlink, it's just a convenience thing, it's not necessary for proper
|
/* We don't care too much for the image symlink, it's just a convenience thing, it's not necessary for proper
|
||||||
* operation otherwise. */
|
* 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;
|
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;
|
const char *a;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(marker);
|
assert(marker);
|
||||||
assert(name_or_path);
|
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)) {
|
root_and_extensions = strv_new(name_or_path);
|
||||||
const char *e, *underscore;
|
if (!root_and_extensions)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
/* We shall match against an image name. In that case let's compare the last component, and optionally
|
r = strv_extend_strv(&root_and_extensions, extension_image_paths, false);
|
||||||
* allow either a suffix of ".raw" or a series of "/".
|
if (r < 0)
|
||||||
* But allow matching on a different version of the same image, when a "_" is used as a separator. */
|
return r;
|
||||||
underscore = strchr(name_or_path, '_');
|
|
||||||
if (underscore)
|
|
||||||
return strneq(a, name_or_path, underscore - name_or_path);
|
|
||||||
|
|
||||||
e = startswith(a, name_or_path);
|
STRV_FOREACH(image_name_or_path, root_and_extensions) {
|
||||||
if (!e)
|
_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 false;
|
||||||
|
|
||||||
return
|
a = last_path_component(image);
|
||||||
e[strspn(e, "/")] == 0 ||
|
|
||||||
streq(e, ".raw");
|
|
||||||
} else {
|
|
||||||
const char *b, *underscore;
|
|
||||||
size_t l;
|
|
||||||
|
|
||||||
/* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to
|
if (image_name_is_valid(*image_name_or_path)) {
|
||||||
* reach the same file. However, in this mode, let's validate any file suffix. */
|
const char *e, *underscore;
|
||||||
|
|
||||||
l = strcspn(a, "/");
|
/* We shall match against an image name. In that case let's compare the last component, and optionally
|
||||||
b = last_path_component(name_or_path);
|
* 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)
|
e = startswith(a, *image_name_or_path);
|
||||||
return false;
|
if (!e)
|
||||||
|
return false;
|
||||||
|
|
||||||
underscore = strchr(b, '_');
|
if(!(e[strspn(e, "/")] == 0 || streq(e, ".raw")))
|
||||||
if (underscore)
|
return false;
|
||||||
l = underscore - b;
|
} 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(
|
static int test_chroot_dropin(
|
||||||
@ -1092,6 +1295,7 @@ static int test_chroot_dropin(
|
|||||||
const char *where,
|
const char *where,
|
||||||
const char *fname,
|
const char *fname,
|
||||||
const char *name_or_path,
|
const char *name_or_path,
|
||||||
|
char **extension_image_paths,
|
||||||
char **ret_marker) {
|
char **ret_marker) {
|
||||||
|
|
||||||
_cleanup_free_ char *line = NULL, *marker = NULL;
|
_cleanup_free_ char *line = NULL, *marker = NULL;
|
||||||
@ -1138,7 +1342,7 @@ static int test_chroot_dropin(
|
|||||||
if (!name_or_path)
|
if (!name_or_path)
|
||||||
r = true;
|
r = true;
|
||||||
else
|
else
|
||||||
r = marker_matches_image(marker, name_or_path);
|
r = marker_matches_images(marker, name_or_path, extension_image_paths);
|
||||||
|
|
||||||
if (ret_marker)
|
if (ret_marker)
|
||||||
*ret_marker = TAKE_PTR(marker);
|
*ret_marker = TAKE_PTR(marker);
|
||||||
@ -1149,6 +1353,7 @@ static int test_chroot_dropin(
|
|||||||
int portable_detach(
|
int portable_detach(
|
||||||
sd_bus *bus,
|
sd_bus *bus,
|
||||||
const char *name_or_path,
|
const char *name_or_path,
|
||||||
|
char **extension_image_paths,
|
||||||
PortableFlags flags,
|
PortableFlags flags,
|
||||||
PortableChange **changes,
|
PortableChange **changes,
|
||||||
size_t *n_changes,
|
size_t *n_changes,
|
||||||
@ -1193,7 +1398,7 @@ int portable_detach(
|
|||||||
if (!IN_SET(de->d_type, DT_LNK, DT_REG))
|
if (!IN_SET(de->d_type, DT_LNK, DT_REG))
|
||||||
continue;
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
@ -1215,12 +1420,20 @@ int portable_detach(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name);
|
return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name);
|
||||||
|
|
||||||
if (path_is_absolute(marker) &&
|
for (const char *p = marker;;) {
|
||||||
!image_in_search_path(IMAGE_PORTABLE, NULL, 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)
|
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))
|
if (!IN_SET(de->d_type, DT_LNK, DT_REG))
|
||||||
continue;
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
typedef struct PortableMetadata {
|
typedef struct PortableMetadata {
|
||||||
int fd;
|
int fd;
|
||||||
char *source;
|
char *source;
|
||||||
|
char *image_path;
|
||||||
char name[];
|
char name[];
|
||||||
} PortableMetadata;
|
} PortableMetadata;
|
||||||
|
|
||||||
@ -18,10 +19,13 @@ typedef struct PortableMetadata {
|
|||||||
#define PORTABLE_METADATA_IS_UNIT(m) (!IN_SET((m)->name[0], 0, '/'))
|
#define PORTABLE_METADATA_IS_UNIT(m) (!IN_SET((m)->name[0], 0, '/'))
|
||||||
|
|
||||||
typedef enum PortableFlags {
|
typedef enum PortableFlags {
|
||||||
PORTABLE_PREFER_COPY = 1 << 0,
|
PORTABLE_RUNTIME = 1 << 0, /* Public API via DBUS, do not change */
|
||||||
PORTABLE_PREFER_SYMLINK = 1 << 1,
|
PORTABLE_PREFER_COPY = 1 << 1,
|
||||||
PORTABLE_RUNTIME = 1 << 2,
|
PORTABLE_PREFER_SYMLINK = 1 << 2,
|
||||||
PORTABLE_REATTACH = 1 << 3,
|
PORTABLE_REATTACH = 1 << 3,
|
||||||
|
_PORTABLE_MASK_PUBLIC = PORTABLE_RUNTIME,
|
||||||
|
_PORTABLE_TYPE_MAX,
|
||||||
|
_PORTABLE_TYPE_INVALID = -EINVAL,
|
||||||
} PortableFlags;
|
} PortableFlags;
|
||||||
|
|
||||||
/* This enum is anonymous, since we usually store it in an 'int', as we overload it with negative errno
|
/* 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_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_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, 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);
|
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 "main-func.h"
|
||||||
#include "os-util.h"
|
#include "os-util.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
|
#include "parse-argument.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "pretty-print.h"
|
#include "pretty-print.h"
|
||||||
|
#include "portable.h"
|
||||||
#include "spawn-polkit-agent.h"
|
#include "spawn-polkit-agent.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
@ -44,6 +46,9 @@ static const char *arg_host = NULL;
|
|||||||
static bool arg_enable = false;
|
static bool arg_enable = false;
|
||||||
static bool arg_now = false;
|
static bool arg_now = false;
|
||||||
static bool arg_no_block = 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) {
|
static bool is_portable_managed(const char *unit) {
|
||||||
return ENDSWITH_SET(unit, ".service", ".target", ".socket", ".path", ".timer");
|
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;
|
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) {
|
static int extract_prefix(const char *path, char **ret) {
|
||||||
_cleanup_free_ char *name = NULL;
|
_cleanup_free_ char *name = NULL;
|
||||||
const char *bn, *underscore;
|
const char *bn, *underscore;
|
||||||
@ -219,15 +256,55 @@ static int maybe_reload(sd_bus **bus) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int inspect_image(int argc, char *argv[], void *userdata) {
|
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, *reply = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
_cleanup_strv_free_ char **matches = NULL;
|
_cleanup_strv_free_ char **matches = NULL;
|
||||||
_cleanup_free_ char *image = NULL;
|
_cleanup_free_ char *image = NULL;
|
||||||
bool nl = false, header = false;
|
bool nl = false, header = false;
|
||||||
const void *data;
|
|
||||||
const char *path;
|
const char *path;
|
||||||
|
const void *data;
|
||||||
size_t sz;
|
size_t sz;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -243,21 +320,9 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
return 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));
|
|
||||||
|
|
||||||
r = sd_bus_message_read(reply, "s", &path);
|
r = sd_bus_message_read(reply, "s", &path);
|
||||||
if (r < 0)
|
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[]) {
|
static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
|
||||||
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL;
|
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL;
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
||||||
_cleanup_strv_free_ char **matches = NULL;
|
_cleanup_strv_free_ char **matches = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -623,21 +687,9 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Could not watch jobs: %m");
|
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)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
return 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));
|
|
||||||
|
|
||||||
r = sd_bus_message_skip(reply, "say");
|
r = sd_bus_message_skip(reply, "say");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -693,7 +745,7 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) {
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(method);
|
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);
|
r = determine_image(argv[1], false, &image);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -717,11 +769,24 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
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);
|
r = sd_bus_message_append_strv(m, matches);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
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)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
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);
|
print_changes(reply);
|
||||||
|
|
||||||
if (streq(method, "AttachImage"))
|
if (STR_IN_SET(method, "AttachImage", "AttachImageWithExtensions"))
|
||||||
(void) maybe_enable_start(bus, reply);
|
(void) maybe_enable_start(bus, reply);
|
||||||
else {
|
else {
|
||||||
/* ReattachImage returns 2 lists - removed units first, and changed/added second */
|
/* 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) {
|
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) {
|
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) {
|
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_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
_cleanup_free_ char *image = NULL;
|
_cleanup_free_ char *image = NULL;
|
||||||
|
const char *method;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = determine_image(argv[1], true, &image);
|
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);
|
(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)
|
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);
|
(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"
|
" --now Immediately start/stop the portable service after\n"
|
||||||
" attach/before detach\n"
|
" attach/before detach\n"
|
||||||
" --no-block Don't block waiting for attach --now to complete\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",
|
"\nSee the %s for details.\n",
|
||||||
program_invocation_short_name,
|
program_invocation_short_name,
|
||||||
ansi_highlight(),
|
ansi_highlight(),
|
||||||
@ -1055,6 +1145,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int parse_argv(int argc, char *argv[]) {
|
static int parse_argv(int argc, char *argv[]) {
|
||||||
|
int r;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ARG_VERSION = 0x100,
|
ARG_VERSION = 0x100,
|
||||||
@ -1068,6 +1159,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_ENABLE,
|
ARG_ENABLE,
|
||||||
ARG_NOW,
|
ARG_NOW,
|
||||||
ARG_NO_BLOCK,
|
ARG_NO_BLOCK,
|
||||||
|
ARG_EXTENSION,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
@ -1087,6 +1179,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "enable", no_argument, NULL, ARG_ENABLE },
|
{ "enable", no_argument, NULL, ARG_ENABLE },
|
||||||
{ "now", no_argument, NULL, ARG_NOW },
|
{ "now", no_argument, NULL, ARG_NOW },
|
||||||
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
{ "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;
|
arg_no_block = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_EXTENSION:
|
||||||
|
r = strv_extend(&arg_extension_images, optarg);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
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) {
|
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;
|
PortableChange *changes = NULL;
|
||||||
|
PortableFlags flags = 0;
|
||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
size_t n_changes = 0;
|
size_t n_changes = 0;
|
||||||
const char *name_or_path;
|
const char *name_or_path;
|
||||||
int r, runtime;
|
int r;
|
||||||
|
|
||||||
assert(message);
|
assert(message);
|
||||||
assert(m);
|
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
|
* detach already deleted images too, in case the user already deleted an image before properly detaching
|
||||||
* it. */
|
* 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)
|
if (r < 0)
|
||||||
return r;
|
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(
|
r = bus_verify_polkit_async(
|
||||||
message,
|
message,
|
||||||
CAP_SYS_ADMIN,
|
CAP_SYS_ADMIN,
|
||||||
@ -286,7 +315,8 @@ static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_e
|
|||||||
r = portable_detach(
|
r = portable_detach(
|
||||||
sd_bus_message_get_bus(message),
|
sd_bus_message_get_bus(message),
|
||||||
name_or_path,
|
name_or_path,
|
||||||
runtime ? PORTABLE_RUNTIME : 0,
|
extension_images,
|
||||||
|
flags,
|
||||||
&changes,
|
&changes,
|
||||||
&n_changes,
|
&n_changes,
|
||||||
error);
|
error);
|
||||||
@ -383,6 +413,16 @@ const sd_bus_vtable manager_vtable[] = {
|
|||||||
"a{say}", units),
|
"a{say}", units),
|
||||||
method_get_image_metadata,
|
method_get_image_metadata,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
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_METHOD_WITH_ARGS("GetImageState",
|
||||||
SD_BUS_ARGS("s", image),
|
SD_BUS_ARGS("s", image),
|
||||||
SD_BUS_RESULT("s", state),
|
SD_BUS_RESULT("s", state),
|
||||||
@ -397,12 +437,29 @@ const sd_bus_vtable manager_vtable[] = {
|
|||||||
SD_BUS_RESULT("a(sss)", changes),
|
SD_BUS_RESULT("a(sss)", changes),
|
||||||
method_attach_image,
|
method_attach_image,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
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_METHOD_WITH_ARGS("DetachImage",
|
||||||
SD_BUS_ARGS("s", image,
|
SD_BUS_ARGS("s", image,
|
||||||
"b", runtime),
|
"b", runtime),
|
||||||
SD_BUS_RESULT("a(sss)", changes),
|
SD_BUS_RESULT("a(sss)", changes),
|
||||||
method_detach_image,
|
method_detach_image,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
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_METHOD_WITH_ARGS("ReattachImage",
|
||||||
SD_BUS_ARGS("s", image,
|
SD_BUS_ARGS("s", image,
|
||||||
"as", matches,
|
"as", matches,
|
||||||
@ -413,6 +470,17 @@ const sd_bus_vtable manager_vtable[] = {
|
|||||||
"a(sss)", changes_updated),
|
"a(sss)", changes_updated),
|
||||||
method_reattach_image,
|
method_reattach_image,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
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_METHOD_WITH_ARGS("RemoveImage",
|
||||||
SD_BUS_ARGS("s", image),
|
SD_BUS_ARGS("s", image),
|
||||||
SD_BUS_NO_RESULT,
|
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) {
|
static int append_fd(sd_bus_message *m, PortableMetadata *d) {
|
||||||
_cleanup_fclose_ FILE *f = NULL;
|
_cleanup_fclose_ FILE *f = NULL;
|
||||||
_cleanup_free_ char *buf = NULL;
|
_cleanup_free_ char *buf = NULL;
|
||||||
size_t n;
|
size_t n = 0;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(d);
|
|
||||||
assert(d->fd >= 0);
|
|
||||||
|
|
||||||
f = take_fdopen(&d->fd, "r");
|
if (d) {
|
||||||
if (!f)
|
assert(d->fd >= 0);
|
||||||
return -errno;
|
|
||||||
|
|
||||||
r = read_full_stream(f, &buf, &n);
|
f = take_fdopen(&d->fd, "r");
|
||||||
if (r < 0)
|
if (!f)
|
||||||
return r;
|
return -errno;
|
||||||
|
|
||||||
|
r = read_full_stream(f, &buf, &n);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
return sd_bus_message_append_array(m, 'y', buf, n);
|
return sd_bus_message_append_array(m, 'y', buf, n);
|
||||||
}
|
}
|
||||||
@ -101,10 +103,12 @@ int bus_image_common_get_metadata(
|
|||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = 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_hashmap_free_ Hashmap *unit_files = NULL;
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
_cleanup_free_ PortableMetadata **sorted = 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;
|
size_t i;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -116,10 +120,29 @@ int bus_image_common_get_metadata(
|
|||||||
m = image->userdata;
|
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);
|
r = sd_bus_message_read_strv(message, &matches);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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,
|
r = bus_image_acquire(m,
|
||||||
message,
|
message,
|
||||||
name_or_path,
|
name_or_path,
|
||||||
@ -136,6 +159,7 @@ int bus_image_common_get_metadata(
|
|||||||
r = portable_extract(
|
r = portable_extract(
|
||||||
image->path,
|
image->path,
|
||||||
matches,
|
matches,
|
||||||
|
extension_images,
|
||||||
&os_release,
|
&os_release,
|
||||||
&unit_files,
|
&unit_files,
|
||||||
error);
|
error);
|
||||||
@ -223,12 +247,12 @@ int bus_image_common_attach(
|
|||||||
Image *image,
|
Image *image,
|
||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
_cleanup_strv_free_ char **matches = NULL;
|
_cleanup_strv_free_ char **matches = NULL, **extension_images = NULL;
|
||||||
PortableChange *changes = NULL;
|
PortableChange *changes = NULL;
|
||||||
PortableFlags flags = 0;
|
PortableFlags flags = 0;
|
||||||
const char *profile, *copy_mode;
|
const char *profile, *copy_mode;
|
||||||
size_t n_changes = 0;
|
size_t n_changes = 0;
|
||||||
int runtime, r;
|
int r;
|
||||||
|
|
||||||
assert(message);
|
assert(message);
|
||||||
assert(name_or_path || image);
|
assert(name_or_path || image);
|
||||||
@ -238,14 +262,44 @@ int bus_image_common_attach(
|
|||||||
m = image->userdata;
|
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);
|
r = sd_bus_message_read_strv(message, &matches);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = sd_bus_message_read(message, "sbs", &profile, &runtime, ©_mode);
|
r = sd_bus_message_read(message, "s", &profile);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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"))
|
if (streq(copy_mode, "symlink"))
|
||||||
flags |= PORTABLE_PREFER_SYMLINK;
|
flags |= PORTABLE_PREFER_SYMLINK;
|
||||||
else if (streq(copy_mode, "copy"))
|
else if (streq(copy_mode, "copy"))
|
||||||
@ -253,9 +307,6 @@ int bus_image_common_attach(
|
|||||||
else if (!isempty(copy_mode))
|
else if (!isempty(copy_mode))
|
||||||
return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", 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,
|
r = bus_image_acquire(m,
|
||||||
message,
|
message,
|
||||||
name_or_path,
|
name_or_path,
|
||||||
@ -274,6 +325,7 @@ int bus_image_common_attach(
|
|||||||
image->path,
|
image->path,
|
||||||
matches,
|
matches,
|
||||||
profile,
|
profile,
|
||||||
|
extension_images,
|
||||||
flags,
|
flags,
|
||||||
&changes,
|
&changes,
|
||||||
&n_changes,
|
&n_changes,
|
||||||
@ -297,19 +349,46 @@ static int bus_image_method_detach(
|
|||||||
void *userdata,
|
void *userdata,
|
||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **extension_images = NULL;
|
||||||
PortableChange *changes = NULL;
|
PortableChange *changes = NULL;
|
||||||
Image *image = userdata;
|
Image *image = userdata;
|
||||||
Manager *m = image->userdata;
|
Manager *m = image->userdata;
|
||||||
|
PortableFlags flags = 0;
|
||||||
size_t n_changes = 0;
|
size_t n_changes = 0;
|
||||||
int r, runtime;
|
int r;
|
||||||
|
|
||||||
assert(message);
|
assert(message);
|
||||||
assert(image);
|
assert(image);
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
r = sd_bus_message_read(message, "b", &runtime);
|
if (sd_bus_message_is_method_call(message, NULL, "DetachWithExtensions")) {
|
||||||
if (r < 0)
|
r = sd_bus_message_read_strv(message, &extension_images);
|
||||||
return r;
|
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(
|
r = bus_verify_polkit_async(
|
||||||
message,
|
message,
|
||||||
@ -328,7 +407,8 @@ static int bus_image_method_detach(
|
|||||||
r = portable_detach(
|
r = portable_detach(
|
||||||
sd_bus_message_get_bus(message),
|
sd_bus_message_get_bus(message),
|
||||||
image->path,
|
image->path,
|
||||||
runtime ? PORTABLE_RUNTIME : 0,
|
extension_images,
|
||||||
|
flags,
|
||||||
&changes,
|
&changes,
|
||||||
&n_changes,
|
&n_changes,
|
||||||
error);
|
error);
|
||||||
@ -510,10 +590,10 @@ int bus_image_common_reattach(
|
|||||||
|
|
||||||
PortableChange *changes_detached = NULL, *changes_attached = NULL, *changes_gone = NULL;
|
PortableChange *changes_detached = NULL, *changes_attached = NULL, *changes_gone = NULL;
|
||||||
size_t n_changes_detached = 0, n_changes_attached = 0, n_changes_gone = 0;
|
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;
|
PortableFlags flags = PORTABLE_REATTACH;
|
||||||
const char *profile, *copy_mode;
|
const char *profile, *copy_mode;
|
||||||
int runtime, r;
|
int r;
|
||||||
|
|
||||||
assert(message);
|
assert(message);
|
||||||
assert(name_or_path || image);
|
assert(name_or_path || image);
|
||||||
@ -523,14 +603,45 @@ int bus_image_common_reattach(
|
|||||||
m = image->userdata;
|
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);
|
r = sd_bus_message_read_strv(message, &matches);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = sd_bus_message_read(message, "sbs", &profile, &runtime, ©_mode);
|
r = sd_bus_message_read(message, "s", &profile);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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"))
|
if (streq(copy_mode, "symlink"))
|
||||||
flags |= PORTABLE_PREFER_SYMLINK;
|
flags |= PORTABLE_PREFER_SYMLINK;
|
||||||
else if (streq(copy_mode, "copy"))
|
else if (streq(copy_mode, "copy"))
|
||||||
@ -538,9 +649,6 @@ int bus_image_common_reattach(
|
|||||||
else if (!isempty(copy_mode))
|
else if (!isempty(copy_mode))
|
||||||
return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", 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,
|
r = bus_image_acquire(m,
|
||||||
message,
|
message,
|
||||||
name_or_path,
|
name_or_path,
|
||||||
@ -557,6 +665,7 @@ int bus_image_common_reattach(
|
|||||||
r = portable_detach(
|
r = portable_detach(
|
||||||
sd_bus_message_get_bus(message),
|
sd_bus_message_get_bus(message),
|
||||||
image->path,
|
image->path,
|
||||||
|
extension_images,
|
||||||
flags,
|
flags,
|
||||||
&changes_detached,
|
&changes_detached,
|
||||||
&n_changes_detached,
|
&n_changes_detached,
|
||||||
@ -569,6 +678,7 @@ int bus_image_common_reattach(
|
|||||||
image->path,
|
image->path,
|
||||||
matches,
|
matches,
|
||||||
profile,
|
profile,
|
||||||
|
extension_images,
|
||||||
flags,
|
flags,
|
||||||
&changes_attached,
|
&changes_attached,
|
||||||
&n_changes_attached,
|
&n_changes_attached,
|
||||||
@ -721,6 +831,15 @@ const sd_bus_vtable image_vtable[] = {
|
|||||||
"a{say}", units),
|
"a{say}", units),
|
||||||
bus_image_method_get_metadata,
|
bus_image_method_get_metadata,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
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_METHOD_WITH_ARGS("GetState",
|
||||||
SD_BUS_NO_ARGS,
|
SD_BUS_NO_ARGS,
|
||||||
SD_BUS_RESULT("s", state),
|
SD_BUS_RESULT("s", state),
|
||||||
@ -734,11 +853,26 @@ const sd_bus_vtable image_vtable[] = {
|
|||||||
SD_BUS_RESULT("a(sss)", changes),
|
SD_BUS_RESULT("a(sss)", changes),
|
||||||
bus_image_method_attach,
|
bus_image_method_attach,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
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_METHOD_WITH_ARGS("Detach",
|
||||||
SD_BUS_ARGS("b", runtime),
|
SD_BUS_ARGS("b", runtime),
|
||||||
SD_BUS_RESULT("a(sss)", changes),
|
SD_BUS_RESULT("a(sss)", changes),
|
||||||
bus_image_method_detach,
|
bus_image_method_detach,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
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_METHOD_WITH_ARGS("Reattach",
|
||||||
SD_BUS_ARGS("as", matches,
|
SD_BUS_ARGS("as", matches,
|
||||||
"s", profile,
|
"s", profile,
|
||||||
@ -748,6 +882,16 @@ const sd_bus_vtable image_vtable[] = {
|
|||||||
"a(sss)", changes_updated),
|
"a(sss)", changes_updated),
|
||||||
bus_image_method_reattach,
|
bus_image_method_reattach,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
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_METHOD_WITH_ARGS("Remove",
|
||||||
SD_BUS_NO_ARGS,
|
SD_BUS_NO_ARGS,
|
||||||
SD_BUS_NO_RESULT,
|
SD_BUS_NO_RESULT,
|
||||||
|
@ -15,6 +15,7 @@ test_append_files() {
|
|||||||
instmods loop =block
|
instmods loop =block
|
||||||
instmods squashfs =squashfs
|
instmods squashfs =squashfs
|
||||||
instmods dm_verity =md
|
instmods dm_verity =md
|
||||||
|
instmods overlay =overlayfs
|
||||||
install_dmevent
|
install_dmevent
|
||||||
generate_module_dependencies
|
generate_module_dependencies
|
||||||
inst_binary losetup
|
inst_binary losetup
|
||||||
|
@ -63,6 +63,36 @@ portablectl detach --now --enable --runtime /tmp/minimal_1 app0
|
|||||||
|
|
||||||
portablectl list | grep -q -F "No images."
|
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
|
echo OK > /testok
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
Loading…
Reference in New Issue
Block a user