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
This commit is contained in:
parent
c3b41d8811
commit
e2c1ddcc49
|
@ -133,11 +133,14 @@
|
||||||
<para>By default, after the unit files are attached the service manager's configuration is reloaded, except
|
<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
|
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>
|
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>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<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
|
<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
|
<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
|
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
|
files. This is a convencience feature to allow all arguments passed as <command>attach</command> also to
|
||||||
<command>detach</command>.</para></listitem>
|
<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>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
@ -311,6 +318,18 @@
|
||||||
contents of the image.</para></listitem>
|
contents of the image.</para></listitem>
|
||||||
</varlistentry>
|
</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="host" />
|
||||||
<xi:include href="user-system-options.xml" xpointer="machine" />
|
<xi:include href="user-system-options.xml" xpointer="machine" />
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ _portablectl() {
|
||||||
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
|
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||||
local -A OPTS=(
|
local -A OPTS=(
|
||||||
[STANDALONE]='-q --quiet --runtime --no-reload --cat --no-pager --no-legend
|
[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'
|
[ARG]='-p --profile --copy -H --host -M --machine'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "bus-error.h"
|
#include "bus-error.h"
|
||||||
|
#include "bus-unit-util.h"
|
||||||
#include "bus-util.h"
|
#include "bus-util.h"
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "dirent-util.h"
|
#include "dirent-util.h"
|
||||||
|
@ -39,6 +40,8 @@ static bool arg_reload = true;
|
||||||
static bool arg_cat = false;
|
static bool arg_cat = false;
|
||||||
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
|
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
|
||||||
static const char *arg_host = NULL;
|
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) {
|
static int determine_image(const char *image, bool permit_non_existing, char **ret) {
|
||||||
int r;
|
int r;
|
||||||
|
@ -388,6 +391,204 @@ static int print_changes(sd_bus_message *m) {
|
||||||
return 0;
|
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) {
|
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_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;
|
||||||
|
@ -439,6 +640,9 @@ static int attach_image(int argc, char *argv[], void *userdata) {
|
||||||
(void) maybe_reload(&bus);
|
(void) maybe_reload(&bus);
|
||||||
|
|
||||||
print_changes(reply);
|
print_changes(reply);
|
||||||
|
|
||||||
|
(void) maybe_enable_start(bus, reply);
|
||||||
|
|
||||||
return 0;
|
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) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||||
|
|
||||||
|
(void) maybe_stop_disable(bus, image, argv);
|
||||||
|
|
||||||
r = sd_bus_call_method(
|
r = sd_bus_call_method(
|
||||||
bus,
|
bus,
|
||||||
"org.freedesktop.portable1",
|
"org.freedesktop.portable1",
|
||||||
|
@ -764,7 +970,8 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||||
" list List available portable service images\n"
|
" list List available portable service images\n"
|
||||||
" attach NAME|PATH [PREFIX...]\n"
|
" attach NAME|PATH [PREFIX...]\n"
|
||||||
" Attach the specified portable service image\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"
|
" inspect NAME|PATH [PREFIX...]\n"
|
||||||
" Show details of specified portable service image\n"
|
" Show details of specified portable service image\n"
|
||||||
" is-attached NAME|PATH Query if portable service image is attached\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"
|
" --no-reload Don't reload the system and service manager\n"
|
||||||
" --cat When inspecting include unit and os-release file\n"
|
" --cat When inspecting include unit and os-release file\n"
|
||||||
" contents\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"
|
"\nSee the %s for details.\n"
|
||||||
, program_invocation_short_name
|
, program_invocation_short_name
|
||||||
, ansi_highlight()
|
, ansi_highlight()
|
||||||
|
@ -807,6 +1018,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
ARG_RUNTIME,
|
ARG_RUNTIME,
|
||||||
ARG_NO_RELOAD,
|
ARG_NO_RELOAD,
|
||||||
ARG_CAT,
|
ARG_CAT,
|
||||||
|
ARG_ENABLE,
|
||||||
|
ARG_NOW,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
|
@ -823,6 +1036,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "runtime", no_argument, NULL, ARG_RUNTIME },
|
{ "runtime", no_argument, NULL, ARG_RUNTIME },
|
||||||
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
|
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
|
||||||
{ "cat", no_argument, NULL, ARG_CAT },
|
{ "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;
|
arg_cat = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_ENABLE:
|
||||||
|
arg_enable = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_NOW:
|
||||||
|
arg_now = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -925,7 +1148,7 @@ static int run(int argc, char *argv[]) {
|
||||||
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||||
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_images },
|
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_images },
|
||||||
{ "attach", 2, VERB_ANY, 0, attach_image },
|
{ "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 },
|
{ "inspect", 2, VERB_ANY, 0, inspect_image },
|
||||||
{ "is-attached", 2, 2, 0, is_image_attached },
|
{ "is-attached", 2, 2, 0, is_image_attached },
|
||||||
{ "read-only", 2, 3, 0, read_only_image },
|
{ "read-only", 2, 3, 0, read_only_image },
|
||||||
|
|
Loading…
Reference in a new issue