1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include "alloc-util.h"
10 #include "bus-locator.h"
11 #include "bus-unit-util.h"
12 #include "bus-wait-for-jobs.h"
14 #include "dirent-util.h"
18 #include "format-table.h"
20 #include "locale-util.h"
21 #include "machine-image.h"
22 #include "main-func.h"
24 #include "parse-util.h"
25 #include "path-util.h"
26 #include "pretty-print.h"
27 #include "spawn-polkit-agent.h"
28 #include "string-util.h"
30 #include "terminal-util.h"
33 static PagerFlags arg_pager_flags
= 0;
34 static bool arg_legend
= true;
35 static bool arg_ask_password
= true;
36 static bool arg_quiet
= false;
37 static const char *arg_profile
= "default";
38 static const char* arg_copy_mode
= NULL
;
39 static bool arg_runtime
= false;
40 static bool arg_reload
= true;
41 static bool arg_cat
= false;
42 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
43 static const char *arg_host
= NULL
;
44 static bool arg_enable
= false;
45 static bool arg_now
= false;
46 static bool arg_no_block
= false;
48 static int determine_image(const char *image
, bool permit_non_existing
, char **ret
) {
51 /* If the specified name is a valid image name, we pass it as-is to portabled, which will search for it in the
52 * usual search directories. Otherwise we presume it's a path, and will normalize it on the client's side
53 * (among other things, to make the path independent of the client's working directory) before passing it
56 if (image_name_is_valid(image
)) {
59 if (!arg_quiet
&& laccess(image
, F_OK
) >= 0)
60 log_warning("Ambiguous invocation: current working directory contains file matching non-path argument '%s', ignoring. "
61 "Prefix argument with './' to force reference to file in current working directory.", image
);
71 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
72 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
73 "Operations on images by path not supported when connecting to remote systems.");
75 r
= chase_symlinks(image
, NULL
, CHASE_TRAIL_SLASH
| (permit_non_existing
? CHASE_NONEXISTENT
: 0), ret
, NULL
);
77 return log_error_errno(r
, "Cannot normalize specified image path '%s': %m", image
);
82 static int extract_prefix(const char *path
, char **ret
) {
83 _cleanup_free_
char *name
= NULL
;
84 const char *bn
, *underscore
;
89 underscore
= strchr(bn
, '_');
95 e
= endswith(bn
, ".raw");
102 name
= strndup(bn
, m
);
106 /* A slightly reduced version of what's permitted in unit names. With ':' and '\' are removed, as well as '_'
107 * which we use as delimiter for the second part of the image string, which we ignore for now. */
108 if (!in_charset(name
, DIGITS LETTERS
"-."))
111 if (!filename_is_valid(name
))
114 *ret
= TAKE_PTR(name
);
119 static int determine_matches(const char *image
, char **l
, bool allow_any
, char ***ret
) {
120 _cleanup_strv_free_
char **k
= NULL
;
123 /* Determine the matches to apply. If the list is empty we derive the match from the image name. If the list
124 * contains exactly the "-" we return a wildcard list (which is the empty list), but only if this is expressly
127 if (strv_isempty(l
)) {
130 r
= extract_prefix(image
, &prefix
);
132 return log_error_errno(r
, "Failed to extract prefix of image name '%s': %m", image
);
135 log_info("(Matching unit files with prefix '%s'.)", prefix
);
137 r
= strv_consume(&k
, prefix
);
141 } else if (strv_equal(l
, STRV_MAKE("-"))) {
144 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
145 "Refusing all unit file match.");
148 log_info("(Matching all unit files.)");
156 _cleanup_free_
char *joined
= NULL
;
158 joined
= strv_join(k
, "', '");
162 log_info("(Matching unit files with prefixes '%s'.)", joined
);
171 static int acquire_bus(sd_bus
**bus
) {
179 r
= bus_connect_transport(arg_transport
, arg_host
, false, bus
);
181 return log_error_errno(r
, "Failed to connect to bus: %m");
183 (void) sd_bus_set_allow_interactive_authorization(*bus
, arg_ask_password
);
188 static int maybe_reload(sd_bus
**bus
) {
189 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
190 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
196 r
= acquire_bus(bus
);
200 r
= sd_bus_message_new_method_call(
203 "org.freedesktop.systemd1",
204 "/org/freedesktop/systemd1",
205 "org.freedesktop.systemd1.Manager",
208 return bus_log_create_error(r
);
210 /* Reloading the daemon may take long, hence set a longer timeout here */
211 r
= sd_bus_call(*bus
, m
, DEFAULT_TIMEOUT_USEC
* 2, &error
, NULL
);
213 return log_error_errno(r
, "Failed to reload daemon: %s", bus_error_message(&error
, r
));
218 static int inspect_image(int argc
, char *argv
[], void *userdata
) {
219 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
220 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
221 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
222 _cleanup_strv_free_
char **matches
= NULL
;
223 _cleanup_free_
char *image
= NULL
;
224 bool nl
= false, header
= false;
230 r
= determine_image(argv
[1], false, &image
);
234 r
= determine_matches(argv
[1], argv
+ 2, true, &matches
);
238 r
= acquire_bus(&bus
);
242 r
= bus_message_new_method_call(bus
, &m
, bus_portable_mgr
, "GetImageMetadata");
244 return bus_log_create_error(r
);
246 r
= sd_bus_message_append(m
, "s", image
);
248 return bus_log_create_error(r
);
250 r
= sd_bus_message_append_strv(m
, matches
);
252 return bus_log_create_error(r
);
254 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
256 return log_error_errno(r
, "Failed to inspect image metadata: %s", bus_error_message(&error
, r
));
258 r
= sd_bus_message_read(reply
, "s", &path
);
260 return bus_log_parse_error(r
);
262 r
= sd_bus_message_read_array(reply
, 'y', &data
, &sz
);
264 return bus_log_parse_error(r
);
266 (void) pager_open(arg_pager_flags
);
269 printf("%s-- OS Release: --%s\n", ansi_highlight(), ansi_normal());
270 fwrite(data
, sz
, 1, stdout
);
274 _cleanup_free_
char *pretty_portable
= NULL
, *pretty_os
= NULL
;
275 _cleanup_fclose_
FILE *f
;
277 f
= fmemopen_unlocked((void*) data
, sz
, "re");
279 return log_error_errno(errno
, "Failed to open /etc/os-release buffer: %m");
281 r
= parse_env_file(f
, "/etc/os-release",
282 "PORTABLE_PRETTY_NAME", &pretty_portable
,
283 "PRETTY_NAME", &pretty_os
);
285 return log_error_errno(r
, "Failed to parse /etc/os-release: %m");
287 printf("Image:\n\t%s\n"
288 "Portable Service:\n\t%s\n"
289 "Operating System:\n\t%s\n",
291 strna(pretty_portable
),
295 r
= sd_bus_message_enter_container(reply
, 'a', "{say}");
297 return bus_log_parse_error(r
);
302 r
= sd_bus_message_enter_container(reply
, 'e', "say");
304 return bus_log_parse_error(r
);
308 r
= sd_bus_message_read(reply
, "s", &name
);
310 return bus_log_parse_error(r
);
312 r
= sd_bus_message_read_array(reply
, 'y', &data
, &sz
);
314 return bus_log_parse_error(r
);
320 printf("%s-- Unit file: %s --%s\n", ansi_highlight(), name
, ansi_normal());
321 fwrite(data
, sz
, 1, stdout
);
326 fputs("Unit files:\n", stdout
);
335 r
= sd_bus_message_exit_container(reply
);
337 return bus_log_parse_error(r
);
340 r
= sd_bus_message_exit_container(reply
);
342 return bus_log_parse_error(r
);
347 static int print_changes(sd_bus_message
*m
) {
353 r
= sd_bus_message_enter_container(m
, 'a', "(sss)");
355 return bus_log_parse_error(r
);
358 const char *type
, *path
, *source
;
360 r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
);
362 return bus_log_parse_error(r
);
366 if (streq(type
, "symlink"))
367 log_info("Created symlink %s %s %s.", path
, special_glyph(SPECIAL_GLYPH_ARROW
), source
);
368 else if (streq(type
, "copy")) {
370 log_info("Copied %s.", path
);
372 log_info("Copied %s %s %s.", source
, special_glyph(SPECIAL_GLYPH_ARROW
), path
);
373 } else if (streq(type
, "unlink"))
374 log_info("Removed %s.", path
);
375 else if (streq(type
, "write"))
376 log_info("Written %s.", path
);
377 else if (streq(type
, "mkdir"))
378 log_info("Created directory %s.", path
);
380 log_error("Unexpected change: %s/%s/%s", type
, path
, source
);
383 r
= sd_bus_message_exit_container(m
);
390 static int maybe_enable_disable(sd_bus
*bus
, const char *path
, bool enable
) {
391 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
392 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
393 _cleanup_strv_free_
char **names
= NULL
;
394 UnitFileChange
*changes
= NULL
;
395 size_t n_changes
= 0;
401 names
= strv_new(path
, NULL
);
405 r
= sd_bus_message_new_method_call(
408 "org.freedesktop.systemd1",
409 "/org/freedesktop/systemd1",
410 "org.freedesktop.systemd1.Manager",
411 enable
? "EnableUnitFiles" : "DisableUnitFiles");
413 return bus_log_create_error(r
);
415 r
= sd_bus_message_append_strv(m
, names
);
417 return bus_log_create_error(r
);
419 r
= sd_bus_message_append(m
, "b", arg_runtime
);
421 return bus_log_create_error(r
);
424 r
= sd_bus_message_append(m
, "b", false);
426 return bus_log_create_error(r
);
429 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
431 return log_error_errno(r
, "Failed to %s the portable service %s: %s",
432 enable
? "enable" : "disable", path
, bus_error_message(&error
, r
));
435 r
= sd_bus_message_skip(reply
, "b");
437 return bus_log_parse_error(r
);
439 (void) bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, &changes
, &n_changes
);
444 static int maybe_start_stop(sd_bus
*bus
, const char *path
, bool start
, BusWaitForJobs
*wait
) {
445 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
446 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
447 char *name
= (char *)basename(path
), *job
= NULL
;
453 r
= sd_bus_call_method(
455 "org.freedesktop.systemd1",
456 "/org/freedesktop/systemd1",
457 "org.freedesktop.systemd1.Manager",
458 start
? "StartUnit" : "StopUnit",
461 "ss", name
, "replace");
463 return log_error_errno(r
, "Failed to %s the portable service %s: %s",
464 start
? "start" : "stop",
466 bus_error_message(&error
, r
));
468 r
= sd_bus_message_read(reply
, "o", &job
);
470 return bus_log_parse_error(r
);
473 log_info("Queued %s to %s portable service %s.", job
, start
? "start" : "stop", name
);
476 r
= bus_wait_for_jobs_add(wait
, job
);
478 return log_error_errno(r
, "Failed to watch %s job for %s %s: %m",
479 job
, start
? "starting" : "stopping", name
);
485 static int maybe_enable_start(sd_bus
*bus
, sd_bus_message
*reply
) {
486 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*wait
= NULL
;
489 if (!arg_enable
&& !arg_now
)
493 r
= bus_wait_for_jobs_new(bus
, &wait
);
495 return log_error_errno(r
, "Could not watch jobs: %m");
498 r
= sd_bus_message_rewind(reply
, true);
501 r
= sd_bus_message_enter_container(reply
, 'a', "(sss)");
503 return bus_log_parse_error(r
);
506 char *type
, *path
, *source
;
508 r
= sd_bus_message_read(reply
, "(sss)", &type
, &path
, &source
);
510 return bus_log_parse_error(r
);
514 if (STR_IN_SET(type
, "symlink", "copy") && ENDSWITH_SET(path
, ".service", ".target", ".socket")) {
515 (void) maybe_enable_disable(bus
, path
, true);
516 (void) maybe_start_stop(bus
, path
, true, wait
);
520 r
= sd_bus_message_exit_container(reply
);
525 r
= bus_wait_for_jobs(wait
, arg_quiet
, NULL
);
533 static int maybe_stop_disable(sd_bus
*bus
, char *image
, char *argv
[]) {
534 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*wait
= NULL
;
535 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
536 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
537 _cleanup_strv_free_
char **matches
= NULL
;
540 if (!arg_enable
&& !arg_now
)
543 r
= determine_matches(argv
[1], argv
+ 2, true, &matches
);
547 r
= bus_wait_for_jobs_new(bus
, &wait
);
549 return log_error_errno(r
, "Could not watch jobs: %m");
551 r
= bus_message_new_method_call(bus
, &m
, bus_portable_mgr
, "GetImageMetadata");
553 return bus_log_create_error(r
);
555 r
= sd_bus_message_append(m
, "s", image
);
557 return bus_log_create_error(r
);
559 r
= sd_bus_message_append_strv(m
, matches
);
561 return bus_log_create_error(r
);
563 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
565 return log_error_errno(r
, "Failed to inspect image metadata: %s", bus_error_message(&error
, r
));
567 r
= sd_bus_message_skip(reply
, "say");
569 return bus_log_parse_error(r
);
571 r
= sd_bus_message_enter_container(reply
, 'a', "{say}");
573 return bus_log_parse_error(r
);
578 r
= sd_bus_message_enter_container(reply
, 'e', "say");
580 return bus_log_parse_error(r
);
584 r
= sd_bus_message_read(reply
, "s", &name
);
586 return bus_log_parse_error(r
);
588 r
= sd_bus_message_skip(reply
, "ay");
590 return bus_log_parse_error(r
);
592 r
= sd_bus_message_exit_container(reply
);
594 return bus_log_parse_error(r
);
596 (void) maybe_start_stop(bus
, name
, false, wait
);
597 (void) maybe_enable_disable(bus
, name
, false);
600 r
= sd_bus_message_exit_container(reply
);
602 return bus_log_parse_error(r
);
604 /* Stopping must always block or the detach will fail if the unit is still running */
605 r
= bus_wait_for_jobs(wait
, arg_quiet
, NULL
);
612 static int attach_image(int argc
, char *argv
[], void *userdata
) {
613 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
614 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
615 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
616 _cleanup_strv_free_
char **matches
= NULL
;
617 _cleanup_free_
char *image
= NULL
;
620 r
= determine_image(argv
[1], false, &image
);
624 r
= determine_matches(argv
[1], argv
+ 2, false, &matches
);
628 r
= acquire_bus(&bus
);
632 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
634 r
= bus_message_new_method_call(bus
, &m
, bus_portable_mgr
, "AttachImage");
636 return bus_log_create_error(r
);
638 r
= sd_bus_message_append(m
, "s", image
);
640 return bus_log_create_error(r
);
642 r
= sd_bus_message_append_strv(m
, matches
);
644 return bus_log_create_error(r
);
646 r
= sd_bus_message_append(m
, "sbs", arg_profile
, arg_runtime
, arg_copy_mode
);
648 return bus_log_create_error(r
);
650 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
652 return log_error_errno(r
, "Failed to attach image: %s", bus_error_message(&error
, r
));
654 (void) maybe_reload(&bus
);
656 print_changes(reply
);
658 (void) maybe_enable_start(bus
, reply
);
663 static int detach_image(int argc
, char *argv
[], void *userdata
) {
664 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
665 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
666 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
667 _cleanup_free_
char *image
= NULL
;
670 r
= determine_image(argv
[1], true, &image
);
674 r
= acquire_bus(&bus
);
678 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
680 (void) maybe_stop_disable(bus
, image
, argv
);
682 r
= bus_call_method(bus
, bus_portable_mgr
, "DetachImage", &error
, &reply
, "sb", image
, arg_runtime
);
684 return log_error_errno(r
, "Failed to detach image: %s", bus_error_message(&error
, r
));
686 (void) maybe_reload(&bus
);
688 print_changes(reply
);
692 static int list_images(int argc
, char *argv
[], void *userdata
) {
693 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
694 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
695 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
696 _cleanup_(table_unrefp
) Table
*table
= NULL
;
699 r
= acquire_bus(&bus
);
703 r
= bus_call_method(bus
, bus_portable_mgr
, "ListImages", &error
, &reply
, NULL
);
705 return log_error_errno(r
, "Failed to list images: %s", bus_error_message(&error
, r
));
707 table
= table_new("name", "type", "ro", "crtime", "mtime", "usage", "state");
711 r
= sd_bus_message_enter_container(reply
, 'a', "(ssbtttso)");
713 return bus_log_parse_error(r
);
716 const char *name
, *type
, *state
;
717 uint64_t crtime
, mtime
, usage
;
720 r
= sd_bus_message_read(reply
, "(ssbtttso)", &name
, &type
, &ro_int
, &crtime
, &mtime
, &usage
, &state
, NULL
);
722 return bus_log_parse_error(r
);
726 r
= table_add_many(table
,
729 TABLE_BOOLEAN
, ro_int
,
730 TABLE_SET_COLOR
, ro_int
? ansi_highlight_red() : NULL
,
731 TABLE_TIMESTAMP
, crtime
,
732 TABLE_TIMESTAMP
, mtime
,
735 TABLE_SET_COLOR
, !streq(state
, "detached") ? ansi_highlight_green() : NULL
);
737 return table_log_add_error(r
);
740 r
= sd_bus_message_exit_container(reply
);
742 return bus_log_parse_error(r
);
744 if (table_get_rows(table
) > 1) {
745 r
= table_set_sort(table
, (size_t) 0, (size_t) -1);
747 return table_log_sort_error(r
);
749 table_set_header(table
, arg_legend
);
751 r
= table_print(table
, NULL
);
753 return table_log_print_error(r
);
757 if (table_get_rows(table
) > 1)
758 printf("\n%zu images listed.\n", table_get_rows(table
) - 1);
760 printf("No images.\n");
766 static int remove_image(int argc
, char *argv
[], void *userdata
) {
767 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
770 r
= acquire_bus(&bus
);
774 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
776 for (i
= 1; i
< argc
; i
++) {
777 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
778 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
780 r
= bus_message_new_method_call(bus
, &m
, bus_portable_mgr
, "RemoveImage");
782 return bus_log_create_error(r
);
784 r
= sd_bus_message_append(m
, "s", argv
[i
]);
786 return bus_log_create_error(r
);
788 /* This is a slow operation, hence turn off any method call timeouts */
789 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
791 return log_error_errno(r
, "Could not remove image: %s", bus_error_message(&error
, r
));
797 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
798 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
799 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
803 b
= parse_boolean(argv
[2]);
805 return log_error_errno(b
, "Failed to parse boolean argument: %s", argv
[2]);
808 r
= acquire_bus(&bus
);
812 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
814 r
= bus_call_method(bus
, bus_portable_mgr
, "MarkImageReadOnly", &error
, NULL
, "sb", argv
[1], b
);
816 return log_error_errno(r
, "Could not mark image read-only: %s", bus_error_message(&error
, r
));
821 static int set_limit(int argc
, char *argv
[], void *userdata
) {
822 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
823 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
827 r
= acquire_bus(&bus
);
831 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
833 if (STR_IN_SET(argv
[argc
-1], "-", "none", "infinity"))
834 limit
= (uint64_t) -1;
836 r
= parse_size(argv
[argc
-1], 1024, &limit
);
838 return log_error_errno(r
, "Failed to parse size: %s", argv
[argc
-1]);
842 /* With two arguments changes the quota limit of the specified image */
843 r
= bus_call_method(bus
, bus_portable_mgr
, "SetImageLimit", &error
, NULL
, "st", argv
[1], limit
);
845 /* With one argument changes the pool quota limit */
846 r
= bus_call_method(bus
, bus_portable_mgr
, "SetPoolLimit", &error
, NULL
, "t", limit
);
849 return log_error_errno(r
, "Could not set limit: %s", bus_error_message(&error
, r
));
854 static int is_image_attached(int argc
, char *argv
[], void *userdata
) {
855 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
856 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
857 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
858 _cleanup_free_
char *image
= NULL
;
862 r
= determine_image(argv
[1], true, &image
);
866 r
= acquire_bus(&bus
);
870 r
= bus_call_method(bus
, bus_portable_mgr
, "GetImageState", &error
, &reply
, "s", image
);
872 return log_error_errno(r
, "Failed to get image state: %s", bus_error_message(&error
, r
));
874 r
= sd_bus_message_read(reply
, "s", &state
);
881 return streq(state
, "detached");
884 static int dump_profiles(void) {
885 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
886 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
887 _cleanup_strv_free_
char **l
= NULL
;
891 r
= acquire_bus(&bus
);
895 r
= bus_get_property_strv(bus
, bus_portable_mgr
, "Profiles", &error
, &l
);
897 return log_error_errno(r
, "Failed to acquire list of profiles: %s", bus_error_message(&error
, r
));
900 log_info("Available unit profiles:");
910 static int help(int argc
, char *argv
[], void *userdata
) {
911 _cleanup_free_
char *link
= NULL
;
914 (void) pager_open(arg_pager_flags
);
916 r
= terminal_urlify_man("portablectl", "1", &link
);
920 printf("%s [OPTIONS...] COMMAND ...\n\n"
921 "%sAttach or detach portable services from the local system.%s\n"
923 " list List available portable service images\n"
924 " attach NAME|PATH [PREFIX...]\n"
925 " Attach the specified portable service image\n"
926 " detach NAME|PATH [PREFIX...]\n"
927 " Detach the specified portable service image\n"
928 " inspect NAME|PATH [PREFIX...]\n"
929 " Show details of specified portable service image\n"
930 " is-attached NAME|PATH Query if portable service image is attached\n"
931 " read-only NAME|PATH [BOOL] Mark or unmark portable service image read-only\n"
932 " remove NAME|PATH... Remove a portable service image\n"
933 " set-limit [NAME|PATH] Set image or pool size limit (disk quota)\n"
935 " -h --help Show this help\n"
936 " --version Show package version\n"
937 " --no-pager Do not pipe output into a pager\n"
938 " --no-legend Do not show the headers and footers\n"
939 " --no-ask-password Do not ask for system passwords\n"
940 " -H --host=[USER@]HOST Operate on remote host\n"
941 " -M --machine=CONTAINER Operate on local container\n"
942 " -q --quiet Suppress informational messages\n"
943 " -p --profile=PROFILE Pick security profile for portable service\n"
944 " --copy=copy|auto|symlink Prefer copying or symlinks if possible\n"
945 " --runtime Attach portable service until next reboot only\n"
946 " --no-reload Don't reload the system and service manager\n"
947 " --cat When inspecting include unit and os-release file\n"
949 " --enable Immediately enable/disable the portable service\n"
950 " after attach/detach\n"
951 " --now Immediately start/stop the portable service after\n"
952 " attach/before detach\n"
953 " --no-block Don't block waiting for attach --now to complete\n"
954 "\nSee the %s for details.\n"
955 , program_invocation_short_name
964 static int parse_argv(int argc
, char *argv
[]) {
980 static const struct option options
[] = {
981 { "help", no_argument
, NULL
, 'h' },
982 { "version", no_argument
, NULL
, ARG_VERSION
},
983 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
984 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
985 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
986 { "host", required_argument
, NULL
, 'H' },
987 { "machine", required_argument
, NULL
, 'M' },
988 { "quiet", no_argument
, NULL
, 'q' },
989 { "profile", required_argument
, NULL
, 'p' },
990 { "copy", required_argument
, NULL
, ARG_COPY
},
991 { "runtime", no_argument
, NULL
, ARG_RUNTIME
},
992 { "no-reload", no_argument
, NULL
, ARG_NO_RELOAD
},
993 { "cat", no_argument
, NULL
, ARG_CAT
},
994 { "enable", no_argument
, NULL
, ARG_ENABLE
},
995 { "now", no_argument
, NULL
, ARG_NOW
},
996 { "no-block", no_argument
, NULL
, ARG_NO_BLOCK
},
1006 c
= getopt_long(argc
, argv
, "hH:M:qp:", options
, NULL
);
1013 return help(0, NULL
, NULL
);
1019 arg_pager_flags
|= PAGER_DISABLE
;
1026 case ARG_NO_ASK_PASSWORD
:
1027 arg_ask_password
= false;
1031 arg_transport
= BUS_TRANSPORT_REMOTE
;
1036 arg_transport
= BUS_TRANSPORT_MACHINE
;
1045 if (streq(optarg
, "help"))
1046 return dump_profiles();
1048 if (!filename_is_valid(optarg
))
1049 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1050 "Unit profile name not valid: %s", optarg
);
1052 arg_profile
= optarg
;
1056 if (streq(optarg
, "auto"))
1057 arg_copy_mode
= NULL
;
1058 else if (STR_IN_SET(optarg
, "copy", "symlink"))
1059 arg_copy_mode
= optarg
;
1060 else if (streq(optarg
, "help")) {
1066 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1067 "Failed to parse --copy= argument: %s", optarg
);
1092 arg_no_block
= true;
1099 assert_not_reached("Unhandled option");
1106 static int run(int argc
, char *argv
[]) {
1107 static const Verb verbs
[] = {
1108 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
1109 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_images
},
1110 { "attach", 2, VERB_ANY
, 0, attach_image
},
1111 { "detach", 2, VERB_ANY
, 0, detach_image
},
1112 { "inspect", 2, VERB_ANY
, 0, inspect_image
},
1113 { "is-attached", 2, 2, 0, is_image_attached
},
1114 { "read-only", 2, 3, 0, read_only_image
},
1115 { "remove", 2, VERB_ANY
, 0, remove_image
},
1116 { "set-limit", 3, 3, 0, set_limit
},
1124 r
= parse_argv(argc
, argv
);
1128 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1131 DEFINE_MAIN_FUNCTION(run
);