]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
portablectl: add --force attach/detach
authorLuca Boccassi <bluca@debian.org>
Thu, 29 Sep 2022 21:41:55 +0000 (22:41 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 30 Sep 2022 12:25:31 +0000 (13:25 +0100)
Allows to skip check that ensures units must not be running.

I have a use case that would use reattach, except the orchestrator
is using a non-standard versioning scheme, so image matching cannot
work. As a workaround, need to be able to detach and then attach
manually, without stopping the units to avoid extended downtimes
and loss of FD store.

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

index 0007e0c2ded918abcc57096da0f1b3da1bcfef93..0e926fe850fabff81fe250cb72a227fbe254776d 100644 (file)
@@ -307,10 +307,13 @@ node /org/freedesktop/portable1 {
       <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>
+      booleans to allow for extendability. <varname>SD_SYSTEMD_PORTABLE_FORCE</varname> will cause
+      safety checks that ensure the units are not running while the new image is attached or detached
+      to be skipped. They are defined as follows:</para>
 
       <programlisting>
 #define SD_SYSTEMD_PORTABLE_RUNTIME  (UINT64_C(1) &lt;&lt; 0)
+#define SD_SYSTEMD_PORTABLE_FORCE    (UINT64_C(1) &lt;&lt; 1)
       </programlisting>
     </refsect2>
 
index ed2089c63a38aa146f8f7b4b21292375a2005a65..64927a2fe67374eeac700f001389df8991e2e9d8 100644 (file)
         and detaching.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--force</option></term>
+
+        <listitem><para>Skip safety checks and attach or detach images (with extensions) without first ensuring
+        that the units are not running.</para></listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="host" />
       <xi:include href="user-system-options.xml" xpointer="machine" />
 
index 5d0a965db5fedb90aeb30f89cb072a0301cd6039..202442903f33b47ba85ea83f61deea838ce081c6 100644 (file)
@@ -1357,19 +1357,20 @@ int portable_attach(
         if (r < 0)
                 return r;
 
-        HASHMAP_FOREACH(item, unit_files) {
-                r = unit_file_exists(LOOKUP_SCOPE_SYSTEM, &paths, item->name);
-                if (r < 0)
-                        return sd_bus_error_set_errnof(error, r, "Failed to determine whether unit '%s' exists on the host: %m", item->name);
-                if (!FLAGS_SET(flags, PORTABLE_REATTACH) && r > 0)
-                        return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' exists on the host already, refusing.", item->name);
+        if (!FLAGS_SET(flags, PORTABLE_REATTACH) && !FLAGS_SET(flags, PORTABLE_FORCE))
+                HASHMAP_FOREACH(item, unit_files) {
+                        r = unit_file_exists(LOOKUP_SCOPE_SYSTEM, &paths, item->name);
+                        if (r < 0)
+                                return sd_bus_error_set_errnof(error, r, "Failed to determine whether unit '%s' exists on the host: %m", item->name);
+                        if (r > 0)
+                                return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' exists on the host already, refusing.", item->name);
 
-                r = unit_file_is_active(bus, item->name, error);
-                if (r < 0)
-                        return r;
-                if (!FLAGS_SET(flags, PORTABLE_REATTACH) && r > 0)
-                        return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is active already, refusing.", item->name);
-        }
+                        r = unit_file_is_active(bus, item->name, error);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is active already, refusing.", item->name);
+                }
 
         HASHMAP_FOREACH(item, unit_files) {
                 r = attach_unit_file(&paths, image->path, image->type, extension_images,
@@ -1599,11 +1600,13 @@ int portable_detach(
                 if (r == 0)
                         continue;
 
-                r = unit_file_is_active(bus, unit_name, error);
-                if (r < 0)
-                        return r;
-                if (!FLAGS_SET(flags, PORTABLE_REATTACH) && r > 0)
-                        return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is active, can't detach.", unit_name);
+                if (!FLAGS_SET(flags, PORTABLE_REATTACH) && !FLAGS_SET(flags, PORTABLE_FORCE)) {
+                        r = unit_file_is_active(bus, unit_name, error);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is active, can't detach.", unit_name);
+                }
 
                 r = set_ensure_consume(&unit_files, &string_hash_ops_free, TAKE_PTR(unit_name));
                 if (r < 0)
index c6061cc589f0a21e2b030cb79e1d5c4c029bef96..dc2f8781b7728e467d3d3d8a5564cb7d61c17390 100644 (file)
@@ -22,10 +22,11 @@ typedef struct PortableMetadata {
 
 typedef enum PortableFlags {
         PORTABLE_RUNTIME        = 1 << 0, /* Public API via DBUS, do not change */
-        PORTABLE_PREFER_COPY    = 1 << 1,
-        PORTABLE_PREFER_SYMLINK = 1 << 2,
-        PORTABLE_REATTACH       = 1 << 3,
-        _PORTABLE_MASK_PUBLIC   = PORTABLE_RUNTIME,
+        PORTABLE_FORCE          = 1 << 1, /* Public API via DBUS, do not change */
+        PORTABLE_PREFER_COPY    = 1 << 2,
+        PORTABLE_PREFER_SYMLINK = 1 << 3,
+        PORTABLE_REATTACH       = 1 << 4,
+        _PORTABLE_MASK_PUBLIC   = PORTABLE_RUNTIME | PORTABLE_FORCE,
         _PORTABLE_TYPE_MAX,
         _PORTABLE_TYPE_INVALID  = -EINVAL,
 } PortableFlags;
index c3c22220c22770c3ab4db84506edcca6f17396bc..7e3627d1dafebf3bf6a183d11592a03bd90e276d 100644 (file)
@@ -48,6 +48,7 @@ static bool arg_enable = false;
 static bool arg_now = false;
 static bool arg_no_block = false;
 static char **arg_extension_images = NULL;
+static bool arg_force = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_extension_images, strv_freep);
 
@@ -868,7 +869,7 @@ static int attach_reattach_image(int argc, char *argv[], const char *method) {
                 return bus_log_create_error(r);
 
         if (STR_IN_SET(method, "AttachImageWithExtensions", "ReattachImageWithExtensions")) {
-                uint64_t flags = arg_runtime ? PORTABLE_RUNTIME : 0;
+                uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE : 0);
 
                 r = sd_bus_message_append(m, "st", arg_copy_mode, flags);
         } else
@@ -940,7 +941,7 @@ static int detach_image(int argc, char *argv[], void *userdata) {
         if (strv_isempty(arg_extension_images))
                 r = sd_bus_message_append(m, "b", arg_runtime);
         else {
-                uint64_t flags = arg_runtime ? PORTABLE_RUNTIME : 0;
+                uint64_t flags = (arg_runtime ? PORTABLE_RUNTIME : 0) | (arg_force ? PORTABLE_FORCE : 0);
 
                 r = sd_bus_message_append(m, "t", flags);
         }
@@ -1241,6 +1242,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "                              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"
+               "     --force                  Skip 'already active' check when attaching or\n"
+               "                              detaching an image (with extensions)\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -1266,6 +1269,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_NOW,
                 ARG_NO_BLOCK,
                 ARG_EXTENSION,
+                ARG_FORCE,
         };
 
         static const struct option options[] = {
@@ -1286,6 +1290,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "now",             no_argument,       NULL, ARG_NOW             },
                 { "no-block",        no_argument,       NULL, ARG_NO_BLOCK        },
                 { "extension",       required_argument, NULL, ARG_EXTENSION       },
+                { "force",           no_argument,       NULL, ARG_FORCE           },
                 {}
         };
 
@@ -1390,6 +1395,10 @@ static int parse_argv(int argc, char *argv[]) {
                                 return log_oom();
                         break;
 
+                case ARG_FORCE:
+                        arg_force = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
index 4b32161e5f956d97f5ca4ea2ea5e83bca6d375c9..072ca921b36c328f7aa89d298b3be279f8e3d37c 100755 (executable)
@@ -124,7 +124,16 @@ systemctl is-active app1.service
 status="$(portablectl is-attached --extension app1 minimal_1)"
 [[ "${status}" == "running-runtime" ]]
 
-portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
+portablectl detach --force --no-reload --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
+portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
+systemctl daemon-reload
+systemctl restart app1.service
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
 
 # Ensure that the combination of read-only images, state directory and dynamic user works, and that
 # state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while