X Tutup
Skip to content

Commit e2c1ddc

Browse files
blucakeszybz
authored andcommitted
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 systemd#10232
1 parent c3b41d8 commit e2c1ddc

File tree

3 files changed

+246
-4
lines changed

3 files changed

+246
-4
lines changed

man/portablectl.xml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,18 +133,25 @@
133133
<para>By default, after the unit files are attached the service manager's configuration is reloaded, except
134134
when <option>--no-reload</option> is specified (see above). This ensures that the new units made available to
135135
the service manager are seen by it.</para>
136+
137+
<para>If <option>--now</option> and/or <option>--enable</option> are passed, the portable service(s) are
138+
immediately started and/or enabled after attaching the image.</para>
136139
</listitem>
137140
</varlistentry>
138141

139142
<varlistentry>
140-
<term><command>detach</command> <replaceable>IMAGE</replaceable></term>
143+
<term><command>detach</command> <replaceable>IMAGE</replaceable> [<replaceable>PREFIX…</replaceable>]</term>
141144

142145
<listitem><para>Detaches a portable service image from the host. This undoes the operations executed by the
143146
<command>attach</command> command above, and removes the unit file copies, drop-ins and image symlink
144147
again. This command expects an image name or path as parameter. Note that if a path is specified only the last
145148
component of it (i.e. the file or directory name itself, not the path to it) is used for finding matching unit
146149
files. This is a convencience feature to allow all arguments passed as <command>attach</command> also to
147150
<command>detach</command>.</para></listitem>
151+
152+
<para>If <option>--now</option> and/or <option>--enable</option> are passed, the portable service(s) are
153+
immediately started and/or enabled before detaching the image. Prefix(es) are also accepted, to be used in
154+
case the unit names do not match the image name as described in the <command>attach</command>.</para>
148155
</varlistentry>
149156

150157
<varlistentry>
@@ -311,6 +318,18 @@
311318
contents of the image.</para></listitem>
312319
</varlistentry>
313320

321+
<varlistentry>
322+
<term><option>--enable</option></term>
323+
324+
<listitem><para>Immediately enable/disable the portable service after attaching/detaching.</para></listitem>
325+
</varlistentry>
326+
327+
<varlistentry>
328+
<term><option>--now</option></term>
329+
330+
<listitem><para>Immediately start/stop the portable service after attaching/before detaching.</para></listitem>
331+
</varlistentry>
332+
314333
<xi:include href="user-system-options.xml" xpointer="host" />
315334
<xi:include href="user-system-options.xml" xpointer="machine" />
316335

