1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <arpa/inet.h>
27 #include <netinet/in.h>
29 #include <sys/mount.h>
30 #include <sys/socket.h>
35 #include "alloc-util.h"
36 #include "bus-common-errors.h"
37 #include "bus-error.h"
38 #include "bus-unit-util.h"
40 #include "cgroup-show.h"
41 #include "cgroup-util.h"
45 #include "hostname-util.h"
46 #include "import-util.h"
48 #include "logs-show.h"
52 #include "parse-util.h"
53 #include "path-util.h"
54 #include "process-util.h"
57 #include "signal-util.h"
58 #include "spawn-polkit-agent.h"
59 #include "stdio-util.h"
61 #include "terminal-util.h"
62 #include "unit-name.h"
67 #define ALL_IP_ADDRESSES -1
69 static char **arg_property
= NULL
;
70 static bool arg_all
= false;
71 static bool arg_value
= false;
72 static bool arg_full
= false;
73 static bool arg_no_pager
= false;
74 static bool arg_legend
= true;
75 static const char *arg_kill_who
= NULL
;
76 static int arg_signal
= SIGTERM
;
77 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
78 static char *arg_host
= NULL
;
79 static bool arg_read_only
= false;
80 static bool arg_mkdir
= false;
81 static bool arg_quiet
= false;
82 static bool arg_ask_password
= true;
83 static unsigned arg_lines
= 10;
84 static OutputMode arg_output
= OUTPUT_SHORT
;
85 static bool arg_force
= false;
86 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
87 static const char* arg_format
= NULL
;
88 static const char *arg_uid
= NULL
;
89 static char **arg_setenv
= NULL
;
90 static int arg_addrs
= 1;
92 static int print_addresses(sd_bus
*bus
, const char *name
, int, const char *pr1
, const char *pr2
, int n_addr
);
94 static OutputFlags
get_output_flags(void) {
96 arg_all
* OUTPUT_SHOW_ALL
|
97 (arg_full
|| !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
98 colors_enabled() * OUTPUT_COLOR
|
99 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
102 typedef struct MachineInfo
{
110 static int compare_machine_info(const void *a
, const void *b
) {
111 const MachineInfo
*x
= a
, *y
= b
;
113 return strcmp(x
->name
, y
->name
);
116 static void clean_machine_info(MachineInfo
*machines
, size_t n_machines
) {
119 if (!machines
|| n_machines
== 0)
122 for (i
= 0; i
< n_machines
; i
++) {
123 free(machines
[i
].os
);
124 free(machines
[i
].version_id
);
129 static int call_get_os_release(sd_bus
*bus
, const char *method
, const char *name
, const char *query
, ...) {
130 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
131 const char *k
, *v
, *iter
, **query_res
= NULL
;
132 size_t count
= 0, awaited_args
= 0;
140 NULSTR_FOREACH(iter
, query
)
142 query_res
= newa0(const char *, awaited_args
);
144 r
= sd_bus_call_method(
146 "org.freedesktop.machine1",
147 "/org/freedesktop/machine1",
148 "org.freedesktop.machine1.Manager",
150 NULL
, &reply
, "s", name
);
154 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
156 return bus_log_parse_error(r
);
158 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
160 NULSTR_FOREACH(iter
, query
) {
161 if (streq(k
, iter
)) {
162 query_res
[count
] = v
;
169 return bus_log_parse_error(r
);
171 r
= sd_bus_message_exit_container(reply
);
173 return bus_log_parse_error(r
);
176 for (count
= 0; count
< awaited_args
; count
++) {
179 out
= va_arg(ap
, char **);
181 if (query_res
[count
]) {
182 val
= strdup(query_res
[count
]);
195 static int list_machines(int argc
, char *argv
[], void *userdata
) {
197 size_t max_name
= STRLEN("MACHINE"), max_class
= STRLEN("CLASS"),
198 max_service
= STRLEN("SERVICE"), max_os
= STRLEN("OS"), max_version_id
= STRLEN("VERSION");
199 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
200 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
201 _cleanup_free_
char *prefix
= NULL
;
202 MachineInfo
*machines
= NULL
;
203 const char *name
, *class, *service
, *object
;
204 size_t n_machines
= 0, n_allocated
= 0, j
;
205 sd_bus
*bus
= userdata
;
210 (void) pager_open(arg_no_pager
, false);
212 r
= sd_bus_call_method(bus
,
213 "org.freedesktop.machine1",
214 "/org/freedesktop/machine1",
215 "org.freedesktop.machine1.Manager",
221 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
225 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
227 return bus_log_parse_error(r
);
228 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
231 if (name
[0] == '.' && !arg_all
)
234 if (!GREEDY_REALLOC0(machines
, n_allocated
, n_machines
+ 1)) {
239 machines
[n_machines
].name
= name
;
240 machines
[n_machines
].class = class;
241 machines
[n_machines
].service
= service
;
243 (void) call_get_os_release(
245 "GetMachineOSRelease",
249 &machines
[n_machines
].os
,
250 &machines
[n_machines
].version_id
);
264 l
= machines
[n_machines
].os
? strlen(machines
[n_machines
].os
) : 1;
268 l
= machines
[n_machines
].version_id
? strlen(machines
[n_machines
].version_id
) : 1;
269 if (l
> max_version_id
)
275 r
= bus_log_parse_error(r
);
279 r
= sd_bus_message_exit_container(reply
);
281 r
= bus_log_parse_error(r
);
285 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
287 /* Allocate for prefix max characters for all fields + spaces between them + STRLEN(",\n") */
288 r
= asprintf(&prefix
, "%-*s",
293 max_version_id
+ 5 + STRLEN(",\n")),
300 if (arg_legend
&& n_machines
> 0)
301 printf("%-*s %-*s %-*s %-*s %-*s %s\n",
302 (int) max_name
, "MACHINE",
303 (int) max_class
, "CLASS",
304 (int) max_service
, "SERVICE",
306 (int) max_version_id
, "VERSION",
309 for (j
= 0; j
< n_machines
; j
++) {
310 printf("%-*s %-*s %-*s %-*s %-*s ",
311 (int) max_name
, machines
[j
].name
,
312 (int) max_class
, machines
[j
].class,
313 (int) max_service
, strdash_if_empty(machines
[j
].service
),
314 (int) max_os
, strdash_if_empty(machines
[j
].os
),
315 (int) max_version_id
, strdash_if_empty(machines
[j
].version_id
));
317 r
= print_addresses(bus
, machines
[j
].name
, 0, "", prefix
, arg_addrs
);
318 if (r
<= 0) /* error or no addresses defined? */
319 fputs("-\n", stdout
);
326 printf("\n%zu machines listed.\n", n_machines
);
328 printf("No machines.\n");
333 clean_machine_info(machines
, n_machines
);
337 typedef struct ImageInfo
{
346 static int compare_image_info(const void *a
, const void *b
) {
347 const ImageInfo
*x
= a
, *y
= b
;
349 return strcmp(x
->name
, y
->name
);
352 static int list_images(int argc
, char *argv
[], void *userdata
) {
354 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
355 size_t max_name
= STRLEN("NAME"), max_type
= STRLEN("TYPE"), max_size
= STRLEN("USAGE"), max_crtime
= STRLEN("CREATED"), max_mtime
= STRLEN("MODIFIED");
356 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
357 _cleanup_free_ ImageInfo
*images
= NULL
;
358 size_t n_images
= 0, n_allocated
= 0, j
;
359 const char *name
, *type
, *object
;
360 sd_bus
*bus
= userdata
;
361 uint64_t crtime
, mtime
, size
;
366 (void) pager_open(arg_no_pager
, false);
368 r
= sd_bus_call_method(bus
,
369 "org.freedesktop.machine1",
370 "/org/freedesktop/machine1",
371 "org.freedesktop.machine1.Manager",
377 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
381 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
383 return bus_log_parse_error(r
);
385 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
386 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
389 if (name
[0] == '.' && !arg_all
)
392 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
395 images
[n_images
].name
= name
;
396 images
[n_images
].type
= type
;
397 images
[n_images
].read_only
= read_only
;
398 images
[n_images
].crtime
= crtime
;
399 images
[n_images
].mtime
= mtime
;
400 images
[n_images
].size
= size
;
411 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
417 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
422 if (size
!= (uint64_t) -1) {
423 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
431 return bus_log_parse_error(r
);
433 r
= sd_bus_message_exit_container(reply
);
435 return bus_log_parse_error(r
);
437 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
439 if (arg_legend
&& n_images
> 0)
440 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
441 (int) max_name
, "NAME",
442 (int) max_type
, "TYPE",
444 (int) max_size
, "USAGE",
445 (int) max_crtime
, "CREATED",
446 (int) max_mtime
, "MODIFIED");
448 for (j
= 0; j
< n_images
; j
++) {
449 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
451 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
452 (int) max_name
, images
[j
].name
,
453 (int) max_type
, images
[j
].type
,
454 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_normal() : "",
455 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
456 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
457 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
462 printf("\n%zu images listed.\n", n_images
);
464 printf("No images.\n");
470 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
471 _cleanup_free_
char *cgroup
= NULL
;
472 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
479 r
= show_cgroup_get_unit_path_and_warn(bus
, unit
, &cgroup
);
492 r
= unit_show_processes(bus
, unit
, cgroup
, "\t\t ", c
, get_output_flags(), &error
);
495 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
498 /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
500 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
) != 0 && leader
<= 0)
503 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, &leader
, leader
> 0, get_output_flags());
505 return log_error_errno(r
, "Failed to dump process list: %s", bus_error_message(&error
, r
));
510 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
, int n_addr
) {
511 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
512 _cleanup_free_
char *addresses
= NULL
;
513 bool truncate
= false;
522 r
= sd_bus_call_method(bus
,
523 "org.freedesktop.machine1",
524 "/org/freedesktop/machine1",
525 "org.freedesktop.machine1.Manager",
526 "GetMachineAddresses",
533 addresses
= strdup(prefix
);
538 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
540 return bus_log_parse_error(r
);
542 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
546 char buf_ifi
[DECIMAL_STR_MAX(int) + 2], buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
548 r
= sd_bus_message_read(reply
, "i", &family
);
550 return bus_log_parse_error(r
);
552 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
554 return bus_log_parse_error(r
);
557 if (family
== AF_INET6
&& ifi
> 0)
558 xsprintf(buf_ifi
, "%%%i", ifi
);
562 if (!strextend(&addresses
, prefix
, inet_ntop(family
, a
, buffer
, sizeof(buffer
)), buf_ifi
, NULL
))
567 r
= sd_bus_message_exit_container(reply
);
569 return bus_log_parse_error(r
);
571 if (prefix
!= prefix2
)
580 return bus_log_parse_error(r
);
582 r
= sd_bus_message_exit_container(reply
);
584 return bus_log_parse_error(r
);
587 fprintf(stdout
, "%s%s", addresses
, truncate
? "..." : "");
592 static int print_os_release(sd_bus
*bus
, const char *method
, const char *name
, const char *prefix
) {
593 _cleanup_free_
char *pretty
= NULL
;
600 r
= call_get_os_release(bus
, method
, name
, "PRETTY_NAME\0", &pretty
, NULL
);
605 printf("%s%s\n", prefix
, pretty
);
610 static int print_uid_shift(sd_bus
*bus
, const char *name
) {
611 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
612 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
619 r
= sd_bus_call_method(bus
,
620 "org.freedesktop.machine1",
621 "/org/freedesktop/machine1",
622 "org.freedesktop.machine1.Manager",
623 "GetMachineUIDShift",
628 return log_debug_errno(r
, "Failed to query UID/GID shift: %s", bus_error_message(&error
, r
));
630 r
= sd_bus_message_read(reply
, "u", &shift
);
634 if (shift
== 0) /* Don't show trivial mappings */
637 printf(" UID Shift: %" PRIu32
"\n", shift
);
641 typedef struct MachineStatusInfo
{
647 const char *root_directory
;
649 struct dual_timestamp timestamp
;
654 static void machine_status_info_clear(MachineStatusInfo
*info
) {
661 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
662 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
663 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
669 fputs(strna(i
->name
), stdout
);
671 if (!sd_id128_is_null(i
->id
))
672 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
676 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
677 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
680 printf("\t Since: %s; %s\n", s2
, s1
);
682 printf("\t Since: %s\n", s2
);
685 _cleanup_free_
char *t
= NULL
;
687 printf("\t Leader: %u", (unsigned) i
->leader
);
689 get_process_comm(i
->leader
, &t
);
697 printf("\t Service: %s", i
->service
);
700 printf("; class %s", i
->class);
704 printf("\t Class: %s\n", i
->class);
706 if (i
->root_directory
)
707 printf("\t Root: %s\n", i
->root_directory
);
709 if (i
->n_netif
> 0) {
712 fputs("\t Iface:", stdout
);
714 for (c
= 0; c
< i
->n_netif
; c
++) {
715 char name
[IF_NAMESIZE
+1] = "";
717 if (if_indextoname(i
->netif
[c
], name
)) {
726 printf(" %i", i
->netif
[c
]);
732 if (print_addresses(bus
, i
->name
, ifi
,
735 ALL_IP_ADDRESSES
) > 0)
738 print_os_release(bus
, "GetMachineOSRelease", i
->name
, "\t OS: ");
740 print_uid_shift(bus
, i
->name
);
743 printf("\t Unit: %s\n", i
->unit
);
744 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
746 if (arg_transport
== BUS_TRANSPORT_LOCAL
)
748 show_journal_by_unit(
753 i
->timestamp
.monotonic
,
756 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
757 SD_JOURNAL_LOCAL_ONLY
,
763 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
764 MachineStatusInfo
*i
= userdata
;
769 assert_cc(sizeof(int32_t) == sizeof(int));
770 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
776 i
->n_netif
= l
/ sizeof(int32_t);
777 i
->netif
= memdup(v
, l
);
784 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
786 static const struct bus_properties_map map
[] = {
787 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
788 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
789 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
790 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
791 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
792 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
793 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
794 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
795 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
796 { "NetworkInterfaces", "ai", map_netif
, 0 },
800 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
801 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
802 _cleanup_(machine_status_info_clear
) MachineStatusInfo info
= {};
810 r
= bus_map_all_properties(bus
,
811 "org.freedesktop.machine1",
819 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
825 print_machine_status_info(bus
, &info
);
830 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
842 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, NULL
, arg_property
, arg_value
, arg_all
, NULL
);
844 log_error_errno(r
, "Could not get properties: %m");
849 static int show_machine(int argc
, char *argv
[], void *userdata
) {
851 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
852 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
853 bool properties
, new_line
= false;
854 sd_bus
*bus
= userdata
;
859 properties
= !strstr(argv
[0], "status");
861 (void) pager_open(arg_no_pager
, false);
863 if (properties
&& argc
<= 1) {
865 /* If no argument is specified, inspect the manager
867 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
872 for (i
= 1; i
< argc
; i
++) {
873 const char *path
= NULL
;
875 r
= sd_bus_call_method(bus
,
876 "org.freedesktop.machine1",
877 "/org/freedesktop/machine1",
878 "org.freedesktop.machine1.Manager",
884 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
888 r
= sd_bus_message_read(reply
, "o", &path
);
890 return bus_log_parse_error(r
);
893 r
= show_machine_properties(bus
, path
, &new_line
);
895 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
901 static int print_image_hostname(sd_bus
*bus
, const char *name
) {
902 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
906 r
= sd_bus_call_method(
908 "org.freedesktop.machine1",
909 "/org/freedesktop/machine1",
910 "org.freedesktop.machine1.Manager",
912 NULL
, &reply
, "s", name
);
916 r
= sd_bus_message_read(reply
, "s", &hn
);
921 printf("\tHostname: %s\n", hn
);
926 static int print_image_machine_id(sd_bus
*bus
, const char *name
) {
927 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
928 sd_id128_t id
= SD_ID128_NULL
;
933 r
= sd_bus_call_method(
935 "org.freedesktop.machine1",
936 "/org/freedesktop/machine1",
937 "org.freedesktop.machine1.Manager",
939 NULL
, &reply
, "s", name
);
943 r
= sd_bus_message_read_array(reply
, 'y', &p
, &size
);
947 if (size
== sizeof(sd_id128_t
))
948 memcpy(&id
, p
, size
);
950 if (!sd_id128_is_null(id
))
951 printf(" Machine ID: " SD_ID128_FORMAT_STR
"\n", SD_ID128_FORMAT_VAL(id
));
956 static int print_image_machine_info(sd_bus
*bus
, const char *name
) {
957 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
960 r
= sd_bus_call_method(
962 "org.freedesktop.machine1",
963 "/org/freedesktop/machine1",
964 "org.freedesktop.machine1.Manager",
965 "GetImageMachineInfo",
966 NULL
, &reply
, "s", name
);
970 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
977 r
= sd_bus_message_read(reply
, "{ss}", &p
, &q
);
983 if (streq(p
, "DEPLOYMENT"))
984 printf(" Deployment: %s\n", q
);
987 r
= sd_bus_message_exit_container(reply
);
994 typedef struct ImageStatusInfo
{
1003 uint64_t usage_exclusive
;
1004 uint64_t limit_exclusive
;
1007 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
1008 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
1009 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
1010 char bs
[FORMAT_BYTES_MAX
], *s3
;
1011 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
1017 fputs(i
->name
, stdout
);
1022 printf("\t Type: %s\n", i
->type
);
1025 printf("\t Path: %s\n", i
->path
);
1027 (void) print_image_hostname(bus
, i
->name
);
1028 (void) print_image_machine_id(bus
, i
->name
);
1029 (void) print_image_machine_info(bus
, i
->name
);
1031 print_os_release(bus
, "GetImageOSRelease", i
->name
, "\t OS: ");
1033 printf("\t RO: %s%s%s\n",
1034 i
->read_only
? ansi_highlight_red() : "",
1035 i
->read_only
? "read-only" : "writable",
1036 i
->read_only
? ansi_normal() : "");
1038 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
1039 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
1041 printf("\t Created: %s; %s\n", s2
, s1
);
1043 printf("\t Created: %s\n", s2
);
1045 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
1046 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
1048 printf("\tModified: %s; %s\n", s2
, s1
);
1050 printf("\tModified: %s\n", s2
);
1052 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
1053 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
1055 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
1057 printf("\t Usage: %s\n", s3
);
1059 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
1060 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
1062 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
1064 printf("\t Limit: %s\n", s3
);
1067 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
1069 static const struct bus_properties_map map
[] = {
1070 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
1071 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
1072 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
1073 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
1074 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
1075 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
1076 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
1077 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
1078 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
1079 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
1083 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1084 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1085 ImageStatusInfo info
= {};
1092 r
= bus_map_all_properties(bus
,
1093 "org.freedesktop.machine1",
1096 BUS_MAP_BOOLEAN_AS_BOOL
,
1101 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
1107 print_image_status_info(bus
, &info
);
1112 typedef struct PoolStatusInfo
{
1118 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
1119 char bs
[FORMAT_BYTES_MAX
], *s
;
1122 printf("\t Path: %s\n", i
->path
);
1124 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
1126 printf("\t Usage: %s\n", s
);
1128 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
1130 printf("\t Limit: %s\n", s
);
1133 static int show_pool_info(sd_bus
*bus
) {
1135 static const struct bus_properties_map map
[] = {
1136 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
1137 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
1138 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
1142 PoolStatusInfo info
= {
1143 .usage
= (uint64_t) -1,
1144 .limit
= (uint64_t) -1,
1147 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1148 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1153 r
= bus_map_all_properties(bus
,
1154 "org.freedesktop.machine1",
1155 "/org/freedesktop/machine1",
1162 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
1164 print_pool_status_info(bus
, &info
);
1170 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
1182 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, NULL
, arg_property
, arg_value
, arg_all
, NULL
);
1184 log_error_errno(r
, "Could not get properties: %m");
1189 static int show_image(int argc
, char *argv
[], void *userdata
) {
1191 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1192 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1193 bool properties
, new_line
= false;
1194 sd_bus
*bus
= userdata
;
1199 properties
= !strstr(argv
[0], "status");
1201 (void) pager_open(arg_no_pager
, false);
1205 /* If no argument is specified, inspect the manager
1209 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
1211 r
= show_pool_info(bus
);
1216 for (i
= 1; i
< argc
; i
++) {
1217 const char *path
= NULL
;
1219 r
= sd_bus_call_method(
1221 "org.freedesktop.machine1",
1222 "/org/freedesktop/machine1",
1223 "org.freedesktop.machine1.Manager",
1229 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
1233 r
= sd_bus_message_read(reply
, "o", &path
);
1235 return bus_log_parse_error(r
);
1238 r
= show_image_properties(bus
, path
, &new_line
);
1240 r
= show_image_info(bus
, path
, &new_line
);
1246 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1247 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1248 sd_bus
*bus
= userdata
;
1253 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1256 arg_kill_who
= "all";
1258 for (i
= 1; i
< argc
; i
++) {
1259 r
= sd_bus_call_method(
1261 "org.freedesktop.machine1",
1262 "/org/freedesktop/machine1",
1263 "org.freedesktop.machine1.Manager",
1267 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1269 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1277 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1278 arg_kill_who
= "leader";
1279 arg_signal
= SIGINT
; /* sysvinit + systemd */
1281 return kill_machine(argc
, argv
, userdata
);
1284 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1285 arg_kill_who
= "leader";
1286 arg_signal
= SIGRTMIN
+4; /* only systemd */
1288 return kill_machine(argc
, argv
, userdata
);
1291 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1292 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1293 sd_bus
*bus
= userdata
;
1298 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1300 for (i
= 1; i
< argc
; i
++) {
1301 r
= sd_bus_call_method(
1303 "org.freedesktop.machine1",
1304 "/org/freedesktop/machine1",
1305 "org.freedesktop.machine1.Manager",
1311 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1319 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1320 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1321 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1322 _cleanup_free_
char *abs_host_path
= NULL
;
1323 char *dest
, *host_path
, *container_path
;
1324 sd_bus
*bus
= userdata
;
1330 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1332 copy_from
= streq(argv
[0], "copy-from");
1333 dest
= argv
[3] ?: argv
[2];
1334 host_path
= copy_from
? dest
: argv
[2];
1335 container_path
= copy_from
? argv
[2] : dest
;
1337 if (!path_is_absolute(host_path
)) {
1338 r
= path_make_absolute_cwd(host_path
, &abs_host_path
);
1340 return log_error_errno(r
, "Failed to make path absolute: %m");
1342 host_path
= abs_host_path
;
1345 r
= sd_bus_message_new_method_call(
1348 "org.freedesktop.machine1",
1349 "/org/freedesktop/machine1",
1350 "org.freedesktop.machine1.Manager",
1351 copy_from
? "CopyFromMachine" : "CopyToMachine");
1353 return bus_log_create_error(r
);
1355 r
= sd_bus_message_append(
1359 copy_from
? container_path
: host_path
,
1360 copy_from
? host_path
: container_path
);
1362 return bus_log_create_error(r
);
1364 /* This is a slow operation, hence turn off any method call timeouts */
1365 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1367 return log_error_errno(r
, "Failed to copy: %s", bus_error_message(&error
, r
));
1372 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1373 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1374 sd_bus
*bus
= userdata
;
1379 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1381 r
= sd_bus_call_method(
1383 "org.freedesktop.machine1",
1384 "/org/freedesktop/machine1",
1385 "org.freedesktop.machine1.Manager",
1396 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1403 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1404 PTYForward
** forward
= (PTYForward
**) userdata
;
1411 /* If the forwarder is already initialized, tell it to
1412 * exit on the next vhangup(), so that we still flush
1413 * out what might be queued and exit then. */
1415 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1419 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1422 /* On error, or when the forwarder is not initialized yet, quit immediately */
1423 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1427 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, PTYForwardFlags flags
, const char *name
) {
1433 assert(master
>= 0);
1436 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1439 if (streq(name
, ".host"))
1440 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1442 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1445 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1446 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1448 r
= pty_forward_new(event
, master
, flags
, forward
);
1450 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1452 r
= sd_event_loop(event
);
1454 return log_error_errno(r
, "Failed to run event loop: %m");
1456 pty_forward_get_last_char(*forward
, &last_char
);
1459 (flags
& PTY_FORWARD_IGNORE_VHANGUP
) &&
1460 pty_forward_get_ignore_vhangup(*forward
) == 0;
1462 *forward
= pty_forward_free(*forward
);
1464 if (last_char
!= '\n')
1465 fputc('\n', stdout
);
1469 log_info("Machine %s terminated.", name
);
1470 else if (streq(name
, ".host"))
1471 log_info("Connection to the local host terminated.");
1473 log_info("Connection to machine %s terminated.", name
);
1476 sd_event_get_exit_code(event
, &ret
);
1480 static int parse_machine_uid(const char *spec
, const char **machine
, char **uid
) {
1482 * Whatever is specified in the spec takes priority over global arguments.
1485 const char *_machine
= NULL
;
1490 at
= strchr(spec
, '@');
1493 /* Do the same as ssh and refuse "@host". */
1497 _uid
= strndup(spec
, at
- spec
);
1504 if (arg_uid
&& !_uid
) {
1505 _uid
= strdup(arg_uid
);
1511 *machine
= isempty(_machine
) ? ".host" : _machine
;
1515 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1516 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1517 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1518 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1519 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot
= NULL
;
1520 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
1522 sd_bus
*bus
= userdata
;
1523 const char *pty
, *match
, *machine
;
1527 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1528 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1532 if (!IN_SET(arg_transport
, BUS_TRANSPORT_LOCAL
, BUS_TRANSPORT_MACHINE
)) {
1533 log_error("Login only supported on local machines.");
1537 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1539 r
= sd_event_default(&event
);
1541 return log_error_errno(r
, "Failed to get event loop: %m");
1543 r
= sd_bus_attach_event(bus
, event
, 0);
1545 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1547 machine
= argc
< 2 || isempty(argv
[1]) ? ".host" : argv
[1];
1549 match
= strjoina("type='signal',"
1550 "sender='org.freedesktop.machine1',"
1551 "path='/org/freedesktop/machine1',",
1552 "interface='org.freedesktop.machine1.Manager',"
1553 "member='MachineRemoved',"
1554 "arg0='", machine
, "'");
1556 r
= sd_bus_add_match_async(bus
, &slot
, match
, on_machine_removed
, NULL
, &forward
);
1558 return log_error_errno(r
, "Failed to request machine removal match: %m");
1560 r
= sd_bus_call_method(
1562 "org.freedesktop.machine1",
1563 "/org/freedesktop/machine1",
1564 "org.freedesktop.machine1.Manager",
1570 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1574 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1576 return bus_log_parse_error(r
);
1578 return process_forward(event
, &forward
, master
, PTY_FORWARD_IGNORE_VHANGUP
, machine
);
1581 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1582 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
1583 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1584 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1585 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot
= NULL
;
1586 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
1588 sd_bus
*bus
= userdata
;
1589 const char *pty
, *match
, *machine
, *path
;
1590 _cleanup_free_
char *uid
= NULL
;
1594 if (!IN_SET(arg_transport
, BUS_TRANSPORT_LOCAL
, BUS_TRANSPORT_MACHINE
)) {
1595 log_error("Shell only supported on local machines.");
1599 /* Pass $TERM to shell session, if not explicitly specified. */
1600 if (!strv_find_prefix(arg_setenv
, "TERM=")) {
1603 t
= strv_find_prefix(environ
, "TERM=");
1605 if (strv_extend(&arg_setenv
, t
) < 0)
1610 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1612 r
= sd_event_default(&event
);
1614 return log_error_errno(r
, "Failed to get event loop: %m");
1616 r
= sd_bus_attach_event(bus
, event
, 0);
1618 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1620 r
= parse_machine_uid(argc
>= 2 ? argv
[1] : NULL
, &machine
, &uid
);
1622 return log_error_errno(r
, "Failed to parse machine specification: %m");
1624 match
= strjoina("type='signal',"
1625 "sender='org.freedesktop.machine1',"
1626 "path='/org/freedesktop/machine1',",
1627 "interface='org.freedesktop.machine1.Manager',"
1628 "member='MachineRemoved',"
1629 "arg0='", machine
, "'");
1631 r
= sd_bus_add_match_async(bus
, &slot
, match
, on_machine_removed
, NULL
, &forward
);
1633 return log_error_errno(r
, "Failed to request machine removal match: %m");
1635 r
= sd_bus_message_new_method_call(
1638 "org.freedesktop.machine1",
1639 "/org/freedesktop/machine1",
1640 "org.freedesktop.machine1.Manager",
1641 "OpenMachineShell");
1643 return bus_log_create_error(r
);
1645 path
= argc
< 3 || isempty(argv
[2]) ? NULL
: argv
[2];
1647 r
= sd_bus_message_append(m
, "sss", machine
, uid
, path
);
1649 return bus_log_create_error(r
);
1651 r
= sd_bus_message_append_strv(m
, strv_length(argv
) <= 3 ? NULL
: argv
+ 2);
1653 return bus_log_create_error(r
);
1655 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1657 return bus_log_create_error(r
);
1659 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1661 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1665 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1667 return bus_log_parse_error(r
);
1669 return process_forward(event
, &forward
, master
, 0, machine
);
1672 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1673 sd_bus
*bus
= userdata
;
1678 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1680 for (i
= 1; i
< argc
; i
++) {
1681 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1682 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1684 r
= sd_bus_message_new_method_call(
1687 "org.freedesktop.machine1",
1688 "/org/freedesktop/machine1",
1689 "org.freedesktop.machine1.Manager",
1692 return bus_log_create_error(r
);
1694 r
= sd_bus_message_append(m
, "s", argv
[i
]);
1696 return bus_log_create_error(r
);
1698 /* This is a slow operation, hence turn off any method call timeouts */
1699 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1701 return log_error_errno(r
, "Could not remove image: %s", bus_error_message(&error
, r
));
1707 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1708 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1709 sd_bus
*bus
= userdata
;
1714 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1716 r
= sd_bus_call_method(
1718 "org.freedesktop.machine1",
1719 "/org/freedesktop/machine1",
1720 "org.freedesktop.machine1.Manager",
1724 "ss", argv
[1], argv
[2]);
1726 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1733 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1734 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1735 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1736 sd_bus
*bus
= userdata
;
1741 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1743 r
= sd_bus_message_new_method_call(
1746 "org.freedesktop.machine1",
1747 "/org/freedesktop/machine1",
1748 "org.freedesktop.machine1.Manager",
1751 return bus_log_create_error(r
);
1753 r
= sd_bus_message_append(m
, "ssb", argv
[1], argv
[2], arg_read_only
);
1755 return bus_log_create_error(r
);
1757 /* This is a slow operation, hence turn off any method call timeouts */
1758 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1760 return log_error_errno(r
, "Could not clone image: %s", bus_error_message(&error
, r
));
1765 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1766 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1767 sd_bus
*bus
= userdata
;
1773 b
= parse_boolean(argv
[2]);
1775 log_error("Failed to parse boolean argument: %s", argv
[2]);
1780 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1782 r
= sd_bus_call_method(
1784 "org.freedesktop.machine1",
1785 "/org/freedesktop/machine1",
1786 "org.freedesktop.machine1.Manager",
1787 "MarkImageReadOnly",
1792 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1799 static int image_exists(sd_bus
*bus
, const char *name
) {
1800 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1806 r
= sd_bus_call_method(
1808 "org.freedesktop.machine1",
1809 "/org/freedesktop/machine1",
1810 "org.freedesktop.machine1.Manager",
1816 if (sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_IMAGE
))
1819 return log_error_errno(r
, "Failed to check whether image %s exists: %s", name
, bus_error_message(&error
, -r
));
1825 static int make_service_name(const char *name
, char **ret
) {
1831 if (!machine_name_is_valid(name
)) {
1832 log_error("Invalid machine name %s.", name
);
1836 r
= unit_name_build("systemd-nspawn", name
, ".service", ret
);
1838 return log_error_errno(r
, "Failed to build unit name: %m");
1843 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1844 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1845 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1846 sd_bus
*bus
= userdata
;
1851 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1853 r
= bus_wait_for_jobs_new(bus
, &w
);
1857 for (i
= 1; i
< argc
; i
++) {
1858 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1859 _cleanup_free_
char *unit
= NULL
;
1862 r
= make_service_name(argv
[i
], &unit
);
1866 r
= image_exists(bus
, argv
[i
]);
1870 log_error("Machine image '%s' does not exist.", argv
[1]);
1874 r
= sd_bus_call_method(
1876 "org.freedesktop.systemd1",
1877 "/org/freedesktop/systemd1",
1878 "org.freedesktop.systemd1.Manager",
1882 "ss", unit
, "fail");
1884 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1888 r
= sd_bus_message_read(reply
, "o", &object
);
1890 return bus_log_parse_error(r
);
1892 r
= bus_wait_for_jobs_add(w
, object
);
1897 r
= bus_wait_for_jobs(w
, arg_quiet
, NULL
);
1904 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1905 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1906 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1907 UnitFileChange
*changes
= NULL
;
1908 unsigned n_changes
= 0;
1909 int carries_install_info
= 0;
1910 const char *method
= NULL
;
1911 sd_bus
*bus
= userdata
;
1916 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1918 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1920 r
= sd_bus_message_new_method_call(
1923 "org.freedesktop.systemd1",
1924 "/org/freedesktop/systemd1",
1925 "org.freedesktop.systemd1.Manager",
1928 return bus_log_create_error(r
);
1930 r
= sd_bus_message_open_container(m
, 'a', "s");
1932 return bus_log_create_error(r
);
1934 for (i
= 1; i
< argc
; i
++) {
1935 _cleanup_free_
char *unit
= NULL
;
1937 r
= make_service_name(argv
[i
], &unit
);
1941 r
= image_exists(bus
, argv
[i
]);
1945 log_error("Machine image '%s' does not exist.", argv
[1]);
1949 r
= sd_bus_message_append(m
, "s", unit
);
1951 return bus_log_create_error(r
);
1954 r
= sd_bus_message_close_container(m
);
1956 return bus_log_create_error(r
);
1958 if (streq(argv
[0], "enable"))
1959 r
= sd_bus_message_append(m
, "bb", false, false);
1961 r
= sd_bus_message_append(m
, "b", false);
1963 return bus_log_create_error(r
);
1965 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1967 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1971 if (streq(argv
[0], "enable")) {
1972 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1974 return bus_log_parse_error(r
);
1977 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, &changes
, &n_changes
);
1981 r
= sd_bus_call_method(
1983 "org.freedesktop.systemd1",
1984 "/org/freedesktop/systemd1",
1985 "org.freedesktop.systemd1.Manager",
1991 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1998 unit_file_changes_free(changes
, n_changes
);
2003 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
2004 const char **our_path
= userdata
, *line
;
2011 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
2013 bus_log_parse_error(r
);
2017 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
2020 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
2023 log_full(priority
, "%s", line
);
2027 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
2028 const char **our_path
= userdata
, *path
, *result
;
2035 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
2037 bus_log_parse_error(r
);
2041 if (!streq_ptr(*our_path
, path
))
2044 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
2048 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
2053 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
2055 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
2059 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
2060 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
2061 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2062 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2063 _cleanup_(sd_event_unrefp
) sd_event
* event
= NULL
;
2064 const char *path
= NULL
;
2071 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2073 r
= sd_event_default(&event
);
2075 return log_error_errno(r
, "Failed to get event loop: %m");
2077 r
= sd_bus_attach_event(bus
, event
, 0);
2079 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
2081 r
= sd_bus_match_signal_async(
2084 "org.freedesktop.import1",
2085 "/org/freedesktop/import1",
2086 "org.freedesktop.import1.Manager",
2088 match_transfer_removed
, NULL
, &path
);
2090 return log_error_errno(r
, "Failed to request match: %m");
2092 r
= sd_bus_match_signal_async(
2095 "org.freedesktop.import1",
2097 "org.freedesktop.import1.Transfer",
2099 match_log_message
, NULL
, &path
);
2101 return log_error_errno(r
, "Failed to request match: %m");
2103 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
2105 log_error("Failed to transfer image: %s", bus_error_message(&error
, -r
));
2109 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
2111 return bus_log_parse_error(r
);
2113 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
2116 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
2118 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
2119 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
2121 r
= sd_event_loop(event
);
2123 return log_error_errno(r
, "Failed to run event loop: %m");
2128 static int import_tar(int argc
, char *argv
[], void *userdata
) {
2129 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2130 _cleanup_free_
char *ll
= NULL
;
2131 _cleanup_close_
int fd
= -1;
2132 const char *local
= NULL
, *path
= NULL
;
2133 sd_bus
*bus
= userdata
;
2140 if (isempty(path
) || streq(path
, "-"))
2146 local
= basename(path
);
2147 if (isempty(local
) || streq(local
, "-"))
2151 log_error("Need either path or local name.");
2155 r
= tar_strip_suffixes(local
, &ll
);
2161 if (!machine_name_is_valid(local
)) {
2162 log_error("Local name %s is not a suitable machine name.", local
);
2167 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
2169 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2172 r
= sd_bus_message_new_method_call(
2175 "org.freedesktop.import1",
2176 "/org/freedesktop/import1",
2177 "org.freedesktop.import1.Manager",
2180 return bus_log_create_error(r
);
2182 r
= sd_bus_message_append(
2185 fd
>= 0 ? fd
: STDIN_FILENO
,
2190 return bus_log_create_error(r
);
2192 return transfer_image_common(bus
, m
);
2195 static int import_raw(int argc
, char *argv
[], void *userdata
) {
2196 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2197 _cleanup_free_
char *ll
= NULL
;
2198 _cleanup_close_
int fd
= -1;
2199 const char *local
= NULL
, *path
= NULL
;
2200 sd_bus
*bus
= userdata
;
2207 if (isempty(path
) || streq(path
, "-"))
2213 local
= basename(path
);
2214 if (isempty(local
) || streq(local
, "-"))
2218 log_error("Need either path or local name.");
2222 r
= raw_strip_suffixes(local
, &ll
);
2228 if (!machine_name_is_valid(local
)) {
2229 log_error("Local name %s is not a suitable machine name.", local
);
2234 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
2236 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2239 r
= sd_bus_message_new_method_call(
2242 "org.freedesktop.import1",
2243 "/org/freedesktop/import1",
2244 "org.freedesktop.import1.Manager",
2247 return bus_log_create_error(r
);
2249 r
= sd_bus_message_append(
2252 fd
>= 0 ? fd
: STDIN_FILENO
,
2257 return bus_log_create_error(r
);
2259 return transfer_image_common(bus
, m
);
2262 static void determine_compression_from_filename(const char *p
) {
2269 if (endswith(p
, ".xz"))
2271 else if (endswith(p
, ".gz"))
2272 arg_format
= "gzip";
2273 else if (endswith(p
, ".bz2"))
2274 arg_format
= "bzip2";
2277 static int export_tar(int argc
, char *argv
[], void *userdata
) {
2278 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2279 _cleanup_close_
int fd
= -1;
2280 const char *local
= NULL
, *path
= NULL
;
2281 sd_bus
*bus
= userdata
;
2287 if (!machine_name_is_valid(local
)) {
2288 log_error("Machine name %s is not valid.", local
);
2294 if (isempty(path
) || streq(path
, "-"))
2298 determine_compression_from_filename(path
);
2300 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2302 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2305 r
= sd_bus_message_new_method_call(
2308 "org.freedesktop.import1",
2309 "/org/freedesktop/import1",
2310 "org.freedesktop.import1.Manager",
2313 return bus_log_create_error(r
);
2315 r
= sd_bus_message_append(
2319 fd
>= 0 ? fd
: STDOUT_FILENO
,
2322 return bus_log_create_error(r
);
2324 return transfer_image_common(bus
, m
);
2327 static int export_raw(int argc
, char *argv
[], void *userdata
) {
2328 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2329 _cleanup_close_
int fd
= -1;
2330 const char *local
= NULL
, *path
= NULL
;
2331 sd_bus
*bus
= userdata
;
2337 if (!machine_name_is_valid(local
)) {
2338 log_error("Machine name %s is not valid.", local
);
2344 if (isempty(path
) || streq(path
, "-"))
2348 determine_compression_from_filename(path
);
2350 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2352 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2355 r
= sd_bus_message_new_method_call(
2358 "org.freedesktop.import1",
2359 "/org/freedesktop/import1",
2360 "org.freedesktop.import1.Manager",
2363 return bus_log_create_error(r
);
2365 r
= sd_bus_message_append(
2369 fd
>= 0 ? fd
: STDOUT_FILENO
,
2372 return bus_log_create_error(r
);
2374 return transfer_image_common(bus
, m
);
2377 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2378 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2379 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2380 const char *local
, *remote
;
2381 sd_bus
*bus
= userdata
;
2387 if (!http_url_is_valid(remote
)) {
2388 log_error("URL '%s' is not valid.", remote
);
2395 r
= import_url_last_component(remote
, &l
);
2397 return log_error_errno(r
, "Failed to get final component of URL: %m");
2402 if (isempty(local
) || streq(local
, "-"))
2406 r
= tar_strip_suffixes(local
, &ll
);
2412 if (!machine_name_is_valid(local
)) {
2413 log_error("Local name %s is not a suitable machine name.", local
);
2418 r
= sd_bus_message_new_method_call(
2421 "org.freedesktop.import1",
2422 "/org/freedesktop/import1",
2423 "org.freedesktop.import1.Manager",
2426 return bus_log_create_error(r
);
2428 r
= sd_bus_message_append(
2433 import_verify_to_string(arg_verify
),
2436 return bus_log_create_error(r
);
2438 return transfer_image_common(bus
, m
);
2441 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2442 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2443 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2444 const char *local
, *remote
;
2445 sd_bus
*bus
= userdata
;
2451 if (!http_url_is_valid(remote
)) {
2452 log_error("URL '%s' is not valid.", remote
);
2459 r
= import_url_last_component(remote
, &l
);
2461 return log_error_errno(r
, "Failed to get final component of URL: %m");
2466 if (isempty(local
) || streq(local
, "-"))
2470 r
= raw_strip_suffixes(local
, &ll
);
2476 if (!machine_name_is_valid(local
)) {
2477 log_error("Local name %s is not a suitable machine name.", local
);
2482 r
= sd_bus_message_new_method_call(
2485 "org.freedesktop.import1",
2486 "/org/freedesktop/import1",
2487 "org.freedesktop.import1.Manager",
2490 return bus_log_create_error(r
);
2492 r
= sd_bus_message_append(
2497 import_verify_to_string(arg_verify
),
2500 return bus_log_create_error(r
);
2502 return transfer_image_common(bus
, m
);
2505 typedef struct TransferInfo
{
2513 static int compare_transfer_info(const void *a
, const void *b
) {
2514 const TransferInfo
*x
= a
, *y
= b
;
2516 return strcmp(x
->local
, y
->local
);
2519 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2520 size_t max_type
= STRLEN("TYPE"), max_local
= STRLEN("LOCAL"), max_remote
= STRLEN("REMOTE");
2521 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2522 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2523 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2524 size_t n_transfers
= 0, n_allocated
= 0, j
;
2525 const char *type
, *remote
, *local
, *object
;
2526 sd_bus
*bus
= userdata
;
2527 uint32_t id
, max_id
= 0;
2531 (void) pager_open(arg_no_pager
, false);
2533 r
= sd_bus_call_method(bus
,
2534 "org.freedesktop.import1",
2535 "/org/freedesktop/import1",
2536 "org.freedesktop.import1.Manager",
2542 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2546 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2548 return bus_log_parse_error(r
);
2550 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2553 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2556 transfers
[n_transfers
].id
= id
;
2557 transfers
[n_transfers
].type
= type
;
2558 transfers
[n_transfers
].remote
= remote
;
2559 transfers
[n_transfers
].local
= local
;
2560 transfers
[n_transfers
].progress
= progress
;
2580 return bus_log_parse_error(r
);
2582 r
= sd_bus_message_exit_container(reply
);
2584 return bus_log_parse_error(r
);
2586 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2588 if (arg_legend
&& n_transfers
> 0)
2589 printf("%-*s %-*s %-*s %-*s %-*s\n",
2590 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2592 (int) max_type
, "TYPE",
2593 (int) max_local
, "LOCAL",
2594 (int) max_remote
, "REMOTE");
2596 for (j
= 0; j
< n_transfers
; j
++)
2597 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2598 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2599 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2600 (int) max_type
, transfers
[j
].type
,
2601 (int) max_local
, transfers
[j
].local
,
2602 (int) max_remote
, transfers
[j
].remote
);
2605 if (n_transfers
> 0)
2606 printf("\n%zu transfers listed.\n", n_transfers
);
2608 printf("No transfers.\n");
2614 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2615 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2616 sd_bus
*bus
= userdata
;
2621 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2623 for (i
= 1; i
< argc
; i
++) {
2626 r
= safe_atou32(argv
[i
], &id
);
2628 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2630 r
= sd_bus_call_method(
2632 "org.freedesktop.import1",
2633 "/org/freedesktop/import1",
2634 "org.freedesktop.import1.Manager",
2640 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2648 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2649 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2650 sd_bus
*bus
= userdata
;
2654 if (STR_IN_SET(argv
[argc
-1], "-", "none", "infinity"))
2655 limit
= (uint64_t) -1;
2657 r
= parse_size(argv
[argc
-1], 1024, &limit
);
2659 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2663 /* With two arguments changes the quota limit of the
2664 * specified image */
2665 r
= sd_bus_call_method(
2667 "org.freedesktop.machine1",
2668 "/org/freedesktop/machine1",
2669 "org.freedesktop.machine1.Manager",
2673 "st", argv
[1], limit
);
2675 /* With one argument changes the pool quota limit */
2676 r
= sd_bus_call_method(
2678 "org.freedesktop.machine1",
2679 "/org/freedesktop/machine1",
2680 "org.freedesktop.machine1.Manager",
2687 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2694 static int clean_images(int argc
, char *argv
[], void *userdata
) {
2695 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2696 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2697 uint64_t usage
, total
= 0;
2698 char fb
[FORMAT_BYTES_MAX
];
2699 sd_bus
*bus
= userdata
;
2704 r
= sd_bus_message_new_method_call(
2707 "org.freedesktop.machine1",
2708 "/org/freedesktop/machine1",
2709 "org.freedesktop.machine1.Manager",
2712 return bus_log_create_error(r
);
2714 r
= sd_bus_message_append(m
, "s", arg_all
? "all" : "hidden");
2716 return bus_log_create_error(r
);
2718 /* This is a slow operation, hence permit a longer time for completion. */
2719 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, &reply
);
2721 return log_error_errno(r
, "Could not clean pool: %s", bus_error_message(&error
, r
));
2723 r
= sd_bus_message_enter_container(reply
, 'a', "(st)");
2725 return bus_log_parse_error(r
);
2727 while ((r
= sd_bus_message_read(reply
, "(st)", &name
, &usage
)) > 0) {
2728 log_info("Removed image '%s'. Freed exclusive disk space: %s",
2729 name
, format_bytes(fb
, sizeof(fb
), usage
));
2735 r
= sd_bus_message_exit_container(reply
);
2737 return bus_log_parse_error(r
);
2739 log_info("Removed %u images in total. Total freed exclusive disk space %s.",
2740 c
, format_bytes(fb
, sizeof(fb
), total
));
2745 static int help(int argc
, char *argv
[], void *userdata
) {
2746 (void) pager_open(arg_no_pager
, false);
2748 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2749 "Send control commands to or query the virtual machine and container\n"
2750 "registration manager.\n\n"
2751 " -h --help Show this help\n"
2752 " --version Show package version\n"
2753 " --no-pager Do not pipe output into a pager\n"
2754 " --no-legend Do not show the headers and footers\n"
2755 " --no-ask-password Do not ask for system passwords\n"
2756 " -H --host=[USER@]HOST Operate on remote host\n"
2757 " -M --machine=CONTAINER Operate on local container\n"
2758 " -p --property=NAME Show only properties by this name\n"
2759 " -q --quiet Suppress output\n"
2760 " -a --all Show all properties, including empty ones\n"
2761 " --value When showing properties, only print the value\n"
2762 " -l --full Do not ellipsize output\n"
2763 " --kill-who=WHO Who to send signal to\n"
2764 " -s --signal=SIGNAL Which signal to send\n"
2765 " --uid=USER Specify user ID to invoke shell as\n"
2766 " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
2767 " --read-only Create read-only bind mount\n"
2768 " --mkdir Create directory before bind mounting, if missing\n"
2769 " -n --lines=INTEGER Number of journal entries to show\n"
2770 " --max-addresses=INTEGER Number of internet addresses to show at most\n"
2771 " -o --output=STRING Change journal output mode (short, short-precise,\n"
2772 " short-iso, short-iso-precise, short-full,\n"
2773 " short-monotonic, short-unix, verbose, export,\n"
2774 " json, json-pretty, json-sse, cat)\n"
2775 " --verify=MODE Verification mode for downloaded images (no,\n"
2776 " checksum, signature)\n"
2777 " --force Download image even if already exists\n\n"
2778 "Machine Commands:\n"
2779 " list List running VMs and containers\n"
2780 " status NAME... Show VM/container details\n"
2781 " show [NAME...] Show properties of one or more VMs/containers\n"
2782 " start NAME... Start container as a service\n"
2783 " login [NAME] Get a login prompt in a container or on the\n"
2785 " shell [[USER@]NAME [COMMAND...]]\n"
2786 " Invoke a shell (or other command) in a container\n"
2787 " or on the local host\n"
2788 " enable NAME... Enable automatic container start at boot\n"
2789 " disable NAME... Disable automatic container start at boot\n"
2790 " poweroff NAME... Power off one or more containers\n"
2791 " reboot NAME... Reboot one or more containers\n"
2792 " terminate NAME... Terminate one or more VMs/containers\n"
2793 " kill NAME... Send signal to processes of a VM/container\n"
2794 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2795 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2796 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2798 " list-images Show available container and VM images\n"
2799 " image-status [NAME...] Show image details\n"
2800 " show-image [NAME...] Show properties of image\n"
2801 " clone NAME NAME Clone an image\n"
2802 " rename NAME NAME Rename an image\n"
2803 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2804 " remove NAME... Remove an image\n"
2805 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
2806 " clean Remove hidden (or all) images\n\n"
2807 "Image Transfer Commands:\n"
2808 " pull-tar URL [NAME] Download a TAR container image\n"
2809 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2810 " import-tar FILE [NAME] Import a local TAR container image\n"
2811 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2812 " export-tar NAME [FILE] Export a TAR container image locally\n"
2813 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2814 " list-transfers Show list of downloads in progress\n"
2815 " cancel-transfer Cancel a download\n"
2816 , program_invocation_short_name
);
2821 static int parse_argv(int argc
, char *argv
[]) {
2824 ARG_VERSION
= 0x100,
2831 ARG_NO_ASK_PASSWORD
,
2839 static const struct option options
[] = {
2840 { "help", no_argument
, NULL
, 'h' },
2841 { "version", no_argument
, NULL
, ARG_VERSION
},
2842 { "property", required_argument
, NULL
, 'p' },
2843 { "all", no_argument
, NULL
, 'a' },
2844 { "value", no_argument
, NULL
, ARG_VALUE
},
2845 { "full", no_argument
, NULL
, 'l' },
2846 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2847 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2848 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2849 { "signal", required_argument
, NULL
, 's' },
2850 { "host", required_argument
, NULL
, 'H' },
2851 { "machine", required_argument
, NULL
, 'M' },
2852 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2853 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2854 { "quiet", no_argument
, NULL
, 'q' },
2855 { "lines", required_argument
, NULL
, 'n' },
2856 { "output", required_argument
, NULL
, 'o' },
2857 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2858 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2859 { "force", no_argument
, NULL
, ARG_FORCE
},
2860 { "format", required_argument
, NULL
, ARG_FORMAT
},
2861 { "uid", required_argument
, NULL
, ARG_UID
},
2862 { "setenv", required_argument
, NULL
, 'E' },
2863 { "max-addresses", required_argument
, NULL
, ARG_NUMBER_IPS
},
2867 bool reorder
= false;
2868 int c
, r
, shell
= -1;
2874 static const char option_string
[] = "-hp:als:H:M:qn:o:E:";
2876 c
= getopt_long(argc
, argv
, option_string
+ reorder
, options
, NULL
);
2882 case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a
2883 * non-option argument was discovered. */
2887 /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
2888 * for switches after the main verb. However, for "shell" we really don't want that, since we
2889 * want that switches specified after the machine name are passed to the program to execute,
2890 * and not processed by us. To make this possible, we'll first invoke getopt_long() with
2891 * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first
2892 * non-option parameter. If it's the verb "shell" we remember its position and continue
2893 * processing options. In this case, as soon as we hit the next non-option argument we found
2894 * the machine name, and stop further processing. If the first non-option argument is any other
2895 * verb than "shell" we switch to normal reordering mode and continue processing arguments
2899 /* If we already found the "shell" verb on the command line, and now found the next
2900 * non-option argument, then this is the machine name and we should stop processing
2901 * further arguments. */
2902 optind
--; /* don't process this argument, go one step back */
2905 if (streq(optarg
, "shell"))
2906 /* Remember the position of the "shell" verb, and continue processing normally. */
2911 /* OK, this is some other verb. In this case, turn on reordering again, and continue
2912 * processing normally. */
2915 /* We changed the option string. getopt_long() only looks at it again if we invoke it
2916 * at least once with a reset option index. Hence, let's reset the option index here,
2917 * then invoke getopt_long() again (ignoring what it has to say, after all we most
2918 * likely already processed it), and the bump the option index so that we read the
2919 * intended argument again. */
2920 saved_optind
= optind
;
2922 (void) getopt_long(argc
, argv
, option_string
+ reorder
, options
, NULL
);
2923 optind
= saved_optind
- 1; /* go one step back, process this argument again */
2929 return help(0, NULL
, NULL
);
2935 r
= strv_extend(&arg_property
, optarg
);
2939 /* If the user asked for a particular
2940 * property, show it to him, even if it is
2958 if (safe_atou(optarg
, &arg_lines
) < 0) {
2959 log_error("Failed to parse lines '%s'", optarg
);
2965 arg_output
= output_mode_from_string(optarg
);
2966 if (arg_output
< 0) {
2967 log_error("Unknown output '%s'.", optarg
);
2973 arg_no_pager
= true;
2981 arg_kill_who
= optarg
;
2985 arg_signal
= signal_from_string_try_harder(optarg
);
2986 if (arg_signal
< 0) {
2987 log_error("Failed to parse signal string %s.", optarg
);
2992 case ARG_NO_ASK_PASSWORD
:
2993 arg_ask_password
= false;
2997 arg_transport
= BUS_TRANSPORT_REMOTE
;
3002 arg_transport
= BUS_TRANSPORT_MACHINE
;
3007 arg_read_only
= true;
3019 arg_verify
= import_verify_from_string(optarg
);
3020 if (arg_verify
< 0) {
3021 log_error("Failed to parse --verify= setting: %s", optarg
);
3031 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
3032 log_error("Unknown format: %s", optarg
);
3036 arg_format
= optarg
;
3044 if (!env_assignment_is_valid(optarg
)) {
3045 log_error("Environment assignment invalid: %s", optarg
);
3049 r
= strv_extend(&arg_setenv
, optarg
);
3054 case ARG_NUMBER_IPS
:
3055 if (streq(optarg
, "all"))
3056 arg_addrs
= ALL_IP_ADDRESSES
;
3057 else if (safe_atoi(optarg
, &arg_addrs
) < 0) {
3058 log_error("Invalid number of IPs");
3060 } else if (arg_addrs
< 0) {
3061 log_error("Number of IPs cannot be negative");
3070 assert_not_reached("Unhandled option");
3079 /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
3080 * argument list initially let's readjust it now, and move the "shell" verb to the back. */
3082 optind
-= 1; /* place the option index where the "shell" verb will be placed */
3085 for (i
= shell
; i
< optind
; i
++)
3086 argv
[i
] = argv
[i
+1];
3093 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
3095 static const Verb verbs
[] = {
3096 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
3097 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
3098 { "list-images", VERB_ANY
, 1, 0, list_images
},
3099 { "status", 2, VERB_ANY
, 0, show_machine
},
3100 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
3101 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
3102 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
3103 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
3104 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
3105 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
3106 { "stop", 2, VERB_ANY
, 0, poweroff_machine
}, /* Convenience alias */
3107 { "kill", 2, VERB_ANY
, 0, kill_machine
},
3108 { "login", VERB_ANY
, 2, 0, login_machine
},
3109 { "shell", VERB_ANY
, VERB_ANY
, 0, shell_machine
},
3110 { "bind", 3, 4, 0, bind_mount
},
3111 { "copy-to", 3, 4, 0, copy_files
},
3112 { "copy-from", 3, 4, 0, copy_files
},
3113 { "remove", 2, VERB_ANY
, 0, remove_image
},
3114 { "rename", 3, 3, 0, rename_image
},
3115 { "clone", 3, 3, 0, clone_image
},
3116 { "read-only", 2, 3, 0, read_only_image
},
3117 { "start", 2, VERB_ANY
, 0, start_machine
},
3118 { "enable", 2, VERB_ANY
, 0, enable_machine
},
3119 { "disable", 2, VERB_ANY
, 0, enable_machine
},
3120 { "import-tar", 2, 3, 0, import_tar
},
3121 { "import-raw", 2, 3, 0, import_raw
},
3122 { "export-tar", 2, 3, 0, export_tar
},
3123 { "export-raw", 2, 3, 0, export_raw
},
3124 { "pull-tar", 2, 3, 0, pull_tar
},
3125 { "pull-raw", 2, 3, 0, pull_raw
},
3126 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
3127 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
3128 { "set-limit", 2, 3, 0, set_limit
},
3129 { "clean", VERB_ANY
, 1, 0, clean_images
},
3133 return dispatch_verb(argc
, argv
, verbs
, bus
);
3136 int main(int argc
, char*argv
[]) {
3140 setlocale(LC_ALL
, "");
3141 log_parse_environment();
3145 r
= parse_argv(argc
, argv
);
3149 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
3151 log_error_errno(r
, "Failed to create bus connection: %m");
3155 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
3157 r
= machinectl_main(argc
, argv
, bus
);
3160 /* make sure we terminate the bus connection first, and then close the
3161 * pager, see issue #3543 for the details. */
3162 sd_bus_flush_close_unref(bus
);
3164 polkit_agent_close();
3166 strv_free(arg_property
);
3167 strv_free(arg_setenv
);
3169 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;