diff options
author | Luca Boccassi <luca.boccassi@microsoft.com> | 2020-01-23 17:50:15 +0100 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2020-02-07 17:09:05 +0100 |
commit | e2c1ddcc492f7d94fd0bdaec719916c62787be44 (patch) | |
tree | 5691c9b7454a665a524dbcb64e9298a168b5b9aa | |
parent | Merge pull request #14805 from yuwata/network-ipv6-token-follow-up (diff) | |
download | systemd-e2c1ddcc492f7d94fd0bdaec719916c62787be44.tar.xz systemd-e2c1ddcc492f7d94fd0bdaec719916c62787be44.zip |
portablectl: add --now and --enable to attach/detach
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
-rw-r--r-- | man/portablectl.xml | 21 | ||||
-rw-r--r-- | shell-completion/bash/portablectl | 2 | ||||
-rw-r--r-- | src/portable/portablectl.c | 227 |
3 files changed, 246 insertions, 4 deletions
diff --git a/man/portablectl.xml b/man/portablectl.xml index 88cf36ae25..d1790db2fa 100644 --- a/man/portablectl.xml +++ b/man/portablectl.xml @@ -133,11 +133,14 @@ <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 @@ -145,6 +148,10 @@ 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> @@ -311,6 +318,18 @@ 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" /> diff --git a/shell-completion/bash/portablectl b/shell-completion/bash/portablectl index b60d8c5c4b..0b84d5c834 100644 --- a/shell-completion/bash/portablectl +++ b/shell-completion/bash/portablectl @@ -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' ) diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index 1fa6dc48c1..deaad0a0b0 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -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 }, |