shell-completion/bash/portablectl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ _portablectl() {
3434
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
3535
local -A OPTS=(
3636
[STANDALONE]='-q --quiet --runtime --no-reload --cat --no-pager --no-legend
37-
--no-ask-password -h --help --version'
37+
--no-ask-password --enable --now -h --help --version'
3838
[ARG]='-p --profile --copy -H --host -M --machine'
3939
)
4040

src/portable/portablectl.c

Lines changed: 225 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "alloc-util.h"
99
#include "bus-error.h"
10+
#include "bus-unit-util.h"
1011
#include "bus-util.h"
1112
#include "def.h"
1213
#include "dirent-util.h"
@@ -39,6 +40,8 @@ static bool arg_reload = true;
3940
static bool arg_cat = false;
4041
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
4142
static const char *arg_host = NULL;
43+
static bool arg_enable = false;
44+
static bool arg_now = false;
4245

4346
static int determine_image(const char *image, bool permit_non_existing, char **ret) {
4447
int r;
@@ -388,6 +391,204 @@ static int print_changes(sd_bus_message *m) {
388391
return 0;
389392
}
390393

394+
static int maybe_enable_disable(sd_bus *bus, const char *path, bool enable) {
395+
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
396+
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
397+
_cleanup_strv_free_ char **names = NULL;
398+
UnitFileChange *changes = NULL;
399+
size_t n_changes = 0;
400+
int r;
401+
402+
if (!arg_enable)
403+
return 0;
404+
405+
names = strv_new(path, NULL);
406+
if (!names)
407+
return log_oom();
408+
409+
r = sd_bus_message_new_method_call(
410+
bus,
411+
&m,
412+
"org.freedesktop.systemd1",
413+
"/org/freedesktop/systemd1",
414+
"org.freedesktop.systemd1.Manager",
415+
enable ? "EnableUnitFiles" : "DisableUnitFiles");
416+
if (r < 0)
417+
return bus_log_create_error(r);
418+
419+
r = sd_bus_message_append_strv(m, names);
420+
if (r < 0)
421+
return bus_log_create_error(r);
422+
423+
r = sd_bus_message_append(m, "b", arg_runtime);
424+
if (r < 0)
425+
return bus_log_create_error(r);
426+
427+
if (enable) {
428+
r = sd_bus_message_append(m, "b", false);
429+
if (r < 0)
430+
return bus_log_create_error(r);
431+
}
432+
433+
r = sd_bus_call(bus, m, 0, &error, &reply);
434+
if (r < 0)
435+
return log_error_errno(r, "Failed to %s the portable service %s: %s",
436+
enable ? "enable" : "disable", path, bus_error_message(&error, r));
437+
438+
if (enable) {
439+
r = sd_bus_message_skip(reply, "b");
440+
if (r < 0)
441+
return bus_log_parse_error(r);
442+
}
443+
(void) bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes);
444+
445+
return 0;
446+
}
447+
448+
static int maybe_start_stop(sd_bus *bus, const char *path, bool start) {
449+
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
450+
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
451+
char *name = (char *)basename(path), *job = NULL;
452+
int r;
453+
454+
if (!arg_now)
455+
return 0;
456+
457+
r = sd_bus_call_method(
458+
bus,
459+
"org.freedesktop.systemd1",
460+
"/org/freedesktop/systemd1",
461+
"org.freedesktop.systemd1.Manager",
462+
start ? "StartUnit" : "StopUnit",
463+
&error,
464+
&reply,
465+
"ss", name, "replace");
466+
if (r < 0)
467+
return log_error_errno(r, "Failed to %s the portable service %s: %s",
468+
start ? "start" : "stop",
469+
path,
470+
bus_error_message(&error, r));
471+
472+
r = sd_bus_message_read(reply, "o", &job);
473+
if (r < 0)
474+
return bus_log_parse_error(r);
475+
476+
if (!arg_quiet)
477+
log_info("Queued %s to %s portable service %s.", job, start ? "start" : "stop", name);
478+
479+
return 0;
480+
}
481+
482+
static int maybe_enable_start(sd_bus *bus, sd_bus_message *reply) {
483+
int r;
484+
485+
if (!arg_enable && !arg_now)
486+
return 0;
487+
488+
r = sd_bus_message_rewind(reply, true);
489+
if (r < 0)
490+
return r;
491+
r = sd_bus_message_enter_container(reply, 'a', "(sss)");
492+
if (r < 0)
493+
return bus_log_parse_error(r);
494+
495+
for (;;) {
496+
char *type, *path, *source;
497+
498+
r = sd_bus_message_read(reply, "(sss)", &type, &path, &source);
499+
if (r < 0)
500+
return bus_log_parse_error(r);
501+
if (r == 0)
502+
break;
503+
504+
if (STR_IN_SET(type, "symlink", "copy") && ENDSWITH_SET(path, ".service", ".target", ".socket")) {
505+
(void) maybe_enable_disable(bus, path, true);
506+
(void) maybe_start_stop(bus, path, true);
507+
}
508+
}
509+
510+
r = sd_bus_message_exit_container(reply);
511+
if (r < 0)
512+
return r;
513+
514+
return 0;
515+
}
516+
517+
static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
518+
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
519+
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
520+
_cleanup_strv_free_ char **matches = NULL;
521+
int r;
522+
523+
if (!arg_enable && !arg_now)
524+
return 0;
525+
526+
r = determine_matches(argv[1], argv + 2, true, &matches);
527+
if (r < 0)
528+
return r;
529+
530+
r = sd_bus_message_new_method_call(
531+
bus,
532+
&m,
533+
"org.freedesktop.portable1",
534+
"/org/freedesktop/portable1",
535+
"org.freedesktop.portable1.Manager",
536+
"GetImageMetadata");
537+
if (r < 0)
538+
return bus_log_create_error(r);
539+
540+
r = sd_bus_message_append(m, "s", image);
541+
if (r < 0)
542+
return bus_log_create_error(r);
543+
544+
r = sd_bus_message_append_strv(m, matches);
545+
if (r < 0)
546+
return bus_log_create_error(r);
547+
548+
r = sd_bus_call(bus, m, 0, &error, &reply);
549+
if (r < 0)
550+
return log_error_errno(r, "Failed to inspect image metadata: %s", bus_error_message(&error, r));
551+
552+
r = sd_bus_message_skip(reply, "say");
553+
if (r < 0)
554+
return bus_log_parse_error(r);
555+
556+
r = sd_bus_message_enter_container(reply, 'a', "{say}");
557+
if (r < 0)
558+
return bus_log_parse_error(r);
559+
560+
for (;;) {
561+
const char *name;
562+
563+
r = sd_bus_message_enter_container(reply, 'e', "say");
564+
if (r < 0)
565+
return bus_log_parse_error(r);
566+
if (r == 0)
567+
break;
568+
569+
r = sd_bus_message_read(reply, "s", &name);
570+
if (r < 0)
571+
return bus_log_parse_error(r);
572+
573+
r = sd_bus_message_skip(reply, "ay");
574+
if (r < 0)
575+
return bus_log_parse_error(r);
576+
577+
r = sd_bus_message_exit_container(reply);
578+
if (r < 0)
579+
return bus_log_parse_error(r);
580+
581+
(void) maybe_start_stop(bus, name, false);
582+
(void) maybe_enable_disable(bus, name, false);
583+
}
584+
585+
r = sd_bus_message_exit_container(reply);
586+
if (r < 0)
587+
return bus_log_parse_error(r);
588+
589+
return 0;
590+
}
591+
391592
static int attach_image(int argc, char *argv[], void *userdata) {
392593
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
393594
_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) {
439640
(void) maybe_reload(&bus);
440641

441642
print_changes(reply);
643+
644+
(void) maybe_enable_start(bus, reply);
645+
442646
return 0;
443647
}
444648

