]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
portablectl: add --now and --enable to attach/detach
authorLuca Boccassi <luca.boccassi@microsoft.com>
Thu, 23 Jan 2020 16:50:15 +0000 (16:50 +0000)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 7 Feb 2020 16:09:05 +0000 (17:09 +0100)
Add shortcuts to enable and start, or disable and stop, portable
services with a single portablectl command.
Allow to pass a filter on detach, as it's necessary to call
GetImageMetadata to get the unit names associated with an image.

Fixes #10232

man/portablectl.xml
shell-completion/bash/portablectl
src/portable/portablectl.c

index 88cf36ae252d8a08cee3697f363a0e806e095b6c..d1790db2fabbf3353ee2469768b6172ce35e7a1c 100644 (file)
         <para>By default, after the unit files are attached the service manager's configuration is reloaded, except
         when <option>--no-reload</option> is specified (see above). This ensures that the new units made available to
         the service manager are seen by it.</para>
+
+        <para>If <option>--now</option> and/or <option>--enable</option> are passed, the portable service(s) are
+        immediately started and/or enabled after attaching the image.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><command>detach</command> <replaceable>IMAGE</replaceable></term>
+        <term><command>detach</command> <replaceable>IMAGE</replaceable> [<replaceable>PREFIX…</replaceable>]</term>
 
         <listitem><para>Detaches a portable service image from the host. This undoes the operations executed by the
         <command>attach</command> command above, and removes the unit file copies, drop-ins and image symlink
         component of it (i.e. the file or directory name itself, not the path to it) is used for finding matching unit
         files. This is a convencience feature to allow all arguments passed as <command>attach</command> also to
         <command>detach</command>.</para></listitem>
+
+        <para>If <option>--now</option> and/or <option>--enable</option> are passed, the portable service(s) are
+        immediately started and/or enabled before detaching the image. Prefix(es) are also accepted, to be used in
+        case the unit names do not match the image name as described in the <command>attach</command>.</para>
       </varlistentry>
 
       <varlistentry>
         contents of the image.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--enable</option></term>
+
+        <listitem><para>Immediately enable/disable the portable service after attaching/detaching.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--now</option></term>
+
+        <listitem><para>Immediately start/stop the portable service after attaching/before detaching.</para></listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="host" />
       <xi:include href="user-system-options.xml" xpointer="machine" />
 