@@ -459,6 +663,8 @@ static int detach_image(int argc, char *argv[], void *userdata) {
459663

460664
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
461665

666+
(void) maybe_stop_disable(bus, image, argv);
667+
462668
r = sd_bus_call_method(
463669
bus,
464670
"org.freedesktop.portable1",
@@ -764,7 +970,8 @@ static int help(int argc, char *argv[], void *userdata) {
764970
" list List available portable service images\n"
765971
" attach NAME|PATH [PREFIX...]\n"
766972
" Attach the specified portable service image\n"
767-
" detach NAME|PATH Detach the specified portable service image\n"
973+
" detach NAME|PATH [PREFIX...]\n"
974+
" Detach the specified portable service image\n"
768975
" inspect NAME|PATH [PREFIX...]\n"
769976
" Show details of specified portable service image\n"
770977
" 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) {
786993
" --no-reload Don't reload the system and service manager\n"
787994
" --cat When inspecting include unit and os-release file\n"
788995
" contents\n"
996+
" --enable Immediately enable/disable the portable service\n"
997+
" after attach/detach\n"
998+
" --now Immediately start/stop the portable service after\n"
999+
" attach/before detach\n"
7891000
"\nSee the %s for details.\n"
7901001
, program_invocation_short_name
7911002
, ansi_highlight()
@@ -807,6 +1018,8 @@ static int parse_argv(int argc, char *argv[]) {
8071018
ARG_RUNTIME,
8081019
ARG_NO_RELOAD,
8091020
ARG_CAT,
1021+
ARG_ENABLE,
1022+
ARG_NOW,
8101023
};
8111024

8121025
static const struct option options[] = {
@@ -823,6 +1036,8 @@ static int parse_argv(int argc, char *argv[]) {
8231036
{ "runtime", no_argument, NULL, ARG_RUNTIME },
8241037
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
8251038
{ "cat", no_argument, NULL, ARG_CAT },
1039+
{ "enable", no_argument, NULL, ARG_ENABLE },
1040+
{ "now", no_argument, NULL, ARG_NOW },
8261041
{}
8271042
};
8281043

@@ -909,6 +1124,14 @@ static int parse_argv(int argc, char *argv[]) {
9091124
arg_cat = true;
9101125
break;
9111126

1127+
case ARG_ENABLE:
1128+
arg_enable = true;
1129+
break;
1130+
1131+
case ARG_NOW:
1132+
arg_now = true;
1133+
break;
1134+
9121135
case '?':
9131136
return -EINVAL;
9141137

@@ -925,7 +1148,7 @@ static int run(int argc, char *argv[]) {
9251148
{ "help", VERB_ANY, VERB_ANY, 0, help },
9261149
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_images },
9271150
{ "attach", 2, VERB_ANY, 0, attach_image },
928-
{ "detach", 2, 2, 0, detach_image },
1151+
{ "detach", 2, VERB_ANY, 0, detach_image },
9291152
{ "inspect", 2, VERB_ANY, 0, inspect_image },
9301153
{ "is-attached", 2, 2, 0, is_image_attached },
9311154
{ "read-only", 2, 3, 0, read_only_image },

0 commit comments

Comments
 (0)
X Tutup