index b60d8c5c4bfa6678a108f6c74fd68fac2ad2ab34..0b84d5c83487b5702418279d4c923c4d8ca6c021 100644 (file)
@@ -34,7 +34,7 @@ _portablectl() {
     local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
     local -A OPTS=(
         [STANDALONE]='-q --quiet --runtime --no-reload --cat --no-pager --no-legend
-                              --no-ask-password -h --help --version'
+                              --no-ask-password --enable --now -h --help --version'
         [ARG]='-p --profile --copy -H --host -M --machine'
     )
 
index 1fa6dc48c11464f440d55becf652c0a4cedd02d5..deaad0a0b05b2124f21d330bbdcc420ec7074cac 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-unit-util.h"
 #include "bus-util.h"
 #include "def.h"
 #include "dirent-util.h"
@@ -39,6 +40,8 @@ static bool arg_reload = true;
 static bool arg_cat = false;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static const char *arg_host = NULL;
+static bool arg_enable = false;
+static bool arg_now = false;
 
 static int determine_image(const char *image, bool permit_non_existing, char **ret) {
         int r;
@@ -388,6 +391,204 @@ static int print_changes(sd_bus_message *m) {
         return 0;
 }
 
+static int maybe_enable_disable(sd_bus *bus, const char *path, bool enable) {
+        _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_strv_free_ char **names = NULL;
+        UnitFileChange *changes = NULL;
+        size_t n_changes = 0;
+        int r;
+
+        if (!arg_enable)
+                return 0;
+
+        names = strv_new(path, NULL);
+        if (!names)
+                return log_oom();
+
+        r = sd_bus_message_new_method_call(
+                bus,
+                &m,
+                "org.freedesktop.systemd1",
+                "/org/freedesktop/systemd1",
+                "org.freedesktop.systemd1.Manager",
+                enable ? "EnableUnitFiles" : "DisableUnitFiles");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append_strv(m, names);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "b", arg_runtime);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        if (enable) {
+                r = sd_bus_message_append(m, "b", false);
+                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 %s the portable service %s: %s",
+                        enable ? "enable" : "disable", path, bus_error_message(&error, r));
+
+        if (enable) {
+                r = sd_bus_message_skip(reply, "b");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+        }
+        (void) bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes);
+
+        return 0;
+}
+
+static int maybe_start_stop(sd_bus *bus, const char *path, bool start) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        char *name = (char *)basename(path), *job = NULL;
+        int r;
+
+        if (!arg_now)
+                return 0;
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        start ? "StartUnit" : "StopUnit",
+                        &error,
+                        &reply,
+                        "ss", name, "replace");
+        if (r < 0)
+                return log_error_errno(r, "Failed to %s the portable service %s: %s",
+                                       start ? "start" : "stop",
+                                       path,
+                                       bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "o", &job);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        if (!arg_quiet)
+                log_info("Queued %s to %s portable service %s.", job, start ? "start" : "stop", name);
+
+        return 0;
+}
+
+static int maybe_enable_start(sd_bus *bus, sd_bus_message *reply) {
+        int r;
+
+        if (!arg_enable && !arg_now)
+                return 0;
+
+        r = sd_bus_message_rewind(reply, true);
+        if (r < 0)
+                return r;
+        r = sd_bus_message_enter_container(reply, 'a', "(sss)");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        for (;;) {
+                char *type, *path, *source;
+
+                r = sd_bus_message_read(reply, "(sss)", &type, &path, &source);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+                if (r == 0)
+                        break;
+
+                if (STR_IN_SET(type, "symlink", "copy") && ENDSWITH_SET(path, ".service", ".target", ".socket")) {
+                        (void) maybe_enable_disable(bus, path, true);
+                        (void) maybe_start_stop(bus, path, true);
+                }
+        }
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
+        _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_strv_free_ char **matches = NULL;
+        int r;
+
+        if (!arg_enable && !arg_now)
+                return 0;
+
+        r = determine_matches(argv[1], argv + 2, true, &matches);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.portable1",
+                                "/org/freedesktop/portable1",
+                                "org.freedesktop.portable1.Manager",
+                                "GetImageMetadata");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "s", image);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append_strv(m, matches);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
+        if (r < 0)
+                return log_error_errno(r, "Failed to inspect image metadata: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_skip(reply, "say");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_enter_container(reply, 'a', "{say}");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        for (;;) {
+                const char *name;
+
+                r = sd_bus_message_enter_container(reply, 'e', "say");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+                if (r == 0)
+                        break;
+
+                r = sd_bus_message_read(reply, "s", &name);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_skip(reply, "ay");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_exit_container(reply);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                (void) maybe_start_stop(bus, name, false);
+                (void) maybe_enable_disable(bus, name, false);
+        }
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        return 0;
+}
+
 static int attach_image(int argc, char *argv[], void *userdata) {
         _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;
@@ -439,6 +640,9 @@ static int attach_image(int argc, char *argv[], void *userdata) {
         (void) maybe_reload(&bus);
 
         print_changes(reply);
+
+        (void) maybe_enable_start(bus, reply);
+
         return 0;
 }
 
@@ -459,6 +663,8 @@ static int detach_image(int argc, char *argv[], void *userdata) {
 
         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
+        (void) maybe_stop_disable(bus, image, argv);
+
         r = sd_bus_call_method(
                         bus,
                         "org.freedesktop.portable1",
@@ -764,7 +970,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "  list                        List available portable service images\n"
                "  attach NAME|PATH [PREFIX...]\n"
                "                              Attach the specified portable service image\n"
-               "  detach NAME|PATH            Detach the specified portable service image\n"
+               "  detach NAME|PATH [PREFIX...]\n"
+               "                              Detach the specified portable service image\n"
                "  inspect NAME|PATH [PREFIX...]\n"
                "                              Show details of specified portable service image\n"
                "  is-attached NAME|PATH       Query if portable service image is attached\n"
@@ -786,6 +993,10 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --no-reload              Don't reload the system and service manager\n"
                "     --cat                    When inspecting include unit and os-release file\n"
                "                              contents\n"
+               "     --enable                 Immediately enable/disable the portable service\n"
+               "                              after attach/detach\n"
+               "     --now                    Immediately start/stop the portable service after\n"
+               "                              attach/before detach\n"
                "\nSee the %s for details.\n"
                , program_invocation_short_name
                , ansi_highlight()
@@ -807,6 +1018,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_RUNTIME,
                 ARG_NO_RELOAD,
                 ARG_CAT,
+                ARG_ENABLE,
+                ARG_NOW,
         };
 
         static const struct option options[] = {
@@ -823,6 +1036,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "runtime",         no_argument,       NULL, ARG_RUNTIME         },
                 { "no-reload",       no_argument,       NULL, ARG_NO_RELOAD       },
                 { "cat",             no_argument,       NULL, ARG_CAT             },
+                { "enable",          no_argument,       NULL, ARG_ENABLE          },
+                { "now",             no_argument,       NULL, ARG_NOW             },
                 {}
         };
 
@@ -909,6 +1124,14 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_cat = true;
                         break;
 
+                case ARG_ENABLE:
+                        arg_enable = true;
+                        break;
+
+                case ARG_NOW:
+                        arg_now = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -925,7 +1148,7 @@ static int run(int argc, char *argv[]) {
                 { "help",        VERB_ANY, VERB_ANY, 0,            help              },
                 { "list",        VERB_ANY, 1,        VERB_DEFAULT, list_images       },
                 { "attach",      2,        VERB_ANY, 0,            attach_image      },
-                { "detach",      2,        2,        0,            detach_image      },
+                { "detach",      2,        VERB_ANY, 0,            detach_image      },
                 { "inspect",     2,        VERB_ANY, 0,            inspect_image     },
                 { "is-attached", 2,        2,        0,            is_image_attached },
                 { "read-only",   2,        3,        0,            read_only_image   },